refactor(spaces): rework sidebar row status, pins and row menus - #4008
Conversation
The cloud workflow holds a run at in_progress while it babysits CI after
opening the PR ("Keeping CI green"). Under the Trunk merge queue that wait
ends only when someone comments /trunk merge, so a run can claim to be
working for hours after the agent stopped: two tasks in the code space had
been spinning for 104 and 220 minutes, one with all 29 checks green.
The dot now goes quiet once a PR exists, and the badge stack shows the PR
even when its GitHub state is unresolved. latest_run.output.pr_url is
written the moment the PR opens, while prState needs a lookup a cloud-only
task never gets (TaskPrStatusService discards fresh state when the task
has no local workspace row). A task that opened a PR must not look like it
did nothing, so the badge appears uncoloured rather than not at all.
Not sure this is the right cut. It treats "has a PR" as terminal, which is
a guess about intent: a run that opens a draft PR and keeps pushing to it
is genuinely still working, and this now draws it as quiet. The real fix is
upstream in products/tasks, where the run should reach a terminal status
rather than being held open for a merge the queue won't perform unattended.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYPVPkN4Lm3gygchrpcoqr
The sidebar list is one list now: pins sit in it with everything else, marked by a badge in the row's own stack rather than living in a section of their own, and canvases carry the same menu tasks do. Rows - Pinned items merge into the sessions list, sorted ahead of newer ones so a pin can't fall off the recents cap. - The pin joins the row's badge stack in the vocabulary's amber; it goes first, which in a reversed stack puts it leftmost and underneath. - Canvas badges are violet, matching the canvas colour everywhere else, and are named on hover like the task badges beside them. - The row's "…" is gone: at rest a row shows status, not controls. Row menu - Actions move into the hover card, built from the same item list the right-click menu uses. - Canvases get that menu: pin, and delete through the existing deleteCanvasWithUndo window. Filing and command-centre cells are task shaped, so they're dropped rather than shown dead. - The card is quill's Card/Item parts throughout — no hand-rolled shadow — and is controlled so it survives its own "File to…" flyout. Chrome - Space rows drop the cube glyph; #me keeps its lock and the Spaces heading keeps its icon. - Project switcher moves to the top of the sidebar, above the nav row. - "New session" leads the space pane's list of pages. - Back row, header buttons and the row menu trigger use quill buttons rather than hand-rolled ones; rows use quill's focus ring. - The "Created by" filter is hidden in #me, where it can only answer "you", and its value is neutralised so a stale filter can't empty the list with no control left to undo it. Unread - Counts and unread dots read amber, the same colour the status dots use for "something is owed to you". Red is left for failure. Fixes a 4px clip on the ⌘-slot hints: quill's `.quill-button kbd` hangs 4px into a button's padding, and the row's truncate wrapper cut it off. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrvaQrZNBNSexddG78u4Nt
|
😎 Merged successfully - details. |
|
React Doctor found 4 issues in 3 files · 4 warnings. 4 warnings
Reviewed by React Doctor for commit |
Prompt To Fix All With AI### Issue 1
packages/ui/src/features/canvas/hooks/useChannelTaskStatus.ts:25-28
**Canvas rows query task status**
Every canvas row passes an empty task ID to `useTaskPrStatus`, leaving the query enabled and causing unnecessary empty-key database and worktree checks on the server.
### Issue 2
packages/ui/src/features/canvas/components/ChannelItemRow.tsx:184-193
**Filing menu loses current space**
The task menu omits the current `channelId`, so the filing flyout marks every space as non-current. Selecting the task's existing space then performs an idempotent no-op while displaying a misleading “Filed to …” success toast.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "refactor(spaces): rework sidebar rows, r..." | Re-trigger Greptile |
|
👋 Visual changes detected for this PR. Review and approve in PostHog Visual Review If these changes are unexpected, they may be caused by a flaky test or a broken snapshot on master. Don't approve — rerun the job or wait for a fix. |
The card was carrying quill's card padding on top of its own sections, which read as a lot of air around four short lines. Its padding is off now and each section pays for its own inset, so the rules run edge to edge and the action rows highlight full width. - Deleting a canvas confirms first, with the same copy the artifacts grid and the canvas header use — it goes for everyone in the space. The undo window still follows the confirm. - A canvas waiting out that window flashes a red dot labelled "Deleting…". It stays in the list rather than vanishing, so the row has to say what is happening to it. - "File to…" opens on hover in the card, the way a submenu does on right-click. Delete is quill's destructive button. - Menu separators are gone from both surfaces. - The quiet dot reads "All caught up" rather than "Nothing owed to you". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrvaQrZNBNSexddG78u4Nt
Unread was amber picked by hand; it's `--primary` now — yellow in dark, orange in light — so the count badges, the unread dots and the row's own dot are one colour, and the numeral takes `--primary-foreground` rather than a contrast token chosen per theme. The pin keeps its own amber: primary means "there is something here for you", and a pin is a shelf, not a claim on your attention. Also stops every badge in a row's stack offering a pointer cursor — quill gives an avatar rendered as a button one, which promised a click that did nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrvaQrZNBNSexddG78u4Nt
Three things the review turned up: - Canvas rows share `useChannelTaskStatus`, which passes an empty task id to `useTaskPrStatus` — every canvas row was spending a round trip to be told nothing. No id now means no query. - The row's menu didn't carry the space it's listed under, so "File to…" ticked nothing and filing a task to where it already lives reported a move that didn't happen. - `TaskRowMenuList` builds its item components from the callbacks it's given, so an inline `onAction` remounted every button in the hover card on each render of the row. Also gates the pending-delete lookup on canvases, which are the only things that can be in that map. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrvaQrZNBNSexddG78u4Nt
It was a scratch page for reading the task-status vocabulary while it was being written; the vocabulary is settled and the page has no reader. Takes the route, its view, the Settings entry that opened it and the tab target with it. The dot-ring spinner stays — it's a primitive the rows use, and only the comment pointing at its old home changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrvaQrZNBNSexddG78u4Nt
puemos
left a comment
There was a problem hiding this comment.
Looks 🔥 (in a good way)
I did add some feedback, but none of it is blocking
| </Badge> | ||
| </div> | ||
| )} | ||
| {author && ( |
There was a problem hiding this comment.
tightened it up and aligned it with top icon
There was a problem hiding this comment.
but ya more can be done here. I want to spend some time redoing this preview card alone.
| </button> | ||
| </Button> | ||
| <DropdownMenu> | ||
| <DropdownMenuTrigger |
There was a problem hiding this comment.
will address later, good idea though
| * working or unread, grey is quiet. The trigger renders as a span because rows are | ||
| * `<button>`s — a nested button would be invalid HTML. | ||
| */ | ||
| export function TaskStatusDot({ dot }: { dot: TaskDot }) { |
| // agent typing right now, not a run status we inferred. | ||
| const runClaimsWork = | ||
| props.taskRunStatus === "in_progress" || props.taskRunStatus === "queued"; | ||
| if (props.isGenerating || (runClaimsWork && !hasPullRequest(props))) { |
There was a problem hiding this comment.
@adamleithp this works way better now, but there's another minor issue: when the task is starting, it shows it as a yellow dot, which is okay but not the best
There was a problem hiding this comment.
ooo nice catch, ill do a follow up
There was a problem hiding this comment.
also the running state in light mode is not noticeable enough, will address in follow up
| }); | ||
| } | ||
| if (badges.length === 0) { | ||
| badges.push({ key: "local", Icon: ChatCircle, label: "Local task" }); |
| so under the layout it sits above the nav row rather than in the | ||
| footer. Its menu opens downward, which is the right direction | ||
| from the top of a sidebar. */} | ||
| <Box className="shrink-0 px-2 pb-1"> |
There was a problem hiding this comment.
addressed, will push my changes through and we'll tweak it later (didn't move the project switcher, just made it more or less noticeable)
There was a problem hiding this comment.
it's looks more tidy now already with the change, thanks!
| <PreviewCard.Root> | ||
| // One tooltip provider per task row, shared by its dot and badges so moving | ||
| // between them doesn't re-wait the open delay. Canvas rows have neither. | ||
| const row = ( |
There was a problem hiding this comment.
🟣 Nice-to-have
I don't like using vars for components because it's harder to reason about later (but I'm only human...). Maybe this belongs in a component? I could be wrong
There was a problem hiding this comment.
ill think about this
…d icon gets primary color for easy finding
…ullRequest) → yellow solid (Pending - no work in flight)






Reworks the spaces sidebar: what a row says about itself, where its actions live, and what colour "unread" is.
Row status vocabulary
taskStatusVocabulary.ts) instead of a per-call-site glyph. A working row gets aDotRingSpinnerin the same 8px box, so labels start in one place down the list./design-systemroute renders the whole vocabulary as a table (taskIconSpecs.ts), so a state that has no name is visible.Pins
PINNEDsection is gone. Pins sit in the one sessions list, marked by an amber pin badge that joins the row's stack — first, which in a reversed stack puts it leftmost and underneath.Row actions
…button is gone from rows — at rest a row shows status, not controls.deleteCanvasWithUndowindow (8s toast, nothing sent to the host until it expires). Filing and command-centre cells are task-shaped, so a canvas's menu drops them rather than showing them dead.Card/Itemparts throughout — no hand-rolled shadow or type scale, and the card's own padding is off so its rules run edge to edge and the action rows highlight full width. It's controlled, so it survives its own portaled "File to…" flyout.SearchableMenuFlyout), pinned to its trigger rather than re-aligning as the list filters, and it opens on hover in the card the way a submenu does on right-click.Delete…is quill's destructive button.Sidebar chrome
#mekeeps its lock; the SPACES heading keeps its icon.#me, where it can only answer "you" — and its value is neutralised there, so a filter set in a shared space can't silently empty the list with no control left to undo it.Unread colour
--primary— yellow in dark, orange in light — so "there is something here for you" is one colour across the window. The numeral takes--primary-foreground, so contrast follows the theme rather than a token picked per scale. Red is left for failure.Fixes
.quill-button kbdhangs a shortcut into its button's padding, and the row'struncatewrapper cut it off. Diagnosed against the running app over CDP — extra row padding doesn't help (theml-autore-absorbs it), zeroing the margin does.in_progresswhile it babysits CI, which under a merge queue can outlast the agent by hours.nativeButton={false}on the nested menu trigger).Testing
pnpm typecheck(24 packages) andpnpm --filter @posthog/ui test(2367 tests) pass. Row/menu/glyph behaviour is covered by unit tests; the badge and hover-card changes were checked against the running app.🤖 Generated with Claude Code
https://claude.ai/code/session_01JrvaQrZNBNSexddG78u4Nt