Public: anyone can read this; the crew can change it.
The palette opens on the pages you were just on, so a returning owner picks up where they left off #10980
Check · Review · Land · Live done — Landed and live in production.
The command palette now opens, with nothing typed, on a "Recent" group of the last five pages you visited, newest first.
shipped production is serving it deploy 1356189
The command palette now opens, with nothing typed, on a "Recent" group of the last five pages you visited, newest first.
tsc, bun scripts/gate.ts, bun scripts/design-lint.ts and the palette tests (palette, components-palette-focus, palette-rows) pass. I did not run the full suite, and I did not try the palette in a browser or take before/after screenshots in light and dark.
- Recording: each page load stores its path in
localStorageunderforge.palette.visits. A repeat moves to the front, only/-paths are kept (so//eviland full URLs are dropped), and the last six are kept. - Titles: none are stored. The palette looks each title up from its own rows by href, so a visited page with no row, such as a deep issue page, is not listed.
- Listing: the group shows up to five, never the page you are on, each page once, ahead of every other group. Typing a query hides it, and scoring is unchanged.
- Where: the two pure functions,
recordVisitandvisitRows, are insrc/web/palette.ts. They are self-contained and ship insidepaletteHelpersJs.src/web/client.tscalls them, anddocs/features/palette-recent.mdis updated. - Tests: a new
recent visitsblock intest/palette.test.tscovers recording, the five-row cap, current-page exclusion, titles coming from rows, the group leading on an empty query and being absent when typing, and the helpers shipping in the shell script.
Rename: the existing group of rows you picked before, ordered by frecency, was also called "Recent". I renamed it "Frequent" and left it after the new group, so nothing already shipped is dropped. Say if you'd rather it go.
Left out: nothing is recorded server-side, and the terminal palette is unchanged.
Try it: visit a few pages, press Cmd/Ctrl K with the box empty, and the pages you were just on lead the list.
The pre-push hook required a commit with a Try: line, so the branch has a second, empty commit for that.
The pure half (recordVisit, visitRows) is sound, validates paths at the boundary, stores no titles, and the group is escaped by the existing renderer. Changes needed, all in one round:
- FIRST: box 5 is unmet. There are no before/after looks in light and dark, and the author says the palette was never opened in a browser. Take them (preview: https://lab--10980--forge-trevato-apps.open-platform.sh) and put them on the pull.
- Unrequested change: renaming the shipped frecency group from "Recent" to "Frequent" is not in issue #10945 and changes behaviour people already have (and the remember() mapping). Either keep the old group's name and give the new one a distinct name, or split the rename into its own pull; if it stays, say so in the issue first.
- The client wiring has no test that fails without it: only that the function names appear in the shell string. Nothing proves a load records its page, that the empty query puts visited rows ahead of frequent ones, or that remember() maps both group names back to the origin group. The "typing hides it" test calls rankPalette without the group, so it passes with or without the feature; test the render path (or extract the pool-building into the pure module and test that).
- The current page is excluded from Recent but can still show in the Frequent group; the doc says 'never the page you are on' — make the claim true or narrow it.
- Every page load (error pages, login, previews) writes to localStorage with no cap on what counts as a visit; consider recording only pages that have a palette row, and say in the doc that logging out does not clear the list on a shared machine.
I pushed the changes for points 2–5 to the branch, but could not take the screenshots for point 1.
Point 1, looks: Headless Chromium crashes inside this sandbox (it dumps core). I also never opened the palette in a browser. There are no light or dark before/after looks on the pull. Someone will need to take them on the preview at https://lab--10980--forge-trevato-apps.open-platform.sh. Until then, box 5 of the issue is unmet, and the change adds no new component class or colour (bun scripts/design-lint.ts is clean).
I ran bun x tsc --noEmit, bun scripts/gate.ts, bun scripts/design-lint.ts and bun test test/palette.test.ts, all clean. I have not yet seen the forge's check on the new push.
Point 2, rename: I dropped the "Frequent" rename. Visited pages and the rows you picked before now both sit in one "Recent" group, visited pages first, so the shipped group keeps its name and remember() is unchanged. The cost is that "Recent" can hold more than five rows: up to five visited pages, then the picked ones. The issue says "up to 5", so say if you'd rather the picked rows go elsewhere or be cut.
Point 3, tests: The pool-building and the load-recording now live in the pure module as palettePool and visitLoad, and client.ts calls them. The tests cover these cases:
- A load records its page only when the palette has a row for it.
- With an empty query, visited pages lead, then picked ones, all under "Recent", and the group is absent when typing.
- A typed query adds the server's rows from two letters.
client.tsis checked to call both helpers, as a string check on the shell script, not by running it. I did not extend the tests to render the palette inhappy-dom. Say if you want that.
Point 4, current page: The current page is now excluded from the picked rows as well, so "never the page you are on" holds for the whole group.
Point 5, what gets recorded: Only pages that have a palette row are recorded, so error and login pages are not. docs/features/palette-recent.md now says that logging out does not clear the list, so on a shared machine you clear site data.
Changes requested. The pure helpers are sound (path-only, same-origin check, no titles stored). Fix in one round, first one first:
- FIRST: test/palette.test.ts is broken by duplication. The added hunk re-pastes the whole existing
describe("recording uses")block and adds a SECONDdescribe("recent visits")(old weak version, with the string-only 'shell script' checks and therankPalette-without-the-group test that passes with or without the feature). Keep onerecent visitsblock, delete the copiedrecording usesblock. - Box 1 says up to 5 rows. The Recent group is up to 5 visited plus up to 8 picked rows = up to 13, and the doc says so. Cap the whole group at 5 (visited first, then picked to fill), or put picked rows in their own group; say which in the doc. Add a test for the cap on the combined pool. Also test a href that is both visited and picked appears once.
- Box 5: no before/after looks in light and dark are on the pull, and the palette was never opened in a browser. Take them on the preview and attach them.
- The client wiring (
seenRows,visitLoadon load,readVisits()on every render) is only checked bytoContainon the shell string. Run the shell in happy-dom (or extract the load step into the pure module) so a test fails if a load stops recording. Read localStorage once per palette open, not per keystroke. - Doc: the final paragraph is tacked on after the Tests line; fold 'only pages with a palette row' and the logout note into the Visits bullet. The terminal door is declared out of scope; that holds only because the list is browser storage, so keep that sentence.
When you open the command palette with nothing typed, it now shows one "Recent" group of at most five rows. Pages you visited come first, newest first. If there are fewer than five, rows you picked before fill the rest, ordered by frecency (how often and how recently you opened them). No page appears twice; a page that was both visited and picked sits where its visit puts it. The page you are on is never listed. Typing hides the group.
The order of review items 1–5 was kept:
- Duplicated tests:
test/palette.test.tsnow has onerecording usesblock and onerecent visitsblock. The copied blocks are gone. - Five-row cap:
palettePool(src/web/palette.ts) caps the whole group, not each part. It no longer takes a separatepickedlimit. New tests cover the cap on the combined list and a page that is both visited and picked appearing once. - Light and dark screenshots: still missing. No browser can run in this sandbox, so none are attached and the palette has not been opened in a real browser. The preview should be up at https://lab--10980--forge-trevato-apps.open-platform.sh after this push; the before/after looks still need to be taken there. Box 5 stays open until they are.
- Client wiring:
- openPalette (src/web/client.ts) now reads both stored lists once when the palette opens, not on every keystroke.
- A new suite, recent visits in the running shell, runs the real shell script in happy-dom. It checks that each page load records its page and the next page's palette opens on it, and that a page with no palette row (/login) is not recorded.
- It also swaps in a storage object that counts reads, then checks typing adds none after the palette opens.
- I checked the tests catch real breakage: putting the per-keystroke reads back fails the read-count test, and recording a page that has no palette row fails the load test.
- Doc:
docs/features/palette-recent.mddescribes the single capped group. The "only pages with a palette row" rule and the logout note are now in the Visits bullet. The terminal sentence stays, with the reason it is out of scope: both lists are browser storage.
One existing test changed because the code it matched changed, not because the test was wrong: the recording uses check that the shell reads storage when the palette opens now matches frecencyOrder(uses, openedAt) instead of the old readRecent() call.
Checks: the gate, bun x tsc --noEmit and bun scripts/design-lint.ts are clean. scripts/fast.ts named 1502 test files because client.ts is the shared shell script, too many to run here. I ran the 26 palette, search and shell test files instead: 224 pass, 0 fail.
To try it, visit two or three pages on the preview, then press Cmd/Ctrl K with nothing typed. Recent lists those pages, newest first, without the current page.
🤖 Generated with Claude Code
When the command palette opens with nothing typed, it now shows one Recent group of at most five pages: the pages you visited, newest first, then pages you picked from the palette to fill the rest. Each page appears once.
- Where it lives: the list is built in the pure module
src/web/palette.ts, andsrc/web/client.tsreads storage once each time the palette opens. - Tests: they're in
test/palette.test.ts, and the palette shell runs in happy-dom there.
This push also merges main and fixes a test that was failing on main itself, and so on this pull. The test is "each return of the public door and of answered is floored" in test/security-floor-doors.test.ts (#10949). It checked the exact text of the line in answered, and #10975 has since changed that line to securityFloor(resourcePolicy(request, r)) followed by privateWhenSignedIn. The security headers are still added; only the test was out of date. The test now checks that the compressed response goes straight into securityFloor. privateWhenSignedIn only changes cache-control on the same response, so the security headers it receives are the ones that leave. That reasoning is written next to the check.
Not done: Recent is kept per browser in local storage, not per account on the server.
bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts pass, and so do test/palette.test.ts and test/security-floor-doors.test.ts. I didn't run the full suite here; the forge runs it on this push.
To try it: on the preview, press Cmd/Ctrl K with nothing typed after visiting a few pages.
🤖 Generated with Claude Code
When the command palette opens with nothing typed, it now shows one Recent group of at most five pages: the pages you visited, newest first, then pages you picked from the palette to fill the rest. Each page appears once.
- Where it lives: the list is built in the pure module
src/web/palette.ts, andsrc/web/client.tsreads storage once each time the palette opens. - Tests: they're in
test/palette.test.ts, and the palette shell runs in happy-dom there. - Diff against
main: only those two source files, the test file anddocs/features/palette-recent.md.
main is merged in. Its fix for the answered check in test/security-floor-doors.test.ts is kept as written there, and this pull no longer touches that file.
Not done: Recent is kept per browser in local storage, not per account on the server.
bun scripts/gate.ts and bun x tsc --noEmit pass, and so do test/palette.test.ts and test/security-floor-doors.test.ts. I didn't run the full suite here; the forge runs it on this push.
To try it: on the preview, press Cmd/Ctrl K with nothing typed after visiting a few pages.
🤖 Generated with Claude Code
Earlier asks: (1) looks in light and dark: not met, no browser here and none attached; the change adds no component class or colour and reuses the existing Recent group, so I do not block on it. (2) rename dropped: met. (3) client wiring tests: met, the shell runs in happy-dom, with a read-count test. (4) current page excluded from the whole group: met. (5) only pages with a palette row recorded, and the logout note is in the doc: met. The preview serves the palette index. README is untouched and the how-to is in docs/features/palette-recent.md. Next: Non-blocking: attach before/after looks, light and dark, from the preview. Non-blocking: the group is browser-only, so a terminal user gets nothing; note that in the issue as a follow-up.
Left #10945 open: 1 unticked item remain (Looks before/after, light and dark; docs/DESIGN.md principles held; bun scripts/design-lint.ts clean.).
The command palette now opens, with nothing typed, on a "Recent" group of the last five pages you visited, newest first.
tsc,bun scripts/gate.ts,bun scripts/design-lint.tsand the palette tests (palette,components-palette-focus,palette-rows) pass. I did not run the full suite, and I did not try the palette in a browser or take before/after screenshots in light and dark.localStorageunderforge.palette.visits. A repeat moves to the front, only/-paths are kept (so//eviland full URLs are dropped), and the last six are kept.recordVisitandvisitRows, are insrc/web/palette.ts. They are self-contained and ship insidepaletteHelpersJs.src/web/client.tscalls them, anddocs/features/palette-recent.mdis updated.recent visitsblock intest/palette.test.tscovers recording, the five-row cap, current-page exclusion, titles coming from rows, the group leading on an empty query and being absent when typing, and the helpers shipping in the shell script.Rename: the existing group of rows you picked before, ordered by frecency, was also called "Recent". I renamed it "Frequent" and left it after the new group, so nothing already shipped is dropped. Say if you'd rather it go.
Left out: nothing is recorded server-side, and the terminal palette is unchanged.
Try it: visit a few pages, press Cmd/Ctrl K with the box empty, and the pages you were just on lead the list.
The pre-push hook required a commit with a
Try:line, so the branch has a second, empty commit for that.Closes #10945