Commit Graph
300 Commits
Author SHA1 Message Date
Bohdan Triapitsyn 4ad5d2f4b7 fix(ui): tolerate invalid message parts 2026-05-01 15:08:44 +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 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
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
Bohdan Triapitsyn c924e85a29 feat(settings): add response style presets 2026-05-01 01:27:31 +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 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 bbd83d60c6 fix: use valid Zen summaries for notes 2026-04-30 13:07:37 +03:00
Bohdan Triapitsyn 6cb5e4342f fix(chat): soften aborted turn message 2026-04-30 00:51:28 +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
Islam NoflandBohdan Triapitsyn 21253d7fc2 feat: fork-aware issue/PR listing & OpenCode startup loading indicator (#1061)
* Add design spec: OpenCode readiness loading indicator

* Add implementation plan: OpenCode readiness loading indicator

* feat: add useOpenCodeReadiness hook

* feat: add i18n keys for common.loading

* feat: add loading state to ModelSelector

* feat: add loading state to AgentSelector

* feat: add loading state to ModelControls chat selectors

* update package-lock

* feat(github): add shared fork detection utility

* feat(github): make issue listing fork-aware

* feat(github): make PR listing fork-aware

* feat(types): add sourceRepo to issue/PR summary types

* feat(ui): add source badges to GitHub integration dialog

* feat(ui): add source badges to issue/PR picker dialogs

* feat(github): pass headRemote in PR creation for fork support

* feat(ui): add source→target label in PR tab for fork workflows

* fix(github): allow PR section on base branch when upstream remote exists

* fix(github): show PR section on any branch including main for fork→upstream PRs

* fix(github): allow PullRequestSection to render on base branch when upstream remote exists

* feat(github): auto-detect upstream repo for fork→upstream PR creation

- Add GET /api/github/repo/upstream endpoint to discover fork's upstream
- Fix PullRequestSection canShow to allow PR creation on base branch when repo is a fork
- Add virtual upstream target in remote dropdown (no explicit upstream remote needed)
- Add targetRepo parameter to /api/github/pr/create for direct upstream targeting
- Add repoUpstream() API client method and GitHubRepoUpstreamResult type

* feat(github): auto-detect upstream repo for fork→upstream PR creation

- Add GET /api/github/repo/upstream endpoint to discover fork's upstream
- Fix PullRequestSection canShow to allow PR creation on base branch when repo is a fork
- Add virtual upstream target in remote dropdown (no explicit upstream remote needed)
- Add targetRepo parameter to /api/github/pr/create for direct upstream targeting
- Add repoUpstream() API client method and GitHubRepoUpstreamResult type

* fix: complete fork→upstream PR workflow

- Server: return defaultBranch from /api/github/repo/upstream endpoint
- Server: fix cross-repo head ref construction (compare repos, not remote names)
- Server: filterActiveRemoteBranches checks all remotes, not just origin
- UI: set targetBaseBranch to upstream's default branch when using detected upstream
- UI: include all remote branches in base branch dropdown when using detected upstream
- UI: skip base===head check for cross-repo PRs (same branch name on different repos is valid)
- Types: add defaultBranch to GitHubRepoUpstreamResult

* chore: delete superpowers folder

* feat: add (local)/(remote) labels to PR branch display and adapt Repository button to selected remote

* feat: Repository button adapts to selected remote (upstream vs origin)

* fix: complete fork→upstream PR feature gaps

Server:
- Extend /api/github/repo/upstream to return defaultBranchSha and remoteName
- Reuse headRepo result instead of redundant resolveGitHubRepoFromDirectory call
- Return clear error when headRepo is null (invalid GitHub URL)

UI:
- Add upstream's default branch to availableBaseBranches when using detected upstream
- Use upstream's default branch SHA in git log for generate description (fixes 'No commits found in range main...main')
- Show qualified names (owner/repo · branch) in base branch dropdown when using detected upstream

Types:
- Add defaultBranchSha and remoteName to GitHubRepoUpstreamResult

* fix: move detectedUpstream state before availableBaseBranches to fix temporal dead zone

* fix: fetch upstream branches from GitHub API for base branch dropdown

- Add GET /api/github/repo/branches endpoint to fetch branches via Octokit
- Add repoBranches() to GitHub API client and interface
- Fetch upstream branches on detection and store in upstreamBranches state
- Include upstreamBranches in availableBaseBranches when using detected upstream
- Re-add availableBaseBranches memo and auto-correction effect that were lost
- Remove unnecessary qualified names from dropdown (upstream is already selected)

* fix: restore prStatusKey and statusEntry declarations lost during refactor

* fix: cleanly re-apply all fork→upstream PR UI changes

Restored PullRequestSection.tsx from clean base and re-applied:
- Expand detectedUpstream type with defaultBranch, defaultBranchSha, remoteName
- Add upstreamBranches state and fetch on upstream detection
- Include upstream branches in availableBaseBranches when using detected upstream
- Use upstream default branch SHA in generate description (fixes 'No commits found')
- Adapt Repository button URL to selected remote
- Add (local)/(remote)/(upstream) labels to branch display

* fix: move detectedUpstream/upstreamBranches before availableBaseBranches to fix TDZ

* style: add pill badge styling to upstream repo source labels

* fix: don't cache error PR status responses, allow force-bypass of server cache

* fix: resolve PR status cache bugs, stale directory fallback, and upstream re-detection

* fix: keep collapse button visible when scrolling long user messages

- Collapse button now sticks to top of scrollable user message content instead of scrolling away

* fix: checkbox focus ring blends into sidebar background

* fix: polish fork PR follow-ups

* fix: remove user message collapse artifact

* fix: tighten fork PR internals

* fix: check all remotes for fork PR status

* fix: recover sidebar PR status misses

---------

Signed-off-by: Islam Nofl <islamnofl.official@gmail.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-29 12:03:39 +03:00
Dave Otero 5f104af9b7 fix: allow Enter/Ctrl+Enter to submit custom answer in QuestionCard (#1059)
* fix: allow Enter/Ctrl+Enter to submit custom answer in QuestionCard

* fix: preserve IME and mobile Enter behavior in QuestionCard
2026-04-28 17:22:45 +03:00
Bohdan Triapitsyn 41369fa5f2 fix: align chat error message icon
Centers the error icon with message text
Improves error text wrapping
2026-04-28 16:37:15 +03:00
Bohdan Triapitsyn 958ffe063e fix: stabilize older message loading 2026-04-28 16:26:35 +03:00
Bohdan Triapitsyn 4f51abddf4 fix: improve external file and path handling
Open external context files read-only
Preserve leading-dot paths in UI
Keep workspace write operations guarded
2026-04-28 14:06:25 +03:00
Bohdan Triapitsyn c83986cd94 feat: improve project directory picker
Adds command-palette style project browsing
Keeps native Finder picker as optional desktop action
Supports disabled existing projects and create-and-add paths
2026-04-27 15:45:14 +03:00
Dave OteroandBohdan Triapitsyn b62faadd15 Improve and unify the model picker across desktop and mobile (#1037)
* feat: improve agent and quick model picker behavior

* fix: keep the active model highlighted in the quick picker

* feat: streamline mobile model selection

Open the full mobile model picker directly and remove the intermediate controls drawer so mobile model changes follow the same core selection flow as desktop.

Add inline thinking-mode chips that show each model's remembered or default variant, apply model and variant together on tap, and fall back to a dedicated overflow panel for larger variant sets. Also keep favorites and recents searchable on mobile and fix clearing remembered default variants so the picker stays consistent across sessions.

* fix: polish desktop model picker interactions

Stabilize desktop model picker behavior by keeping keyboard and hover selection in sync, preventing hover-driven closes, and making the footer hints visually stable.

Also make quick-picker thinking mode changes apply consistently when switching plan/build or agent mode inside the picker, clamp left/right variant cycling at the ends, and keep thinking feedback visible even when the selected variant cannot move further.

* fix: condense mobile model picker rows

Tighten the mobile model picker to use a more compact, consistent row layout across favorites, recents, and provider sections while keeping context length and capability icons easy to scan.

Also preserve inline thinking-mode selection, improve metadata spacing, and keep the mobile controls readable without reintroducing the heavier drawer-based flow.

* fix: include all primary-like agents in picker cycling

Keep desktop Tab cycling and mobile tap cycling aligned with the rest of the selection UI by including agents marked as all or left unset, not just strict primary agents.

* fix: preserve remembered agent variants in picker flows

* Fix model picker variant restore

* Polish favorite model drag handle

* Fix Korean model picker locale

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-27 13:28:51 +03:00
vhqtvnandvhqtvn 4b171ee207 Prevent page overscroll in chat layout (#1034)
Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com>
2026-04-27 12:14:09 +03:00
0bb30887b7 Improve assistant message action placement for split responses (#1032)
* Improve assistant message action placement for split responses

* Add toggle for split assistant message actions

* Address split message actions review feedback

* Fix inline assistant actions

---------

Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-27 11:41:14 +03:00
Bohdan Triapitsyn b5c58320cf feat: rename workspace review command
Renamed OpenChamber review command to /workspace-review
Kept built-in /review available separately
Updated autocomplete and localized settings copy
2026-04-26 23:15:35 +03:00
Bohdan Triapitsyn a3b300f7a9 feat: add keyboard turn navigation
Navigate chat turns with ArrowUp and ArrowDown
Only triggers when the chat area is focused
Supports scrolling to the latest visible turn
2026-04-26 18:17:13 +03:00
Bohdan Triapitsyn c8eb9a6c5f feat: widen chat content area
Chat messages use more horizontal space
Chat input keeps its previous max width
2026-04-26 18:04:42 +03:00
Bohdan Triapitsyn 6176ee2b0f refactor: simplify mobile keyboard handling
Removed fragile global keyboard viewport manager
Restored native mobile viewport behavior
Fixed textarea content shifting outside inputs
2026-04-26 17:44:33 +03:00
Bohdan Triapitsyn 209ffc16e4 fix: handle multi-file tool diffs safely
Split multi-file patches before diff rendering
Fixed hook dependency lint warnings
2026-04-26 16:58:07 +03:00
Islam NoflandBohdan Triapitsyn 4523e9c486 perf: reduce re-renders, fix mobile keyboard handling, add chunk load recovery, and improve PATH management (#1028)
* fix: exclude file content from reverted prompt text

Revert and fork now restore only the user's original prompt, not server-injected file content
Uses existing isSyntheticPart helper for type-safe filtering

* fix: keep scrollbar visible when hovering over thumb

* fix: prevent ESC abort from triggering when terminal is focused

* fix: pass directory to permission/question reply calls so approvals actually resolve

* fix: default model selection not responding after Base UI migration

* fix: prevent modal content from shifting and clipping footer buttons

* fix: improve session switching performance and add sub-agent export with prompt collapse

Defer viewport anchor saving to eliminate ~800ms UI freeze when switching sessions
Add export dialog to include sub-agent tasks recursively in markdown export
Add collapse chevron button for expanded user prompts in sticky header

* fix: resolve sidebar scroll and TDZ crash in session sidebar

* perf: reduce CPU overhead and re-renders across chat, layout, and settings

* fix: position collapse button at top of message and prevent ESC abort in terminal

* fix: position collapse button at top and add padding only when expanded

* refactor: extract shared PATH utilities and mobile keyboard hook

* refactor: import shared path-utils in electron, use module-level style constants

- Electron now imports pathLooksUserConfigured/mergePathValues from
  shared path-utils.js instead of inline duplication
- ToolPart collapsedCustomStyle moved from useMemo([]) to module const

* fix: resolve remaining merge conflicts and type errors

- Remove duplicate variable declarations in SessionNodeItem
- Remove orphaned export callback body from conflict resolution
- Fix HelpDialog description -> descriptionKey (i18n rename)

* fix: resolve type-check and lint errors in session-actions.test.ts

- Added missing bun:test type declarations (beforeEach, mock, mock.module)
- Removed unused State import
- Replaced 'as any' casts with proper OpencodeClient and ChildStoreManager types
- Added eslint-disable for unused _ parameter in mock function

* fix PR 1028 export and PATH edge cases

* fix startup retry exhaustion state

* remove opencode package lock change

* fix sub-session rename cancellation

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-26 16:24:07 +03:00
Shyamalan KannanandShyamalan Kannan 632e6cc97b fix: make todo list update dynamically when task status changes (#999)
areRenderRelevantPartsEqual did not compare the tool part output field,
so when the AI updated a todo list (e.g., marking items completed) the
ToolPart memo comparator returned true and skipped re-render. The UI
stuck showing stale task status.

Fix: add output reference comparison for tool parts in
areRenderRelevantPartsEqual so streaming tool output updates trigger
re-renders correctly.

Co-authored-by: Shyamalan Kannan <yabuku@Shyamalans-MacBook-Pro.local>
2026-04-26 15:10:20 +03:00
Bohdan Triapitsyn 2fc2914fde fix: improve mobile text selection menu
Use a mobile bottom sheet layout for selection actions
Prevent long localized labels from being squeezed
2026-04-26 14:37:34 +03:00
Bohdan Triapitsyn a2dc5021bc fix: stabilize model and task UI hints
Keep thinking shortcut hint visible without layout shift
Restore JSON file icons across shared file lists
Use task status icons in the input status row
2026-04-26 14:27:06 +03:00
Bohdan Triapitsyn 7d7285655d Add i18n foundation and translations (#1027)
* feat: add i18n foundation

* feat: localize sessions sidebar

* Localize multirun/scheduled tasks and fix dialog dropdown interactions

* localize git sidebar surface and add zh-CN keys

* feat(ui): localize context panel, diff/plan views, and context sidebar content

* fix(config): resolve user config home via fs/home before embedded home

* localize header/chat UI and complete model/worktree panel strings

* localize worktree + github issue/pr dialog flows

* localize settings sections and split settings i18n dictionaries

* localize additional settings sections and sidebars

* localize more settings pages and dialogs

* fix settings select trigger localization

* localize tunnel settings ui surface

* localize additional settings sections

* localize keyboard shortcuts labels in settings

* localize terminal and utility dialogs surfaces

* feat(i18n): localize remaining UI strings

* Add Ukrainian locale

* Add Spanish locale

* Add Brazilian Portuguese locale

* Polish locale translations
2026-04-26 14:03:39 +03:00
87db2ea210 fix(ui): stabilize mobile keyboard viewport handling (#1026)
* fix(ui): stabilize mobile keyboard viewport handling

* fix(ui): tighten mobile keyboard viewport handling

* Avoid redundant keyboard open store writes

---------

Co-authored-by: vhqtvn <8930337+vhqtvn@users.noreply.github.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-25 20:58:40 +03:00
Bohdan Triapitsyn 7049aeffd3 fix: show all mention results without tabs 2026-04-25 20:44:12 +03:00
Bohdan Triapitsyn d40041fccd Fix startup connection and chunk recovery 2026-04-25 16:56:37 +03:00
Bohdan Triapitsyn 8e610ca8b3 refactor: scope mention autocomplete lists to active tab
Files/directories/recentFiles render only on the 'files' tab; agents
only on 'agents'. Memoize visible slices and drop loading spinner when
the files tab is inactive.
2026-04-24 12:30:11 +03:00
Bohdan Triapitsyn 6a70d51ea7 refactor: drop mobile keyboard/viewport hacks, rely on browser
Remove body position:fixed lock, visualViewport listener in MainLayout
(keyboard-inset heuristics, scroll-to-zero lock, focusin/focusout RAF),
[data-keyboard-avoid-active] translateY rule, and related CSS vars
(--oc-keyboard-inset, --oc-keyboard-avoid-offset, --oc-keyboard-home-indicator,
--oc-visual-viewport-offset-top). Strip data-keyboard-avoid* attrs and
keyboardAvoid props from Dialog/ScrollableOverlay and consumers. Drop
isKeyboardOpen from useUIStore.

With body unlocked and the layout plain flex-col (h-dvh), the browser
shrinks the viewport on keyboard open naturally — composer sits above
the keyboard, header stops lagging, input no longer jitters.

FilesView.nudgeEditorSelectionAboveKeyboard now derives the occluded
bottom locally from visualViewport + documentElement.clientHeight.
2026-04-24 12:30:11 +03:00
Shyamalan KannanandShyamalan Kannan ecb22e19c3 perf: lazy-load heavy dependencies (MarkdownRenderer + CodeMirror languages) (#997)
* perf: drastically improve cold-start, bundle size, and streaming performance

Cold-start optimizations:
- main.tsx: Remove blocking await on prefs I/O — render immediately with
  defaults, hydrate persisted settings asynchronously. Cuts 50-200ms from
  time-to-first-paint.
- bootstrap.ts: Split directory bootstrap into 3 phases:
  * Phase 1 (blocking): path, config, provider, session status — minimum
    data needed to render UI. Mark status complete after this phase.
    path.get and session.status must both succeed; they have no fallback.
  * Phase 2 (deferred): agents, commands, mcp, lsp, vcs, questions,
    permissions — fetched after first paint without blocking.
  * Phase 3 (lazy): session messages — loaded without blocking init.
- App.tsx: Keep identical provider tree before/after init to prevent
  full subtree remount when isInitialized flips. FireworksProvider and
  VoiceProvider are lightweight shells; overlays deferred until init.

Bundle-size optimizations:
- App.tsx + MainLayout.tsx + VSCodeLayout.tsx: Code-split heavy views
  (SettingsView, GitView, DiffView, TerminalView, FilesView, PlanView,
  OnboardingScreen, SettingsWindow, MultiRunWindow) with React.lazy.
  Views load on demand when user switches panels.
- vite.config.ts: Lower chunkSizeWarningLimit from 1200KB to 500KB.

Streaming render optimizations:
- streaming.ts: Throttle streaming store writes ~60Hz → ~1Hz. Busy-session
  only scan (Set, O(1)).
- MessageList.tsx: Lower virtualization threshold 40 → 15.
- ChatMessage.tsx: React.memo with areRenderRelevantMessagesEqual.
- MarkdownRenderer.tsx: React.memo with explicit prop comparators.

* fix: address Greptile review feedback on bootstrap and provider tree

- bootstrap.ts: Tighten Phase 1 error guard. path.get and session.status
  must both succeed; they have no global fallback.
- bootstrap.ts: Replace dead .catch() on Promise.allSettled() with .then()
  that inspects individual results for errors.
- App.tsx: Keep identical provider tree before/after init to prevent full
  subtree remount when isInitialized flips.

* perf: lazy-load heavy dependencies (MarkdownRenderer + CodeMirror languages)

MarkdownRenderer dynamic import:
- Move heavy implementation (marked, react-markdown, beautiful-mermaid,
  react-syntax-highlighter, ~1500 lines) to MarkdownRendererImpl.tsx
- Replace MarkdownRenderer.tsx with thin lazy wrapper using React.lazy
- All 11 existing imports work unchanged — no consumer code modified
- Full markdown stack loads on first render of markdown content

CodeMirror language lazy loading:
- languageByExtension.ts: remove static imports for 10+ less-common
  language packages (@codemirror/lang-go, lang-rust, lang-sql, etc.)
- Keep only 6 most common languages static: javascript, json, css, html,
  markdown, python, shell
- Less common languages return null from languageByExtension, causing
  callers to fall back to loadLanguageByExtension which dynamically
  loads from @codemirror/language-data
- Reduces initial bundle by ~200KB+ of language parsers

---------

Co-authored-by: Shyamalan Kannan <yabuku@Shyamalans-MacBook-Pro.local>
2026-04-23 12:31:42 +03:00
Bohdan Triapitsyn 522cebf127 fix: hide empty input status row
Only show the pending changes accessory when workspace changes exist
Remove wasted vertical space above the composer when the status row is empty
2026-04-23 10:55:27 +03:00
Bohdan Triapitsyn cb6ba34811 feat: add local workspace review slash command
Add /review command with high-signal diff review instructions
Expose workspace review prompts in Magic Prompts settings
Show /review in command autocomplete with OpenChamber command metadata
2026-04-23 10:45:41 +03:00
Bohdan Triapitsyn 6886713b8e fix: preserve duplicate slash commands in autocomplete
Show commands with identical names from different sources
Add stable command IDs so selection no longer depends on name dedupe
Keep command insertion flow working across chat and prompt editors
2026-04-22 22:08:34 +03:00
Bohdan Triapitsyn 863be8073b feat(chat): /summary slash command for non-destructive session summaries
Typing /summary (optionally with a topic hint) produces a structured
summary as a normal assistant message — without compacting or mutating
session history. Reuses the existing "Start new session from this answer"
button for hand-off.

Prompts are customizable in Settings → Magic Prompts under a new Session
group. Autocomplete shows an 'openchamber' badge to distinguish our
commands from OpenCode-built-in ones.
2026-04-22 21:56:35 +03:00
Bohdan Triapitsyn f24e6de21b fix: improve event stream reconnect reliability
Recover stalled event streams without dropping the session
Wait briefly for reconnection before showing connection lost errors
Persist Electron server logs for easier disconnect debugging
2026-04-22 21:03:02 +03:00
jwcrystal 17dd526731 fix: eliminate parent-child session desync across reconnect and navigation (#985)
* fix(pipeline): distinguish transport switch from real disconnect

WS_FALLBACK errors (e.g. ready timeout → SSE fallback) are transport
switches, not disconnections. No events are lost because lastEventId is
preserved across the switch.

Previously, every WS timeout triggered onDisconnect → onReconnect with a
full resyncDirectoryAfterReconnect, which:
- Missed idle parent sessions in candidate selection (root cause 1)
- Could overwrite in-flight SSE state with stale fetch data (root cause 4)
- Caused isConnected to flash false→true

Now: WS_FALLBACK fires onTransportSwitch (sets isConnected only).
Real disconnections (heartbeat timeout, network error) still fire the
full onDisconnect → onReconnect → resync cycle.

* fix(sync): relationship-aware reconnect with merge-not-replace

Two changes to resyncDirectoryAfterReconnect:

1. Candidate selection now also includes parent sessions of any child
   sessions in the directory. Previously, if a child completed during
   the disconnect gap (busy→idle), neither child nor parent was selected
   because both appeared idle. The parent's task tool part would remain
   permanently stale.

2. Parent resync merges parts instead of replacing. Previously, the
   resync deleted parts for messages not in the fetch snapshot, which
   could erase parts delivered by SSE events that arrived between the
   fetch and the setState. Now only parts for messages in the snapshot
   are overwritten; everything else is preserved.

* fix(sync): demand-load child session messages on access

Bootstrap only loads session metadata — messages are populated
exclusively by SSE events. When a user navigates to an old session
that spawned subagents, child session messages were never in the store.

Add useEnsureSessionMessages hook that detects this gap (session exists
in state.session but state.message[sessionID] is absent) and triggers a
background API fetch to load messages and parts.

ToolPart already calls useSessionMessageRecords(taskSessionId) which
returns empty when not loaded. Now it also calls useEnsureSessionMessages
to populate the store on first access.

* fix(sync): unmount-safe parent resync when child session goes idle

When a child session transitions to idle (completes), the sync layer
now schedules a targeted parts repair for the parent session's task
tool part. Previously this only happened when the ToolPart component
was mounted and had observed the child being active (taskChildSeenActive).

This covers:
- User navigated away while child was running
- App restarted with active subagent sessions
- SSE reconnect where child completed during disconnect

Uses the existing repairSessionParts mechanism with its 5s cooldown
to avoid redundant fetches.

* fix: type-check fixes for sync-layer parent resync

Fix TypeScript errors in Fix 5 implementation:
- Convert currentSessionId from null to undefined for resolveFallbackTaskSessionId
- Add default empty string for dir parameter in getScopedSdkClient
- Use explicit sessionID parameter for scopedClient.session.messages

All type-checks now pass.

* fix(sync): address PR review feedback on deduplication

- Use enqueuePartsRepair for session.idle parent resync instead of
direct repairSessionParts call. enqueuePartsRepair already has a 5s
cooldown to prevent redundant parallel API calls when multiple child
sessions go idle concurrently.

- Move useEnsureSessionMessages loading guard from component-scoped
React.useRef to a module-level Set keyed by directory:sessionID.
Prevents parallel fetches when multiple ToolPart instances mount
for the same child session.

* fix(sync): add missing semicolon on useEnsureSessionMessages call

Address Greptile P2 review comment on ToolPart.tsx:1931.
2026-04-22 20:10:21 +03:00
Bohdan Triapitsyn ff1e043bbe fix(ui): scroll-shadow bottom fade + hide tasks row when nothing active
- ScrollShadow: 1px subpixel tolerance so bottom fade clears at scroll end
  on Retina (fractional scrollTop was keeping data-bottom-scroll stuck)
- StatusRow: hide tasks indicator when no in-progress/pending todos remain
2026-04-22 17:50:59 +03:00
jwcrystalandBohdan Triapitsyn 81591430a1 fix(chat): restore desktop editor file-open in PendingChangesBar (#981)
* fix(chat): restore desktop editor file-open in PendingChangesBar (#979)

Commit d1553ba removed the runtime?.editor branch from handleOpenFile,
breaking file-click in VS Code and Electron desktop runtimes.

Restore the 3-branch logic: editor.openDiff(patch) → editor.openFile()
→ openContextDiff() web fallback. Display paths remain relative.

* fix(chat): route non-git changed files to file view in desktop

DiffView requires a git repository to display diffs. In desktop
(Electron/Tauri), runtime.editor is unavailable, so non-git files
fell through to openContextDiff which shows "Not a git repository".

Route non-git files to openContextFile instead, which works without
git. Git-tracked files continue to use openContextDiff.

* feat(chat): per-turn changes dropdown for non-git; self-sufficient git sync in bar

- PendingChangesBar seeds git store + listens to onGitRefreshHint; works without RightSidebarTabs (fixes VS Code where bar never rendered).
- Bar is git-only now; non-git shows per-turn dropdown at end of completed assistant turns.
- Dropdown uses base-ui Popover for collision-aware position; icon-only collapse with tooltips at narrow container widths.
- Extract shared helpers (changedFiles.ts), popover list (ChangedFilesList.tsx), styles (changedFilesPopover.ts).

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-22 11:31:21 +03:00
Bohdan Triapitsyn 5ec7f46105 fix: surface disconnect reason, switch health probe to /global/health (#978)
- event-pipeline: onDisconnect now carries a reason tag (ws_closed:code=N, ws_error_frame, ws_closed_before_ready, sse_error)
- useConfigStore: store lastDisconnectReason, clear on reconnect
- send guards: embed reason in Connection lost toast so we can tell which path tripped
- lifecycle: isOpenCodeProcessHealthy hits /global/health (healthy flag) with 5s timeout instead of /session with 2s, avoids false restarts under stream load
2026-04-22 00:33:21 +03:00
jwcrystalandBohdan Triapitsyn d4a4f43a83 feat: show file change summary bar (#950)
* feat: add FileChangeSummary component for multi-file diff preview in ToolPart

Provides an aggregated diff card for apply_patch and multi-edit tools,
showing per-file stats with click-to-expand diff view.

* feat: add PendingChangesBar above chat input with collapse/expand and file opening

- Collapsed/expanded toggle with aggregate +N -N stats (green/red)
- Relative path display, chat-column alignment with ChatInput
- Click file to open in diff viewer (web/desktop) or editor (VS Code)
- Support edit/multiedit/apply_patch/write tool metadata extraction
- Add PendingChangesBar to main chat view (ChatContainer)

* feat: dual-mode ChangedFilesBar — Git diff state vs latest AI turn

Git mode: reads git status from useGitStore (status.files + diffStats),
auto-clears on commit/restore. Shows 'N files changed in workspace'.

Non-Git mode: latest assistant turn only (no accumulation), clears on new
user message or manual dismiss. Shows 'AI updated N files in the last reply'.

Both modes: dismiss button with signature-based tracking.
Add pendingChangesBarDismissed state to session-ui-store, cleared on sendMessage.

* fix: address code review issues in ChangedFilesBar

- Gate non-git mode on streaming state to prevent flicker during AI turns
- Return null when isGitRepo is unknown (loading state)
- Add group/row class for reject button visibility in FileChangeSummary
- Use per-session Map for dismiss tracking to prevent cross-session leaks
- Include additions/deletions in dismiss signature for re-edit detection
- Extract shared parsePatchStats/parseCount to fileChangeHelpers

* chore: revert local .opencode/package-lock.json changes from PR

* fix: per-part fallback guard and git-only reject button

- Fix extractChangedFiles to use per-part files.length snapshot instead
  of global guard, preventing file entries from being skipped when
  earlier parts already contributed files
- Hide reject button in FileChangeSummary when not in a git repo,
  preventing silent revert failures

* refactor: remove FileChangeSummary — dead code redundant with ToolPart

FileChangeSummary duplicated diff rendering that ToolPart already
provides (PatchDiff, per-file stats, DiffViewToggle). The only unique
feature was a git revert button, which conflicts with the design
principle of not having accept/reject on file change previews.

Moved parsePatchStats/parseCount back into PendingChangesBar (sole
consumer) and deleted the shared helper module.

* chore: remove stray Tester.txt

* fix: deduplicate Fallback 4 'Diff' placeholder via seen set

* chore: update non-Git mode copy to neutral 'changed in the last reply'

* fix(chat): unify changes row with tasks

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-21 22:34:06 +03:00
youfchandBohdan Triapitsyn bc6cb91954 Chat input: drag-drop files and folders from file tree with @folder autocomplete (#963)
* feat(chat): drag-drop files/folders from file tree into chat input

Features:
- Support drag-and-drop files and folders from file tree into chat input
- Support @folder autocomplete for file/folder mentions in chat input
- Add drag support to file tree nodes for dropping into chat input
- Persist confirmed mentions to localStorage across sessions

Fixes:
- Fix drag state getting stuck with drag enter counter and onDragEnd cleanup
- Fix pendingSearchRef counter leak on query cancellation
- Fix confirmed mentions highlight/delete for folder paths
- Fix clear confirmed mentions on send and autocomplete loading state
- Guard drag-start against empty and root-relative paths
- Reset loading state when pendingSearchRef reaches zero

* fix(chat): address Greptile PR review findings

- P1: move confirmedMentionsRef.clear() out of handleQueueMessage to
  prevent bare-name mentions from being lost when queued messages are
  sent via handleSubmit
- P2: distinguish internal file-tree drag from external file drag in
  overlay text (Drop to insert as mention vs Drop files here to attach)
- P2: add directories to marquee overflow effect dependency array

* fix(chat): prune stale confirmed mentions

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-21 20:31:14 +03:00
Les De Ridder 36b177dc81 Single select question radios (#965)
* fix(ui): make radios not jump around upon state change

* fix(ui): use radios for single-select questions instead of checkboxes
2026-04-21 20:28:50 +03:00
Bohdan Triapitsyn daa485c5d9 refactor: drop ellipsis dots on active tool labels, use opacity only
Ellipsis dots after tool names were visually noisy. Replace with a
subtle opacity dip (100% -> 70% with 200ms transition) while the tool
is active.
2026-04-20 22:28:50 +03:00
Bohdan Triapitsyn 9f81c8fa46 perf: trim observer work and defer Prism during streaming
- Drop characterData from scroll-follow and file-link MutationObservers;
  ResizeObserver already catches content growth.
- extractMermaidBlocks: bail out when markdown has no 'mermaid' at all.
- MarkdownCodeBlock: skip Prism highlighting while code is actively
  changing, render plain pre until settled for 300ms.
2026-04-20 20:52:57 +03:00
Bohdan Triapitsyn ec38468e93 refactor: refresh chat indicators, settings pages, and shared UI primitives 2026-04-20 18:48:33 +03:00