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

The terminal launcher finds every repository you can see, not only the first 200 #10985

Merged opened this

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

Merged

scout/forge:scout/1068-the-terminal-launcher-finds-every-reposi → main 2 commits fork

  • Merged. scout/1068-the-terminal-launcher-finds-every-reposi was merged into main as b17e261.

Review

Sign in to review this change.
2 files changed +69 −1
src/tui/screens/dashboard.ts +40 −1
@@ -175,6 +175,8 @@ const ORDER: readonly PaneId[] = ["repos", "onyou", "machine"];
175175 export function dashboardScreen(f: Session, ctx: Ctx): Screen {
176176 let repos: Repo[] = [];
177177 let all: Repo[] = [];
178 /** What a typed filter found past `all`, for the query it was read for. */
179 let found: { query: string; repos: Repo[] } | undefined;
178180 let pins: Pin[] = [];
179181 let pinned = new Set<string>();
180182 let onYou: OnYouItem[] = [];
@@ -341,6 +343,7 @@ export function dashboardScreen(f: Session, ctx: Ctx): Screen {
341343 everyday = everydayHead();
342344 pinned = f.user ? pinnedRepoIds(f.db, f.user.id) : new Set<string>();
343345 all = listReposVisibleTo(f.db, f.user, { limit: 200 });
346 found = undefined;
344347 onYou = assignedToMe();
345348 onYouSteps = new Map(
346349 onYou.flatMap((item) => {
@@ -854,10 +857,27 @@ export function dashboardScreen(f: Session, ctx: Ctx): Screen {
854857 /** Yours with an empty filter; anything you can see once you type —
855858 * the one way to find someone else's repository from here. */
856859 const shown = (): Repo[] =>
857 exploring() ? [] : (filter.query !== "" ? all : repos).filter((repo) =>
860 exploring() ? [] : (filter.query !== "" ? typedPool() : repos).filter((repo) =>
858861 filter.matches(`${repo.ownerName}/${repo.name} ${repo.description}`),
859862 );
860863
864 /**
865 * Everything a typed filter can reach: the newest 200 already read, and
866 * whatever the forge's own search finds among every visible repository
867 * past them, read once per query so drawing stays a filter.
868 */
869 const typedPool = (): Repo[] => {
870 const query = filter.query.trim();
871 if (found === undefined || found.query !== query) {
872 const seen = new Set(all.map((r) => r.id));
873 found = {
874 query,
875 repos: launcherSearch(f.db, f.user, query).filter((r) => !seen.has(r.id)),
876 };
877 }
878 return found.repos.length === 0 ? all : [...all, ...found.repos];
879 };
880
861881 /**
862882 * The panes the last frame drew, which is what `tab` can reach — a focus
863883 * ring on a card that is not on screen is worse than no focus ring. The
@@ -1577,3 +1597,22 @@ function note(s: Surface, r: Rect, c: Tokens, lines: readonly string[]): void {
15771597 ),
15781598 );
15791599 }
1600
1601 /**
1602 * The repositories a typed launcher filter finds among every one the viewer
1603 * can see: the listing's own visibility rule and its owner/name match, so
1604 * nothing past the first page is out of reach. `owner/name` is split, since
1605 * the match reads the two columns apart.
1606 */
1607 export function launcherSearch(
1608 db: Parameters<typeof listReposVisibleTo>[0],
1609 viewer: Parameters<typeof listReposVisibleTo>[1],
1610 query: string,
1611 ): Repo[] {
1612 const q = query.trim();
1613 if (q === "") return [];
1614 const slash = q.indexOf("/");
1615 if (slash > 0 && slash < q.length - 1)
1616 return listReposVisibleTo(db, viewer, { owner: q.slice(0, slash), query: q.slice(slash + 1), limit: 200 });
1617 return listReposVisibleTo(db, viewer, { query: q.replace(/\//g, ""), limit: 200 });
1618 }
test/tui-apps.test.ts +29
@@ -457,6 +457,35 @@ describe("terminal: apps, runs and the crew", () => {
457457 }
458458 }, 30_000);
459459
460 test("a typed filter finds a repository past the newest 200 the launcher read first (#1068)", async () => {
461 // Rows alone: the launcher reads the table, and 250 bare repositories
462 // would take minutes to init. Ids sort newest first, so the lantern is oldest.
463 const insert = forge.db.query(
464 "INSERT INTO repo (id, owner_id, name, visibility) VALUES (?1, ?2, ?3, ?4)",
465 );
466 insert.run("zz1068-000", alice.id, "faraway-lantern", "public");
467 insert.run("zz1068-001", alice.id, "faraway-secret", "private");
468 for (let i = 2; i <= 250; i++)
469 insert.run(`zz1068-${String(i).padStart(3, "0")}`, alice.id, `filler-${i}`, "public");
470 const bob = getUserByName(forge.db, "bob")!;
471 const h = boot(forge, identityFor(bob));
472 try {
473 await h.until("On you");
474 await h.press("/");
475 await h.type("faraway");
476 await h.until("alice/faraway-lantern");
477 // Past the first page, visibility still holds.
478 expect(h.text()).not.toContain("faraway-secret");
479 await h.press("esc");
480 await h.press("/");
481 await h.type("alice/faraway-l");
482 await h.until("alice/faraway-lantern");
483 } finally {
484 h.stop();
485 forge.db.run("DELETE FROM repo WHERE id LIKE 'zz1068-%'");
486 }
487 }, 30_000);
488
460489 test("the removed screens are unreachable: no palette entry, no dashboard or account key", async () => {
461490 const h = boot(forge, identityFor(alice));
462491 try {

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs