Skip to content

Surface unsent drafts in the sidebar - #131

Merged
badcuban merged 3 commits into
mainfrom
feature/sidebar-drafts
Aug 10, 2026
Merged

Surface unsent drafts in the sidebar#131
badcuban merged 3 commits into
mainfrom
feature/sidebar-drafts

Conversation

@badcuban

Copy link
Copy Markdown
Collaborator

Typing into a new-thread composer and navigating away used to strand the draft: it survived in the store but nothing pointed back to it, and the next "new thread" could repurpose or garbage-collect it.

What changed

  • Every draft session with real user content (typed text, attachments, or context chips — model/mode picks alone don't count) now renders a two-line row at the top of the sidebar: amber pen glyph + project favicon + project name, then the first line of the prompt. Clicking navigates back to /draft/$draftId with composer text, attachments, and workspace context intact. The block vanishes at count 0.
  • New-thread surfaces (button, hotkeys, "/" landing, palette) only ever reuse a draft with no user content. An invested draft stays parked in the sidebar and a fresh draft is minted beside it.
  • Store: composerDraftHasUserContent gates remap GC and persistence — invested sessions survive unmapped, zombie sessions are dropped together with their composer blobs at partialize time; clearProjectDraftThreadId sweeps a project's unmapped sessions too; getDraftSessionByProjectRef prefers the currently-mapped draft over older unmapped ones.
  • Discarding a row asks for confirmation (same AlertDialog pattern as the prompt stash). Discarding the open draft lands back on the home composer via the draft route's existing redirect.
  • While a draft is open, its row renders a snapshot frozen at route-change time so typing never repaints the sidebar; the parent subscribes to a row count only.

Behavior notes

  • One deliberate change: "New thread" with a half-typed draft no longer reuses/wipes it — you get a fresh composer and the old draft stays one click away.
  • General-chat drafts are excluded from the block (general chats never join the inbox).
  • A draft whose project is unknown (other machine, deleted project) renders as "Unknown project" unscoped and is hidden under any project scope.

Testing

  • vp fmt / vp lint / vp run typecheck clean; full web unit suite (2104 tests) and browser suite (393 tests, incl. 3 new SidebarDrafts cases) green.
  • Live-verified in a throwaway stack (scratch server + web, Playwright): row appears on navigate-away, restore, newest-first ordering, reload persistence (debounced localStorage), discard confirm/cancel, discarding the open draft (redirects to a fresh home composer), no row for a never-left draft, and the empty-state gate. No console errors; hover reveals the discard X with zero layout shift; light and dark mode checked.

Typing into a new-thread composer and navigating away used to strand the
draft: it survived in the store but nothing pointed back to it, and the
next "new thread" could repurpose or garbage-collect it. Now every draft
with real user content (typed text, attachments, or context chips) gets a
compact row at the top of the sidebar — project name over the first line
of the prompt — that navigates back to /draft/$draftId with everything
intact. New-thread surfaces only ever reuse untouched drafts; an invested
draft stays parked in the sidebar and a fresh one is minted beside it.
Discarding a row asks for confirmation, matching the prompt stash.

Store changes: composerDraftHasUserContent gates remap GC and persistence
(invested sessions survive unmapped, zombie sessions drop with their
composer blobs), clearProjectDraftThreadId sweeps a project's unmapped
sessions too, and getDraftSessionByProjectRef prefers the mapped draft.
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
threadlines Ready Ready Preview Aug 10, 2026 6:43am

Request Review

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XL labels Aug 10, 2026
The empty-state gate counted every invested draft session, including the
open never-left one — which renders no row — so typing a first draft made
"No threads yet" vanish with nothing replacing it. The frozen open-draft
row now lives in a sidebar-owned hook and feeds both the block and the
count, so the two can never disagree: the open draft counts only when its
frozen snapshot is actually rendered.
@badcuban
badcuban merged commit 8525766 into main Aug 10, 2026
15 checks passed
@badcuban
badcuban deleted the feature/sidebar-drafts branch August 10, 2026 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant