Commit Graph
23 Commits
Author SHA1 Message Date
Mohamed Boudraandpaseo-bot[bot] e135e9c101 Open files and subagents in a closable sheet on phones (#6827)
* feat(app): open files and subagents in a closable sheet on compact

On phones, opening a file, a provider subagent, or a Paseo subagent now shows it
in a bottom sheet over the current tab instead of adding a tab, so the tab strip
stays for terminals and root agents. Paseo subagents get "Open as tab" to move
the same tab (same tabId and state) into a full tab with a composer; agents in a
sheet hide the composer.

The decision lives in the workspace layout store's openTab, driven by a
per-kind `compactSheet` rule in the panel manifest, so every open path (chat
links, Explorer, subagents track, notifications, plugins) follows it without
call-site branching, and a new kind opts in with one manifest entry. The app
shell publishes the layout mode (compact / single-pane / split-panes) once, so
callers stop passing isCompact for placement.

A sheet holding unsaved edits is never implicitly displaced; later opens go to
tabs underneath, and closing it goes through the existing discard guard. The
editor host is position: sticky because CodeMirror's scrollIntoView otherwise
scrolls the bottom sheet's container and drags the sheet to full height.

* test(app): fail the compact sheet snap check when the sheet is oversized

---------

Co-authored-by: paseo-bot[bot] <paseo-bot[bot]@users.noreply.github.com>
2026-10-14 00:20:10 +07:00
paseo-bot[bot]andOuyang Xingyuan 9eaf7ad0c1 Render local images in Markdown file previews (#6750)
* fix(app): render local relative images in markdown file preview

File preview dropped relative images because it only accepted http/data URIs and never resolved paths against the opened markdown file.

* test(app): cover local relative images in markdown preview e2e

* fix(app): show local images in Markdown file previews

Carry forward document-relative image loading and preserve the host home
directory when resolving images beside home-relative Markdown files.
Decode file URIs once, and require decoded images in the file-pane regression.

Co-authored-by: Ouyang Xingyuan <galaxyroaming@qq.com>

* fix(app): resolve local Markdown image URL suffixes

---------

Co-authored-by: Ouyang Xingyuan <galaxyroaming@qq.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-13 01:51:07 +02:00
Mohamed Boudra cc744cc669 feat(app): let users set the chat and Markdown content width (#5680)
* feat(app): let users set the chat and Markdown content width

Wide and ultrawide screens were stuck with an 820px reading column. The
width is now an Appearance setting that flows through the Unistyles theme,
like the font sizes, so every content surface picks it up.

The stored value stays null until the user picks a width, so a future
change to the default reaches everyone who never customized it. Reset
writes null back.

The native composer dock keeps plain React Native styles and takes the width
inline because those styles land on Reanimated views, where theme-driven
Unistyles crash on theme changes.

* fix(app): pass the content width into the web stream viewport

The viewport read app settings itself, which required a QueryClient in every
render of the stream strategy. The agent stream view already reads settings,
so it hands the width over with the rest of the render input.
2026-09-29 13:49:03 +01:00
Mohamed Boudra 2c8e8a8268 fix(app): use Command+F for Find on macOS across chat, file, and terminal (#5129)
Control+F moves the caret forward on macOS, so chat Find and file pane Find
were stealing a keystroke the text editor owns. Chat Find listens on
`document` in the capture phase, so the composer received an already-cancelled
Control+F and the caret never moved.

Each Find surface carried its own copy of the shortcut predicate and only the
terminal's was platform-aware. There is now one predicate, `isFindShortcut` in
`@/pane-find`, that takes the platform as a parameter: Command without Control
on macOS, Control without Command elsewhere, no Shift or Alt. Chat Find, file
pane Find, the Find widget's own query box, terminal Find, and the terminal
emulator runtime all call it, and `terminal-find-shortcut.ts` is gone.

App surfaces source the platform from `findShortcutPlatform()`, which wraps the
app's existing `getShortcutOs()`. The predicate stays free of React Native
imports because the terminal emulator runtime is bundled on its own into the
terminal WebView; its default platform and `getShortcutOs()`'s browser branch
now share one user-agent read in `@/utils/mac-user-agent`.

Closes #5097
2026-09-21 12:34:41 +02:00
Mohamed Boudra 26e0273acb Add pane Find to editable and read-only file source (#4589)
* feat(app): add pane Find to file source

* fix(app): float the pane Find widget and tighten its chrome

Find pushed the first lines of the file down, ran 480px wide with 32px
ghost buttons, and let the replacement input start left of the query
input. Float the CodeMirror top panel out of flow so the widget overlays
the top right of the pane, move the disclosure toggle into a gutter that
spans both rows so the two fields share a leading rail, put the match
count inside the query field, and drop the icon controls to the shared
pane-toolbar chrome the file bar above already uses.

* fix(app): keep the active match visible and drop the Find button

The floating widget covered the active match whenever the match sat under
it and the document was too short to scroll clear. CodeMirror's own panel
scroll margin cannot reveal a first line, so the file integration now owns
placement: the widget renders as an overlay beside the editor, the model
measures it against the active match, and flips it to the opposite corner
only when the current corner collides. Hysteresis keeps navigation from
bouncing the widget between corners.

Also removes the standalone Find button shown while Find is closed, per
user direction. Cmd/Ctrl+F is the entry point.

* fix(app): stop the Find placeholder from hiding Go to line

Hiding `.cm-panels` suppressed every CodeMirror panel in the file editor,
including the Go to line dialog that Mod-Alt-g opens through the same
search keymap. Hide only the inert Find placeholder panel instead, by its
own class, and leave the shared panel container alone.
2026-09-15 17:20:27 +02:00
Mohamed Boudra ec5ab3fc4d revert(app): undo file Find merge pending maintainer review
Reverts 69510ac72f. The feature must return to an open pull request for maintainer review.
2026-09-09 19:39:14 +02:00
Mohamed Boudra 69510ac72f feat(app): add pane Find to file source (#4586)
Make Find discoverable at the top right of editable and read-only web file panes, using shared controls backed by CodeMirror search. Preserve literal case-insensitive buffer search, keyboard focus and navigation, optional replacement, Undo, and save.
2026-09-09 19:28:36 +02:00
Mohamed Boudra eef9be8863 feat(app): add zoomable image previews (#4032)
Image files now zoom in place, while timeline images open the existing lightbox. A shared viewport extracted from Mermaid keeps fit, focal zoom, pan bounds, and consumer toolbar actions consistent across content types.
2026-08-29 22:08:17 +02:00
Mohamed Boudra c914bcb44b feat(app): make Explorer a first-class pane host (#3826) 2026-08-25 13:25:57 +02:00
Mohamed Boudra c763b80824 fix(files): keep large source previews responsive (#3665)
Large read-only files bypassed the editor's virtualization and eagerly mounted every highlighted token, which could stall the renderer for seconds and consume gigabytes of memory. Route source previews through bounded platform renderers and reject reads above their display budgets before accumulation.
2026-08-22 10:13:31 +02:00
Mohamed Boudra 19a7d26349 feat(app): make the side panel user-directed (#3605)
* 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.
2026-08-20 21:49:23 +02:00
Mohamed Boudra 5ec26f0c09 feat(app): align interface typography to base size
Treat the appearance setting as the rendered base size so the type ramp scales predictably. Native starts one pixel larger while web and desktop preserve the authored 14px base, with legacy values migrated on load.
2026-08-17 22:55:46 +02:00
Mohamed Boudra ab274d635e Make the workspace right pane a first-class tab host (#3287)
* 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
2026-08-16 18:18:32 +02:00
Mohamed Boudra 23d7a8eaf9 Improve Markdown file preview readability (#3240)
* feat(app): improve markdown file previews

Keep rendered documents on the chat reading rail, surface YAML front matter as readable metadata, and make shared Markdown links retain the chat accent and hover treatment.

* fix(app): preserve unsafe frontmatter source

* test(app): cover markdown link behavior
2026-08-12 00:18:30 +02:00
e35ad9f692 feat(app): preview HTML files in the file pane (#2712)
* feat(app): preview HTML files in the file pane

Renders .html/.htm files as a page in the file pane, with the same
Preview/Source toggle Markdown already has. The main use is reading
richer visual plans an agent wrote as a self-contained HTML page.

A preview is a viewer, not a browser. Each document is prefixed with a
doctype and a strict policy, so the policy is always the first element
and always lands in the parser-created head: inline script and style
run, and remote subresources, fetch, XHR, WebSocket, beacon, and form
posts are refused. The frame has an opaque origin, so it cannot reach
the app's DOM and storage and cookie APIs throw inside it.

One gap is left on web and cannot be closed there: a sandboxed
document may navigate itself, and no CSP directive in current browsers
prevents it -- navigate-to was dropped from CSP3 and is unenforced, and
meta refresh needs no script (both verified against the Chromium the
app ships against). The opaque origin bounds it to the page's own
contents, and SECURITY.md documents it. Native narrows it further by
refusing navigation after the initial document, with the caveat that
the decision runs in app JS.

Self-contained pages only -- a page that pulls a CDN script renders
unstyled, and Source still shows everything.

* fix(app): harden HTML preview behavior and tests

---------

Co-authored-by: Nicholas Salgueiro <nicholas.salgueiro@britecore.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-08-01 14:30:59 +00:00
Mohamed Boudra 8e983c5d27 Stop transient file conflict callouts (#2694)
* fix(app): prevent transient file conflict callouts

Watcher versions are candidates until a matching disk read settles them. The editor now classifies the settled bytes once, preserving dirty buffers and keeping reload content paired with its revision.

* fix(app): preserve external BOM changes
2026-07-31 19:03:11 +08:00
Mohamed Boudra b6f1274f47 Clarify changed and deleted files in the file viewer (#2670)
* fix(app): distinguish changed and deleted files

Successful reads now reconcile transient missing events without letting stale
cached data mask a deletion. File notices expose only actions valid for the
current buffer state.

* fix(app): preserve file state ordering

* fix(app): allow retrying file checks

* fix(app): clear unchanged file conflicts

* fix(app): keep file conflict recovery consistent

Own disk observation and read ordering in one model so stale reads and failed subscriptions cannot produce contradictory actions.

* fix(app): allow retrying file preview errors

Preserve daemon read errors and expose retry for initial and non-editable file loads.
2026-07-30 22:50:58 +02:00
Mohamed Boudra b97d6d13f3 Load complete chat history when reaching the top (#2481)
* fix(chat): load older timeline history consistently

Pagination depended on scroll events, so short or compacted initial pages could never request older history. Reevaluate edge visibility as layout and history change, and standardize loading indicators on the canonical app spinner.

* fix(chat): keep older history loading reliably

Use authoritative timeline cursor progress so merged rows cannot stall pagination. Wait for bottom anchoring before evaluating the history edge, and re-arm retries on a fresh upward gesture without automatic failure loops.
2026-07-27 18:21:07 +08:00
Mohamed Boudra 1a1ff8828f feat(editor): wrap long Markdown lines (#2459) 2026-07-27 00:20:39 +02:00
Mohamed Boudra 779a56ed36 fix(app): hide Markdown source toggle when editing is unavailable (#2382) 2026-07-24 18:14:08 +08:00
维她命@andMohamed Boudra a3438f96f8 fix(app): preserve file line endings and UTF-8 BOM (#2277)
* fix(app): preserve file line endings and UTF-8 BOM

* refactor(app): simplify line ending preservation

Let the editor parse newline variants and serialize with the file's first separator. Mixed-ending files become uniform on edit without a separate normalization layer.

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-07-23 19:43:09 +02:00
Mohamed Boudra cb6d2f1459 Open chat file links at the referenced line (#2309)
* fix(app): open chat file links at referenced lines

Pass chat file locations through the editable CodeMirror path so opening a reference selects and scrolls to its requested line. Cover the real assistant-link flow with a browser regression.

* fix(app): repeat chat file navigation

Track file navigation separately from stable tab identity so reopening the same path and line recenters both editable and read-only panes. Extend the browser regression to cover moving away and clicking the same link again.

* test(app): stabilize repeated file link navigation
2026-07-21 21:34:20 +02:00
Mohamed Boudra 4bda2dfea9 Edit workspace files directly on web (#2270)
* feat(files): edit workspace files on web

Keep source buffers synchronized with host file changes and require an explicit overwrite or reload when revisions diverge.

* feat(panels): surface and protect modified tabs

Expose tooltip and modification state through the generic panel boundary so tabs can show stable metadata and guard every close route consistently.

* fix(tests): use portable fake timeout handle

* fix(files): harden editor conflict handling

Preserve modified panel state across tab eviction, use precise revisions for optimistic writes, coalesce concurrent file watchers, and localize the editor interface.

* fix(files): close editor concurrency gaps

Coalesce clean reloads, preserve subscriber identities and file permissions, suspend pending saves during close confirmation, and carry precise revisions through file reads.

* test(files): expect read revision metadata
2026-07-21 04:41:19 +08:00