Commit graph

732 commits

Author SHA1 Message Date
Brooklyn Nicholson
bd53230739 refactor(desktop): extract composer drag-and-drop into useComposerDrop
Lift the attachment drop engine (dragActive + the 7 drag/drop handlers + the
in-app-ref vs OS-upload split) out of ChatBar into
composer/hooks/use-composer-drop.ts. Self-contained, off the keystroke path —
consumes insertInlineRefs + onAttachDroppedItems + requestMainFocus. Verbatim
move, behaviour-preserving.
2026-06-30 04:19:10 -05:00
Brooklyn Nicholson
cf05b38683 refactor(desktop): extract composer voice engine into useComposerVoice
Lift the dictation + voice-conversation + auto-speak subsystem out of ChatBar
into composer/hooks/use-composer-voice.ts. It owns voiceConversationActive,
lastSpokenIdRef, the pending-reply readers, submitVoiceTurn, the voice
hooks (recorder/conversation/auto-speak), the Ctrl+B toggle event, and
handleToggleAutoSpeak; it exposes dictate/voiceStatus/voiceActivityState/
conversation/start+endConversation/handleToggleAutoSpeak for the controls.

Self-contained: consumes the draft/submit primitives (insertText, clearDraft,
focusInput, onSubmit) passed in, nothing depends back on it — so unlike the
queue subsystem (which is circularly coupled to the draft helpers) it lifts
cleanly. Behaviour-preserving; verbatim move.
2026-06-30 04:19:10 -05:00
Brooklyn Nicholson
00694b935f perf(desktop): composer typing no longer re-renders ChatBar (imperative draft sync)
The real composer state-engine fix. ChatBar subscribed to the full draft string
(`useAuiState(s => s.composer.text)`), so every keystroke re-rendered the whole
~2k-line component even though the contentEditable DOM already owns the text.

Replace that with:
- an imperative composer-runtime subscription (useComposerRuntime().subscribe)
  that mirrors text into draftRef, repaints the editor ONLY on external changes
  (clear/restore/insert; the focused editor is the source otherwise), and drives
  the debounced per-session stash — all without a React render. This folds the
  old `[draft]` sync effect and the `[draft]` debounced-stash effect into one
  place keyed off the runtime, surviving core rebinds via the effect dep.
- coarse edge selectors (hasText / isHelpHint / isSteerableText, plus
  isEmpty / hasHardNewline in useComposerMetrics) for the chrome, which only
  re-render when an edge actually flips.

Net: typing within a line does zero ChatBar re-renders / style invalidations;
work happens only on real edges. Behaviour-preserving — draftRef + editor are
already kept current by every mutation path; verified by the composer DOM repro
tests (enter-submit, IME composition, slash-nav) + text-guard.
2026-06-30 04:19:10 -05:00
Brooklyn Nicholson
e0a78336c1 refactor(desktop): extract composer sizing into useComposerMetrics
First step of decomposing the ChatBar god component (composer/index.tsx). Pull
the self-contained *sizing* engine — stacked/inline layout + the measured-height
CSS vars the thread reads for clearance — into composer/hooks/use-composer-metrics.ts.

The hook owns: the media-query `narrow`, `expanded`/`tight`, the 8px height
bucketing (so per-keystroke growth never invalidates the tree's computed style),
the ResizeObserver, the popout re-sync, and the CSS-var cleanup. ChatBar now
just calls `useComposerMetrics(...)` and consumes `stacked`.

Behaviour-preserving (no keystroke/IME/contentEditable path touched): code moved
verbatim. Deliberately a low-risk first slice on the app's most fragile file;
the draft/state-engine spine is the next, dogfood-heavy step
(see desktop-composer-plan.md).
2026-06-30 04:19:10 -05:00
Brooklyn Nicholson
f47459cdbd fix(desktop): proper agent icon for subagents + give the queue an icon
Two status-stack icon nits:
- Subagents used `hubot`; switch to the dedicated `agent` codicon.
- The queue section had no icon while every other group (todos, subagents,
  background) has one. Give it `layers` (a stack of pending turns), matched to
  the group-icon styling so all four sections read consistently.
2026-06-30 04:16:37 -05:00
Brooklyn Nicholson
57462341f4 fix(desktop): keep composer preview links visible when a bg task appears
Preview links (detected HTML files / localhost dev URLs) were rendered as
CHILDREN of the background StatusSection, which is collapsed by default — so the
moment a background task appeared, the previews got swallowed into the collapsed
"N Background" expandable and vanished until you manually expanded it. With no
background group they rendered as a standalone always-visible block, so the bug
only showed once a bg task was running.

Render the preview links as their own always-visible block right after the
background section instead of as collapsible children. They stay visually
associated with the background group (a localhost dev server and its preview are
the same thing) but are no longer hidden by its collapse — a one-tap open is the
whole point.
2026-06-30 04:06:26 -05:00
brooklyn!
f9b619dfae
Merge pull request #55504 from NousResearch/bb/desktop-split-prompt-body
refactor(desktop): decompose use-prompt-actions (slash + submit sub-hooks)
2026-06-30 03:22:43 -05:00
brooklyn!
90f59ecdbb
Merge pull request #55501 from NousResearch/bb/desktop-split-message-stream
refactor(desktop): split use-message-stream (utils + gateway-event sub-hook)
2026-06-30 03:22:21 -05:00
Brooklyn Nicholson
7337248a4c refactor(desktop): extract submit pipeline into use-prompt-actions/submit
After the slash dispatcher, the next-largest body unit was submitPromptText —
a ~280-line submit pipeline. Lift it into a colocated useSubmitPrompt sub-hook
(use-prompt-actions/submit.ts) with a typed SubmitPromptDeps object; body moves
verbatim. SubmitTextOptions moves to utils.ts (shared by submit + submitText).

Pure restructuring, no behaviour change (full use-prompt-actions suite green).
index.ts: 1,212 -> 937.
2026-06-30 03:15:10 -05:00
Brooklyn Nicholson
51a710e57e refactor(desktop): extract gateway-event dispatcher into its own sub-hook
The remaining bulk of useMessageStream was handleGatewayEvent — a ~550-line
event-type dispatcher. Lift it into a colocated useGatewayEventHandler sub-hook
(use-message-stream/gateway-event.ts): the values it closed over (sibling
streaming callbacks + the 3 stable refs the deps array omitted + options)
become a typed GatewayEventDeps object; the dispatcher body moves verbatim.

Pure restructuring, no behaviour change (utils tests still green). index.ts:
1,120 -> 540.
2026-06-30 03:11:14 -05:00
Brooklyn Nicholson
08c83d0555 refactor(desktop): extract slash dispatcher into use-prompt-actions/slash
The usePromptActions body's largest unit was executeSlashCommand — a ~530-line
`/command` dispatcher. Lift it into a colocated useSlashCommand sub-hook
(use-prompt-actions/slash.ts): the ~13 values it closed over become a typed
SlashCommandDeps object the parent passes in; the dispatcher body (and its inner
runSlash recursion) moves verbatim. SlashActionCtx (slash-only) moves with it.

Pure restructuring, no behaviour change (verified: full use-prompt-actions test
suite still green). index.ts: 1,772 -> ~1,250.
2026-06-30 03:03:45 -05:00
Brooklyn Nicholson
086343854d refactor(desktop): split use-message-stream into folder + utils
Extract the standalone gateway-event helpers (session-info patch derivation,
completion-error detection, todo-payload routing, delegate_task -> subagent
spec mapping, + the stream-flush/subagent-event constants) out of the
1,285-line hook into a colocated, tested use-message-stream/utils.ts. index.ts
keeps the stateful streaming hook and consumes the helpers.

Pure restructuring, no behaviour change; folder index keeps the import path
intact. index.ts: 1,285 -> ~1,120. Adds unit tests for the pure helpers.
2026-06-30 02:58:45 -05:00
Brooklyn Nicholson
ed47f2b4aa refactor(desktop): split use-session-actions into folder + utils
Extract the ~16 standalone helpers (message reconciliation, optimistic/resolved
session upserts, stored-session resolution, runtime-info application, error
classification) out of the 1,254-line god hook into a colocated, tested
use-session-actions/utils.ts. index.ts keeps the hook orchestrator (the
stateful action callbacks) and consumes the helpers.

Pure restructuring, no behaviour change; folder index keeps the import path
(`@/app/session/hooks/use-session-actions`) intact. index.ts: 1,254 -> ~950.
Adds unit tests for the pure helpers.
2026-06-30 02:54:46 -05:00
Brooklyn Nicholson
fa7bce0789 refactor(desktop): colocate hook/component families into scoped folders
Single-scoped helpers/sub-files were sitting flat in shared/grab-bag dirs.
Fold each family into its own folder (index = the export, dir resolution keeps
public import paths intact), dropping the now-redundant filename prefix:

- session/hooks/use-prompt-actions.ts (+ -utils, + tests)
  -> use-prompt-actions/{index,utils}.ts (+ tests)
- components/assistant-ui/thread* + assistant/system/user message renderers
  -> assistant-ui/thread/{index,content,status,message-parts,timestamp,types,
     list,timeline,timeline-data,assistant-message,system-message,user-message,
     user-edit-composer,user-message-text} (+ tests)
- components/assistant-ui/tool-fallback(+model)/tool-approval
  -> assistant-ui/tool/{fallback,fallback-model,approval} (+ tests)

Pure move + import rewrites; no behaviour change. App-wide shared primitives
(markdown-text, directive-text, tooltip-icon-button, clarify-tool, ansi-text,
message-render-boundary) stay flat. desktop-controller intentionally left in
app/ (route root; foldering would churn ~80 relative imports for no gain).
2026-06-30 02:42:07 -05:00
brooklyn!
a1b6e7eadc
Merge pull request #55470 from NousResearch/bb/desktop-button-consistency
refactor(desktop): formalize row-as-button primitive (RowButton)
2026-06-30 02:19:37 -05:00
brooklyn!
8f8487b54f
Merge pull request #55468 from NousResearch/bb/desktop-icon-size-token
refactor(desktop): add iconSize token, migrate ad-hoc icon sizes onto it
2026-06-30 02:19:28 -05:00
brooklyn!
28ba01c603
Merge pull request #55459 from NousResearch/bb/desktop-split-prompt-actions
refactor(desktop): extract use-prompt-actions standalone helpers into utils
2026-06-30 02:19:12 -05:00
brooklyn!
b69c2d2fcd
Merge pull request #55456 from NousResearch/bb/desktop-split-controller
refactor(desktop): thin desktop-controller by extracting session-list actions
2026-06-30 02:19:03 -05:00
brooklyn!
116acf3821
Merge pull request #55455 from NousResearch/bb/desktop-split-composer
refactor(desktop): extract composer pure helpers into composer-utils
2026-06-30 02:18:41 -05:00
brooklyn!
61211967e1
Merge pull request #55453 from NousResearch/bb/desktop-split-sidebar
refactor(desktop): split sidebar/index.tsx god file into focused modules
2026-06-30 02:18:32 -05:00
brooklyn!
374d38f09d
Merge pull request #55451 from NousResearch/bb/desktop-split-thread
refactor(desktop): split thread.tsx god file into focused modules
2026-06-30 02:17:56 -05:00
Brooklyn Nicholson
03311abe49 fix(desktop): make ⌘K / session-switcher HUDs ignore titlebar drag band
The top-center floating HUDs (command palette + session switcher) pin at
top-3, overlapping the titlebar's `[-webkit-app-region:drag]` bands. Drag
regions win hit-testing over the DOM regardless of z-index, so the top of
each surface — the search input — swallowed clicks, leaving only a ~2px
strip focusable. Add `[-webkit-app-region:no-drag]` to the shared
HUD_SURFACE so the whole surface is interactive.
2026-06-30 02:06:30 -05:00
Brooklyn Nicholson
57dd86f247 docs(desktop): tighten RowButton doc comment 2026-06-30 02:05:55 -05:00
Brooklyn Nicholson
f3cd744f5c docs(desktop): tighten iconSize doc comment 2026-06-30 02:05:31 -05:00
Brooklyn Nicholson
c2fb651c5e refactor(desktop): formalize row-as-button primitive (RowButton)
Finding 2 of the desktop UI-consistency pass. Several surfaces intentionally
make an entire row/cell the click target while hosting nested layout inside a
raw <button> (each re-justifying the pattern in a local comment). Introduce a
zero-style RowButton primitive (components/ui/row-button.tsx) that bakes in the
shared semantics — type="button" + a stable data-slot — without imposing any
styling, then migrate every genuine row-button onto it:

- app/overlays/panel.tsx
- app/artifacts/index.tsx
- app/chat/sidebar/chrome.tsx (SidebarRowBody, SidebarRowLink)
- app/settings/providers-settings.tsx
- components/desktop-onboarding-overlay.tsx (PROVIDER_ROW_CLASS rows)

Fully behavior-preserving: RowButton adds no classes, so each row keeps its
exact layout/look (verified by a unit test asserting className passthrough).

Left as-is (not row-buttons; converting would risk visual regressions): the
compact bespoke buttons in shell/statusbar-controls.tsx (STATUSBAR_ACTION_CLASS,
also a nested DropdownMenuTrigger asChild) and pet-generate/reference-chip.tsx.
2026-06-30 01:56:57 -05:00
Brooklyn Nicholson
5e51f9c689 refactor(desktop): add iconSize token and migrate ad-hoc icon sizes onto it
Finding 1 of the desktop UI-consistency pass: SVG icon sizing had four
competing conventions with no source of truth. Introduce a named icon-size
scale (iconSize.xs/sm/md/lg/xl -> size-3/3.5/4/5/6) in lib/icons.ts and migrate
the genuine icon deviants onto it:

- desktop-install-overlay.tsx: Loader2/Check/AlertTriangle/Chevron* (h-4 w-4,
  h-3.5 w-3.5 -> iconSize.md/sm)
- composer/controls.tsx, voice-activity.tsx, queue-panel.tsx: numeric size={N}
  on Tabler icons -> iconSize classes

Sizes snap to the nearest scale step; the only rendered deltas are size={11}
-> 12px (queue/stop glyphs, +1px) and AudioLines size={15} -> 14px (-1px, now
matches its sibling toolbar icons). All other migrations are exact (12/14/16px).

Out of scope (different sizing mechanisms, left untouched): non-icon h-N w-N
layout (sliders, skeletons, swatches), sprite size props (PixelEggSprite), and
Codicon font-icon sizing. Broader size-N -> token adoption is follow-up.
2026-06-30 01:50:27 -05:00
Brooklyn Nicholson
bde2dc1051 refactor(desktop): extract use-prompt-actions standalone helpers into utils
The usePromptActions hook is the textbook "god hook" AGENTS.md warns against.
As a first, safe slice, pull its module-level standalone helpers (no closure
over hook state) into a focused, testable use-prompt-actions-utils.ts sibling:

- error classifiers: isSessionNotFoundError, isSessionBusyError,
  isProviderSetupError, inlineErrorMessage
- session-busy retry: withSessionBusyRetry (+ its constants)
- attachment IO: base64FromDataUrl, imageFilenameFromPath,
  readImageForRemoteAttach, readFileDataUrlForAttach, friendlyRemoteAttachError
- misc: delay, isSessionIdCandidate, blobToDataUrl, renderCommandsCatalog,
  slashStatusText, appendText, visibleUserOrdinal, visibleUserIndexAtOrdinal,
  the _submitInFlight guard set, and the GatewayRequest type

Pure restructuring, no behavior change; the usePromptActions and
uploadComposerAttachment exports (and their import paths) are unchanged. Adds
unit tests for the pure helpers. use-prompt-actions.ts: 1,956 -> 1,772.
2026-06-30 01:38:58 -05:00
Brooklyn Nicholson
25c7900fb5 refactor(desktop): thin desktop-controller by extracting session-list actions
DesktopController is a route root that had grown a controller's worth of
session-list plumbing inline. Extract the cohesive fetch/paging cluster into
a focused hook and a tested pure helper, per AGENTS.md's "keep route roots
thin" guidance:

- use-session-list-actions.ts: refreshSessions / loadMoreSessions /
  loadMoreSessionsForProfile / loadMoreMessagingForPlatform / refreshCronJobs
  (plus the private cron/messaging refreshers, sessionsToKeep, and the
  excluded-source constants)
- desktop-controller-utils.ts: pure sameCronSignature helper (+ unit tests)

Pure restructuring, no behavior change. desktop-controller.tsx: 1,441 -> 1,233.
2026-06-30 01:34:12 -05:00
Brooklyn Nicholson
dd659c8d17 refactor(desktop): extract composer pure helpers into composer-utils
Pull ChatBar's module-level pure helpers, constants, and the QueueEditState
type out of the 2.3k-line composer/index.tsx into a focused, testable
composer-utils.ts sibling:

- constants: COMPOSER_STACK_BREAKPOINT_PX, COMPOSER_SINGLE_LINE_MAX_PX,
  COMPOSER_FADE_BACKGROUND, DRAFT_PERSIST_DEBOUNCE_MS
- helpers: pickPlaceholder, COMPLETION_ACTIONS, slashChipKindForItem,
  slashArgStage, slashCommandToken, cloneAttachments
- type: QueueEditState

Pure restructuring, no behavior change; adds unit tests for the slash helpers.
(The ChatBar component itself is a single tightly-coupled megacomponent; a
deeper hook-based decomposition is left for a dedicated follow-up.)
2026-06-30 01:30:52 -05:00
Brooklyn Nicholson
88e29e35bd refactor(desktop): split sidebar/index.tsx god file into focused modules
Behavior-preserving extraction of the 1,963-line ChatSidebar file into the
existing sidebar/ sibling-module convention:

- order.ts: add pure orderByIds / reconcileOrderIds / sameIds helpers (+ tests)
- reorderable-list.tsx: the generic ReorderableList + useSortableBindings DnD
  primitive
- section-states.tsx: SidebarSessionSkeletons / SidebarBlankState /
  SidebarPinnedEmptyState
- sessions-section.tsx: SidebarSectionHeader + the large SidebarSessionsSection
  renderer + its sortable row wrappers

index.tsx now holds only the ChatSidebar component (1,963 -> 1,416 lines).
2026-06-30 01:26:37 -05:00
Brooklyn Nicholson
7ff6908a59 refactor(desktop): split thread.tsx god file into focused modules
Behavior-preserving extraction of the 1,942-line thread.tsx transcript
renderer into co-located sibling modules, matching the existing flat
assistant-ui/ convention:

- thread-content.ts / thread-timestamp.ts: pure helpers (+ unit tests)
- thread-types.ts: shared RestoreMessageTarget
- thread-status.tsx: loading / stall / background-resume indicators
- thread-message-parts.tsx: reasoning + tool part components
- assistant-message.tsx, system-message.tsx, user-message.tsx,
  user-edit-composer.tsx: the message renderers

thread.tsx now holds only the Thread route component (1,942 -> 119 lines).
Also drops a dead readAloudAudio module variable (no references).
2026-06-30 01:21:55 -05:00
Brooklyn Nicholson
04639adace feat(desktop): flag already-installed themes in the install pickers
The Cmd-K "Install theme…" palette listed Marketplace themes with no hint
that you already had them, and clicking one re-downloaded + re-installed a
theme you owned. The Appearance settings grid already detected this, but by
parsing theme descriptions inline on every render — plumbing that never made
it to the palette.

Lift it into one reactive source and reuse it everywhere:
- $marketplaceInstalls (computed over $userThemes): extensionId -> installed
  theme, derived once via marketplaceIdOf and memoized, instead of rebuilding
  a Set per render.
- Both install surfaces now mark owned rows installed and, on click,
  re-activate the installed theme rather than re-fetching it.
- Drops the duplicated description-parsing in settings and the per-session
  "installed here" state in both surfaces (the store is the source of truth,
  so previously-installed themes show correctly too).
2026-06-29 23:29:20 -05:00
Brooklyn Nicholson
30cd39dc56 refactor(desktop): collapse stroll-direction coin to a single draw
DRY: the roomier-side bias computed its probability two ways
(STROLL_TOWARD_ROOM and 1 - STROLL_TOWARD_ROOM). One draw XNOR'd against
the roomier side says the same thing more plainly.
2026-06-29 22:53:38 -05:00
Brooklyn Nicholson
b7322f946d feat(desktop): calmer, more realistic pet roam + split roam modules
The floating pet wandered almost constantly: every idle beat picked a new
walk and hops fired ~45% of the time, so it read as nervous rather than
alive. Make movement the exception, not the default, and split the
overgrown roam hook into focused modules.

Behavior (per ambient game-AI: GameAIPro ch.36 + idle/wander state
machines):
- Loaf, don't pace: most decision beats just keep resting (REST_CHANCE
  0.62) instead of always re-walking.
- Memoryless dwell: pauses now draw from an exponential distribution
  (mostly short rests, the occasional long loaf) instead of a uniform
  1.8-5.2s window, so the cadence never reads as a metronome.
- Hops dialed back 0.45 -> 0.2 (the jumpiest, noisiest motion).

Structure (no god-file; a hook should own one narrow job):
- roam-behavior.ts - what to do & when (dwellMs, chooseMove,
  pickStrollTarget) + tuning. Pure, rng-injectable.
- roam-geometry.ts - where it can stand (snapshotLedges, overlayLedge,
  resolveLedge, overlapsX, groundTop). DOM measurement + pure ledge math.
- use-pet-roam.ts - the physics/RAF loop only.

Tests: deterministic, rng-seeded unit coverage for the decision + geometry
helpers (behavior contracts, not snapshots).
2026-06-29 22:51:09 -05:00
Brooklyn Nicholson
596b813c9b feat(desktop): add read-replies-aloud toggle and wire auto-speak 2026-06-29 15:22:37 -05:00
Brooklyn Nicholson
fcdc05c891 feat(desktop): add auto-speak watcher hook 2026-06-29 15:22:37 -05:00
Brooklyn Nicholson
572c7dbd93 feat(desktop): add read-replies-aloud composer strings 2026-06-29 15:22:37 -05:00
Brooklyn Nicholson
09abbf8a63 feat(desktop): mirror voice.auto_tts into an $autoSpeakReplies store 2026-06-29 15:22:37 -05:00
Brooklyn Nicholson
bff91f978f feat(desktop): type voice.auto_tts in desktop config 2026-06-29 15:22:37 -05:00
Brooklyn Nicholson
a1e699ae55 feat(desktop): roaming pet patrols the base of an open overlay
When a full-screen route overlay (settings/profiles/cron/agents/command-center) is up, the pet's walkable surface swaps to a single ledge at the overlay card's bottom edge — derived from OverlayView's shared inset, not measured — so it patrols there; closing the overlay restores the normal surfaces and it drops back down.
2026-06-29 14:57:26 -05:00
Brooklyn Nicholson
0e2a5a3206 feat(desktop): ground the roaming pet — sprite-paced walk + feet on surface
Walk speed is derived from the sprite's animation loop + on-screen size (one body-width per loop) instead of a fixed px/s, so it steps rather than glides; the pet also sinks a few px so its feet meet the surface instead of hovering.
2026-06-29 14:47:37 -05:00
Brooklyn Nicholson
b72c9e1b2c feat(desktop): add pet roam opt-in toggle + i18n 2026-06-29 14:26:02 -05:00
Brooklyn Nicholson
4da744ef9b feat(desktop): let the pet perch on the status bar and profile rail
Tag both bars with data-slots; the roam loop stands on the status bar's top edge (not over it) and treats the profile rail as a climbable ledge.
2026-06-29 14:26:02 -05:00
Brooklyn Nicholson
7d3c1d55f4 feat(desktop): wire roaming into the floating pet 2026-06-29 14:26:02 -05:00
Brooklyn Nicholson
a8f1d9cc76 feat(desktop): add surface-aware pet wander loop
usePetRoam re-measures ledges from the live DOM each beat and walks/hops/falls between them, driving DOM position imperatively (no per-frame re-render).
2026-06-29 14:26:02 -05:00
Brooklyn Nicholson
964ec680cc feat(desktop): pick directional run row from travel direction
roamWalkRow() prefers running-left/running-right rows, falling back to the generic running row with a mirror for pets that lack them.
2026-06-29 14:26:02 -05:00
Brooklyn Nicholson
c6d6a1c30d feat(desktop): add pet roam + motion/direction store signals
Opt-in $petRoam (localStorage), $petMotion (run/jump pose) and $petRoamDir (-1/0/1) feed the shared $petState only while the agent is at rest ($petAtRest), so a wander never overrides real activity.
2026-06-29 14:26:02 -05:00
Brooklyn Nicholson
7a6b3cb923 fix(desktop): show Gateway statusbar tooltip via composed trigger Slots
The Gateway item is the only statusbar entry with variant === 'menu'.
Since da73223f4 wrapped every render branch in `Tip`, the menu branch
nested `<DropdownMenu>` (a Radix Root that renders no DOM node) inside
`Tip`'s `<TooltipTrigger asChild>`. With no element to attach to, Radix
could never wire hover listeners, so the tooltip silently never showed.

