Commit Graph

666 Commits

Author SHA1 Message Date
webtecnica 464e7e4e5f fix(docker): read attached binary files in backend (#76577) 2026-08-08 05:44:18 -07:00
Jakub Wolniewicz 24b7ca7258 fix(desktop): preserve root recovery through StrictMode replay 2026-08-07 12:28:19 -05:00
Jakub Wolniewicz bb9434d307 fix(desktop): match current assistant-ui lookup errors 2026-08-07 12:27:34 -05:00
Jakub Wolniewicz 0405c26645 fix(desktop): recover root boundary from tapClientLookup races 2026-08-07 12:27:33 -05:00
Adolanium a62eaaf316 fix(desktop): self-retry transient boundary errors, reactive edit composer context
Two correctness holes left by the session-switch perf work (#72504 / #72524):

1. MessageRenderBoundary only cleared a swallowed transient useClientLookup
   error when the structural resetKey changed. Mid-turn, ids/roles/count are
   stable, so a lookup race during a stream left the boundary rendering null
   for the rest of the turn. The boundary now self-retries on a 0ms timer
   (rAF never fires in a parked renderer), bounded to 5 consecutive
   transient catches with the budget reset on recovery; the structural
   resetKey path is unchanged, and non-transient errors still re-throw.

2. cwd / gateway / sessionId were removed from the messageComponents memo
   deps and read through a render-time ref so session switches stop
   reminting the component types. But a mounted UserEditComposer only
   reads that ref when it renders, and a same-session change (cwd remap,
   gateway reconnect) leaves every ThreadMessageList prop referentially
   equal, so the memo'd list bails out and the open composer keeps stale
   values: @-completions, slash completions, and OS-drop uploads target
   the old cwd / gateway / session. Thread now provides the three values
   through a memoized ThreadEditContext; context propagates through the
   bail-out, the component type identity is untouched, and the transcript
   never remounts.
2026-08-07 12:27:23 -05:00
Austin Pickett 5c6aff1430 fix(desktop): keep the chat in front of the terminal in Focus layout (#81019)
* fix(desktop): hide the terminal overlay when its pane is inactive

One xterm is CSS-overlayed onto whichever `<TerminalSlot />` is active,
positioned with `position: fixed` from the slot's bounding rect. Keep-alive
tab layers stay MOUNTED when inactive — hidden with `visibility: hidden` +
`data-pane-hidden`, deliberately preserving their layout box so scroll state
and xterm survive a tab round-trip.

So an inactive terminal slot still reports a full-size rect identical to the
front tab's, and `rect.width > 0 && rect.height > 0` cannot tell the two
apart. The overlay stayed painted at z-4 over whatever tab the user switched
to, swallowing its clicks.

Sample the hidden state alongside the geometry: `Rect` carries `hidden` from
`isElementInHiddenPane(slot)`, `sameRect` compares it so a tab switch wakes
the tracker, the ancestor MutationObserver watches `PANE_HIDDEN_ATTR`, and
the overlay gates on `!rect.hidden`. `TerminalWorkspace` stays mounted
throughout — PTYs are never torn down, only the surface stops painting.

`opacity: 0` rides alongside `visibility: hidden` because Electron can keep
xterm's WebGL canvas composited after an ancestor goes hidden.

Refs #71407

* fix(desktop): collapse an active tool pane onto the workspace, not a neighbour

`setPaneCollapsed` on the ACTIVE pane of a shared zone that holds the
uncloseable workspace handed the active slot to `group.panes[at - 1]` — the
tab to its left, whichever that happened to be.

The workspace can't minimize (it would strand the app), so tab-switching to a
sibling is the right shape; picking a positional neighbour is not. In the
Focus preset the terminal is a tab in the workspace's own group:

    [workspace, files, review, terminal]

Collapsing the active terminal therefore selected `review`. The user asked for
the terminal to go away and landed on a diff pane they never opened — and with
the overlay still painting (before the previous commit), it read as "the
terminal came back".

Hand the slot to the uncloseable pane itself. That pane is the zone's anchor:
it's the one member guaranteed to be a real destination rather than another
tool the user was not asking for. The positional fallback stays for the
defensive case of collapsing the uncloseable pane itself.

This is deliberately broader than one entry point — every route into
`setPaneCollapsed` for a shared zone gets it: the rail, the tab toggle, and
⌃`. Pure tool-only zones are untouched and still fold as a unit.

* fix(desktop): front the workspace when a fresh chat starts

`startFreshSessionDraft` resets the whole view — messages, usage, timers,
route intent, cwd — but left `$terminalTakeover` set. That atom is not a
cosmetic flag: `controller.tsx` binds it as the terminal's toggle store via
`bindToolPaneCollapse`, so while it stays true the terminal keeps the pane
fronted and ⌘N appeared to bounce straight back into the shell.

Clear it, then `revealTreePane('workspace')`. The reveal is not redundant
with the clear: takeover can already be false while the terminal is simply
the active tab (the flag stays true behind a stacked sibling, and tile flows
never touch it), so the state the user sees and the state the flag describes
drift apart. Clearing homes the common case; revealing states the intent
outright — a new chat shows the chat.

The terminal is not torn down. Tool panels collapse to a rail and keep their
PTYs; re-opening finds the same shell.

The `+` / ⌘T tile path needs no takeover clear — it fronts its new tile
through `revealTreePane` and relies on the hidden-pane-aware overlay.

* fix(desktop): reveal the workspace without closing the terminal

The fresh-session commit cleared `$terminalTakeover` on the way to fronting
the workspace. That atom is not a Focus-only fronting flag — it is the
terminal's open/closed state in every layout, and clearing it is wrong twice
over.

Only the Focus preset stacks the terminal with the workspace. Default,
Terminal deck, and Quad each give it a zone of its own, where it sits beside
the chat and obscures nothing — and there ⌘N minimized a terminal the user
had deliberately open.

The flag is also persisted, so the damage outlived the session. On the next
boot the Focus terminal tab is still in the strip and its zone is not
minimized, so clicking it only calls `activateTreePane`; `PersistentTerminal`
mounts its workspace solely while takeover is true, so the tab fronted empty.

`revealTreePane('workspace')` already carries the whole intent. Behind another
tab the terminal is HIDDEN, not closed: it keeps its PTYs, and the overlay
stops painting on the pane-hidden marker from the first commit in this branch
— which is what was actually covering the chat. Removing the clear costs
nothing and keeps the toggle store truthful.

Two regression tests, both verified to fail when the clear is reinstated: a
terminal in its own zone stays open and visible across a fresh chat, and a
Focus terminal tab still mounts after a restart.

Reported by Copilot review on #81019.

---------

Co-authored-by: izumi0uu <izumi0uu@gmail.com>
Co-authored-by: Ritesh Patel <60716910+DECRUX9812@users.noreply.github.com>
2026-08-07 09:23:03 -04:00
brooklyn! 623d5c93e0 Merge pull request #80736 from NousResearch/bb/reasoning-summary-blocks
Reasoning steps read as separate blocks again instead of one glued paragraph
2026-08-06 22:02:13 -06:00
Brooklyn Nicholson a5cddcd8dc fix(desktop): render already-glued reasoning as separate blocks
Repairs what is already in the transcript: reasoning persisted before the
backend fix, and any provider still gluing its parts. Handles both shapes —
heading-onto-heading (the **** run) and prose-onto-heading (vercel/ai#6742).
Verified against 46 real glued messages from a gpt-5.6-sol session; all repair
cleanly and idempotently.
2026-08-06 22:02:46 -05:00
hermes-seaeye[bot] eb8421ba98 fmt(js): npm run fix on merge (#80725)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-07 02:42:42 +00:00
brooklyn! aacd3194c9 Merge pull request #80719 from NousResearch/bb/running-process-elapsed
Keep elapsed status text from overlapping
2026-08-06 20:33:35 -06:00
Brooklyn Nicholson 0265797b8d fix(desktop): keep elapsed status text from overlapping 2026-08-06 21:26:29 -05:00
Brooklyn Nicholson 75717d29eb feat(desktop): stop hiding a session behind Show earlier
On real sessions the button showed up two or three turns from the bottom, over
a screen and a half of transcript that had barely painted anything.

The budget now spends paint weight, which is what the DOM actually mounts, and
600 units of it — 10-20 agentic turns measured, where a tool-heavy turn prices
at 30-90 and a plain exchange at 5-10. A floor of 8 turns covers the session of
enormous turns that a weight-only cut still truncates hard; it applies to a
real page only, so the small first-paint commit stays small and the backfill a
frame later fills the rest.

Measured on four stored sessions at the same budget: one went from 3 turns
visible to 12, another from 3 to 4, two unchanged. The store window still caps
what the DOM can reach at all.
2026-08-06 21:20:57 -05:00
Brooklyn Nicholson 7eb461d693 refactor(desktop): share how a tool row renders
The transcript decides what a tool call draws and the render budget has to
price it. Both sides need the same answer, so the classification moves out of
the tool renderer into its own module rather than the budget importing the
formatting and i18n weight of fallback-model to ask one question.

Adds isSilentTool for the rows that render nothing at all: todo is hoisted to
its own panel, and a reaction's UI is the emoji on the bubble.
2026-08-06 21:19:44 -05:00
brooklyn! c8648278c3 In-app browser and previews are real layout-tree tabs (#77705)
* feat(desktop): shared pane-strip primitives — one bar, one glyph, one close menu

The zone header hand-rolled its tab bar, its close-verb context menu, and the
bare-glyph "+" inline; the preview rail kept a second copy of all three. Extract
PaneTabStrip (the bar), PaneStripGlyph/PaneStripTool (glyph buttons as data,
titlebar-tool style), and paneTabCloseItems (the four close verbs) into the
pane-tab primitives, and render the zone header through them. Panes contribute
strip glyphs via PaneChrome.stripTools; $stripToolsRevision tells the strip to
re-read.

* refactor(desktop): preview tabs are layout-tree tiles like session and page tiles

The in-app browser / preview rail carried its own tab strip beside the zone's
own — a second bar at a different height with its own close menu, label casing,
⌘W rung, and welded to the file browser's zone so ⌘J toggled it away. It
predated the layout tree.

$previewTabs now mirrors into pane contributions through the same paneMirror
session and route tiles use, so a preview tab IS a zone tab: one strip, drag/
stack/split, the shared close verbs, plain ⌘W, its own zone docked beside main.
URL tabs are titled Browser (the tab names the surface, not the page); files
keep their filename and a file-type lead glyph.

Deleted with the rail: the preview pane contribution + PREVIEW_PANE_ID + its
visibility binding, the 'preview' placements in the default tree and presets,
the ⌘W rail rung, the reveal listener, and the preview.close* i18n keys (copies
of zones.*). lone-header now keys on "closeable placement:main" instead of the
session-tile: id prefix, so any tile dragged into its own zone keeps its tab.

* fix(desktop): preview console/DevTools live on the strip, and DevTools tells the truth

The two toggles were titlebar tools — far from the preview they act on and one
ambiguous global pair once two previews were open. They're strip glyphs now,
contributed per-tab as PaneStripTool data with real tooltips: the console store
is cached by tab id so the glyph and the panel read the same logs, and the pane
registers a DevTools handle for its tab.

DevTools active state was also a lie: it tracked our click handler, so closing
the DevTools window itself left the glyph stuck on. The webview's
devtools-opened/closed events drive it now.

* fix(desktop): ⌘W and ⌃Tab work over preview and page zones

The generic tab verbs keyed zone eligibility on the CHAT strip (workspace /
session-tile: ids), so a zone holding only a Browser or page tile was invisible
to them: ⌃Tab skipped it, and ⌘W fell through the chat rung and emptied the
MAIN chat while you were looking at a preview. ⌘1…⌘9 already worked — the
verbs disagreed about what counts as a tab strip.

New isMainStripPane (any placement:'main' tenant — sessions, pages, previews)
drives ⌘W and ⌃Tab; isSessionStripPane keeps gating what it should: where a
session may dock (⌘T's anchor, the strip's +).

* fix(desktop): preview tab selection follows the tree, not just the reverse

openPreview drove tree reveals, but clicking a preview TAB only activated its
pane in the tree — $rightRailActiveTabId kept naming the previous tab, so
$previewTarget (⌘L quote labels, the titlebar's has-preview state) reported a
tab that wasn't on screen. The mirror now also listens tree→store: when the
interacted zone's active pane is a preview tile, the store selection follows.
Both directions converge on the same id, so no ping-pong.

* fix(desktop): session drags land in preview and page zones

tileZoneHost replaces chatZonePane: a zone hosting any main tile (a Browser
tile, a page) accepts stack and split drops — the known asymmetry where you
could drag a preview tab out but never drag a session in. Only a CHAT zone's
center is the link-to-composer drop; a preview zone's center stacks, since
there's no composer to link to.

* chore(desktop): drop the rail's dead multi-close verbs

closeActiveRightRailTab / closeOtherRightRailTabs / closeRightRailTabsToRight
lost their last callers when ⌘W and the close menu moved to the zone strip's
shared rungs; the tests now exercise closeRightRailTab's own fallback
behavior directly.

* fix(desktop): open_preview lands whenever its session is on screen

The preview.open handler honored the event only when its session was the
FOCUSED one — but the turn that runs open_preview is usually a tile's session,
and by the time the tool fires the user's last click has often parked focus on
main (or anywhere else). The tool reported success, the store never wrote, and
nothing appeared: an explicit 'open reddit' silently vanished.

On-screen is the right bar: honor the open when the session is the primary
chat or any open tile, which keeps truly invisible background sessions from
yanking the pane (offer, don't hijack) without eating opens the user asked
for.

* fix(desktop): one Browser — a second URL navigates it, not a second tab

Tabs were keyed url:<address>, so every distinct page the agent opened
stacked another BROWSER tab — three opens, three Browsers, each titled
identically because the tab deliberately names the surface, not the page.
The title already said singleton; the key disagreed.

URL targets now share one url:browser id: openPreview re-fronts the tab and
swaps its target, and the pane rebuilds its webview against the new address.
Files and artifacts keep per-identity tabs. Restored storage rekeys old
per-address rows and keeps only the most recent.
2026-08-05 16:22:58 +00:00
hermes-seaeye[bot] 18ed612e5c fmt(js): npm run fix on merge (#79496)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-05 16:16:43 +00:00
ethernet b846f0c002 fix(desktop): stop dialogs clipping popovers opened inside them
DialogContent published itself as the portal container for popovers opened
inside a dialog (so focus stays in the dialog and dismissal doesn't close it),
but that same element carried `overflow-y-auto`. Every Select/Popover/
DropdownMenu in a dialog was therefore born inside a scroll box and got
cropped at the dialog's edge — most visibly the worktree dialog's base-branch
combobox, where the branch list was cut off entirely and only the search field
showed.

Split the box in two: the shell keeps position/size/skin and no longer clips
(it stays the portal container), while a new inner body div owns layout and
scrolling. Popovers remain DOM descendants of the dialog, so focus and
dismissal behave exactly as before, but they can now paint past the dialog's
bounds. The banner variant had the same `overflow-hidden` on its shell; its
clip moves to the banner itself, which keeps the rounded bottom edge.

Callers that passed layout/scroll classes (grid, gap-*, p-*, overflow-*) now
pass them via the new `bodyClassName`; `className` keeps sizing and skin.
2026-08-05 11:43:09 -04:00
hermes-seaeye[bot] 2221010717 fmt(js): npm run fix on merge (#79155)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-05 07:06:05 +00:00
brooklyn! 0106844c9b Merge pull request #78849 from NousResearch/bb/multi-tab-drag
Select multiple tabs and drag them to a zone together
2026-08-04 13:26:30 -06:00
Brooklyn Nicholson 33c1d1f266 feat(desktop): shift-click and opt-click select tabs to drag together
Chrome's grammar on every zone tab strip: Shift-click ranges from the
anchor, ⌥-click (Ctrl-click off-Mac — ⌘ stays close, ⌃ stays the macOS
context menu) toggles, plain click collapses back to one tab. Selected
tabs wear an accent wash; dragging any of them carries the block — the
ghost chip counts it — into a strip slot, a zone edge, or a Shift-span,
so three tabs land in a new zone as one gesture.
2026-08-04 13:18:40 -06:00
Brooklyn Nicholson cc8e97499c feat(desktop): the layout tree moves a tab block as one unit
movePanes/reorderPanesInGroup/mergeZonesWithPane now take a block of pane
ids in strip order: the lead pane decides the drop geometry (slot, split,
span-merge) and the rest stack in behind it, with the pressed tab fronting
at the destination. The tab-selection store holds the block (Chrome
grammar: toggle, anchor range, collapse on plain click), drag-session
carries it — every dragged tab dims, the insertion slot skips the whole
block, and a landed drop spends the selection while a deny-area release
keeps it for a retry.
2026-08-04 13:18:33 -06:00
brooklyn! b281b2c87b Merge pull request #78683 from NousResearch/bb/55191-transcript-window
fix(desktop): oversized sessions open without crashing the renderer
2026-08-04 11:57:44 -06:00
Brooklyn Nicholson 62012a5362 feat(desktop): Show earlier pages the DOM, then pulls older history from the store
Show earlier spends the already-materialized DOM budget first and only asks the
session store for another page once that is exhausted, so the click stays cheap
and the store window stays as small as it can be.

Paging has no ceiling: each expand grows the window by one budget page until the
whole transcript is loaded. Branch persistence stays wired throughout —
setMessages is never dropped, so switchToBranch and applyBranchVisibility keep
working on a windowed session.

Co-authored-by: HexLab <8422520+HexLab98@users.noreply.github.com>
2026-08-04 11:32:11 -06:00
Brooklyn Nicholson 1ed702be73 refactor(desktop): share render weight between the two transcript budgets
messageRenderWeight moves out of thread/list.tsx into lib/render-weight.ts.
The DOM page budget already spends render cost rather than message count —
the store window added next needs the same currency, and one weight function
keeps the two layers from drifting apart.

No behavior change.
2026-08-04 11:31:51 -06:00
Brooklyn Nicholson 91337e5789 fix(desktop): ⌘1 / ⌃Tab return to the chat from a full-page view
Hitting ⌘1 (or cycling ⌃Tab onto the main tab) while Capabilities /
Messaging / Artifacts covered the workspace looked dead: the workspace
pane was already the zone's active tab behind the page, so fronting it
changed nothing on screen.

activateTreeTabSlot / cycleTreeTabInFocusedZone now return the activated
pane id, and the keybind handlers route back to the loaded session (or
the new-chat draft) when the landing pane is the workspace under a full
page — the same rule openSession already applies.
2026-08-04 11:26:12 -06:00
hermes-seaeye[bot] 9076adaca5 fmt(js): npm run fix on merge (#78271)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-04 07:10:23 +00:00
kshitij be54f28b16 test(desktop): cover minimized/hidden window-state + visibilitychange pause for GlyphSpinner
Regression coverage requested in review of #74357: mock
window.hermesDesktop.onWindowStateChanged (pattern from
persistent.test.tsx) and assert minimized/hidden clears the spinner
interval while restore resumes it; also cover document.visibilityState
hidden/visible via visibilitychange.
2026-08-04 12:30:44 +05:30
Mykola Bashmakov 9a20d7f680 perf(desktop): keep spinner frames out of React commits
Advance the existing animated status glyph through its DOM text node instead of React state, and pause its timer for hidden panes or inactive windows. Cover frame advancement, zero update-phase commits, and timer suspension with behavior tests.
2026-08-04 12:30:44 +05:30
kshitij aece98c5f3 refactor(desktop): shared pulse beat + fully-gated cron peek (simplify folds)
Two findings from the simplify pass on the final trio diff:

- status-pulse: one pause controller + one aligned period timer shared by all StatusPulse instances (ref-counted), instead of N x (document/window/bridge listeners + unsynchronized 5s wakes) — a sidebar can show dozens of pulsing dots. Pause still cancels in-flight animations so the compositor sleeps immediately.
- cron-jobs-section: the runs-peek effect created its interval even while the pane was hidden (callback no-oped but the timer still woke the renderer every 8s/60s per expanded job). Early-return when hidden — visibility is already in the dep array, so becoming visible restarts load + timer.
2026-08-03 20:29:58 +05:30
Daisuke Suzuki 52fb96de4b perf(desktop): pause hidden-pane timers in agents view, cron sidebar, and floating pet
Partial pick of the surviving renderer hunks from #75395 (perf commit
6502e441d plus fixup 3fbbc9c1d): gate the 500ms subagent now-ticker and
the cron sidebar 1s ticker/run-poll on usePaneVisible, and skip the
legacy floating-pet poll while the document is hidden. Dropped hunks
(electron/main.ts, vitest.setup.ts/config) intentionally excluded.
2026-08-03 20:29:58 +05:30
Mykola Bashmakov 7700597a17 perf(desktop): stop scroll and status loops in busy sessions 2026-08-03 20:29:58 +05:30
kshitij 1f692a8be2 refactor(desktop): hoist the reference-line matcher; drop dead textWithoutImageRefs
Follow-up to #77653: textWithoutReferenceLines built a fresh /g RegExp per call and hand-managed lastIndex — but it runs on both sides of every message comparison in the reconcile loops. An anchored non-global regex has no shared-lastIndex hazard and can be hoisted to module scope. Also removes textWithoutImageRefs, whose last production consumer #77653 replaced (kept IMAGE_REF_LINE_RE for extractImageRefs), and retargets its now-stale comment.
2026-08-03 18:37:31 +05:30
Paulo Nascimento 3060beea07 fix(desktop): dedupe optimistic user turns for all wire references, not only images 2026-08-03 18:10:23 +05:30
kshitij b0089e8bfa perf(desktop): skip store update when pet metadata is unchanged
mergePetInfoMeta now returns the same object reference when all fields
match, and callers skip setPetInfo on reference equality. Without this,
every 15s poll and window-focus refetch fired a nanostores set with a
new-allocated object, triggering a React re-render of FloatingPet even
when nothing changed — a regression from the old samePetRevision guard
which returned without calling setPetInfo.
2026-08-03 13:41:12 +05:30
kshitijk4poor cb0226d4ae docs: update stale samePetRevision comment reference
The helper was extracted and renamed to hasPetSpriteForMeta +
mergePetInfoMeta; the pet.changed comment still cited the old name.
2026-08-03 13:41:12 +05:30
embwl0x 1573829a7c fix(desktop): avoid repeated pet spritesheet fetches 2026-08-03 13:41:12 +05:30
Johnny 1051434325 perf(desktop): isolate right pane layout work 2026-08-03 13:39:33 +05:30
Brooklyn Nicholson 17bdc99db4 feat(desktop): reload a tab from its right-click menu
Right-click any tab and pick Reload: the pane's content remounts in
place — effects re-run, state resets, measurements are retaken — while
the tab keeps its slot and every other tab is untouched.

A per-pane epoch atom keys the contribution inside the zone body, so
reload never rewrites the layout tree. Both tab menus offer it: the
zone strip menu (tool panels, the file tree, a fresh draft's main tab)
and the session tab menu (tiles + the loaded main tab).
2026-08-02 22:08:09 -05:00
Jason Chai c5829fbb34 fix(desktop): account for transcript payload size 2026-08-03 00:08:30 +05:30
brooklyn! eca996aa33 Merge pull request #76401 from NousResearch/bb/composer-link-open
Act on composer directive chips from a hover pill
2026-08-01 20:29:14 -05:00
Brooklyn Nicholson 5ba2564ca0 feat(desktop): act on composer directive chips from a hover pill
A directive chip (`@url:`, `@session:`) reads as the thing it points at and is
coloured like one, but a composer is an editor — a click inside the
contenteditable only places the caret, so there was no way to actually act on
the reference.

Hovering a chip whose kind has an action now floats a pill above it that runs
it: `@url:` opens in the browser, `@session:` opens the session as a tab. It's
a small registry (`DIRECTIVE_ACTIONS`), so a new actionable kind is one entry,
not another watcher.

The pill portals to `<body>` and anchors to the chip's rect, so it can't end
up inside the submitted draft, and it re-anchors on scroll and resize rather
than stranding itself over a reference that moved or was deleted. The press is
swallowed before it reaches the editor — mousedown in a contenteditable moves
the caret, and the edit composer reads a blur as "cancel".

Listeners bind to `document`, not the editor: the edit composer's
contenteditable isn't reliably attached when the effect first runs, so an
editor-bound listener never fired there. A document listener that reads the
editor lazily works in both composers, and each instance filters to its own
editor so one chip never shows two pills.
2026-08-01 20:21:28 -05:00
brooklyn! baec57de66 Merge pull request #76429 from NousResearch/bb/composer-placeholder
fix(desktop): the composer hint stops acting like text you typed
2026-08-01 20:14:06 -05:00
Brooklyn Nicholson 414e5af114 fix(desktop): the composer hint stops acting like text you typed
The empty-composer prompt was painted with an inline `::before`, which puts a
real box in the contenteditable's text flow. Click an empty composer and the
caret lands past the hint instead of at the field's left edge, and a hint that
wraps — a narrow composer, a long locale string — makes the empty composer two
lines tall. It is a hint, so it now sits out of flow: absolutely positioned,
clipped to one line, unselectable and untouchable by the pointer. Measured in
Chromium against the built stylesheet, the caret lands at the left edge for a
wide composer, a narrow one, and a Japanese hint alike.

Backspace on a fresh `@folder:` chip had a second, related problem. Committing
a completion empties the typed token's text node rather than removing it, and
`Range.insertNode` splits the line around the caret, so the chip ends up
between zero-length text nodes. Those read as content: the atomic chip-delete
declined, Chromium's own backspace bounced between the leftovers, and the chip
took extra presses to remove — leaving a `"\n"` draft with the hint still
hidden behind it. Emptiness, the chip-delete, and the DOM normalizer now all
step over that litter.

The stylesheet owns the rule now that it needs `position: relative` on the
editor, so the utility-class constant both composers imported is gone.
2026-08-01 16:34:46 -05:00
Cad from Arca d6be88fbc8 fix(desktop): map SSH profiles to remote profiles 2026-08-01 14:30:16 -07:00
Brooklyn Nicholson a09ad04653 refactor(desktop): share the composer's floating pill treatment
The micro-action strip owned this skin inline: a full-radius hairline pill
on the composer's own fill behind a blur, sized to `--composer-control-size`.
It's the right look for anything that floats over the composer, so lift it
into `composer-dock` next to the other shared composer surfaces and have the
strip compose it with its own width cap and disabled state.
2026-08-01 15:45:42 -05:00
hermes-seaeye[bot] c5be6e7792 fmt(js): npm run fix on merge (#76404)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-01 20:45:40 +00:00
brooklyn! 9b1d8341b2 Merge pull request #75988 from NousResearch/bb/cwd-focus-and-display
Session workspace tracks focus — and paints as ~/…
2026-08-01 02:53:44 -05:00
Brooklyn Nicholson 47d7b7fb1d fix(desktop): keep review/files rails at their declared size
All-fixed splits used to promote the last track to flex-grow and drop its
max clamp. Review and files both declare maxWidth, so ⌘G/⌘J ballooned them
and sash overrides only set a basis that grow still expanded past.
2026-08-01 02:45:47 -05:00
Brooklyn Nicholson 3707741d9f fix(desktop): statusbar cwd follows the focused session
Workspace indicator was stuck on the primary $currentCwd while timers
and context already tracked focus. Resolve from the focused runtime
slice, then the stored session row, with a mid-switch ownership gate.
Path tips across chrome use displayPath (~/).
2026-08-01 02:32:28 -05:00
brooklyn! 57b1eb8c4d Merge pull request #75975 from NousResearch/bb/terminal-session-link
Link terminal tabs to the session you're working in
2026-08-01 02:22:30 -05:00
brooklyn! bfc014e3a8 Merge pull request #75966 from NousResearch/bb/dither-tail-only
fix(desktop): thinking indicator can no longer appear mid-transcript
2026-08-01 02:18:12 -05:00