Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4200e1d
feat(og): software canvas, PNG encoder and original stroke font
Nov 7, 2026
c981927
feat(og): procedural 1200x630 world and homepage share cards
Nov 7, 2026
5851246
feat(og): cache-only share image routes
Nov 7, 2026
a506e78
feat(meta): Open Graph and Twitter share meta on home and world pages
Nov 7, 2026
74d7b64
test(og): PNG validity, meta escaping, base path and cache-miss fallback
Nov 7, 2026
2aa999c
feat(atlas): exponential camera easing with anchored log-space zoom
Nov 7, 2026
d4d10ea
feat(atlas): parallax star map with style planets, ghosted unchecked …
Nov 7, 2026
83730ba
feat(atlas): observatory chrome, list swatches, legend and field guide
Nov 7, 2026
3603fed
test(atlas): encode 30fps ambient cap, reduced-motion idle and hidden…
Nov 7, 2026
81c8464
feat(world): optional camera target prop for scripted framing
Nov 7, 2026
ef8376c
feat(tour): pure beat scheduler with cache-only stop collection
Nov 7, 2026
6ec7c32
feat(tour): cinematic auto-tour route across cached gallery worlds
Nov 7, 2026
d25af02
feat(home): Take the tour entry next to Explore Atlas
Nov 7, 2026
19b24ff
test(tour): offline e2e for beats, styles, GET-only reads, controls a…
Nov 7, 2026
17c452f
Merge branch 't3/atlas-star-map-redesign' into integ/six-improvements
Nov 7, 2026
3117d32
Merge branch 't3/social-share-previews' into integ/six-improvements
Nov 7, 2026
4d87f50
feat(gallery): calm provenance chip on cards with a full disclosure
Nov 7, 2026
b3b6e60
feat(world): viewport-aware label culling avoids overlap and chrome
Nov 7, 2026
28053df
feat(world): calm provenance chip and compact mobile world chrome
Nov 7, 2026
d2b0ca5
Merge branch 't3/cinematic-auto-tour' into integ/six-improvements
Nov 7, 2026
7a8c3a8
Merge branch 't3/refine-provenance-mobile-world' into integ/six-impro…
Nov 7, 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
50 changes: 46 additions & 4 deletions e2e/atlas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ test("SYNTHETIC unchecked, real cache miss and 503 stay separate, with no implic
await expect(page.locator(".atlas-card")).not.toContainText("Not collected");
expect(api.requests.every((r: any) => r.method === "GET")).toBe(true);
});
test("SYNTHETIC actual painted map label selects the same repository as its visible list and stays idle", async ({ page, api }) => {
const motion = (page: any) => page.locator(".atlas-map").getAttribute("data-motion");
const frames = (cv: any) => cv.evaluate((c: any) => c.__frames ?? 0);
test("SYNTHETIC actual painted map label selects the same repository as its visible list; ambient motion is capped and reduced motion stays idle", async ({ page, api }) => {
await page.emulateMedia({ reducedMotion: "no-preference" });
await fixture(page, api);
await page.goto(atlasPath);
Expand All @@ -87,17 +89,29 @@ test("SYNTHETIC actual painted map label selects the same repository as its visi
await page.getByRole("button", { name: "Close selected landmark" }).click();
const mapTab = page.getByRole("button", { name: "Map", exact: true }); if (await mapTab.isVisible()) await mapTab.click();
const cv = page.locator(".atlas-map > canvas");
// Labels only move while the camera eases; read them once it has settled.
await expect.poll(() => motion(page)).toBe("ambient");
await expect.poll(() => cv.evaluate((c: any, key: string) => c.__labels?.some((p: any) => p.text === key), keys[0])).toBe(true);
const label = await cv.evaluate((c: any, key: string) => c.__labels.find((p: any) => p.text === key), keys[0]), box = (await cv.boundingBox())!;
await page.mouse.click(box.x + label.x, box.y + label.y);
await expect(page).toHaveURL(new RegExp(`/world/${keys[0]}\\?`));
await page.getByRole("link", { name: "Back to Atlas", exact: true }).click();
await expect(row(page, keys[0])).toHaveAttribute("aria-pressed", "true");
await expect(page.locator(".atlas-card")).toContainText("a".repeat(40));
// Ambient twinkle/route motion runs at about 30fps, never the display rate.
await expect.poll(() => motion(page)).toBe("ambient");
const start = await frames(cv);
await page.waitForTimeout(1000);
const ambient = await frames(cv) - start;
expect(ambient).toBeGreaterThan(5);
expect(ambient).toBeLessThanOrEqual(36);
// Switching to reduced motion stops the loop: static frames on demand only.
await page.emulateMedia({ reducedMotion: "reduce" });
await expect.poll(() => motion(page)).toBe("still");
await page.waitForTimeout(150);
const before = await cv.evaluate((c: any) => c.__frames);
await page.waitForTimeout(200);
expect(await cv.evaluate((c: any) => c.__frames)).toBe(before);
const before = await frames(cv);
await page.waitForTimeout(250);
expect(await frames(cv)).toBe(before);
const dimensions = await cv.evaluate((c: any) => ({ width: c.width, height: c.height, css: c.getBoundingClientRect().width }));
expect(dimensions.width / dimensions.css).toBeLessThanOrEqual(1.501);
expect(dimensions.width * dimensions.height).toBeLessThanOrEqual(4000000);
Expand Down Expand Up @@ -135,6 +149,34 @@ test("SYNTHETIC hidden Atlas map stops drawing, resumes one state-correct frame
expect(api.requests.length).toBe(requests);
await page.getByRole("button", { name: "Map", exact: true }).click();
await expect.poll(() => cv.evaluate((c: any) => c.__frames)).toBeGreaterThan(frames);
await expect.poll(() => motion(page)).toBe("still");
});
test("SYNTHETIC ambient Atlas motion pauses for the hidden list pane and hidden tab, then resumes", async ({ page, api }) => {
await page.emulateMedia({ reducedMotion: "no-preference" });
await page.setViewportSize({ width: 390, height: 844 });
await fixture(page, api);
await page.goto(atlasPath);
const cv = page.locator(".atlas-map > canvas");
await expect.poll(() => motion(page)).toBe("ambient");
await expect.poll(() => frames(cv)).toBeGreaterThan(2);
const requests = api.requests.length;
await showList(page);
await expect.poll(() => motion(page)).toBe("paused");
let paused = await frames(cv);
await page.waitForTimeout(400);
expect(await frames(cv)).toBe(paused);
await page.getByRole("button", { name: "Map", exact: true }).click();
await expect.poll(() => motion(page)).toBe("ambient");
await expect.poll(() => frames(cv)).toBeGreaterThan(paused + 2);
// SYNTHETIC tab-hidden signal: Playwright cannot background a tab, so fake document.hidden.
await page.evaluate(() => { Object.defineProperty(document, "hidden", { configurable: true, get: () => true }); document.dispatchEvent(new Event("visibilitychange")); });
await expect.poll(() => motion(page)).toBe("paused");
paused = await frames(cv);
await page.waitForTimeout(400);
expect(await frames(cv)).toBe(paused);
await page.evaluate(() => { Object.defineProperty(document, "hidden", { configurable: true, get: () => false }); document.dispatchEvent(new Event("visibilitychange")); });
await expect.poll(() => frames(cv)).toBeGreaterThan(paused + 2);
expect(api.requests.length).toBe(requests);
});
test("SYNTHETIC real two-touch pinch and cancellation never become repository entry or collection", async ({ page, api, context }) => {
await page.setViewportSize({ width: 390, height: 1000 });
Expand Down
29 changes: 24 additions & 5 deletions e2e/gallery-preview.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,28 @@ test("SYNTHETIC cache world shares the real renderer, preserves source stamp, pa
await page.goto(`${basePath}/`);
const card = page.locator(".project-card").filter({ has: page.getByRole("heading", { name: "SYNTHETIC Atlas", exact: true }) });
await card.scrollIntoViewIfNeeded();
await expect(card).toContainText("Stale cache");
await expect(card).toContainText("1234567");
await expect(card).toContainText("2 observed / 1 inferred");
// Calm summary on the art; the full capture record sits one keyboard press away.
const shell = page.locator(".project-card-shell").filter({ has: card });
const chip = shell.locator(".card-provenance .provenance-trigger");
await expect(chip).toContainText("Stale snapshot");
await expect(chip).toContainText("districts");
await expect(chip).toHaveAttribute("aria-expanded", "false");
const record = shell.getByRole("group", { name: "Capture details for synthetic-moonshot/browser-fixture", includeHidden: true });
await expect(record).toBeHidden();
await expect(record).toContainText("Stale cache");
await chip.focus();
await page.keyboard.press("Enter");
await expect(chip).toHaveAttribute("aria-expanded", "true");
await expect(record).toBeVisible();
await expect(record).toContainText("Stale cache · current state unknown");
await expect(record).toContainText("1234567");
await expect(record).toContainText("2 observed / 1 inferred");
await expect(record).toContainText("2026-11-04 16:00 UTC");
await expect(record).toContainText("Motion illustrates links · not live activity");
await page.keyboard.press("Escape");
await expect(record).toBeHidden();
await expect(chip).toBeFocused();
await expect(page.getByRole("link", { name: "GitHub source: synthetic-moonshot/browser-fixture", exact: true })).toHaveAttribute("href", "https://github.com/synthetic-moonshot/browser-fixture");
await expect(card).toContainText("2026-11-04 16:00 UTC");
const canvas = card.getByTestId("repository-preview-canvas");
await expect(canvas).toBeVisible();
await card.screenshot({ path: "artifacts/gallery-preview-synthetic.png" });
Expand Down Expand Up @@ -96,7 +113,9 @@ test("SYNTHETIC all five visible cached styles animate without interaction and f
await page.locator(".worlds-section").scrollIntoViewIfNeeded();
const canvases = page.getByTestId("repository-preview-canvas");
await expect(canvases).toHaveCount(5);
await expect(page.getByText("Motion illustrates links · not live activity", { exact: true })).toHaveCount(5);
// Every card keeps the "not live activity" note in its provenance record (disclosed on demand).
await expect(page.locator(".card-provenance .provenance-panel").getByText("Motion illustrates links · not live activity", { exact: true })).toHaveCount(5);
await expect(page.locator(".card-provenance .provenance-trigger")).toHaveCount(5);
const signatures = () => canvases.evaluateAll((elements) => elements.map((element) => {
const canvas = element as HTMLCanvasElement;
const pixels = canvas.getContext("2d")!.getImageData(0, 0, canvas.width, canvas.height).data;
Expand Down
34 changes: 34 additions & 0 deletions e2e/share-preview.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { expect, test } from "@playwright/test";

// Server-rendered share metadata as a crawler sees it (no JavaScript), plus the
// image endpoints. The offline runtime has no cached GitHub world, so world
// cards must fall back to the generic card without any outbound request.
const meta = (html: string, key: string) =>
html.match(new RegExp(`<meta (?:name|property)="${key.replace(/[:.]/g, "\\$&")}" content="([^"]*)"`))?.[1];

test("SYNTHETIC crawlers get absolute per-world share meta under the base path", async ({ request, baseURL }) => {
const base = new URL(baseURL!);
const html = await (await request.get("world/synthetic-moonshot/browser-fixture?style=factorio")).text();
expect(html).toContain("<title>synthetic-moonshot/browser-fixture as a Factorio world · Moon Factory</title>");
expect(meta(html, "og:image")).toMatch(new RegExp(`^https?://[^/]+${base.pathname}og/world/synthetic-moonshot/browser-fixture\\?style=factorio$`));
expect(meta(html, "og:url")).toMatch(new RegExp(`^https?://[^/]+${base.pathname}world/synthetic-moonshot/browser-fixture\\?style=factorio$`));
expect(meta(html, "twitter:card")).toBe("summary_large_image");
expect(html.match(/property="og:image"/g)).toHaveLength(1);
const home = await (await request.get("")).text();
expect(meta(home, "og:image")).toMatch(new RegExp(`${base.pathname}og/home\\.png$`));
expect(meta(home, "theme-color")).toBeTruthy();
});

test("SYNTHETIC share images are PNGs; a cache miss serves the generic card briefly", async ({ request }) => {
const home = await request.get("og/home.png");
expect(home.status()).toBe(200);
expect(home.headers()["content-type"]).toBe("image/png");
const body = await home.body();
expect([...body.subarray(0, 8)]).toEqual([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
expect([body.readUInt32BE(16), body.readUInt32BE(20)]).toEqual([1200, 630]);
const miss = await request.get("og/world/synthetic-moonshot/browser-fixture?style=civilization");
expect(miss.status()).toBe(200);
expect(miss.headers()["x-og-image"]).toBe("home");
expect(miss.headers()["cache-control"]).toBe("public, max-age=300, s-maxage=600");
expect((await request.get("og/world/..%2F/x")).status()).toBe(400);
});
Loading
Loading