`Tip` also can't be moved inside `DropdownMenuTrigger asChild` (the shape
proposed in #54859): it's a plain component, not a Slot-forwarding one, so
the trigger's injected ref/handlers would land on `TooltipContent` instead
of the button and break the menu's click + popper anchoring.

Fix by composing both trigger Slots directly onto a single <button>
(`TooltipTrigger asChild` over `DropdownMenuTrigger asChild`), the pattern
already used in profile-switcher.tsx, and skip the tooltip wrapper entirely
when the item has no title.

Supersedes #54859.

Co-authored-by: wnuuee1 <wnuuee1@users.noreply.github.com>
2026-06-29 13:48:56 -05:00
brooklyn!
929dd9c0d7
Merge pull request #55033 from NousResearch/bb/subagent-watch-readonly
feat(desktop): read-only spectator transcript for subagent watch windows
2026-06-29 12:09:53 -05:00
Brooklyn Nicholson
7cf6758e33 feat(desktop): read-only spectator transcript for subagent watch windows
Subagent session pop-outs (`watch=1`) spectate a run driven elsewhere, so
editing/steering the transcript from there makes no sense. Gate the composer
and the user-bubble mutations on `isWatchWindow()`:

- hide the composer (folds into `showChatBar`)
- user prompts become a read-only button that toggles the 2-line clamp so long
  prompts stay fully readable, instead of opening the edit composer
- drop the stop/restore actions and the checkpoint branch-picker

Keyed off the narrow `isWatchWindow()` (not `isSecondaryWindow()`), so the
new-session and cmd-click pop-outs are unaffected.
2026-06-29 12:06:25 -05:00