Desktop secondary-panel + sidebar UX batch (sawyer-next)#57
Merged
Conversation
The macOS traffic lights + native title-bar chrome tracked the OS appearance because the window was created without setting nativeTheme.themeSource (Electron defaults it to "system"). When the OS was dark but bb was set to light (or vice versa) the window chrome mismatched the bb UI. Bridge bb's resolved theme renderer → main over a new bb-desktop:set-theme IPC channel (validated via a shared zod enum): a useDesktopThemeSync() hook pushes usePreferredTheme() to window.bbDesktop.setTheme on mount and on every change (including OS appearance changes when the preference is "system"); main assigns nativeTheme.themeSource. The log viewer window inherits it since themeSource is app-global. No-ops cleanly in the web build. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Create App… tile was skipped by the launcher's ArrowUp/ArrowDown +
Enter active-descendant flow because it rendered as a plain button
outside the listbox. Fold it into the navigation model via a
section-layer discriminated union (FileSearchSectionEntry =
{kind:"suggestion"} | {kind:"create-app"}) so the data-layer
suggestion union stays honest; one navigableEntries index space drives
keyboard nav, with Create App appended to the end of the Apps section
(reachable even in the empty state). Extract a shared LauncherTile
shell so the app row and Create App tile share one role/aria/active
contract; the tile now renders role="option" inside the listbox and
aria-activedescendant resolves to its stable id. Enter routes to the
same prefill path as click (confirm + attachment-clear preserved).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Manager thread rows now show a cluster of their installed-app icons at the trailing edge, left of the branch/environment icon (the status glyph stays far-right). Click an icon to open that app in the secondary panel (navigating to the thread if needed). Visible icons cap at 3 with an informational +N chip whose tooltip names the hidden apps; empty rows render nothing. The apps query + open-app hooks live inside ThreadRowAppCluster, which ThreadRow mounts ONLY for manager rows — so non-manager rows never instantiate a useThreadApps observer or cache entry (only managers have apps today). The 30s staleTime is centralized as the useThreadApps default so the sidebar and detail view share one cache window. Reuses the secondary panel's open-app path via an extracted useOpenThreadAppTab(threadId) hook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a collapse control (chevron on the thread/panel seam + a mirrored PanelLeft button in the conversation header) that collapses the conversation/timeline pane so the secondary panel fills the content area. Animated via a shared PANEL_COLLAPSE_TRANSITION_CLASS token; panel lifts to 100% via react-resizable-panels setLayout in a layout effect (no flicker). State persists in a global threadConversationCollapsedAtom. Gated to isSecondaryPanelOpen && !isCompactViewport — no control when the panel is closed, full no-op on the compact drawer viewport. The seam toggle is a higher-stacked sibling of the PanelResizeHandle (not a child) so react-resizable-panels' overlap-exclusion applies and clicking it never starts a resize drag — RRP arms the resize from a capture-phase body pointerdown, which a child guard can't beat. The collapsed conversation subtree is marked `inert`, removing its header/timeline/composer from the tab order + a11y tree. Header control uses aria-expanded to match the seam toggle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The left-sidebar resize tracked the drag with a window mousemove listener but had no iframe guard, so an open app iframe in the secondary panel swallowed the move stream once the cursor crossed it and the resize froze (most visible with the panel full-width). Mirror the secondary-panel resize's [&_iframe]:pointer-events-none guard: while the sidebar is resizing, neutralize iframe pointer events on the app-layout root. The shared class is extracted into lib/iframe-drag-guard.ts and used by both the sidebar and secondary-panel resizes. The guard is removed on every drag-end path (mouseup, window blur, Escape, effect cleanup) so iframes can't get stuck non-interactive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the seam-chevron-only collapsed state with the mockup's left rail: a 48px vertical bar (rotated mono "CONVERSATION" label + an active-state CircleDashed glyph in the foot) that is fully clickable to expand. The horizontal PanelGroup stays mounted (timeline → 0%, panel → 100% via the existing layout effect) and the rail renders as a flex sibling in front of it — same [sidebar][48px rail][full-width panel] visual the mockup shows, without unmounting/reloading the panel iframe on every toggle. Eases in via the shared transition token. Expanded rail is w-0 + inert + aria-hidden (no reserved space, no phantom tab stop); the collapsed conversation stays inert. Expand stays reachable via the rail, the seam chevron, and the header button. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per Sawyer's design: drop the header "Collapse conversation" (PanelLeft) and wide-mode "Show secondary panel" (PanelRight) icon buttons AND the separate seam chevron, replacing them with a single SeamPanelArrow on the conversation/panel seam. State→direction: - panel closed → ◀ at the content's right edge → "Show panel" - panel open, conversation expanded → ◀ → collapse conversation - conversation collapsed → ▶ → expand conversation The 48px rail still expands too. Terminal toggle + ⋯ menu kept; compact viewport keeps its drawer toggle. The arrow is a higher-stacked sibling of the resize handle so RRP's capture-phase pointerdown never starts a resize from it. Reuses threadConversationCollapsedAtom, the visibility hook, the iframe-drag guard, and the inert collapsed conversation; ConversationCollapseToggle removed in favor of SeamPanelArrow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a desktop-only web browser as a secondary-panel tab. A new `browser` tab kind (peer of app/file, per-thread persisted) hosts an Electron WebContentsView — a real top-level browser context, so sites that refuse framing (e.g. google.com) load. Chrome: address bar with URL/search heuristic + https indicator, back/forward/reload/stop, title in the tab pill, and a new-tab screen (search + quick links + per-thread recently-visited). The New Tab launcher's OPEN section gains "Open browser" (gated on window.bbDesktop; web build hides it). Security: dedicated persist:bb-browser partition; sandbox + contextIsolation + no node + webSecurity; NO bb bridge injected into browsed pages; a webRequest firewall blocks loopback/private/LAN hosts (IPv4 + IPv6, incl. ::ffff: mapped) so untrusted pages can't reach bb's local services; navigation locked to http(s); popups denied as OS windows (reopened as in-panel tabs, rate-limited); permissions + downloads denied; favicons dropped (no attacker URL in the trusted renderer); URL/title length caps; all IPC zod-validated at both boundaries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sticky thread-level model + reasoning-level override applied live next turn. CLI: bb thread update --model/--reasoning-level. Same-provider only; codex gated out for v1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-thread recently-opened panel items with dedup + cap, file-type accents, relative timestamps, and full keyboard-nav integration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…seam button (#80) Header top-right chevron opens the panel / collapses the conversation; the 48px collapsed rail carries an explicit expand chevron. SeamPanelArrow removed; the label/direction/handler mapping extracted to a shared helper. Also fixes the rail title/aria-label mismatch and a stale seam comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…psed rail Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e header (#82) Header toggle renders the PanelRight glyph when the panel is closed (matching the in-panel hide button) so it reads as 'open the side panel'; chevron retained for the open/collapse states. iconName moved into resolvePanelToggleControl. Also fixes a latent rail-label test left by the chat-icon change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…idebar+conversation both collapsed (#81) Rail expand chevron gets a top inset and the secondary panel top chrome gets a left inset, gated to macOS desktop + main sidebar collapsed (+ conversation collapsed for the panel inset), reusing the existing traffic-light reserve tokens and the AppPageHeader gate signal. Adds parent-gate test coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A manager's installed apps render as rows in the manager's expand/collapse child region (before worker rows, at the worker indent), opening the app in the panel on click and collapsing with the manager. Scope-tinted icon tile with a tile->grip hover crossfade matching the mockup. Removes the duplicate inline icon cluster (#65). Global/project/agent scopes + drag-to-move deferred. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ns app expanded with single selection (#85) Removes the scope-tinted tile bg behind the app icon (shared glyph-slot class; dead --scope-* tokens removed), drops the GripVertical drag affordance (icon removed), and makes clicking an app collapse the conversation so the app opens fully expanded with a single selected row (manager row no longer double-highlights). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… tag (#88) Search field leads (shared Input primitive); removed the globe/heading hero and the Isolated-session pill; Quick Links + Recently Visited restyled as bb dense rows with the launcher's relative-time + hover-open affordance. Behavior + browser security unchanged. New BrowserNewTabScreen test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold the secondary-panel resize-handle hairline to zero width while the conversation is collapsed (it was doubling the rail's recessed edge), and narrow the collapsed rail from 48px to 36px. Traffic-light insets + resize behavior intact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On desktop macOS the rail now renders a transparent h-12 window-drag top strip (mirroring the sidebar) so its recessed background starts below the traffic-light strip; the lights sit on clean window chrome. Unifies the old chevron inset into the strip and removes the now-dead reserve token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Scrollable middle tab region (anchored info + new-tab/panel-toggle), reused + extended OverflowFade for left/right edge fades, hover/focus scroll chevrons on the overflowing side, active-tab auto-scroll-into-view, and non-passive wheel-to-horizontal that releases at the edges so page scroll still works. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nt-row restore (#92) Conversation-collapse toggle moves to the secondary-panel header (left of hide-panel) as Maximize2/Minimize2; conversation header keeps only the closed-state open button; rail unchanged. Conversation-collapsed state is now per-thread (atomFamily keyed by threadId), and selecting a thread row restores only that thread's conversation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… trigger when fully collapsed (#93) When desktop macOS + sidebar + conversation all collapsed, the panel header's left reserve was pl-16 applied on the same element as px-4 (twMerge replaced it), landing content at 100px under the pinned sidebar trigger. Use the full pl-20 reserve so leading content sits at 116px — clear of the lights + aligned with the sidebar-collapse control, matching the page-header pattern. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Persistent BrowserTabDeck keeps every open browser tab's native WebContentsView alive (inactive = display:none, destroyed only on close), with a deck-owned visibility coordinator that always hides the current view before bounds-syncing and showing the next — so switching (either direction) never reloads, flashes, or shows two overlays. Security posture unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Removes the ChevronRight caret from the collapsed rail (the panel-header Maximize/Minimize is the canonical expand/collapse affordance); recenters the chat glyph + working indicator. Whole bar still expands on click. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keep the native browser view visible during a drag-resize and track its bounds live (rAF-batched) instead of hiding it; the collapse/expand transition still hides via isActive. Regression test exercises the real ResizeObserver path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Desktop setting (default on, toggle hidden on web) routes normal http/https links in assistant chat markdown into the in-app browser surface instead of the OS browser; mailto/file/relative/internal links unchanged. Reuses openBrowserTab + the existing browser security model. onOpenLink is assistant-only (user messages render as plain text). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In desktop chrome the chevrons picked up MACOS_WINDOW_NO_DRAG_CLASS (relative z-50), and tailwind-merge dropped their absolute positioning, so both rendered as flex children before the tabs. Use the pure app-region no-drag class, keep them absolutely positioned at the scroller's left/right edges. Test asserts the split placement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The flanking chevrons used the transparent ghost variant, so tab labels bled through. Add bg-background (the same opaque surface the edge fade resolves to) so the chevron cleanly occludes the tabs beneath it; ghost hover preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Visual polish from live iteration across the secondary panel: browser New Tab screen, collapsed conversation rail, New Tab launcher recent rows, and the tab overflow chevrons. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bb-native New Tab screen no longer renders quick links (the feature was removed), but BrowserNewTabScreen.test.tsx still asserted a GitHub quick-link button, leaving a failing test. Remove it; the component has no quick-link code remaining. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Batch of desktop/secondary-panel UX work accumulated on
sawyer-next, rebased onto latestmain. 31 feature commits + 1 stale-test cleanup.Highlights
Collapsed conversation rail / panel toggles
Apps as first-class sidebar rows
Browser surface
Secondary-panel tabs
Other
Migrations
0009_famous_the_hunter(model/reasoning override) sits after main's0008_thread_pinning;drizzle-kit checkclean, migrations apply in order.Validation
@bb/db294 tests,@bb/app833 tests — all green on the rebased tip.Merge style: Rebase and merge (linear history, one commit per change on main).
🤖 Generated with Claude Code