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

The command palette still says Code, Commits and New issue on every studio page #266

Merged opened this

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

Merged

scout/forge:scout/246-the-command-palette-still-says-code-comm → main 2 commits fork

  • Merged. scout/246-the-command-palette-still-says-code-comm was merged into main as 88e5406.

Review

Sign in to review this change.
3 files changed +60 −13
src/web/chrome.ts +17 −11
@@ -289,24 +289,30 @@ export { hostOf };
289289
290290 /** This repo's own rows for the command palette — the studio's nouns for a
291291 * studio-mode viewer of an app, so the palette never sends someone to a
292 * list under a name the address bar would not use. */
292 * list under a name the address bar would not use. Commits is dev's own
293 * page (no studio tab reaches it, `scopeHeader` above), so it drops out of
294 * a studio app's rows rather than sit there under a dev word; New issue
295 * gives way to the studio's own first verb, telling the crew, which posts
296 * from the app page itself (`${base}#tell`, `studio/app.ts`). */
293297 export function repoPalette(ctx: Ctx, repo: Repo): PaletteItem[] {
294298 const base = repoBase(repo);
295299 const name = `${repo.ownerName}/${repo.name}`;
296300 const studio =
297301 ctx.mode === "studio" && getAppByRepo(ctx.app.db, repo.id) !== undefined;
302 if (studio) {
303 return [
304 { t: `${name} — Studio`, h: base, c: "This repo" },
305 { t: `${name} — Told`, h: tellsHref(base), c: "This repo" },
306 { t: `${name} — Changes`, h: changesHref(base), c: "This repo" },
307 { t: `${name} — Tell the crew`, h: `${base}#tell`, c: "This repo" },
308 { t: `${name} — Runs`, h: `${base}/runs`, c: "This repo" },
309 { t: `${name} — Settings`, h: `${base}/settings`, c: "This repo" },
310 ];
311 }
298312 return [
299313 { t: `${name} — Code`, h: base, c: "This repo" },
300 {
301 t: `${name} — ${studio ? "Told" : "Issues"}`,
302 h: studio ? tellsHref(base) : `${base}/issues`,
303 c: "This repo",
304 },
305 {
306 t: `${name} — ${studio ? "Changes" : "Pull requests"}`,
307 h: studio ? changesHref(base) : `${base}/pulls`,
308 c: "This repo",
309 },
314 { t: `${name} — Issues`, h: `${base}/issues`, c: "This repo" },
315 { t: `${name} — Pull requests`, h: `${base}/pulls`, c: "This repo" },
310316 { t: `${name} — Commits`, h: `${base}/commits`, c: "This repo" },
311317 { t: `${name} — New issue`, h: `${base}/issues/new`, c: "This repo" },
312318 { t: `${name} — Runs`, h: `${base}/runs`, c: "This repo" },
test/fork-flow.test.ts +5 −1
@@ -793,7 +793,11 @@ describe("the fork flow", () => {
793793 body: JSON.stringify({ fullName: `${UPSTREAM_OWNER}/late` }),
794794 });
795795 expect(started.status).toBe(202);
796 setTimeout(() => void seedUpstream("late"), 1_200);
796 // Seeded well inside the first retry's own wait (`firstFetchRetry.ms`,
797 // `src/origins/mirror.ts`, 2s by default): the immediate first fetch
798 // still finds nothing and fails, same as it would on a slower machine,
799 // but the retry has margin to spare rather than racing it.
800 setTimeout(() => void seedUpstream("late"), 300);
797801 const job = await mirrorAndWait(started);
798802 expect(job?.stage).toBe("done");
799803 expect(job?.repo?.name).toBe("late");
test/studio-words.test.ts +38 −1
@@ -183,6 +183,7 @@ describe("dev's vocabulary never reaches a studio page", () => {
183183 let mergedPullNumber: number;
184184 let openBranch: string;
185185 let mergedBranch: string;
186 let repoPath: string;
186187 /** Every branch this fixture made. No studio page names one of them. */
187188 const branches: string[] = [];
188189 /** The table every test below walks. Filled at the end of `beforeAll`. */
@@ -477,7 +478,7 @@ describe("dev's vocabulary never reaches a studio page", () => {
477478 });
478479 if (!blankEnabled.ok) throw new Error(String(blankEnabled.error));
479480
480 const repoPath = "/alice/vocab";
481 repoPath = "/alice/vocab";
481482 // Every studio page, one row each. A new studio page goes here — the
482483 // path it answers at, the word it speaks, and dev's own words at the
483484 // same path — and nothing else in this file changes.
@@ -723,6 +724,42 @@ describe("dev's vocabulary never reaches a studio page", () => {
723724 expect(h1).not.toContain(runId);
724725 });
725726
727 test("the command palette speaks the studio's own nouns for an app (#246) — it rides a script tag the vocabulary walk above never opens", async () => {
728 const res = await fetch(`${base}${repoPath}`, {
729 headers: { cookie: `${cookie}; forge_mode=studio` },
730 redirect: "manual",
731 });
732 expect(res.status).toBe(200);
733 const html = await res.text();
734 const block = html.match(
735 /<script type="application\/json" id="palette-index">([\s\S]*?)<\/script>/,
736 )?.[1];
737 if (block === undefined) throw new Error("no palette-index on the app page");
738 const index = JSON.parse(block) as { items: { t: string; h: string }[] };
739 const rows = new Map(index.items.map((i) => [i.t, i.h]));
740
741 // Dev's nouns and dev's pages: gone from a studio app's rows.
742 for (const t of [
743 "alice/vocab — Code",
744 "alice/vocab — Commits",
745 "alice/vocab — New issue",
746 "alice/vocab — Issues",
747 "alice/vocab — Pull requests",
748 ]) {
749 expect(rows.has(t), `palette still offers "${t}" on a studio app page`).toBe(
750 false,
751 );
752 }
753
754 // The studio's own nouns, at the studio's own addresses.
755 expect(rows.get("alice/vocab — Studio")).toBe(repoPath);
756 expect(rows.get("alice/vocab — Told")).toBe(`${repoPath}/tells`);
757 expect(rows.get("alice/vocab — Changes")).toBe(`${repoPath}/changes`);
758 // The studio's first verb — telling the crew — reachable from the
759 // palette, landing on the app page where the tell box lives.
760 expect(rows.get("alice/vocab — Tell the crew")).toBe(`${repoPath}#tell`);
761 });
762
726763 test("?mode=dev on the same routes still carries dev's own words — the assertions above are real, not vacuous", async () => {
727764 let proved = 0;
728765 for (const p of pages) {

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs