Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c89a9ed
docs: define media kit publication and explicit picture selection
BASIC-BIT Oct 1, 2026
75f61cd
docs: plan media kit publication workflow
BASIC-BIT Oct 1, 2026
4b502d1
docs: bind correction to selection provenance
BASIC-BIT Oct 1, 2026
b1bc1f5
chore: ignore local development worktrees
BASIC-BIT Oct 1, 2026
8345c94
feat: publish media contributions into the kit
BASIC-BIT Oct 2, 2026
b7d82ee
fix: bind media inspection to external identity links
BASIC-BIT Oct 2, 2026
134e108
feat: separate profile picture selection from publication
BASIC-BIT Oct 5, 2026
c137c2d
fix: retain cleanup for uploaded identity proposals
BASIC-BIT Oct 5, 2026
3f08352
feat: manage own published media contributions
BASIC-BIT Oct 5, 2026
c9b8f8f
Merge remote-tracking branch 'origin/main' into codex/media-kit-publi…
BASIC-BIT Oct 5, 2026
abb94e3
feat: expose media kit contribution actions through MCP
BASIC-BIT Oct 5, 2026
a7df1b2
fix: describe media kit MCP actions
BASIC-BIT Oct 5, 2026
9d2e47e
feat: simplify media kit publication and picture actions
BASIC-BIT Oct 6, 2026
4151fce
feat: add bounded media kit conversion and lifecycle preparedness
BASIC-BIT Oct 6, 2026
a9d5fe1
fix(media): guard kit publication and private review placement
BASIC-BIT Oct 6, 2026
f282926
docs: quote recovery log pattern for MDX
BASIC-BIT Oct 6, 2026
106c534
Merge commit '3e267825ddd40838fc8df785367b924de66cafd1' into codex/me…
BASIC-BIT Oct 6, 2026
b3b4b37
fix: align contribution management capabilities with feature gates
BASIC-BIT Oct 6, 2026
b89889f
fix(media): admit gallery uploads and retain legacy provenance
BASIC-BIT Oct 6, 2026
11daf00
fix(media): gate placement and preserve draft inspection versions
BASIC-BIT Oct 13, 2026
f97c61d
fix: accept gallery media submission MCP results
BASIC-BIT Oct 13, 2026
cebb456
fix(media): recheck kit admission and candidate approval
BASIC-BIT Oct 13, 2026
4702c5d
fix(media): enforce replacement admission and scoped review
BASIC-BIT Oct 13, 2026
e34483a
Fix rendered identity replacement and asset-backed expiry
BASIC-BIT Oct 13, 2026
2133ada
Refuse replacement of hidden-only identity placements
BASIC-BIT Oct 13, 2026
786fa60
fix(convex): regenerate media submission cleanup API types
BASIC-BIT Oct 13, 2026
fec2860
Fix guarded media migration and lifecycle cooldown
BASIC-BIT Oct 13, 2026
1a26caa
fix(media): preserve review and provenance boundaries
BASIC-BIT Oct 13, 2026
0959c3a
fix(media): bound native review endpoint reads
BASIC-BIT Oct 13, 2026
4aca25e
test: assert exact public media asset credits
BASIC-BIT Oct 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ __pycache__/

# Claude Code personal settings
.claude/settings.local.json

# Local isolated development checkouts
.worktrees/
6 changes: 6 additions & 0 deletions apps/web/e2e/image-readiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { expect, type Locator } from "@playwright/test";

export async function expectDecodedImage(image: Locator, timeout = 15_000) {
await image.scrollIntoViewIfNeeded();
await expect.poll(() => image.evaluate((element: HTMLImageElement) => element.complete && element.naturalWidth > 0), { timeout }).toBe(true);
}
27 changes: 27 additions & 0 deletions apps/web/e2e/image-readiness.visual.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import sharp from "sharp";
import { expect, test } from "@playwright/test";
import { expectDecodedImage } from "./image-readiness";

test("image readiness waits for loading then actual decoded pixels @storybook-visual", async ({ page }) => {
let release!: () => void;
const ready = new Promise<void>(resolve => { release = resolve; });
const bytes = await sharp({ create: { width: 48, height: 32, channels: 3, background: "#173e35" } }).png().toBuffer();
await page.route("https://image.example/slow.png", async route => { await ready; await route.fulfill({ contentType: "image/png", body: bytes }); });
await page.setContent('<div style="height:2000px"></div><img loading="lazy" src="https://image.example/slow.png">');
const image = page.locator("img");
await image.scrollIntoViewIfNeeded();
expect(await image.evaluate((element: HTMLImageElement) => element.complete && element.naturalWidth > 0)).toBe(false);
const check = expectDecodedImage(image);
release();
await check;
expect(await image.evaluate((element: HTMLImageElement) => element.naturalWidth)).toBe(48);
});

test("image readiness still fails for broken pixels @storybook-visual", async ({ page }) => {
await page.route("https://image.example/broken.png", route => route.fulfill({ status: 404, body: "missing" }));
await page.setContent('<img src="https://image.example/broken.png">');
let failure: unknown;
try { await expectDecodedImage(page.locator("img"), 600); } catch (error) { failure = error; }
expect(failure).toBeInstanceOf(Error);
expect(await page.locator("img").evaluate((element: HTMLImageElement) => element.naturalWidth)).toBe(0);
});
18 changes: 18 additions & 0 deletions apps/web/e2e/media-admission.storybook.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expect, test } from "@playwright/test";
import { gotoComponentStory, prepareStorybookVisualPage, captureStorybookScreenshot } from "./storybook-components";

test.beforeEach(async ({ page }) => { await prepareStorybookVisualPage(page); });
test("available contribution form keeps the credit URL limit @storybook-visual", async ({ page }, testInfo) => {
await gotoComponentStory(page, "profiles-media-contribution-editor--inline");
await expect(page.getByRole("heading", { name: "Media contributions" })).toBeVisible();
await expect(page.locator('input[name="creditUrl"]')).toHaveAttribute("maxlength", "2048");
await captureStorybookScreenshot(page, testInfo, "contribution-intake-available");
});
test("unavailable kit hides contribution intake @storybook-visual", async ({ page }, testInfo) => {
await gotoComponentStory(page, "profiles-media-contribution-editor--unavailable");
await expect(page.getByRole("heading", { name: "Edit profile" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Media contributions" })).toHaveCount(0);
await expect(page.locator('input[type="file"]')).toHaveCount(0);
await expect(page.locator('input[name="creditUrl"]')).toHaveCount(0);
await captureStorybookScreenshot(page, testInfo, "contribution-intake-unavailable");
});
259 changes: 237 additions & 22 deletions apps/web/e2e/media-contribution.flow.spec.ts

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion apps/web/e2e/media-kit.visual.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ test("public profile media kit @visual @fixture", async ({ page }, testInfo) =>
mediaKit.getByRole("link", { name: "Artwork by Afterglow Studio" }),
).toHaveAttribute("href", "https://example.invalid/afterglow-studio");
await expect(
mediaKit.getByRole("link", { name: "https://example.invalid/aurora-source" }),
mediaKit.locator('a[href="https://example.invalid/aurora-source"]'),
).toHaveAttribute("href", "https://example.invalid/aurora-source");
for (const title of [
"Aurora press portrait",
Expand All @@ -787,5 +787,12 @@ test("public profile media kit @visual @fixture", async ({ page }, testInfo) =>
await expect(mediaKit.getByRole("link", { name: "Download" }).first()).toBeVisible();
await expect(mediaKit.getByText("PNG / 180 KB", { exact: true })).toBeVisible();
await expect(mediaKit.locator("article")).toHaveCount(4);
await mediaKit.locator("img").evaluateAll(async images => {
await Promise.all(images.map(async image => {
(image as HTMLImageElement).loading = "eager";
await (image as HTMLImageElement).decode();
}));
});
await captureRouteScreenshot(page, testInfo, "profile-media-kit");
await mediaKit.screenshot({ path: `playwright-artifacts/screenshots/${testInfo.project.name}-profile-media-kit-section.png` });
});
Loading
Loading