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.
The Host field used to render only with two or more hosts, and Add host
only appeared on compact layouts. Single-host users had no sign that
Paseo can drive other machines from this screen. The field now always
renders and its menu always ends with Add host.
* fix(server): close stale agent status after daemon restart
Repair persisted active statuses before serving clients. Let Claude process-tree cleanup run immediately after SDK close rather than waiting on an already closed control channel.
* test(app): expect closed agent status after daemon restart
* feat(git): default review to all branch changes
Keep the selected comparison stable while agents edit and commit so reviewing the complete result does not require switching scopes or interrupt unfinished comments.
* test(git): release Windows checkout handles during cleanup
Allow transient filesystem handles from subscription teardown to close before removing the test repository.
* fix(server): batch agent reports and completion notifications
Admit agent-originated messages immediately so a child can report and finish before one recipient-owned delivery. Keep human prompts direct, retain individual provenance through replay, and complete send receipts only after provider dispatch.
* test(server): wire queued delivery into bridge integration fixture
* test(server): resolve batching CLI fixture independently of cwd
Workspace CI runs from packages/server, while the original regression was verified from the repository root. Resolve the CLI entrypoint relative to this test file in both contexts.
* test(server): run batching CLI fixture with source exports
Server integration CI builds only the client dependency stack. Match the existing CLI subprocess fixtures so the send regression exercises workspace sources without requiring compiled server output.
* test(server): await persisted batch receipts within integration deadline
The provider-start callback precedes asynchronous receipt commits. Use the same explicit integration deadline as dispatch instead of the matcher default of one second on Windows CI.
* feat(hosts): connect to saved hosts through a daemon
* fix(hosts): validate safe targets and verify encrypted relay routes
* test(hosts): include relay coverage in existing CI contracts
* fix(cli): preserve validated addresses in daemon command hints
* fix(hosts): share daemon links across isolated routed sessions
Multiplex logical sessions over the monitored daemon connection and wrap original binary bytes in routing headers. Admit channels under their authenticated parent and current grants; preserve scoped delivery and physical send completion.
* fix(hosts): bound encrypted overload and initial acquisition
Check encrypted wire capacity before the async channel converts a resource limit into a physical connection error. Bound initial routed acquisition with the existing client timeout, then enable reconnect after admission.
* fix(hosts): keep compact row actions clear of host labels
Give compact connected-host rows separate text and action widths so long names and producer labels cannot compete with primary controls.
* refactor(hosts): remove management settings UI from phase one
Keep connected-host management in the CLI and shared SDK while preserving app saved connections and plugin access.
* test(hosts): control physical write backpressure across platforms
Hold real TCP writes until assertions finish instead of assuming a paused receiver exhausts every platform receive buffer. Preserve physical send settlement, byte identity, capacity and sibling checks.
* refactor(hosts): use the ordinary client contract over channel transports
Keep queuing, permission codes and reconnection independent of transport selection. Track submitted request outcomes generically while retaining connection error codes and structured causes.
* test(e2e): keep transient developer toasts from stalling checks
Concurrent locator handlers can wait for a dismiss button another handler removed. Dismiss existing toast DOM at document lifetime, including its open shadow root, without intercepting app actions.
* Create, prompt, and monitor agents across connected hosts (#7507)
* feat(hosts): delegate agents across connected hosts
Resolve MCP host operations through in-process commands for self and the existing routed client transport for other destinations. Keep ordinary parent labels, app sessions and idle finish notifications.
* test(app): normalize subagent fixtures through snapshots
* docs(protocol): clarify same-host parent labels
* fix(hosts): resolve subagents through destination sessions
A child address identifies its destination regardless of the connection used. Reuse the existing host candidate selection for discovered routes and direct connections, and keep navigation and row actions independent of the parent transport.
* fix(hosts): keep routed destinations distinct during discovery
A routed candidate names only the origin daemon. Match it within the named destination profile so two destinations discovered through the same origin retain separate identities and connections, including existing saved routes.
* feat(hosts): prompt and query agents on selected hosts
Extend the existing MCP host operation boundary with prompt, status, activity and list operations. Local calls retain their handlers and notification owner; selected destinations use ordinary client RPCs and the existing idle finish wait.
* test(e2e): preserve toast coverage with CSS hiding
* docs(hosts): translate connected host references
* 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.
* feat(app): record Android stalls in opt-in diagnostic builds
* refactor(app): own watchdog sinks and bounded log storage
* fix(app): preserve cumulative browser profiling history
On every page load the layout mode publisher sets the workspace layout store
before its stored state has been read. Zustand persist wrote the unhydrated
defaults, and hydration merges the stored layout into memory without writing
it back, so storage held no tabs until the next layout change. Leaving the
page inside that window lost every workspace's tabs. Validated persist storage
now drops writes that arrive while its read is in flight.
* feat(website): serve paseo.sh in Simplified Chinese
Every page of the website now has a Simplified Chinese version under
/zh-CN: the interface, docs, blog posts, comparison pages, legal pages,
all changelog releases and the homepage mockup. English URLs are unchanged.
The URL alone decides the language. There is no browser-language redirect
and no stored preference, because either would have to redirect the
unprefixed English URLs that people share and search engines index.
Internal links carry the page's language instead, and hreflang alternates
point Chinese search results at /zh-CN.
Translated headings pin the English anchor with {#id}, so section links
work in both languages. content-translations.test.ts fails when a page
has no translation or a translation's headings drift from the English.
Changelog releases and plugin listings without a translation render
whole in English under a notice, so a release never waits on one.
Plugin manifests and the registry gain optional translations and
OVERVIEW.zh-CN.md fields; older clients and registries ignore them.
* feat(website): replace the native language select with an EN/ZH menu
The header showed an OS-native select whose popup did not match the site.
The trigger now shows the language abbreviated (EN, ZH) and opens a menu in
the app's dropdown style that names each language in itself, with its name
in the page language underneath. Rows are real links to the same page in
that language; arrows, Escape, outside presses and taps behave as a menu
button, and the menu keeps itself on screen when a narrow header pushes the
trigger toward an edge.
Also drops the stray space after the full-width comma in the Chinese
homepage subtitle.
* docs(website): translate main's new docs and 0.12.0 release notes
Main added Keep run workspaces to the schedules docs, rewrote plugin icons
around inline SVG, and promoted 0.12.0 with new entries. Bring the Chinese
pages and changelog in line so no section falls back to English.
* fix(server): leave plugin listing translations to the registry
The daemon rejected manifests whose translations the registry would refuse,
so a plugin main installs could fail to load here. Paseo never reads
translations; the registry validates them at publish. Docs now say which
checks are automated at publish and which are human review.
* docs(plugins): describe the registry's final overview translation discovery
* style(server): format manifest test
* feat(plugins): translate plugin UI in each app language
Keep translation resources and subscriptions inside each installation. Resolve host labels against the presentation locale so live language changes preserve mounted drafts and RPC state. Existing descriptor types retain string defaults for source compatibility.
* test(plugins): dismiss developer chrome in second translation client
* test(app): measure sidebar rows one at a time
After a reload the developer error toast appears while expectVerticalOrder
measures every row at once. Each concurrent boundingBox runs the toast
locator handler, and only one of them resumes, so the poll timed out on
every attempt since #7424. Measuring sequentially keeps one action in flight.
* test(app): return sidebar row measuring to main
#7480 hides the developer toast instead of running a locator handler, so
measuring the rows concurrently no longer races it. Reverts 41ac23016.
* test(app): wait for the compact shell before opening compact Changes
The shell survives the breakpoint, so right after the resize the wide
Explorer could still be visible: the test skipped the Explorer toggle and
the tab then disappeared, or toggled at the wrong time. Wait for the
compact-shell marker, as the composer helpers do since #7424.
* docs(website): translate main's plugin file panel docs
* feat(website): serve paseo.sh in Simplified Chinese
Every page of the website now has a Simplified Chinese version under
/zh-CN: the interface, docs, blog posts, comparison pages, legal pages,
all changelog releases and the homepage mockup. English URLs are unchanged.
The URL alone decides the language. There is no browser-language redirect
and no stored preference, because either would have to redirect the
unprefixed English URLs that people share and search engines index.
Internal links carry the page's language instead, and hreflang alternates
point Chinese search results at /zh-CN.
Translated headings pin the English anchor with {#id}, so section links
work in both languages. content-translations.test.ts fails when a page
has no translation or a translation's headings drift from the English.
Changelog releases and plugin listings without a translation render
whole in English under a notice, so a release never waits on one.
Plugin manifests and the registry gain optional translations and
OVERVIEW.zh-CN.md fields; older clients and registries ignore them.
* feat(website): replace the native language select with an EN/ZH menu
The header showed an OS-native select whose popup did not match the site.
The trigger now shows the language abbreviated (EN, ZH) and opens a menu in
the app's dropdown style that names each language in itself, with its name
in the page language underneath. Rows are real links to the same page in
that language; arrows, Escape, outside presses and taps behave as a menu
button, and the menu keeps itself on screen when a narrow header pushes the
trigger toward an edge.
Also drops the stray space after the full-width comma in the Chinese
homepage subtitle.
* docs(website): translate main's new docs and 0.12.0 release notes
Main added Keep run workspaces to the schedules docs, rewrote plugin icons
around inline SVG, and promoted 0.12.0 with new entries. Bring the Chinese
pages and changelog in line so no section falls back to English.
* fix(server): leave plugin listing translations to the registry
The daemon rejected manifests whose translations the registry would refuse,
so a plugin main installs could fail to load here. Paseo never reads
translations; the registry validates them at publish. Docs now say which
checks are automated at publish and which are human review.
* docs(plugins): describe the registry's final overview translation discovery
* style(server): format manifest test
* test(app): measure sidebar rows one at a time
After a reload the developer error toast appears while expectVerticalOrder
measures every row at once. Each concurrent boundingBox runs the toast
locator handler, and only one of them resumes, so the poll timed out on
every attempt since #7424. Measuring sequentially keeps one action in flight.
* test(app): return sidebar row measuring to main
#7480 hides the developer toast instead of running a locator handler, so
measuring the rows concurrently no longer races it. Reverts 41ac23016.
* test(app): wait for the compact shell before opening compact Changes
The shell survives the breakpoint, so right after the resize the wide
Explorer could still be visible: the test skipped the Explorer toggle and
the tab then disappeared, or toggled at the wrong time. Wait for the
compact-shell marker, as the composer helpers do since #7424.
* docs(website): translate main's plugin file panel docs
* test(app): edit the code font size after the font family remount
A committed appearance change remounts the settings screen through
AppearanceStyleBoundary, and the new form starts from the stored size.
Filling the size before that remount lost the edit in CI.
* docs(website): translate main's client/ui import docs
Follows #7474: one /client/ui entry, StreamedText instead of useRevealedText,
SettingsGroup dropped from the settings table, and the new
'UI imports in v0.13' migration section.
* test(app): wait for the previous layout's file panel picker to leave
After a resize the old layout's picker animates out while the new one is
already mounted, so the Use button matched two elements and the strict
locator failed. Wait for the shell's layout and a single picker first.
Preview links used the workspace root even when the document was nested. Carry a separate relative-path base through the file-link resolver so clicks and hover paths agree while chat keeps workspace-relative resolution.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(files): report document MIME types for binary files
File reads now carry the document's MIME type for binary files, so a file panel can
hand the bytes to a viewer that needs the real type (application/pdf).
* feat(plugins): file panels
Plugins contribute file panels with addFilePanel. A file tab hosts the panels that claim the
file: the built-in Markdown, HTML and image panels, plugin panels, and Source, Paseo's editor,
which claims every text file and is always the last candidate. The panel is the tab body;
the bar shows the document's status and a switcher between the file's panels (side by side
up to three, two on a phone, a menu beyond that).
Paseo asks which panel opens a file only when a plugin panel is among several candidates,
once per open, and remembers the answer per extension or exact filename when told to. Open
with in the tab menu and the switcher change the tab's panel; Always use remembers it.
Opening a file at a line switches the tab to Source.
Panels read and write the host's document, so dirty state, autosave and conflicts stay
Paseo's. DocumentFrame shows bytes documents such as PDFs in the browser's viewer.
Replaces the file tab's Preview/Source mode toggle.
* feat(file-panels): choose and forget file panels in Settings > Files
Settings > Files lists each extension or filename where a plugin panel competes with
another candidate, or that has a remembered panel. Each row chooses the panel that opens
it, or Ask every time; Ask again forgets the choice. Source appears as a candidate for
text files. Panels no connected host offers can be forgotten but not chosen.
* docs(plugins): file panel examples and author reference
Adds the pdf-panel and markdown-panel examples, the File panels section of the plugin
reference, and an e2e spec that installs both examples, opens a PDF in the browser
viewer, and edits Markdown through Paseo's document across the switcher, Source,
autosave and conflicts.
* feat(file-panels): panels read and write through a file handle
The file tab stats the file and mounts its panel without reading it. Every
panel, built-in or plugin, gets a PluginFile handle: readText, readBytes, url,
version-checked writeText/writeBytes (PluginFileConflictError unless force),
and watch. useFileText, useFileBytes and useFileUrl read through it, and
Loading joins @getpaseo/plugin/client/ui. Panels own their loading state.
The host document pipeline, the explorer preview cache and the preview
lifecycle are deleted. The daemon classifies a file version from a bounded
sniff and writes base64 content as bytes behind fileByteWrites.
A deleted file keeps its panel mounted; a read confirms the deletion first,
so a remove-and-rewrite is not reported as one. Native Source decodes with
builtins and highlights in idle-time slices, the lines around the opening
line first, so a large file no longer blocks the JS thread.
* fix(file-panels): Settings offers Source where the picker does, and review follow-ups
- Settings > Files offers Source on every key except those a built-in panel of
another kind (Image) claims, so a plugin that alone claims a text extension
shows the same choices as the picker.
- Delete the unused isSourceFilePanel.
- The Markdown example warns with the file name when its unmount write fails,
rather than dropping the edit silently.
- Document that PluginFile stat fields are read at render time and that watch
or the read hooks are how a panel learns they changed.
* refactor(plugin): consolidate the client UI entrypoint
* fix(files): report document MIME types for binary files
File reads now carry the document's MIME type for binary files, so a file panel can
hand the bytes to a viewer that needs the real type (application/pdf).
* feat(plugins): file panels
Plugins contribute file panels with addFilePanel. A file tab hosts the panels that claim the
file: the built-in Markdown, HTML and image panels, plugin panels, and Source, Paseo's editor,
which claims every text file and is always the last candidate. The panel is the tab body;
the bar shows the document's status and a switcher between the file's panels (side by side
up to three, two on a phone, a menu beyond that).
Paseo asks which panel opens a file only when a plugin panel is among several candidates,
once per open, and remembers the answer per extension or exact filename when told to. Open
with in the tab menu and the switcher change the tab's panel; Always use remembers it.
Opening a file at a line switches the tab to Source.
Panels read and write the host's document, so dirty state, autosave and conflicts stay
Paseo's. DocumentFrame shows bytes documents such as PDFs in the browser's viewer.
Replaces the file tab's Preview/Source mode toggle.
* feat(file-panels): choose and forget file panels in Settings > Files
Settings > Files lists each extension or filename where a plugin panel competes with
another candidate, or that has a remembered panel. Each row chooses the panel that opens
it, or Ask every time; Ask again forgets the choice. Source appears as a candidate for
text files. Panels no connected host offers can be forgotten but not chosen.
* docs(plugins): file panel examples and author reference
Adds the pdf-panel and markdown-panel examples, the File panels section of the plugin
reference, and an e2e spec that installs both examples, opens a PDF in the browser
viewer, and edits Markdown through Paseo's document across the switcher, Source,
autosave and conflicts.
* feat(file-panels): panels read and write through a file handle
The file tab stats the file and mounts its panel without reading it. Every
panel, built-in or plugin, gets a PluginFile handle: readText, readBytes, url,
version-checked writeText/writeBytes (PluginFileConflictError unless force),
and watch. useFileText, useFileBytes and useFileUrl read through it, and
Loading joins @getpaseo/plugin/client/ui. Panels own their loading state.
The host document pipeline, the explorer preview cache and the preview
lifecycle are deleted. The daemon classifies a file version from a bounded
sniff and writes base64 content as bytes behind fileByteWrites.
A deleted file keeps its panel mounted; a read confirms the deletion first,
so a remove-and-rewrite is not reported as one. Native Source decodes with
builtins and highlights in idle-time slices, the lines around the opening
line first, so a large file no longer blocks the JS thread.
* fix(file-panels): Settings offers Source where the picker does, and review follow-ups
- Settings > Files offers Source on every key except those a built-in panel of
another kind (Image) claims, so a plugin that alone claims a text extension
shows the same choices as the picker.
- Delete the unused isSourceFilePanel.
- The Markdown example warns with the file name when its unmount write fails,
rather than dropping the edit silently.
- Document that PluginFile stat fields are read at render time and that watch
or the read hooks are how a panel learns they changed.
* fix(plugins): file events are objects, deletion is a typed error, DocumentFrame owns its fallback
- watch listeners receive { type: "changed" } | { type: "deleted" }, so later
versions can add fields without breaking them.
- PluginFileDeletedError joins PluginFileConflictError. The read hooks report a
deleted file with it, and a write over a deleted file rejects with it; Source
shows that as the deleted notice. PluginFileConflictError now means only
"changed on disk".
- DocumentFrame on Android shows Paseo's "<TYPE> files can't be shown on this
device" message for documents its WebView cannot display, so the PDF example
is useFileUrl + Loading + DocumentFrame with no platform branch.
* test(file-panels): Settings offers Source for a remembered panel no host has
* test(e2e): hide the developer error toast instead of clicking it away
The locator handler that dismissed #error-toast could match a toast that closed before its
click, then wait on it until the test timed out (agent-timeline-resume.spec.ts:45 failed every
attempt on this branch). Hiding the toast with CSS keeps it off app actions with no race.
* test(e2e): desktop browser-tabs hides developer toasts on its attached page
* fix(android): restore development builds after Expo upgrade
Port the native touch-cancellation patch to the gesture-handler API while retaining drawing order and surviving native gestures. Match the bundled Skia font scanner and Unicode paragraph construction APIs.
* test(e2e): hide the developer error toast instead of clicking it away
The locator handler that dismissed #error-toast could match a toast that closed before its
click; dispatchEvent then waited for the gone button until the test timed out. Hiding the toast
with CSS keeps it off app actions with no race.
* test(e2e): desktop browser-tabs hides developer toasts on its attached page
* test(cli): keep the Hub deploy fixture alive on malformed requests
An empty request body crashed the HTTP fixture outside its assertions. Return HTTP 400 for invalid JSON and exercise that request before the full validation and installation assertions.
* test(e2e): hide the developer error toast instead of clicking it away
The locator handler that dismissed #error-toast could match a toast that closed before its
click; dispatchEvent then waited for the gone button until the test timed out. Hiding the toast
with CSS keeps it off app actions with no race.
* test(e2e): desktop browser-tabs hides developer toasts on its attached page
* 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>
* perf(app): load translations when their language is selected
Eager locale imports evaluated and registered all nine translation bundles on every cold start. Load bundled locales through a synchronous i18next backend so language selection keeps its before-render contract and offline behavior.
Co-authored-by: Ivan Malison <IvanMalison@gmail.com>
* test(app): normalize translation entry paths across platforms
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Ivan Malison <IvanMalison@gmail.com>
* fix(android): align Unistyles NDK version
* fix(android): use the project NDK for every native module
The diff module still selected the Android Gradle default NDK, causing a second installation after Unistyles was aligned. Inherit the project toolchain and check all configured native projects.
---------
Co-authored-by: dwyanewang <908280968@qq.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(workspaces): observe setup output only in the visible setup panel
Setup logs were requested for the whole session, including unrelated workspaces. Use directory status for discovery and source-owned workspace observations for details, preserving legacy delivery only for clients without the negotiated capability.
* fix(workspaces): publish external setup progress once per daemon
MCP and scheduled setup enter through the broadcast API. Publish them to the shared setup owner before session fanout so each observation gets one update and snapshots survive periods without connected clients. Keep the setup action available on older hosts to explain the required host update.
* fix(app): replace setup details with each observation snapshot
A host restart can return an empty setup snapshot. Apply that authoritative snapshot so an open panel does not retain output from the previous daemon. Assert setup membership requests as well as delivery in the browser lifecycle regression.
* test(workspaces): cover setup status catch-up after directory release
* test(workspaces): synchronize setup output and release fixture resources
* 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>
Tab and workspace navigation need a final 1–9 index. Prevent saving an incomplete recorded sequence and explain the requirement while preserving arbitrary chord prefixes.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Every contribution `icon` now takes a Lucide name, `{ svg }`, or a
component. Attachment sources take only the first two because composer
drafts persist the source icon.
The daemon's provider-icon check and the app's inline-SVG check are the
same function in the shared SDK root, so a plugin sees the same reasons
in both places. The app parses each icon once at registration and fails
the plugin with that reason. Rendering goes through one module that
turns any form into a component, so surfaces never branch on the form.
* perf(app): throttle workspace layout writes and skip no-op persists
Zustand persist calls setItem after every set, even when the updater
returns the same state. The layout store re-normalized every workspace
tree, re-validated the envelope, stringified it, and wrote to
AsyncStorage on each agent status change because reconcileTabs runs from
an effect keyed on agent visibility.
The draft store's throttled writer moves to storage/ as a generic
throttled persist storage, gains a referential short-circuit for a value
whose partialized state was already accepted, and now backs both the
draft and layout stores. The layout store's partialize reuses its
previous projection while every persisted slice is the same reference,
and the store is flushed alongside drafts when the app leaves the
foreground.
Before: 50 no-op reconciles produced 50 writes. After: 0 writes; 50 real
sets inside one interval produce 2 writes (leading and trailing).
Fixes#4814
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(app): build layout stores with options and flush before rehydrating
Six call sites still passed the id factories positionally, which this
branch replaced with an options object, so those stores silently used
default ids. The once-only PR test also rehydrated a second store before
the throttled write landed; flush the source first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(app): avoid shadowing layout options
---------
Co-authored-by: Ivan Malison <IvanMalison@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
The compact Changes toolbar flattens every git action into one menu, and
`secondary` keeps the actions the policy can promote to `primary`. When the
policy promoted one of them - Pull behind origin, Push ahead of it, the merge
and PR actions - it rendered twice, under the same React key.
Dedupe by id in `actionsWithoutPrimary` so the split button and the command
center share one rule, cover it in policy.test.ts against real buildGitActions
output, and pin the rendered menu in a compact Changes browser spec.
(cherry picked from commit 9bb26cc471)
Co-authored-by: az33zy <10743275+az33zy@users.noreply.github.com>
* fix(app): dismiss touch composer focus when switching tabs
A retained tab can hide and return before Chromium paints, preserving the focused textarea without another focus call. Blur web touch editors when their pane becomes inactive so returning leaves the conversation ready to read. Exercise the same mounted editor through a second edit and keyboard navigation cycle.
* test(app): reproduce touch tab focus race before paint
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
The subagent track rows were as dense on a phone as in the desktop popover,
and the archive and detach actions were hand-rolled pressables that only
recoloured the glyph, so a tap or hover never showed the rounded highlight
every other icon action has.
Rows now take the compact menu-row height and padding below the `md`
breakpoint, decided once in the track row style; the desktop popover keeps
its density. The actions are `ToolbarButton`, which owns the hitbox, the
rounded highlight on hover and press, the compact size, and the tooltip.
A repo oxlint rule, `paseo/no-raw-pressable`, reports `Pressable` and
`Touchable*` imports outside `components/ui/` so the next tap target reaches
for a primitive. Existing importers are on a burn-down list they can only
leave. The rule is its own name rather than another `no-restricted-imports`
entry so its override cannot interact with the existing burn-downs. Its test
runs the real oxlint binary and is wired into the CI quality job.
* 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>
Hold the stream through the click so autoscroll cannot move the group between pointer-down and pointer-up. Resume after expansion and verify that inline calls keep updating.
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.
* perf(app): skip workspace agent visibility rebuild on stream ticks
The workspace screen derived agent visibility on every session-store
update: it copied every agent and agent detail into a new Map, walked
all agents, and allocated two Sets, then compared them. Stream ticks
update the store but leave the agent maps untouched, so this was pure
waste at stream rate.
createWorkspaceAgentVisibilitySelector returns the previous result until
session.agents or session.agentDetails changes, and keeps it when a
recompute yields the same sets. Parent lookups read the two maps
directly instead of merging them.
Fixes#4817
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* perf(app): skip subagent row rebuild on stream ticks
Every mounted agent panel selected its subagent rows by walking all
session agents, allocating rows, sorting, and deep-comparing on every
session-store update, including every stream tick.
createSubagentsForParentSelector returns the previous rows until
session.agents changes, and keeps them when a recompute yields equal
rows. The hook rebuilds the selector when the parent or the pending
archive ids change.
Refs #4817
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* perf(app): check pending submissions without allocating
selectAgentTurnPresentation runs from several session-store selectors
and filtered the submission list only to read its length. Use some().
Refs #4817
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Ivan Malison <IvanMalison@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* 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(app): report offline hosts in new agent drafts
A disconnected client still exists, and pending form resolution and retained loading catalogs outlive the connection. Use host connection state for draft readiness and model loading while keeping known models available for reconnect.
Co-authored-by: Ivan Malison <IvanMalison@gmail.com>
* test(app): confirm draft recovery on a fresh host connection
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Ivan Malison <IvanMalison@gmail.com>
* 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 subagent rows in the subagents list had an empty info strip: the row
only rendered the daemon-provided subtitle, which only provider subagents
carry. The row now uses the same facts line an agent's sheet shows when it
opens without its composer, so both surfaces resolve model, thinking, mode,
and context the way the composer's controls do.
The facts sit under the title instead of beside it, so a long task name
keeps the row's width instead of fighting the strip for it.