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.
* 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>
* 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.
* feat(app): let the file panel close its tree rail
The file panel's tree rail was permanent on desktop — the changes panel
had a folder-tree toggle in its toolbar, the file toolbar had nothing, so
the rail was there whether you wanted it or not.
The changes panel's private DesktopTreeToggle becomes a shared
TreeRailToggle so both toolbars render the same affordance; that also
drops the treeToggleStyle prop threaded through ChangesToolbarProps,
which recomputed exactly what the component already built internally.
The i18n keys move out of workspace.git.diff into workspace.tree, since a
shared component reaching into the git namespace would rot.
The two rails keep independent flags — the file rail in panel-store, the
changes rail in useChangesPreferences — so closing one leaves the other
alone. FileTreeToggle wires itself to the store rather than taking a
callback: the bar renders several components below FilePanel, where the
rail actually lives.
Both rails also get narrower (320 -> 260 default, 200 -> 180 minimum).
The persist version bump resets treeRailWidth on purpose: nearly every
existing install carries the old 320 default, so without the reset the
narrower rail would only ever reach new users. Anyone who dragged their
rail to a deliberate custom width loses it once.
Note that MIN_CONTENT_WIDTH still clamps the rail to
`containerWidth - 240`, so in a narrow pane the new default never
applies — that floor, not the default, is what sets the width there.
* feat(app): make the side panel an empty workspace surface
The fixed, hideable side panel keeps supporting context beside an agent without seeding content. Pane-local choices stay local, while implicit supporting opens follow a persisted routing preference.
* test(app): exercise side panel policy through its capability boundary
The browser rail journey already covers the real tree-toggle behavior. Side-panel policy tests now pass their platform capability through the public boundary instead of replacing a production module.
* test(app): expect an empty side panel after upgrade
* test(app): expect an empty first split pane
* test(app): account for the empty side panel in split retention
* feat(app): make workspace panel tabs independent
Give explicit New actions fresh tab instances, keep panel state opaque to tab management, preserve File navigation context, and stabilize uncached File transitions.
* fix(app): preserve file conflicts during preview refresh
Keep the current preview mounted for same-target reads and conversions so the editor retains its conflict observation state. Clear retained previews only when the file target changes.
The empty-state text inherited the platform default color, making it unreadable on dark surfaces. Use the app foreground tokens so it follows the active theme.
* feat(app): add persistent workspace side panes
Keep the common review and file-browsing workflow beside the active agent without tearing down pane contents. Workspace tabs now share one compact chip language, assistant file links route into the retained right pane, and diff/file trees share stable geometry.
* fix(app): localize pull request empty state
* test(app): update explorer pane coverage
* fix(app): preserve explorer pane workflows
* test(app): make explorer tab selection idempotent
* fix(app): persist workspace review pins
* fix(app): preserve sidebar scrim drag hook
* fix(app): restore companion pane interactions
* test(app): follow selected sidebar surface