A way back on every page, plus two fixes it surfaced - #408
Conversation
/employer/settings was a dead end — you left it by editing the URL. So were
employees, statistics, metadata, upload, contact, agent-sessions and five of
the six tools: an audit found a back affordance on exactly two screens out of
twenty-nine. The two that had one had each invented it separately, a "Back to
all artifacts" link mid-page and a "Back to Studio" pinned to the bottom of a
rail, which is the other half of the problem — even where it existed you had to
go and find it.
It now lives in DriftShell, so every route under /employer gets the same
control in the same place.
**In flow, not floating.** Nearly every screen here already owns its top-left
corner — the Studio rail's logo, the Prospects rail, the settings rail — so a
fixed pill would have landed on top of one of them on most pages. A 38px bar
above the page cannot collide with anything.
**It goes up, not back.** `backTarget.ts` derives the destination from the
path rather than from history, because history is wrong in two ordinary cases:
a deep link opened in a fresh tab has nothing behind it, and a page reached
sideways would send someone somewhere unrelated. A computed parent is the same
answer every time from the same page, which is what lets you stop thinking
about it. It names the destination ("Studio", "Prospects", "Companies") rather
than saying "Back", and a section's landing page is the parent for what is
under it but never for itself.
Even the Studio has somewhere up — the workspace picker — so no page is a dead
end.
The bar also renders whatever crumbs a page publishes, which finally gives
BreadcrumbContext a consumer: it has been collecting trails since the topbar
that displayed them was removed.
The two immersive shells claim `100dvh`, so they now subtract the bar's height
through `--drift-backbar-h`. Verified in the browser rather than reasoned
about: on the workspace, bar 38 + shell 1316 = 1354 viewport exactly, with no
page scroll.
Checked all 16 reachable routes serve the control, and the routing table is
unit-tested including the never-points-at-itself property.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Notes are an annotation on a document, not a destination. The Studio carried them as a top-level feature anyway: a tile, a ⌘K entry with its own Mod+Shift+N binding, a `?feature=notes` route and a pane wrapping a cross-document scratchpad. All of that goes. What stays is the thing notes actually are — the Notes tab in DocumentViewer, backed by DocumentNotesPanel, which is untouched. `NotebookPane` (420 lines) had no other caller once the pane went, so it is deleted rather than left unreachable. Its shared pieces — NoteCard, NoteDraftEditor, BacklinksPanel — stay; the document panel uses them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Opening a template rendered the whole board at 5%. Same bug the preview had, written a second time: frame once on the first measurement over 40px, latch a ref, never look again. Neither surface is at its final size when it mounts — the preview opens while its panel animates, the editor opens with both side panels sliding in — so the fit is computed against a half-width stage and then kept forever. The preview's fix is now a hook both use. Refitting on every resize alone would be worse, yanking the board back under someone who had just panned; telling the two apart needs no state in the store, only the viewport our own framing produced and a check that the live one still matches it. `frame()` also re-arms tracking, so everything that means "frame this now" — the Fit control, a page change, entering presentation — goes through it and starts following along again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_e316c0c4-3441-496d-b2be-f71b832353b0) |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 822f670029
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const pathname = usePathname(); | ||
| const { crumbs } = useBreadcrumbs(); | ||
| const target = backTargetFor(pathname ?? ""); |
There was a problem hiding this comment.
Account for query-backed editor states
usePathname() omits the query string, but the Studio represents source preview and mindmap editing as /employer/documents?source=<id>&edit=1 in WorkspaceShell. Consequently, the visible bar in a mindmap editor treats it as the Studio root and links to Workspaces, skipping the intended editor → preview → library hierarchy and the editor's explicit save-before-return handler. Include the search-parameter-backed state when deriving this target.
Useful? React with 👍 / 👎.
| const FEATURE_IDS = new Set([ | ||
| "draft", | ||
| "rewrite", | ||
| "notes", | ||
| "workflows", |
There was a problem hiding this comment.
Keep freeform notes reachable after saving
Removing notes from the reachable feature set and deleting its only pane leaves no UI that lists freeform notes, while the chat context-menu action in WorkspaceShell still posts unanchored notes to /api/notes and reports “Saved to your notebook.” Those newly created notes—and users' existing notebook notes—cannot then be opened, edited, or deleted through the application. Either retain a reachable notebook surface or remove/redirect the save action and migrate existing freeform notes.
Useful? React with 👍 / 👎.
One conflict, in WorkspaceShell's FEATURE_ROUTES: main moved Prospects under the new Growth app while this branch removed the `notes` route. Took both — Growth's paths, without notes. The move also stranded this branch's back-target table, which still pointed at `/employer/tools/prospects/*`. Retargeted onto Growth, with Brand and Growth itself as sections of their own, and the old Prospects path kept as an entry that forwards to where it lives now rather than to a route that no longer exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The "10 tokens" under an answer was never a token count. WorkspaceShell set `tokens: data.chunksAnalyzed`, and the route sets `chunksAnalyzed: documents.length` — so the figure was the number of retrieved chunks, wearing a tokens label. It read low because a dozen chunks is a plausible-looking number next to a real count in the thousands. The real numbers were already there. The route calls `normalizeTokenUsage()`, logs the prompt/completion split, and debits credits against it — then dropped it instead of returning it. It now travels in `tokenUsage`, which is the same figure metering bills, so what a turn says it cost and what it actually cost cannot drift. Both numbers are worth seeing and they are different things, so the line now reads "1,432 tokens · 10 chunks" — what the model billed, and what it read — with the prompt/completion split on hover. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_b338b42a-fa60-414c-af50-d16f64b9d887) |
"Explain this", "Summarise this" and the composer's quote action all write a
Markdown blockquote into the message. The chat renders a user turn as plain
text in a div with `white-space: normal` — so the `>` markers showed up
literally and every line break collapsed. A two-line quote arrived as
Explain this passage from "Annual Report": > Revenue grew 14% year over
year, > driven mainly by enterprise renewals.
one run-on line in which the question and the thing being quoted are
indistinguishable.
The markers are now read back out. `parseQuotedMessage` splits a message into
what was asked, the passage, and anything typed after it; the turn draws the
passage with a left rule in the secondary ink, and `pre-wrap` keeps line breaks
in every part — which also fixes plain multi-line questions, which collapsed
the same way.
Only a contiguous run of `>` lines counts, so a chevron later in a question
does not silently swallow the rest of it. Six cases pinned, including blank
lines inside a passage and an empty blockquote.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_b003c7ef-06f6-4078-90f8-fc26b96a80b4) |
…ow deleting a run **The add-button caret.** The `+` was a split button whose second half opened a menu of "New mindmap" and "Add a source". Both already live in the Add-source dialog's Create group — one click further in through the button's own primary half — and mindmap is in the ⌘K palette besides. The plain `+` stays; the now-unused `onNewMindmap` prop goes with it. **The page badge.** `p. 1` beside a citation was not a location. Indexing writes `page_number: 1` for every chunk of every document — all 82 rows in this database, including all 33 of a 40-page PDF — so it read "p. 1" whether a passage came from the first page or the last. A citation that looks precise and is wrong is worse than one that says nothing, so the badge and the page in the copied citation string are both gone. That is a removal, not a fix: making it true means recording real pages in the converter and chunker and reindexing every existing document. The page still travels in the reference, and the tests pin its absence so restoring it is a deliberate act. **Deleting a run.** Every non-chat row was permanent — the menu offered "Open" and nothing else, and a vertical with no surface yet offered an empty menu, so those rows could never be removed at all. Delete is now part of the loader contract, beside load. Each pipeline loader deletes from its own table scoped by company, so the rail stays ignorant of `distribution_runs` and `trend_search_jobs`; a kind with no safe delete omits `remove` and its rows offer none, the way a kind with no `href` offers no open. `DELETE /api/workspace/history/<kind>/<refId>` dispatches, and a row belonging to another workspace answers 404 exactly as one that never existed, so the endpoint cannot be used to probe for ids. Chat is refused there on purpose: sessions are personal, not workspace-scoped, and have their own delete with its own ownership rule. The rail asks first — a run cannot be restored — and removes the row optimistically, restoring it if the server disagrees, matching the chat delete beside it. Kept as one commit because WorkspaceShell carries both the caret removal and the new delete handler; splitting by file would leave a commit that does not compile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_350bc030-f325-4de0-9434-73c2942f3eac) |
Summary
Three independent commits, each revertable on its own:
/employer/settingswas a dead end you left by editing the URL — and an audit found a back affordance on 2 screens out of 29. A 38px bar inDriftShellnow gives every route under/employerthe same control in the same place.Mod+Shift+N,?feature=notesroute, pane — is gone. The Notes tab inDocumentVieweris untouched.Related
Follows #398, which fixed the preview half of the framing bug.
Checklist
pnpm checkpasses (lint + typecheck)pnpm --filter @launchstack/web testpassespackages/core/changed —pnpm changeset).env.exampleandapps/web/src/env.tsOn the unticked boxes: the full
@launchstack/websuite was not run end to end — 647 tests across the 45 affected suites (_mindmap,_workspace,back-target) were. No changeset (packages/core/untouched); no env vars added.pnpm checkneeds more than the default 4 GB heap here —eslint .aborts with an OOM at 4 GB and passes clean underNODE_OPTIONS=--max-old-space-size=8192. The same limit now breaksmake up(the in-Docker Next.js build OOMs on current main), somake up-fastis the working path. Unrelated to this change, but worth knowing.Testing
Built and deployed to the local stack, then verified in the browser rather than reasoning about it.
Back bar — all 16 reachable routes serve
data-testid="back-bar". Measured on both kinds of page:/employer/settings#company← Studio / Launchstack / Settings/employer/documents(immersive)← Workspaces / Documents/employer/tools/prospects/companies(immersive)← ProspectsNotes — no "Notebook" in the served Studio HTML, no
feature=notesroute, nothing in the ⌘K palette.Template zoom — created a SWOT mindmap via
POST /api/mindmapsand opened the editor: stage 1230×1234, viewBox 915×918, zoom 134%. Previously 5%.647 tests pass across 45 suites.
Notes for reviewers
Why the bar is in flow rather than floating. Nearly every screen here already owns its top-left corner — the Studio rail's logo, the Prospects rail, the settings rail. I checked settings visually first; a fixed pill would have landed on top of one of those on most pages. A bar above the page cannot collide with anything.
Why it goes up rather than back.
backTarget.tsderives the destination from the path, not from history, because history is wrong in two ordinary cases: a deep link opened in a fresh tab has nothing behind it, and a page reached sideways sends you somewhere unrelated. A computed parent is the same answer every time from the same page. It names the destination rather than saying "Back", and a section's landing page is the parent for what is under it but never for itself — unit-tested, including a "never points at itself" property. Even the Studio has somewhere up (the workspace picker), so no page is a dead end.The riskiest hunk is the viewport math. Two immersive shells claim
100dvhand now subtract the bar through--drift-backbar-h.DriftShell.module.csscarries a long comment about why that chain is delicate, so I verified the arithmetic in the browser rather than trusting it.Side benefit: the bar renders page breadcrumbs, which finally gives
BreadcrumbContexta consumer — it has been collecting trails since the topbar that displayed them was removed.NotebookPane.tsx(420 lines) is deleted, not left unreachable, since the pane was its only caller. Its shared pieces —NoteCard,NoteDraftEditor,BacklinksPanel— stay; the document panel uses them. Onegit revertaway if that is the wrong call.On the framing hook: refitting on every resize alone would be worse than the bug, yanking the board back under someone who had just panned. Telling "the stage is still settling" apart from "the author moved it" needs no state in the store — only the viewport our own framing produced, and a check that the live one still matches it.
🤖 Generated with Claude Code
Note
Medium Risk
Changes global employer chrome viewport math and adds workspace-scoped destructive deletes across several history tables; chat API/UI metering fields also shift what clients display.
Overview
Adds a shell-level back bar on every
/employerroute: path-derived parents viabackTargetFor(Studio → section landings → Workspaces), breadcrumb trail fromBreadcrumbContext, and immersive layouts that subtract--drift-backbar-hso tools and Studio still fit the viewport.Studio / workspace polish: removes the standalone Notebook feature (pane, shortcuts,
?feature=notes); simplifies the source rail Add control (drops the split “new mindmap” menu). History rail can delete non-chat pipeline runs (confirm → optimistic UI →DELETE /api/workspace/history/<kind>/<refId>with per-verticalremoveloaders).Mindmaps: shared
useAutoFitrefits while the stage resizes until the user pans/zooms, fixing tiny zoom on template open and preview panels.Ask / citations: document Q&A responses now expose
tokenUsage; the chat UI shows tokens vs chunks separately and renders quoted questions as blockquotes. Citation copy/UI stops showing page numbers (indexing didn’t have real pages).Reviewed by Cursor Bugbot for commit 41f087c. Bugbot is set up for automated code reviews on this repo. Configure here.