Public: anyone can read this; the crew can change it.

The studio front door loads a full-size screenshot per app tile where a 200x125 thumb already exists #320

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/287-the-studio-front-door-loads-a-full-size → main 1 commit fork

  • Merged. scout/287-the-studio-front-door-loads-a-full-size was merged into main as e88c865.

Review

Sign in to review this change.
3 files changed +113 −10
src/web/pages/looks.ts +4 −2
@@ -40,8 +40,10 @@ export const lookUrl = (
4040 ): string => `${base}/looks/${env}/${commitOid}/${lookSlot(page, kind)}.png`;
4141
4242 /** The newest picture at one width, when there is one. */
43 const pictured = (looks: readonly Look[], kind: LookKind): Look | undefined =>
44 looks.find((l) => l.kind === kind && l.error === null);
43 export const pictured = (
44 looks: readonly Look[],
45 kind: LookKind,
46 ): Look | undefined => looks.find((l) => l.kind === kind && l.error === null);
4547
4648 /**
4749 * The picture for a small slot: the dedicated thumb, or — for a commit
src/web/pages/site.ts +7 −8
@@ -52,7 +52,7 @@ import {
5252 import { field, readForm } from "../form.ts";
5353 import { html, when, type Html } from "../html.ts";
5454 import { icon, type IconName } from "../icons.ts";
55 import { lookUrl } from "./looks.ts";
55 import { lookUrl, pictured } from "./looks.ts";
5656 import {
5757 eventActor,
5858 eventVerb,
@@ -553,22 +553,21 @@ function studioLanding(ctx: Ctx): Html {
553553 const production = (
554554 runtime === undefined ? [] : runtime.status(record)
555555 ).find((env) => env.env === "production");
556 const look = looks.find(
556 const ofApp = looks.filter(
557557 (l) =>
558558 l.appId === record.id &&
559559 l.env === "production" &&
560 l.page === DEFAULT_PAGE &&
561 l.kind === "desktop" &&
562 l.error === null,
560 l.page === DEFAULT_PAGE,
563561 );
562 const look = pictured(ofApp, "thumb") ?? pictured(ofApp, "desktop");
564563 const base = repoBase(repo);
565564 return html`<a class="app-tile" href="${base}">
566565 ${look
567566 ? html`<img
568567 class="app-tile-look"
569 src="${lookUrl(base, "production", look.commitOid, DEFAULT_PAGE, "desktop")}"
570 width="${LOOK_SIZES.desktop.width}"
571 height="${LOOK_SIZES.desktop.height}"
568 src="${lookUrl(base, "production", look.commitOid, DEFAULT_PAGE, look.kind)}"
569 width="${LOOK_SIZES[look.kind].width}"
570 height="${LOOK_SIZES[look.kind].height}"
572571 alt=""
573572 loading="lazy"
574573 />`
test/studio.test.ts +102
@@ -1535,6 +1535,108 @@ describe("the studio", () => {
15351535 expect(dev).toContain("Your code, your machine");
15361536 });
15371537
1538 test("the front door's tile is the thumb, not the full desktop picture, when both exist (#287)", async () => {
1539 const created = await createRepo(app, alice, {
1540 name: "tinytile",
1541 visibility: "public",
1542 description: "picked its own thumb",
1543 });
1544 if (!created.ok) throw new Error(String(created.error));
1545 const seeded = await commitToBranch(app, alice, created.value, {
1546 message: "Start",
1547 files: [
1548 {
1549 path: "package.json",
1550 content: '{"name":"tinytile","scripts":{"start":"bun run server.ts"}}\n',
1551 },
1552 {
1553 path: "server.ts",
1554 content:
1555 "Bun.serve({ port: Number(process.env.PORT), fetch: () => new Response('ok') });\n",
1556 },
1557 ],
1558 });
1559 if (!seeded.ok) throw new Error(JSON.stringify(seeded.error));
1560 const enabled = await enableApp(app, alice, created.value, {
1561 slug: "tinytile",
1562 });
1563 if (!enabled.ok) throw new Error(String(enabled.error));
1564 const record = getAppByRepo(app.db, created.value.id)!;
1565 const commit = seeded.value.commitOid;
1566 const drop = (kind: "desktop" | "thumb") => {
1567 const rel = join("looks", record.id, `production-${kind}.png`);
1568 const abs = join(app.config.dataDir, rel);
1569 mkdirSync(dirname(abs), { recursive: true });
1570 writeFileSync(abs, PNG);
1571 recordLook(app.db, { appId: record.id, env: "production", commitOid: commit, kind, path: rel });
1572 };
1573 drop("desktop");
1574 drop("thumb");
1575
1576 const anon = await (
1577 await fetch(`${base}/`, { headers: { cookie: "forge_mode=studio" } })
1578 ).text();
1579 const tile = anon
1580 .split("<img")
1581 .find((chunk) => chunk.includes(`/${created.value.ownerName}/${created.value.name}/looks/`));
1582 expect(tile).toBeDefined();
1583 expect(tile).toContain(`/looks/production/${commit}/thumb.png`);
1584 expect(tile).toContain('width="200"');
1585 expect(tile).toContain('height="125"');
1586 });
1587
1588 test("the front door's tile still renders at desktop's size for a commit from before thumbs existed (#287)", async () => {
1589 const created = await createRepo(app, alice, {
1590 name: "oldtile",
1591 visibility: "public",
1592 description: "no thumb yet",
1593 });
1594 if (!created.ok) throw new Error(String(created.error));
1595 const seeded = await commitToBranch(app, alice, created.value, {
1596 message: "Start",
1597 files: [
1598 {
1599 path: "package.json",
1600 content: '{"name":"oldtile","scripts":{"start":"bun run server.ts"}}\n',
1601 },
1602 {
1603 path: "server.ts",
1604 content:
1605 "Bun.serve({ port: Number(process.env.PORT), fetch: () => new Response('ok') });\n",
1606 },
1607 ],
1608 });
1609 if (!seeded.ok) throw new Error(JSON.stringify(seeded.error));
1610 const enabled = await enableApp(app, alice, created.value, {
1611 slug: "oldtile",
1612 });
1613 if (!enabled.ok) throw new Error(String(enabled.error));
1614 const record = getAppByRepo(app.db, created.value.id)!;
1615 const commit = seeded.value.commitOid;
1616 const rel = join("looks", record.id, "production-desktop.png");
1617 const abs = join(app.config.dataDir, rel);
1618 mkdirSync(dirname(abs), { recursive: true });
1619 writeFileSync(abs, PNG);
1620 recordLook(app.db, {
1621 appId: record.id,
1622 env: "production",
1623 commitOid: commit,
1624 kind: "desktop",
1625 path: rel,
1626 });
1627
1628 const anon = await (
1629 await fetch(`${base}/`, { headers: { cookie: "forge_mode=studio" } })
1630 ).text();
1631 const tile = anon
1632 .split("<img")
1633 .find((chunk) => chunk.includes(`/${created.value.ownerName}/${created.value.name}/looks/`));
1634 expect(tile).toBeDefined();
1635 expect(tile).toContain(`/looks/production/${commit}/desktop.png`);
1636 expect(tile).toContain('width="1280"');
1637 expect(tile).toContain('height="800"');
1638 });
1639
15381640 test("the front door leads with apps the viewer can write, never a stranger's public one — but /apps still lists it, owner named", async () => {
15391641 // A public app of alice's own, distinct from "notes" (private) and
15401642 // "beacon" (made public by an earlier test) — self-contained so this

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs