Commit Graph
1091 Commits
Author SHA1 Message Date
Bohdan Triapitsyn c80c2b62a8 feat: add one-click git sync button
Combine fetch, pull with rebase, and push into one sync action
Keep remote dropdown focused on safe fetch actions
Block sync when uncommitted changes would conflict with rebase
2026-05-05 20:45:31 +03:00
Bohdan Triapitsyn ddfd2db0b8 fix: keep button text unselectable outside markdown
Restore non-selectable text for app buttons
Keep markdown file links selectable
2026-05-05 20:45:31 +03:00
2b0e1ef42e fix(sync): preserve pending questions across session switch and directory eviction (closes #918) (#1103)
* fix(sync): preserve pending questions across session switch and directory eviction

Closes #918, completes the gap left by #909.

The 'agent question disappears after switching session / coming back
later' bug had two root causes that #909 only partially addressed:

1. Directory-eviction TTL (20 min) silently dropped child stores that
   held pending questions/permissions. The discard wasn't gated on
   in-flight blocking-request state, so any 'question.asked' event
   that arrived during the eviction-then-rehydrate window was routed
   to a non-existent store and silently lost.

2. PR #909 re-fetches listPendingQuestions/Permissions only on SSE
   reconnect. Switching sessions within the same socket — including
   navigating back to a directory whose child store was rebuilt after
   eviction — left the UI relying on store state that may have missed
   events that fired while a different session was active.

Three edits, in src/sync:

- eviction.ts / types.ts / child-store.ts: add hasPendingBlockingRequests
  to EvictPlan + DisposeCheck and never evict a directory whose store
  carries a non-empty state.question or state.permission record.
- sync-context.tsx: extract resyncBlockingRequestsForDirectory from the
  reconnect path and call it on currentSessionId changes (debounced
  250ms), reusing PR #909's signature-based merge so concurrent SSE
  updates aren't clobbered.
- __tests__/eviction.test.ts, __tests__/session-switch-resync.test.ts:
  new unit coverage for the eviction guard and resync semantics
  (deduped fetch per switch, in-flight SSE preservation, stale entry
  cleanup, unknown-session filtering).

* fix(sync): refresh store before blocking request resync

---------

Co-authored-by: Alexander Busse <alex@ableph.net>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-05 19:39:35 +03:00
Bohdan Triapitsyn a49e2ce1ae perf: optimize markdown file link detection
Only mark existing files as openable links
Avoid file-link behavior while messages stream
Limit file existence checks to 4 concurrent requests
2026-05-05 19:21:44 +03:00
Bohdan Triapitsyn fd6635a963 fix: prevent generate button flicker during git refresh
Keep Generate enabled during changed-file refreshes
Preserve existing blockers for generation and commits
2026-05-05 19:00:35 +03:00
Bohdan Triapitsyn 6dd322ddbe fix: reduce local server status overhead 2026-05-05 18:47:00 +03:00
962ee41bba fix(ui): add opt-in mobile keyboard resize mode and stabilize touch terminal input (#1107)
* fix(ui): add opt-in mobile keyboard resize mode

* fix(ui): stabilize touch terminal input and tab layout

* fix(ui): narrow touch terminal handling to mobile and tablet

* fix(ui): refine touch terminal input handling

* fix(ui): re-key terminal viewport on session id

* fix(ui): avoid touch-laptop terminal overlay regression

* fix(ui): hide ghostty system caret surfaces

* fix(settings): normalize mobile keyboard mode sanitization

* fix(ui): honor terminal quick keys toggle on mobile

* fix(ui): gate mobile keyboard resize mode on iOS

---------

Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-05 12:26:34 +03:00
jwcrystalandBohdan Triapitsyn 63b4a5b996 feat(ui): enable TimelineDialog with full-text search across all message roles in one session (#1104)
* feat: register open_timeline_dialog shortcut (mod+t)

* feat: enhance TimelineDialog with full-text search across all roles

* feat: add open_timeline_dialog shortcut labels to all locales

* feat: wire TimelineDialog into ChatContainer

* fix: add setTimelineDialogOpen to hook dependency array

* fix: tighten timeline dialog interactions

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-05 11:21:06 +03:00
Bohdan Triapitsyn 495b9d56d2 release v1.10.0 2026-05-05 01:39:33 +03:00
Bohdan Triapitsyn eadff4e338 fix: resolve message action lint warning
Added the missing hook dependency for message action rendering
Lint and type-check now pass cleanly
2026-05-05 01:26:37 +03:00
David SazandBohdan Triapitsyn 5475ef2db3 Feat/add ide opened file to chat context (#1106)
* feat(chat): add active editor file context and related functionality

* feat(chat): improve active editor file context handling and update translations

* feat(i18n): standardize quotation marks in file attachment messages

* update Korean translation for image removal action in file attachment

* feat(chat): refine active editor file handling and optimize broadcast logic

* fix(chat): stabilize VS Code editor context chips

---------

Signed-off-by: David Saz <david.saz.g@gmail.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-05 01:24:03 +03:00
Bohdan Triapitsyn 7fc22bc69b feat: auto-detect OpenCode CLI on onboarding screen
Polls every 2.5s and transitions automatically once the CLI is reachable
Advanced settings and troubleshooting moved into collapsible accordions
New i18n keys added for status indicator and section titles across 6 locales
2026-05-05 00:41:30 +03:00
Bohdan Triapitsyn 581f7c34dc fix: align iPad PWA window controls spacing
Adds tablet standalone PWA detection
Aligns header and sidebar action padding
Extends safe-area handling for tablet PWAs
2026-05-04 00:04:29 +03:00
Bohdan Triapitsyn fc2f4746e5 fix: avoid iPad PWA traffic light overlap
Adds tablet standalone PWA detection
Reuses macOS traffic-light header spacing
Protects session sidebar and multirun headers
2026-05-03 21:39:30 +03:00
Bohdan Triapitsyn 9964dcdb99 fix(cli): tolerate legacy daemon flag (#1097) 2026-05-03 21:38:45 +03:00
Bohdan Triapitsyn 6595322f1a fix: show tablet actions without hover
Makes hover-only controls visible on tablets
Preserves existing mobile and desktop layouts
Covers chat, session sidebar, file, tab, and picker actions
2026-05-03 17:19:55 +03:00
Bohdan Triapitsyn 43f98e0344 feat(ui): add recent section toggle 2026-05-03 16:32:14 +03:00
Bohdan Triapitsyn 5614012acb fix(ui): keep streaming deltas through pipeline 2026-05-03 13:42:50 +03:00
Bohdan Triapitsyn f810a3316c fix(ui): prevent streaming text flicker and first-chunk loss 2026-05-03 02:32:05 +03:00
Bohdan Triapitsyn 4ad5d2f4b7 fix(ui): tolerate invalid message parts 2026-05-01 15:08:44 +03:00
Shyamalan KannanandBohdan Triapitsyn a9ee499ae4 fix: add concurrency controls for multiple sessions using the same provider (#1069)
* fix: add concurrency controls for multiple sessions using the same provider

Adds OS-inspired scheduling primitives (from HiveMind/AIMD research) to prevent
concurrent sessions from the same provider from experiencing slowdowns, random
stops, and cascading failures.

Server-side:
- Health check skips OpenCode restart when sessions are actively busy — a busy
  server under concurrent load can fail the health check timeout without being
  dead. Staleness guard forces restart if unhealthy+busy persists >2 minutes.
- Upstream SSE stall timeout scaled from 20s to 60s to avoid unnecessary
  reconnections when multiple sessions are waiting for LLM responses.

Client-side (HiveMind primitives, arXiv:2604.17111):
- Transparent retry with exponential backoff (1s→2s→4s, max 32s) for
  429/502/503/504 errors — the #1 most effective primitive from the paper.
- Circuit breaker: opens after 3 consecutive retryable errors, cooldown
  doubles each trip (30s→60s→120s, capped 128s), matching TCP AIMD.
- Per-provider session tracking with TTL eviction (1h idle sweep).
- Fetch-level retry gated on AbortError/TypeError only (not DNS failures).

Refs github-code-review skill findings (all 8 issues resolved).

* fix: use definite assignment assertion for response variable

Fixes TS2454: Variable 'response' is used before being assigned
in strict mode. The for-loop body always assigns it on every path
that reaches the post-loop code, but TS can't prove that.

* fix: add cleanupSession to error paths and remove unreachable code

P1 fixes (Greptile review):
- cleanupSession called on fetch error throw path
- cleanupSession called on non-retryable HTTP error throw path
- Removed unreachable post-loop code (loop always terminates via return or throw)

Adds explicit post-loop throw to satisfy TypeScript strict return check.

* fix: address Greptile review feedback on concurrent session controls

Removes client-side session tracking that leaked on normal completion paths.

The session tracking was redundant — the server-side health check already reads from

sessionRuntime.getSessionActivitySnapshot() for busy-session detection.

Changes:

- Remove activeSessions Set and all session-tracking functions from provider-tracker

- Remove trackSessionStarted/cleanupSession calls from client.ts

- Remove unreachable (response as Response) block after retry loop

- Make upstreamStallTimeoutMs conditional: 60s when >1 sessions, 20s otherwise

Refs #1069

* fix: enforce dynamic concurrency safeguards

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-01 14:57:31 +03:00
pasta-paulandBohdan Triapitsyn f9094bc3cc fix(ui): prevent queued message truncation from stale React closure (#1087)
* fix(ui): read textarea DOM value in queue handler to prevent truncation

When the user types quickly and clicks the Queue button, React may not
have committed the latest `message` state yet, causing handleQueueMessage
to capture a stale closure (often just the first character typed).

Read textareaRef.current.value directly from the DOM instead, which
always reflects the current input regardless of React's render cycle.
Fall back to the React state when the ref is unavailable.

Also recompute hasContent from the DOM value to ensure the guard check
is consistent with the actual message being queued.

* fix(ui): use current input value when sending

---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-01 14:26:14 +03:00
Bohdan Triapitsyn a077735bcf fix: keep chat pinned during delayed rendering
Keeps long historical sessions scrolled to the bottom after load
Preserves bottom follow while assistant responses grow
2026-05-01 14:13:18 +03:00
Bohdan Triapitsyn d820e25ea1 fix: keep generated prompts scrolled into view
Force chat to bottom on normal sends
Scroll generated commit prompts into view
Preserve saved scroll restore for passive navigation
2026-05-01 13:48:07 +03:00
pasta-paulandBohdan Triapitsyn 1991736ebf fix(server): prevent streaming hang during long agent sessions (#1088)
* fix(server): increase WS buffer/replay limits and add backpressure warning

During long-running agent sessions (e.g. ultrawork loops with many tool
calls), the browser WebSocket client can briefly fall behind the server.
When the outbound buffer exceeds the limit, the server force-disconnects
with close code 1013, and the small replay buffer (512 events) is
insufficient to recover all missed events — leaving the UI permanently
stalled.

Changes:
- Raise MESSAGE_STREAM_WS_MAX_BUFFERED_BYTES from 4 MB to 16 MB to
  tolerate larger bursts without disconnecting
- Add MESSAGE_STREAM_WS_BACKPRESSURE_WARN_BYTES (12 MB) threshold that
  sends a one-shot "backpressure" frame to the client before the hard
  disconnect, giving it a chance to shed low-priority updates
- Raise MESSAGE_STREAM_GLOBAL_REPLAY_LIMIT from 512 to 2048 so more
  events survive brief reconnection gaps
- Add tests for the backpressure warning behavior (emit, dedup, reset)

* fix(ui): batch event flushes under backpressure

---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-01 13:25:19 +03:00
jwcrystalandBohdan Triapitsyn 03c9065c90 fix: preserve per-session scroll position on session switch (#1083)
* fix: restore scroll position when switching chat sessions

When switching between chat sessions, scroll position now restores to
where the user left off instead of always jumping to the bottom.

- Save pixel-level scrollPosition (scrollTop/scrollHeight/clientHeight)
  in viewport store on every scroll event
- Add restoreSavedScrollPosition to timeline controller for ratio-based
  restoration (handles content size changes between visits)
- Suppress intermediate scroll events during session transition with an
  explicit flag, cleared deterministically after restore completes
- Cancel in-flight animations/follow-loops on session switch
- Preserve scrollPosition when session-ui-store rebuilds SessionMemoryState

* fix: keep streaming sessions pinned on restore

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-01 12:39:04 +03:00
Islam NoflandBohdan Triapitsyn a23f5e7545 fix: cross-verify update API claims against npm registry (#1082)
* fix: cross-verify update API claims against npm registry

* Update packages/web/server/lib/package-manager.js

Signed-off-by: Islam Nofl <islamnofl.official@gmail.com>

* fix: show live server version in AboutDialog instead of stale build-time constant

* fix: add comment to empty catch block to satisfy lint no-empty rule

* fix: preserve live about dialog version in electron

* fix: scope update checks by runtime

---------

Signed-off-by: Islam Nofl <islamnofl.official@gmail.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-01 12:23:58 +03:00
Bohdan Triapitsyn c924e85a29 feat(settings): add response style presets 2026-05-01 01:27:31 +03:00
Shyamalan KannanandBohdan Triapitsyn bcbf34b1d1 feat: add Behavior settings page for global AGENTS.md (#1079)
* feat: add Behavior settings page for global AGENTS.md

- Add new 'Behavior' settings page to manage global system prompt
- Sync global behavior prompt to ~/.config/opencode/AGENTS.md
- Add GET/PUT /api/behavior/agents-md endpoints with 1MB size limit
- Add AbortController guards and parallel fetches in BehaviorPage
- Add i18n translations for en, es, ko, pt-BR, uk, zh-CN
- Add globalBehaviorPrompt to DesktopSettings type and sanitizer
- Add /api/behavior to express.json() body-parser whitelist
- Ensure atomic save: AGENTS.md written before settings updated

* fix: address Greptile review feedback

- Rename misleading 'trimmed' variable to 'value' in settings-helpers.js
- Add Content-Length guard for /api/behavior before 50mb JSON parser
- Use express.json({ limit: '1mb' }) for behavior endpoints
- Add actual translations for es, ko, pt-BR, uk, zh-CN locales

* fix(vscode): support behavior settings endpoint

* fix(behavior): wait for settings persistence

* fix(behavior): end agents file with newline

* fix(behavior): avoid duplicate textarea resize handles

* fix(behavior): clarify global rules copy

* fix(behavior): move rules note into tooltip

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-01 00:39:55 +03:00
ArtёmandBohdan Triapitsyn d35ff8a2db fix: support slash-containing model IDs (#1074)
* fix: support slash-containing model IDs

* fix: parse worktree default model identifiers

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-01 00:34:36 +03:00
Bohdan Triapitsyn f96374c738 fix(ui): remove unused lint leftovers 2026-04-30 23:47:43 +03:00
Bohdan Triapitsyn 68ef6a0b18 fix(settings): contain settings page scrolling 2026-04-30 23:45:17 +03:00
ricautomation 56d27ed5d3 fix(opencodeConfig): retain headers for remote MCP configurations (#1072)
Resolves issue #1033 where remote GitHub MCP setups failed to work in OpenChamber because the `headers` property (which contains authorization credentials) was being dropped during configuration parsing in `packages/vscode/src/opencodeConfig.ts`.
2026-04-30 23:34:12 +03:00
Bohdan Triapitsyn 123805a112 style(ui): disable text selection on buttons 2026-04-30 23:08:59 +03:00
Bohdan Triapitsyn 41cc8e556f fix(git): cap fetch/pull remotes dropdown size and reposition
Constrain width and add scroll so long remote URL lists stay inside the
dropdown, and anchor it to the left of the trigger to match the chat
input model picker.
2026-04-30 22:54:37 +03:00
Bohdan Triapitsyn 63da6e6292 feat(ui): unify overlay animations and trim tooltip delays
Use base-ui's transition-status pattern (data-starting-style /
data-ending-style) for Dialog, DropdownMenu, Select, Tooltip and Popover
so open/close animate consistently at 150ms ease-out without flicker.
Wrap dialog popups in a centered flex container so the scale animation
no longer fights with translate-based positioning, and switch
PendingChangesBar to a real Popover so it actually animates closed.

Convert ScheduledTaskEditorDialog from raw Radix to the shared Dialog
wrapper, give ScheduledTasksDialog a stable min-height to prevent layout
shift mid-animation, and reset NewWorktreeDialog form state on open
instead of close so fields don't empty during the close transition.

Drop tooltip delay from 700ms to 300ms globally and remove all
per-component overrides except the model/variant/agent selectors in the
chat input (600ms).
2026-04-30 22:41:33 +03:00
Bohdan Triapitsyn 24533dfe32 feat(palette): unify quick open into command palette with multi-source search
Merge file picker into command palette. Single Cmd+P entry searches
files, sessions, settings pages and commands; groups re-order by best
fuzzy score per source. Sessions show branch labels; git status is
lazily fetched for all session directories on open.

Drop QuickOpenDialog and Cmd+K shortcut.
2026-04-30 18:37:37 +03:00
Bohdan Triapitsyn 57eec76a7b fix: polish VS Code chat actions and bash tool text
Hide multi-run action in VS Code chat
Remove rounded bash tool text surfaces
2026-04-30 14:19:20 +03:00
Bohdan Triapitsyn 8152bd7808 perf: reduce desktop quit risk polling
Refresh quit risk only when quitting
Use in-process status for Electron local server
Avoid repeated scheduled task status scans
2026-04-30 13:54:58 +03:00
Bohdan Triapitsyn bbd83d60c6 fix: use valid Zen summaries for notes 2026-04-30 13:07:37 +03:00
Shyamalan Kannan fa71954ef1 fix: invalidate model metadata cache after OpenCode restart (#1065)
After OpenCode restarts (via 'Reload Opencode' or server reconnection),
the UI's cached model metadata (context windows, pricing, etc.) was
never refreshed — the cache was fetched once at boot and reused forever.
This meant new/updated models from providers were invisible until a full
page reload.

Add invalidateModelMetadataCache action to useConfigStore that clears
the metadata cache and resets the in-flight tracker. Call it from
performConfigRefresh before re-loading providers.
2026-04-30 12:25:42 +03:00
Ariza 897c435d14 fix: Use light blue to display light theme conversation text selections (#1071) 2026-04-30 12:24:30 +03:00
Bohdan Triapitsyn 90c8c948f8 chore: update changelogs with unreleased changes 2026-04-30 12:22:49 +03:00
Bohdan Triapitsyn 371eac9db3 fix: stabilize remote preview proxy 2026-04-30 12:22:49 +03:00
Jinhyeok Lee d7ddc7cf4d fix(i18n): polish Korean localization copy (#1067)
* fix(i18n): polish Korean localization copy

* fix(i18n): refine Korean terminology and copy consistency

* fix(i18n): improve Korean copy consistency in settings and service UI

* fix(i18n): reconcile Korean locale gaps with follow-up review

---------

Signed-off-by: Jinhyeok Lee <zenyr@zenyr.com>
2026-04-30 12:17:55 +03:00
Bohdan Triapitsyn 6cb5e4342f fix(chat): soften aborted turn message 2026-04-30 00:51:28 +03:00
Bohdan Triapitsyn e6c7cc5589 feat(chat): add wide layout setting 2026-04-30 00:45:25 +03:00
Bohdan Triapitsyn e9edfa5302 fix: proxy preview app requests 2026-04-30 00:29:35 +03:00
Bohdan Triapitsyn fb66542d87 fix: rewrite preview module imports 2026-04-30 00:21:25 +03:00
bd9a91335c feat(preview): embedded dev-server preview pane + dev shutdown controls (#1062)
* feat: embedded preview proxy for local dev servers

Add a same-origin server proxy under /api/preview/proxy/:id and
matching UI surfaces so local dev servers (Vite, Next, etc.) can be
embedded inside OpenChamber.

Server (packages/web/server):
- New lib/preview/proxy-runtime.js: cookie-gated HTTP+WebSocket proxy
  to loopback hosts only, with TTL'd targets and SSRF allowlist.
- index.js wires the runtime alongside terminal/event-stream.

UI (packages/ui):
- ContextPanel preview tab with iframe, reload, and open-in-browser.
- Inline html code-block preview in MarkdownRenderer.
- Terminal auto-detects loopback URLs and offers to open them.
- i18n keys across en, es, pt-BR, uk, zh-CN.

* perf(preview): cache proxy targets across PreviewPane remounts

Module-scoped Map keyed by upstream URL so tab switches and component
remounts within the same page session reuse the existing proxy
registration instead of POSTing a fresh target each time.

In-memory only by design: the server holds the target map in memory
and the auth cookie is HttpOnly + scoped to the proxy id, so a stale
persisted entry would 404 after a server restart. Entries are evicted
on registration error and on a 30s safety margin before TTL expiry.

* feat(preview): surface dev-server-down state with retry overlay

Iframes don't expose HTTP status to the parent, so when the proxy
returns a 502 (upstream dev server is offline) the iframe just renders
the raw JSON error body. Probe the proxy URL out-of-band with HEAD
(falling back to GET on 404/405) and replace the iframe with a
friendly 'Dev server is not responding' overlay + retry button when
the upstream is unreachable.

Re-probes on reload, on URL change, and on proxy re-registration.

* feat(preview): strip frame-busting response headers

Many dev servers (Next.js, others) send X-Frame-Options: SAMEORIGIN
and/or a CSP with frame-ancestors that block embedding inside the
OpenChamber iframe. The proxy is same-origin and already
authenticated per-target, so embedding is otherwise safe.

- Drop X-Frame-Options outright on proxied responses.
- Surgically remove only the frame-ancestors directive from
  Content-Security-Policy and Content-Security-Policy-Report-Only,
  preserving every other directive. Drops the header entirely if no
  directives remain.
- Verified end-to-end: upstream sending both headers comes through
  with X-Frame-Options removed, CSP retaining default-src/script-src
  but no frame-ancestors, and unrelated headers untouched.

* docs(preview): design for remote-host relay agent

Design-only doc for the next phase of the embedded preview feature:
when OpenChamber runs remotely (cloud/shared/tunnel) and the user's
dev server runs on their local machine. Covers architecture (local
agent + outbound control WebSocket + server dispatch), pairing flow,
wire protocol, security model, failure modes, open questions, and
implementation milestones. No code changes.

* feat(preview): auto-open preview pane for loopback URLs in chat

Detect http(s) loopback URLs in incoming assistant messages and open the
preview pane automatically, deduped per (session, url) pair so re-renders
or repeated mentions do not steal focus. Add an inline Preview button
next to loopback links in chat markdown as a manual fallback when the
auto-open was dismissed or the URL appeared in an older message.

- url.ts: isLoopbackHttpUrl / extractLoopbackUrls helpers
- ChatContainer: module-level dedupe Set + effect on active session tail
- MarkdownRendererImpl: optional onPreviewLoopback in main renderer only
  (SimpleMarkdownRenderer for tool diffs is intentionally untouched)
- Reuses existing terminalView.preview.open i18n keys

* feat: preview enhancements, dev shutdown, and reliability fixes

Add preview start/stop UI in ContextPanel/Header, improve URL detection (Python HTTP server logs, trailing punctuation, IPv6 loopback), fix proxy path filtering to avoid disrupting non-preview WebSockets. Add dev-only /api/system/dev-shutdown endpoint and Header button to terminate local dev processes and orphaned preview servers. Improve terminal cleanup with process group killing, event pipeline reconnect backoff. Update file read APIs with optional flag and cache control. Add /api/system/free-port endpoint, detectDevServer.ts utility, and preview/shutdown i18n strings for 5 languages.

* fix: harden preview support

* fix: keep terminal toolbar interactive

* fix: keep expanded terminal below header

* fix: keep preview iframe under proxy path

* fix: respect project action preview urls

* fix: rewrite preview asset urls

* feat: capture preview console logs

* feat: annotate preview elements

* feat: attach preview annotation screenshots

* fix: improve proxied preview hmr

* feat: refine preview action UX

* fix: address preview review feedback

* fix: show auto-discover preview wait state

---------

Co-authored-by: William Biggers <will@Williams-MacBook-Pro.local>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-30 00:03:38 +03:00