Keep compact sidebars open while users inspect Search, Import session, Add project, or file sheets. Reveal the resolved destination when navigation commits, and place compact workspace sheets above both sidebars while preserving shared editing and cache state.
Share tab agent presentation and effective workspace status with Command Center. Preserve unchanged result identities so live directory updates only rerender the changed row, with bounded retention and browser regression coverage.
Keep iOS command and file suggestions above the composer and keyboard across chat, workspace drafts and New workspace. Share transform-free anchor measurement and keyboard translation through keyboard/shift.
Includes failing-first native placement and exact-insertion coverage, plus two CI test reliability fixes.
Co-authored-by: sam <76036008+masz77@users.noreply.github.com>
Order ordinary sidebar workspaces by Created or Last prompt, preserve project and pin ordering, and keep later active or unacknowledged work visible beyond the first seven rows. Preserve reveal state through live updates and verify sorting, visibility, compatibility, and rendering behavior.
* refactor(terminal): remove the WebView terminal renderer
Native uses the native grid renderer only; web keeps xterm. The legacy
renderer setting, its diagnostics toggle, the esbuild WebView bundle and
its CI drift check are gone.
* test(terminal): target the visible terminal surface in the file paste spec
A second page opening a terminal in the same workspace adds a retained,
hidden surface to the first page, so the unscoped locator matched two
textareas and failed strict mode.
* test(server): wait for the shortstat warm instead of polling 500ms
The background warm shells out to git; on Windows CI that regularly
outlasts the 20x25ms poll and fails the test. Use the completion callback
the warm already exposes and keep the non-blocking check.
* feat(terminal): support image paste into agent TUIs, local and remote (#3725)
Codex and OpenCode receive image pastes by binding Ctrl+V (\x16) and
reading the OS clipboard of the machine they run on; no image bytes
travel through the pty. Paseo's terminal swallowed that keystroke and
only ever pasted text, so image paste silently did nothing.
- web/Electron runtime: forward \x16 when the clipboard holds no text,
keep the existing text-paste path otherwise (bash quoted-insert stays
intact)
- new terminal.clipboard.write_image RPC ships client clipboard bytes to
the daemon, which writes them onto its host clipboard (osascript /
wl-copy / xclip / Set-Clipboard) so remote clients work like local
ones; gated by server_info.features.terminalClipboardImage with a
bare-\x16 fallback for older daemons
- clipboard tools are waited on via their exit event, not stream close:
wl-copy and xclip fork a selection owner that inherits our pipes, so
waiting for stream closure hung every Linux write until timeout
- headless hosts without a clipboard get a temp-file fallback: the
daemon materializes the image under os.tmpdir() and returns its path;
the client injects it as bracket-pasted text
- all four renderers covered (web DOM, Electron, native grid, WebView
bridge); mobile Paste button enables for image clipboards and toasts
failures in all 10 locales
* fix(terminal): bind image paste to private retained files
* fix(terminal): recover interrupted image metadata safely
* fix(terminal): retire image files with background lifecycle maintenance
* fix(terminal): check live paste modes and refresh native bridge
* fix(terminal): order image paste input and isolate failures
* fix(terminal): normalize paste and release failed creation reservations
* fix(terminal): validate missing Windows launch paths before spawn
* fix(terminal): keep input live during cancellable image uploads
* test(terminal): follow streamed input-mode updates
* feat(terminal): upload dropped images through the paste flow
* feat(terminal): paste and drop images through file uploads
Terminal image paste and drop now reuse the existing upload path
(client.uploadFile -> file.upload.request -> FileUploadStore) and paste
the quoted host path through the pane's input, bracketed when the app
asked for it. This replaces the PR's daemon-side image store, RPCs,
paste permits and file lifecycle, so the daemon, protocol and client
diffs for this feature are empty.
- terminal/image-paste owns a per-pane serial queue: gesture order,
keystrokes stay live, Ctrl+C and pane/connection teardown cancel
pending pastes, and a cancelled path is never pasted.
- Clipboard text wins over an image; any image/* type is accepted up
to the composer's 50MB upload limit.
- Host paths are quoted for the host OS by path shape.
- One failure toast; i18n keeps two keys.
- Browser e2e covers two panes pasting distinct PNGs, drop, ordering,
Ctrl+C cancel and disconnect against a real daemon.
Co-authored-by: Gerard Balaoro <miogerard@yahoo.com>
* fix(terminal): translate the disconnected image upload error
* feat(terminal): report image uploads through toasts
Show a lasting "Uploading image…" toast while an image uploads, replace
it with "Image uploaded" or a failure toast that carries the reason, and
dismiss it when Ctrl+C cancels the upload. Toast show now returns a dismiss
scoped to that toast, so the pane can clear its own toast without
removing a newer one.
* build(terminal): regenerate the WebView HTML for the toast events
* fix(terminal): keep i18n out of the terminal WebView bundle
The image paste queue imported i18n for its size-limit message, which pulled
every translation into the WebView HTML. The queue now reports a typed
TerminalImageTooLargeError and the pane words it.
* refactor(terminal): route dropped files by host locality and share the drop overlay
A dropped file pastes its own path only when the host is the desktop app's
own daemon (useIsLocalDaemon); anywhere else the host cannot read the
client's disk, so the file uploads through client.uploadFile and the host
path is pasted. File type no longer plays a part: images and other files
take the same route, and clipboard images always upload because they have
no path.
The terminal pane now sits in the shared FileDropZone, so dragging files
over it shows the agent panels' "Drop files here" overlay. The emulator's
own drag listeners, blue tint and 180 ms timeout are gone.
FileDropSink.onFiles is optional so a sink can take raw files without
persisting raster images as attachments.
terminal/image-paste becomes terminal/file-paste; the queue, callbacks,
toasts and i18n keys say "file".
* test(terminal): keep the fake desktop bridge in an e2e support helper
The CI contract test forbids browser specs from installing paseoDesktop
directly; the local-host drop scenario now imports it from support.
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
Popovers on Android landed one status bar below their trigger. React Native
0.86 stopped subtracting the status bar from window coordinates under
edge-to-edge, so measureInWindow, touch pageY and a full-screen Modal already
share screen-top coordinates; the old StatusBar.currentHeight compensation in
the menu engine, tooltip, context menu and sidebar long-press now
double-shifted every anchor.
Compact tabs keep their natural width and the rail scrolls as soon as they
overflow, instead of squeezing labels first and then scrolling anyway. They
drop the inline close because the long-press sheet already closes a tab and a
narrow X beside the label is easy to hit. Any scrolling rail keeps natural
widths rather than collapsing every chip to the minimum, and the rail resets
its offset when scrolling ends so tabs scrolled out of view come back.
The Explorer rail paints the sidebar surface, so its chips climb the sidebar
hover and selected tokens. Light themes paint the sidebar with surface2, which
made the surface2 active chip invisible there.
The compact Explorer closes with the same X as the compact agent list, and the
compact + menu omits Diff because compact Changes already shows diffs inline.
* perf(workspaces): stop duplicating setup output in progress snapshots
Keep the required combined log field empty for setup clients, which display per-command output. Preserve concatenation only for legacy agent bootstrap timeline cards whose released renderers still read it directly.
* refactor(workspaces): own setup exclusively at the workspace boundary
Preserve keyed modern agent worktree creation while retiring legacy placement that selected an agent-owned setup lifecycle. Keep output in per-command workspace snapshots and retain empty strings required by existing wire schemas.
* fix(server): avoid duplicate cwd in worktree provisioning request
* test(e2e): keep developer toasts out of concurrent app assertions
Concurrent layout measurements could invoke the same Playwright locator handler before its first dismissal finished, leaving checks waiting for a removed button. Hide only the Expo toast host through test styling while retaining console error capture.
* Upgrade to Expo 57 and unify Explorer across layouts
* feat(app): retain Explorer across layout and appearance changes
Keep one workspace Explorer host and destination-aware navigation so folding, theme updates and compact sheets preserve the current panel. Scope pull request discovery and timeline demand to active surfaces.
* fix(app): keep Explorer resize handles stretched through the dock
The shared frame moved the divider into an absolute column, leaving its horizontal handle without height. Restore the row so pointer drags reach it, preserving the existing resize and performance contract.
* fix(app): keep Explorer drag widths out of the shared stylesheet
Carry the existing inline sizing policy into the shared frame so pointer previews do not accumulate CSS rules.
* fix(test): align isolated test runtimes with Expo 57
Hoisted React consumers need the matching DOM renderer at the repository root. Automated Metro processes must not install native DevTools into disposable homes, and development toast overlays must not cover compact actions.
Keep Explorer journeys on the shared host and synchronize setup fixtures on daemon output rather than a one-second file-marker window. A delayed real child start reproduces the readiness failure on every platform.
* fix(test): await retained shell layout after composer resize
The gesture host now stays mounted across breakpoints, so its presence no longer identifies compact layout. Report the current layout on the retained host and synchronize composer journeys with that state.
* fix(app): preserve browser interactions in retained workspace hosts
Initially disabled gesture hosts still registered pointer and wheel listeners, while hover cards used window positions inside a workspace-local sheet portal. Respect initial gesture enablement and position cards relative to their measured floating host.
Keep developer-toast dismissal independent of pointer input and replacement toast visibility so modal, hover, and focus checks retain their input state.
* fix(test): share developer toast handling with Electron browser runs
The standalone Electron browser runner bypassed the browser fixture, allowing developer errors to cover Settings. Use the same non-pointer dismissal in both runners while retaining their console logs.
Recognize Android certificate-path validation failures before the generic not-found classification, so Direct connection no longer tells users to debug DNS for an untrusted certificate. Keep the existing TLS copy and technical details.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Retained panels keep their children mounted, so mount-only registration kept hidden rings subscribed to the shared UI clock. Detach inactive rings and retain a local phase so hidden animated styles also stop receiving updates while visible rings continue.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Pete Keen <pete@petekeen.net>
* fix(app): reveal Android settings inputs above the keyboard
PageLayout kept its full-height scroll viewport while the keyboard controller handled Android insets, leaving Code size covered. Use the existing keyboard-aware scroller for Android pages and cover the real focused field and keyboard geometry in the mobile regression harness.
* fix(app): keep settings field frames above the Android keyboard
Keyboard scrolling revealed only the native text input, leaving the enclosing field border and padding covered. Allow theme spacing around the input and assert the visible Plugins source frame against the real keyboard.
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(terminal): select complete Korean words on long press
Wide-character continuation cells carry a blank character but occupy no extra glyph. Treat only actual cells as word separators so either half selects the complete word while real spaces still delimit it.
* fix(terminal): preserve selected words until a drag begins
An unmoved touch release contracted the word selected by the long-press timer. Only extend selection after crossing the existing drag threshold. The Android regression checks the copied whole word after the finger lifts.
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
The shared MD4C permissive autolink scanner consumed trailing path periods and rejected colon boundaries. Fix recognition in the dependency patch and rebuild its web parser so native and web agree. Preserve balanced path parentheses and explicit destinations.
The Markdown DOM container bypasses React Native Web style prefixing. Safari inherits the gesture host selection suppression unless the selectable boundary also sets WebkitUserSelect. Keep container styling in the existing style configuration so its selection contract is covered by the renderer tests.
* Add missing monochrome provider icons (kiro, devin, hermes, minimax-code, traecli, grok, deepseek-tui)
* fix(app): add Kiro catalog icon and Antigravity builtin icon
The ACP catalog's kiro entry had iconId null and no vendored svg, and
the native Antigravity provider resolved to the Bot fallback before the
daemon snapshot arrived. Vendor the Kiro brand mark, wire iconId, and
register a builtin AntigravityIcon matching the plugin's theme-aware
icon.svg.
* fix(app): keep Kiro eyes in monochrome icon
* test(providers): cover catalog artwork and snapshot-free icons
Lock down the missing provider marks through the catalog and the shared icon hook. Exercise the rendered catalog artwork in Chromium alongside coverage for the pre-snapshot fallback.
Co-authored-by: kevenhu001-cyber <keven.hu001@gmail.com>
Co-authored-by: Lu <jack1125516912@outlook.com>
* fix(website): preserve unrelated agent directory artwork
Drop the unrelated website mark introduced with the provider artwork. Cover the existing directory fallback and retained provider mark in a browser regression.
---------
Co-authored-by: kevenhu001-cyber <keven.hu001@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Lu <jack1125516912@outlook.com>
* fix(app): wrap sidebar empty-state actions so longer labels stay inside the card
The "Add project" / "Import session" row in the sidebar's empty state is a
non-wrapping flex row. With longer translations (e.g. Russian) the second
button overflows the card at the default 320px sidebar width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(app): cover sidebar empty-state actions staying inside the card
Renders the empty state in a real browser at the default sidebar width and
checks both actions' bounds against the card, for Russian (fails without the
wrap) and English.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(app): keep long sidebar actions inside narrow cards
Wrapping the row still lets a single translated action exceed its available width. Bound each empty-state action to the row and allow its label to shrink and wrap.
Co-authored-by: Vyacheslav Scherbinin <gentslava@mail.ru>
---------
Co-authored-by: Vyacheslav Scherbinin <gentslava@mail.ru>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Grouped sidebar projects chose the first eligible host even while a workspace on another host was active. Prefer that active host when online and eligible, keeping the existing fallback for other placements.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Michael Whitman <michael.whitman@getsteward.ai>
* fix(scripts): recover installs when a dependency patch changed
patch-package cannot apply a patch over a package patched with an earlier
version of it, and without --error-on-fail it exits 0 locally. Checkouts kept
the stale patch silently: the Markdown code block spacing fix never reached
them. The script now fails the install and removes that package so the next
npm install reinstalls it clean with the current patch. A patch-package that
cannot start fails the install without removing anything.
* fix(app): give Markdown blocks one gap and roomier desktop spacing
Every Markdown block is followed by one gap, density.blockGap: 12 on compact
layouts and 16 wider. The library's element styles and the views stacked
outside it (Mermaid segments on native, streaming head and tail) take the same
token, so no block type carries its own margins. Desktop was too tight at 12.
Markdown images show a zoom-in cursor on web, since tapping opens the lightbox.
Keep drag widths out of the shared stylesheet and skip unchanged panes during width previews. Preserve the existing resize state and persistence behavior.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Ivan Malison <1246619+colonelpanic8@users.noreply.github.com>
* feat(app): rank, highlight, and timestamp Command Center results
Workspace and agent rows now show when they were last active, rank by
match quality and then recency, mark the query terms in every result
kind, and lead workspaces with their project icon.
Ties used to be broken by where the match landed in the string, so
"Deliver sidebar usage" beat "expose usage data" on sentence position
and recency almost never decided anything. Built-in rows now compare
only which fields matched and the matcher tier, then the most recent
row wins. The empty query follows the same rule, so it lists the most
recent workspaces and agents instead of alphabetical workspaces and
status-first agents.
Row and section heights live in list-metrics: a layout picks one row
inset and every height derives from it plus shared line boxes, which
the styles also read. The layout is chosen once at the top, so rows
never branch on it, and two-line rows are now vertically centered.
Desktop is denser (32/46); compact keeps 44pt touch rows (44/58).
findHighlightRanges takes the matcher's options so the marks match
what the list matched: the Command Center is not typo-tolerant, the
history list is.
* fix(app): keep the project icon out of Command Center row names
The fallback project icon draws the project's initial as text, which
became the first word of the row's accessible name ("C Command Center
..."), so screen readers announced it. The icon is decorative.
The scroll spec now reads the newest seeded workspace: the empty query
lists the most recent workspaces, so the first one seeded is no longer
among them.
Paseo tool rows put the variable part first ("QA messenger finished"), so
truncation hid the fixed part, and MCP calls showed only the humanized tool
name with no context. Every Paseo tool now has one catalog entry in
protocol/paseo-tool-presentation owning its fixed label, the input field
shown as context, and its detail layout. Agent-message events read
noun-first ("Agent finished" + sender) and use the Paseo icon; the app
ignored it before because the wire detail's legacy "bot" icon won.
Summaries are capped in code at 120 characters because prompts can be one
enormous line; the renderer is not trusted to clip them.
The activity curator behind `paseo logs` and get_agent_activity printed a
bare "[Agent message]" because the server tool-call type never declared the
agentMessage field the projection sets; it now prints the event, sender,
and body.
* fix(markdown): restore link hover underline and code block spacing on web
The web renderer swap dropped both. Links: the library styles anchors
inline with text-decoration: none, so the link renderer has to track hover
itself; a stylesheet :hover rule cannot win. Spacing: the patched
renderers.codeBlock override returned the custom block bare, so it lost
the library's codeBlock margins and whatever followed sat flush against
it. The override now keeps those margins, including the zeroed trailing
margin of the last block, so web and native share one spacing config.
* test(desktop): expect Find to return focus to the chat viewport
#7263 moved focus on closing Find from the no-drag focus scope to the
scroll viewport so Page Up and Page Down keep working. The viewport emits
no window region by design (#5233), so the old assertion that the focused
element is no-drag failed on every run. Assert the new focus target and
re-check that the titlebar still has no drag exclusions.
* fix(app): restore inline-code file path links
Classify complete inline-code spans with the file-link classifier before adapting them to the Markdown engine recognition pattern. Keep bare-text heuristics separate so route punctuation and line fragments work without adding prose false positives.
* perf(app): avoid parsing inline-link source on every reveal
Collect candidate values between equal-length backtick runs and let the Markdown engine own code boundaries. This preserves classifier decisions without reparsing long fenced blocks in JavaScript during streaming.
* fix(app): keep the compact sidebar close button beside the first nav row
The close button was absolutely positioned over the nav group, so it covered
the first row's label and hover fill. It now shares a flex row with the first
item, which shrinks to make room, and uses the compact header's icon button so
it gets the same size and round press highlight.
* test(app): cover the compact sidebar close button beside the first nav row
An image alone on its line, inside a paragraph with other lines, rendered inline
on iOS and web: the library only treats sole-child paragraph images as blocks,
and with hardSoftBreaks every newline is a break node. The source adaptation now
gives such images their own paragraph (keeping quote markers and list
indentation), so all platforms render them as blocks with image margins. Images
sharing a line with text stay inline. Verified through the library's md4c wasm
parser in the renderer tests.
* fix(android): preserve dictation across audio interruptions
Playback focus loss does not mean microphone capture is unavailable. Keep dictation alive across media takeovers and wait for Android input silencing to clear, while preserving conversation interruption protection.
* test(android): verify dictation clears its selected audio route
* fix(app): render terminal-profile icons instead of the generic Bot
* test(app): verify terminal profile artwork in the launch menu
Exercise the rendered SVG, dimensions, theme color, catalog artwork and fallback through the real browser app instead of inspecting hook results in JSDOM. The browser regression fails on main before the lookup fix.
---------
Co-authored-by: Noah <noahguerin20@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(app): group native assistant prose into selectable runs
* fix(app): render selectable native prose with enriched markdown
* docs: explain native prose selection boundaries and tradeoffs
* fix(app): preserve markdown semantics and render limits in prose runs
* docs: record native prose compatibility fixes and heading limits
* fix(app): allow additive task lists and admonitions in native prose
* fix(app): preserve inline image formatting and reference scope
* fix(app): preserve markdown source and file actions in native prose
Prepare original source blocks with source-position edits from the assistant parser. Keep reference scope and live/history projection consistent, and retain link text when dispatching workspace file actions.
* fix(app): preserve native prose entities and links while bounding preparation cost
* fix(app): preserve provisional syntax at cached reveal boundaries
* fix(app): bound prose append work and preserve markdown edge cases
* fix(app): preserve home paths and normalized prose links
* fix(app): preserve parser-owned links and streaming tilde tails
* fix(app): keep growing markdown on the streaming block renderer
* fix(app): preserve bracketed IPv6 links in native prose
* fix(app): skip prose classification for blocks over the render cap
* fix(app): share selectable native prose across Markdown surfaces
* fix(app): preserve Markdown containers before grouping prose
* revert(app): remove shared prose-run renderer changes
Reverts ab4737bb2 and ac47e1063 before replacing the Markdown engine across all platforms.
* feat(markdown): use enriched renderer across all platforms
* test: verify library image controls and isolate website config
* fix(markdown): preserve file links and image revisions
* fix(markdown): restore diagrams and preserve link origin
* fix(markdown): retain diagram identity and reference scope
* fix(markdown): avoid empty web history measurements
* fix(markdown): preserve authored empty link titles
* test(markdown): exercise find across whole-message history
* fix(markdown): preserve encoded image paths after rebase
Keep the shared renderer image normalization and file-read regression coverage that originally lived in the main integration merge.
* fix(markdown): derive table and task list colours from the theme
* fix(markdown): restore selection highlight on web
* fix(markdown): restore file link hover tooltips on web
* fix(markdown): normalize inline HTML before rendering
* fix(markdown): create renderer types when applying package patch
The published nightly omits both MarkdownRenderers type files. Represent them as new files so patch-package succeeds on a fresh npm install, matching the already patched development package.
* fix(markdown): keep the renderer tree stable while images resolve
* fix(markdown): size inline images to the line and collapse failed loads
* fix(markdown): normalize only parsed HTML nodes
* fix(markdown): keep the first image reference definition
* fix(markdown): preserve complete assistant messages on native
* fix(markdown): classify web links explicitly and complete renderer types
* fix(markdown): preserve spacing between HTML elements
* fix(markdown): normalize HTML following code elements
* fix(markdown): preserve literal image fallback labels
* fix(markdown): retry transient image decode failures
* docs(markdown): record native selection and image limitations
* fix(markdown): preserve first definitions across Mermaid segments
* fix(markdown): reserve pending images and harden web rendering
* test(markdown): cover state retention during image arrival
* fix(markdown): preserve reader position while responses grow
Keep the same message component boundary when a live row becomes retained history, preserving Mermaid runtimes and selection nodes. Observe paced child text commits at the web viewport so sticky-bottom follows before the next frame.
* test(markdown): require growth while holding a streaming paragraph
* test(markdown): isolate image arrival from reload positioning
Mounted row totals change as history virtualizes, so observe the new response and tool directly. Hold the real PNG response after reload until the reading position settles, then verify its reserved height and viewport instead of attributing initial bottom-follow positioning to image decoding.
* fix(markdown): preserve adjacency around inline HTML
* fix(markdown): keep summary block boundaries outside bold text
* perf(markdown): scan HTML block boundaries incrementally
* test(markdown): gate delivery while establishing a held reader
* test(markdown): name shared renderer setup and file actions
* test(markdown): assert linear HTML boundary work without timing
* test(markdown): check large HTML table output without observing built-ins
* fix(markdown): restore accentBright link colour
* fix(markdown): require letters and known extensions in bare path recognition
* fix(markdown): match main's blockquote, bullet and h6 colours
* fix(android): keep inverted chat still during text selection
React Native decomposes Android list inversion into a 180-degree rotation with positive scaleY, so the existing visibility-scroll guard never recognized the inverted viewport. Inspect the composed matrix and reproduce the actual list transform through the native view manager in regression tests.
* fix(markdown): linked inline code keeps inline code size
* fix(markdown): reserve a placeholder box with an image glyph while images load
* fix(markdown): bare prose paths need an extension, line suffix or depth
Slash-separated English pairs were recognized as file links in prose. Require stronger path intent there while preserving the existing inline-code recognition rule.
* fix(markdown): horizontal blocks own their drags on compact layouts
* perf(markdown): keep native streaming updates in the unfinished tail
Native markdown reparses each updated view in full. Keep parser-closed blocks in a memoized head during paced reveal, then reunite completed prose for cross-paragraph selection. Preserve trailing spacing and source indentation at the split.
* perf(markdown): reuse Android measurements across view mounts
* test(markdown): bare Fabel/model and daemon/client stay plain text when copied
a502cfd0d stopped linking bare two-segment prose paths; the clipboard
expectation still carried the links from the earlier loose pattern and
failed on CI (playwright shard 1/4 on 60b4e2d05). main expects the plain
text, so restore that.
* perf(app): limit native chat mount batches
Whole-message Markdown rows measure synchronously during Fabric layout. Render fewer history rows per batch and retain a smaller offscreen window so opening a chat does not measure twelve history rows at once.
* fix(app): keep populated markdown in sync with native layout
Fabric can collapse streaming blocks before asynchronous rendering replaces the old text, producing a blank frame. Commit populated-view updates synchronously while retaining worker preparation for first mounts to avoid moving large history parsing onto the UI thread.
* perf(app): preserve unchanged sidebar workspace projections
Reopening a retained sidebar rebuilt its entry collection and invalidated every project row despite unchanged workspace data. Preserve collection identity as well as row identity, while keeping membership and ordering changes observable.
* Revert "perf(app): preserve unchanged sidebar workspace projections"
This reverts commit a3919faed4.
* fix(app): show press feedback on sidebar rows, footer icons, and settings tiles
Touch has no hover, so these controls gave no acknowledgement when pressed.
Rows take the workspace row's pressed fill; the sidebar header row paints it
on the row so the trailing slot lights up with the button. Footer icon buttons
take the round header-control highlight.
* style(app): format sidebar header row
* fix(app): keep footer icon corners tappable on web
Web hit-testing follows border-radius, so rounding the footer buttons turned
their 44pt square targets into circles and taps near the corners fell through.
Paint the round highlight as a layer inside the square button instead.
Header islands lose their filled background and shadow; the round tap
highlight stays. The floating header's fade now carries the icon contrast
the island fill used to give, so it stays solid through the header and
runs a longer tail into the content. The fade lives inside the overlay
because Android clips children that overhang their parent, and keeps
three gradient stops because Android rendered a four-stop version far
more transparent than specified.
Single-line header titles read one step larger. The compact composer
gets a soft drop shadow. The compact settings root groups App and Host
into islands of tiles, driven by breakpoint style values so desktop
keeps its plain list.
* fix(app): scroll the Add Project results to the keyboard-selected row
The results list had no scroll ref, so ArrowUp/ArrowDown moved the selection off-screen; the autocomplete reveal math is now shared through useRevealActiveItem.
* test(app): verify the directory selected beyond the results viewport
Check scrolling in both directions and across wrapping, then confirm Enter opens the selected directory. The previous regression used an aria-selected attribute that result buttons do not render.
Co-authored-by: superheher <heh@vivaldi.net>
* test(app): verify scroll reveal through browser behavior
Keep pure offset checks and cover keyboard navigation, wrapping, and replacing search results in the real Add Project flow.
* fix(app): measure retained Add Project rows when revealing selection
* fix(app): measure scroll content through a native View reference
---------
Co-authored-by: superheher <heh@vivaldi.net>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(app): refine provider catalog actions and error feedback
Use compact row tiles and the shared error toast so failed Add actions remain visible while browsing the catalog. Point Hoplite installation instructions at the CLI install section.
* test(app): supply toast context in provider settings tests
Import the toast API independently of its renderer so the provider section only depends on the context it consumes.
Preserve the bundled Hoplite ACP adapter and provisioning transformer, activate it only after Add, and provide a searchable provider grid. Restore the contributor's original icon without the copyright mark.
Co-authored-by: Odin Glynn-Martin <odin.glynn@icloud.com>
* Put sheet tabs on the one sheet stack
Sheets are one primitive with one stack. A sheet opened from any sheet
stacks on top of it, and closing it uncovers the sheet underneath. The
compact workspace's file and subagent sheets were an in-tree Gorhom
BottomSheet with their own stacking, so a subagent chosen from the
subagents list sheet replaced the list instead of covering it.
- SheetHostScope: a subtree hosts the sheets presented inside it, so the
sheet tabs draw inside the compact column and read the workspace
providers while still joining the app-wide stack. Needs Gorhom's
BottomSheetModalInternalProvider, which the patch exports.
- IsolatedBottomSheetModal takes enableDismissOnClose; false keeps the
content mounted through a close so the unsaved-changes guard can veto
it. Its backdrop then closes the sheet rather than dismissing the top
modal. The Gorhom patch also keeps a dismissing modal's status through
its final onChange(-1), without which such a modal unmounting while
presented never left the portal.
- SheetTabs (was WorkspaceSheetHost) renders one IsolatedBottomSheetModal
per store entry. The store never reorders sheets; a reveal of a covered
sheet drops the unmodified sheets above it.
- Subagent rows that open a sheet keep the list open (closeOnSelect
false); rows that open a tab still close it.
- A menu closes when its retained panel goes inactive, instead of hiding
and reappearing with the panel on the next tab switch.
Co-Authored-By: Claude <noreply@anthropic.com>
* Show an agent's model and thinking in its sheet, and fit Open as tab in the header rail
A Paseo agent shown in a sheet has no composer, so the facts the composer's
model and thinking controls would show now sit in one line under the sheet
header, the same row provider subagents already get from the daemon. The line
resolves exactly as the controls do (resolveAgentModelSelection), from the
session agent and the providers snapshot, and shows wherever the pane host is
a sheet.
Open as tab becomes the full-screen glyph beside Close, drawn by the shared
SheetHeaderIconButton, so the title keeps its width. The tab switcher sheet is
titled "Workspace tabs" so it reads as what it lists.
Co-Authored-By: Claude <noreply@anthropic.com>
* Show mode and context usage in the sheet facts line
The line under a sheet's header now carries everything the composer's
controls and context meter show: model, thinking, mode, and used/max context
tokens with the share, each resolved as its control resolves it.
Co-Authored-By: Claude <noreply@anthropic.com>
* Re-render subagent rows when the workspace's tabs change
A row decides at render time whether choosing it opens a sheet or reveals
a tab, and keeps the list open only for a sheet. The hook behind it only
subscribed to the layout mode, so a row rendered while its agent was a
full tab kept closing the list after that tab was gone: on iOS, promoting
a managed child, closing its tab, and reopening it from the list dropped
the list under the sheet. Subscribe to the workspace's layout too.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Decode assistant Markdown image destinations before resolving a filesystem read. Keep plain filesystem paths and direct image URIs unchanged so Markdown Preview and literal percent filenames are decoded only once.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* Stack workspace sheets and tighten sidebar density
- Workspace sheets stack: opening a target from a sheet covers it, Back and
drag close only the top sheet, and the header is the sheet's drag handle.
- The workspace sheet reuses the shared sheet handle, background and header.
- Fix the shared sheet handle indicator rendering black.
- Sidebar text returns to semantic sizes; Pinned and Workspaces share one
section header.
* Pause covered workspace sheets and mock the shared sheet handle in tests
* Export BottomSheetHandle from the browser test Gorhom stub
* Let a stacked sheet's backdrop catch taps on iOS
* Bring a revealed sheet to the top instead of duplicating it as a tab
Keep a matched heading together before splitting its image and text contents. Splitting first stranded the opening heading tag in a text slice without its closing tag.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(app): keep long custom models removable
Custom model labels and IDs could consume the row width and push Remove outside the sheet. Allow those text cells to shrink while preserving the button width and short-name alignment.
* test(app): wait for legacy agent creation before checking prompts
Agent-list membership includes initialization. Waiting for finish at that point can return on the initial idle transition before the initial prompt starts. Wait for the matching creation response before checking each completed turn, preserving the existing exact-once assertions.
* test(app): wait for directory search keyboard focus
The search page becomes visible before its deferred focus callback runs. Wait for the input to own keyboard focus so the journey types its query rather than dropping it during navigation.
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* Show Claude option previews in the question card
* Wrap long lines in question option previews
* Check option previews under the option that owns them
* Show the whole option preview on native so every line is reachable
On Android, a slow drag inside a preview capped at 240px did not scroll
it, so lines past the cap could not be read. The preview's nested
scroll sits inside the option's Pressable; the same drag scrolls it
once the preview is outside the Pressable. Native now renders the
preview at full height so the chat scroll reaches every line, and a tap
on it still selects the option. Web keeps the 240px cap.
The mock agent's rollout preview is now taller than the cap so the
Android replay can check the last line.
---------
Co-authored-by: Gabriel Giordano <hello@gabrielgiordano.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
The New workspace screen submitted the composer's remembered mode as-is,
so a saved mode for a provider that offers no modes (or no longer offers
that one) failed creation with "Invalid mode '<mode>' for provider
'<provider>'". A new chat tab and the workspace setup dialog already
reconciled the mode against the provider's modes, each with its own copy
of the rule.
The draft composer state now exposes effectiveModeId, the mode the picker
shows, and all three create paths submit it. While the provider is still
loading, the selected mode is kept because its modes aren't known yet.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Matt Cowger <matt@cowger.us>
* feat(app): add fullscreen editing to the compact composer
* fix(app): preserve read-only terminal command previews
Static command previews must render their published source; the live editor buffer is updated after render and has no mounted input to repaint read-only content.
* fix(app): import React for composer editing scope rendering
Workspace pane unit tests use the classic JSX transform, so the editing-scope provider needs React in scope when mounted.
* fix(app): preserve fullscreen composer controls and editing
Keep the fullscreen frame inside the visual viewport, gate hidden autocomplete keys, apply replacement commands to local line measurement, and return to inline before starting voice so its controls stay accessible.
* test(app): cover draft replacement through the production composer
Replace the measurement-hook fixture with profile switching and typing through the real composer. Keep measurement state inside its production surface; Android manual red/green evidence covers the native replacement flow.
The html-ish pass feeds markdown to htmlparser2 so it can render
<details>, <img> and friends. htmlparser2 reads any `<` followed by a
letter as a tag and collects attributes up to the next `>`. A cell such
as `1<ratio<2` in a table therefore swallowed the rows after it, and the
re-serialized "tag" dropped the newlines and pipes that made them rows.
The same path broke autolinks (`<https://x/y>` became `<https: x y>`)
and cut text after a trailing `a<b`.
Only hand htmlparser2 what CommonMark reads as raw HTML: open tags,
closing tags and comments. Every other `<` stays text.
Unknown tags are rendered back as text, but they were rebuilt from the
parser's lowercased name and attributes, so `As<Integer>()` showed as
`As<integer>()`. Echo the source slice instead.
Preserve returned change request bodies as search-only option text so client filtering keeps description matches. Keep title and ID matches ranked ahead of secondary text.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>