Skip to content

Give the desktop cold start its first-run setup - #128

Merged
badcuban merged 3 commits into
mainfrom
fix/desktop-first-run
Aug 8, 2026
Merged

Give the desktop cold start its first-run setup#128
badcuban merged 3 commits into
mainfrom
fix/desktop-first-run

Conversation

@badcuban

@badcuban badcuban commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

A fresh desktop launch bootstraps no project from a launch folder the way npx does, so a cold start lands on the no-active-thread shell - which had none of the first-run guidance. Closes the last three gaps from the fresh-install audit (preview-7).

Setup card on the no-thread shell

useFirstRunSetupCard now owns the gate, the workspace-project fallback, and all three actions; both empty canvases (draft thread, no-thread shell) pass only what differs. On the no-project surface the amber "Choose a folder" row leads the checklist, since the missing folder is the whole reason the canvas is empty. Dismissal moved from per-component state to a shared store seeded from the same localStorage record, so "Skip for now" on either canvas settles both in the same tick. While the card owns the canvas, the header label reads "Set up Threadlines" instead of "No active thread".

Update prompt waits for onboarding

ProviderUpdateLaunchNotification mounts above the app shell, so it derives "is this environment still in first-run setup" from live state (useActiveEnvironmentFirstRunSetupPending) rather than a registration signal that would race first paint. The prompt is deferred, not consumed - the guard runs before the seen-key set, so it opens on the same key once setup is completed or skipped. Running/failed update progress still reports immediately.

Warm relaunch reopens where you were

Root cause: uiStateStore.lastChatThreadRef was write-only dead state, and the only navigation off / was the npx bootstrap payload desktop never sends - so every relaunch opened a brand-new draft. Replaced with a per-environment, schema-validated localStorage record (lastVisitedThreadRoute.ts) covering both server threads and drafts. Restore runs once per app load and is validated against live state: deleted threads fall back to the default-draft behaviour, and "Go to Home" / settings-back never bounce back to the thread.

Also extracts deriveDisplayProviderInstanceEntries (the filter/sort/derive trio was duplicated across ChatView and ChatComposer and needed a third copy here).

Tests

  • firstRunSetup.test.ts: card on noThread, shared dismissal across surfaces, per-surface exclusions.
  • FirstRunSetupCard.browser.tsx: folder row leads with amber dot when no project exists, returns to the end when one does.
  • ProviderUpdateLaunchNotification.logic.test.ts: prompt waits while first-run is pending, opens on the same key afterwards.
  • lastVisitedThreadRoute.test.ts: restore when live, null on missing thread/draft/record, unreadable record reads as nothing remembered.

Gates: fmt/lint/typecheck clean; apps/web unit suite 2100 tests green; full browser suite 390 tests green.

A fresh desktop launch bootstraps no project, so a cold start lands on
the no-active-thread shell instead of a draft thread - and got none of
the first-run guidance built for the npx door. Three fixes:

- Setup card on the no-thread shell. useFirstRunSetupCard now owns the
  gate, project fallback, and actions for both empty canvases; the
  no-project surface leads with the amber "Choose a folder" row.
  Dismissal moved to a shared store so skipping on one surface settles
  both in the same tick. While the card owns the canvas the header
  reads "Set up Threadlines" instead of "No active thread".
- The "Updates available" prompt waits while first-run setup is
  pending. Deferred, not consumed: the guard runs before the seen-key
  set, so the same prompt opens once setup completes or is skipped.
  Running/failed update progress is untouched.
- Warm relaunch reopens the last thread or draft. A per-environment
  schema-validated localStorage record replaces the write-only
  lastChatThreadRef dead state; restore runs once per app load and is
  checked against live state, so deleted threads fall back cleanly and
  "Go to Home" never bounces back.

Also extracts deriveDisplayProviderInstanceEntries, deduplicating the
filter/sort/derive trio from ChatView and ChatComposer.
@vercel

vercel Bot commented Aug 8, 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 8, 2026 6:40am

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 8, 2026
Ranking only consulted searchTerms, so typing exactly what an item says
on screen ("Go to Home") found nothing unless someone remembered to
duplicate the title into the terms - some items did, most didn't. The
title now ranks first when it is a plain string; hidden searchTerms
follow. Found by the desktop first-run verification walk (V-7).
@badcuban
badcuban merged commit 7d0c1fb into main Aug 8, 2026
15 checks passed
@badcuban
badcuban deleted the fix/desktop-first-run branch August 8, 2026 06:49
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