Commit Graph
29 Commits
Author SHA1 Message Date
Alex Kutas 49f0a9e62f OPE-296: Add linear integration for starting sessions from issues (#3235)
* feat(linear): start sessions from Linear issues
Authorize a Linear workspace on this OpenChamber server, map teams to
projects, attach an issue from chat, start a session or worktree from an
issue, and post started/completed/failed comments that open the session.
Hidden in VS Code.

* feat(linear): connect more than one Linear workspace

Store each OAuth grant on this OpenChamber server and keep one current, so Settings can add and switch workspaces without dropping the others. Project mapping is per workspace. Remove the Linear button next to New Chat; start-from-issue stays on New Worktree.

* feat(linear): add a right-hand issues panel

Browse and filter issues in the rail, open a card to change status or start a session, and collapse search plus most filters to icons on a narrow panel.

* feat(linear): open issues in the rail and filter by Linear status

The rail icon only shows after Linear is connected. Clicking a Linear row on work status opens the panel. Status options match the card, including Done, Canceled, and Duplicate. The Integrations experimental warning sits under Third-party integrations.

* fix(linear): use stable OAuth callback broker

* fix(chat): preview Linear issue attachments

The context switch missed linear-issue, so tsc treated the preview helpers as incomplete.

* fix(ui): restore Linear i18n parity and the #2903 sync harness

Turkish was missing the Linear dictionaries, and the subagent test still wrapped only SyncContext after reads moved to SyncRuntimeContext.

* fix(linear): drop changelog hunks and close review races

Keep changelogs out of this PR, restore CodeMirror ranges, ignore stale Linear list pages, and leave a persisted Linear tab open until auth has actually resolved.

* fix(linear): tint active issue filters and clear them in one click

* fix(markdown): read escaped brackets as text, not display math

`\[...\]` is display math in LaTeX and an escaped bracket pair in
CommonMark. The block tokenizer claimed every `\[`, so prose like
`[title \[Bug\] more](url)` was handed to KaTeX: "Bug" rendered as a
centered formula and the block token split the paragraph, tearing the
link into three pieces. Linear, GitHub and any other source that escapes
brackets the way CommonMark requires hit this.

Display math now has to own its line — `\[` starts one and `\]` ends
one. A formula on its own line still renders; `\[` mid-sentence stays an
escape, which is what CommonMark says it is and what prose almost always
means. Inline `\(...\)` keeps the same ambiguity, but inline math is
legitimately mid-sentence, so there is no position to judge it by.

Covered by regression tests, including the verbatim comment body that
surfaced this.

* feat(linear): make session status comments opt-in and public-only

A status comment lands in a Linear workspace the whole team reads, and
the link it carried pointed at whatever origin started the session —
usually loopback or a LAN address. Everyone but its author got a dead
link, and nobody had agreed to the comments in the first place.

Comments are now off until the user turns them on in Settings ->
Integrations -> Linear, and the check lives on the server: the event hub
posts completed and failure without going through the interface, so a
client-side gate would not hold. When the resolved origin is not
publicly reachable the server posts nothing at all rather than a link
only its author can open; `isPublicSessionOrigin` rejects loopback,
private LAN, carrier-grade NAT, link-local and single-label hosts. The
desktop deep-link origin is gone with it, since no one else can follow
one either.

The comment body also dropped the session title. It repeated the issue
the comment already sits on, and issue titles routinely carry brackets
("[Bug] ...") that broke the markdown link. The body is now one short
link, and `sessionTitle` is gone from the route, client and types.

Also caps the dedupe file at the newest 500 sessions; it grew forever.

* fix(linear): match the pull request panel and clear review findings

Comments in the Linear panel now render as the same avatar timeline the
pull request panel uses, with the shared time-format preference instead
of a raw locale string. Comment authors carry `avatarUrl`, which the
GraphQL selection was not requesting.

Review findings from the same pass:

- `status-runtime.js` hand-rolled `typeof` narrowing and failed the
  vendored anti-slop lint; it now parses through `parse.js` like every
  other file in the module.
- `useLinearAuthStore` turned any failed request into `connected: false`
  with `hasChecked: true`. Since the rail icon, the composer entry and
  the worktree option all gate on `connected === true`, one network blip
  hid Linear for the rest of the session, and Settings only re-checked
  when it had never checked. It now keeps the last known status and
  leaves `hasChecked` false so the next caller retries.
- `LinearIssuesView` (1096 lines) was a static import in `ContextPanel`,
  shipping in the main bundle although its rail icon stays hidden until
  a workspace is connected. It is lazy now, like `GitView`.
- Dropped dead code: the unused port helpers left over from the loopback
  callback, two re-exported default values nothing read, and a redundant
  export in `linkedIssues`.
- Integrations is no longer badged beta.
2026-08-30 02:18:40 +03:00
Bohdan Triapitsyn 3d397b4ad4 Merge pull request #1752 from bashrusakh/fix/1736-branch-filter
fix(worktree): hide non-matching branches during search
2026-08-28 01:24:36 +03:00
Serhii DziupinandSerhii Dziupin 9832c0a4a8 fix(git): handle worktrees from forked PRs safely (#2693)
* fix(git): create worktrees from forked PRs via refs/pull/<n>/head fallback

A worktree created from a linked GitHub PR whose head branch lives in a fork
failed when the fork's head repository was missing (deleted fork) or
unfetchable (auth, network): the dialog threw 'PR head repository URL is
unavailable' before any git command ran, and the server had no fallback to
refs/pull/<n>/head, which GitHub serves on the base repository.

- NewWorktreeDialog: when pr.headRepo is absent, send a prRef config
  (refs/pull/<n>/head from origin) instead of throwing; the fork config now
  also carries prRef so the server can fall back when the fork fetch fails.
- git service: fetchPullRequestHeadRef fetches refs/pull/<n>/head into
  refs/remotes/<remote>/pr-<n>-head (same refspec shape as
  fetchRemoteBranchRef) and both validateWorktreeCreate and
  attachGitWorktreeToCandidate fall back to it when the fork path fails;
  fallback worktrees get --no-track and no upstream config because a PR ref
  is not pushable. When both paths fail the original fork error surfaces.
- Focused tests cover the prRef-only path and the fork-unreachable fallback.

Fixes #2422

* fix(git): harden PR worktree fallback against stale fork refs (#12)

After a fork fetch fails, resolve immediately from refs/pull/<n>/head
instead of accepting a cached remotes/<fork>/<branch> tracking ref.
Match the PR base repository by URL (not a hardcoded origin remote),
store fetched PR heads under refs/openchamber/pull/<n>/head, and share
one existing-mode resolver between validate and create.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* fix(git): make PR head SHA authoritative and namespace private refs

Reuse local/remote branches for linked PRs only when their tip matches
pr.headSha; otherwise fall through to fork fetch / refs/pull. Store PR
heads under refs/openchamber/github/<owner>/<repo>/pull/<n>/head, prefer
HTTPS for direct base-repo fallback, and surface composite fork+fallback
errors when both paths fail.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* test(ui): assert validate/create forward deleted-fork PR payload fields

Guards the dialog wiring regression where validate omitted prRef while
create included it, by asserting worktreeManager forwards prRef,
prBaseRepoUrl, and related fields for deleted-fork configs.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* refactor(git): always checkout linked PRs from refs/pull/<n>/head

Move PR worktree resolution to the server. The UI now sends only
pullRequest identity (number + baseRepoUrl + optional head fields);
the server always fetches the authoritative PR head and best-effort
configures fork upstream afterward.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* refactor(ui): drop PrWorktreeConfig; send PR identity only

Delete the prWorktreeConfig module. NewWorktreeDialog maps linked PRs
straight to pullRequest identity, skips upstream defaults for that path,
and leaves checkout + optional fork tracking to the server.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* refactor(git): linked PRs are {number, baseRepoUrl} only

Drop fork upstream / tracking and head/base owner-repo fields from the
linked-PR worktree path. Fetch refs/pull/<n>/head, create --no-track, done.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* fix(git): meet #2422 Must/Should without refs/pull fallback

Linked PRs send fork identity only; the server provisions pr-<owner>,
fetches the head branch, and fails clearly when the fork is missing or
unreachable. Local reuse requires a matching headSha. Prefer HTTPS for
headRepoUrl. Do not write upstream tracking when the upstream ref was
never fetched.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* fix(git): drop invalid upstream fallback and PR branch collisions

Remove setBranchTrackingFallback: if upstream fetch fails, leave tracking
unset. When a linked PR's head branch already exists locally with a
different tip, create pr-<number> instead of git worktree add -b on the
colliding name.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* fix(git): strip PR worktree create back to fork-remote provision (#15)

Keep the original ensureRemoteName/Url path for linked fork PRs, prefer
HTTPS clone URLs, fail clearly when the fork is unreachable, and leave
upstream tracking unset when the upstream ref was never fetched.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>


Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-18 16:56:55 +03:00
Bohdan Triapitsyn f4743ea060 feat(chat): work-status panel, and MCP auth and settings fixes (#2776)
Adds a work-status panel beside the transcript. Context fill, model and
cost, todos, running subagents and the permission requests blocking
them, branch and working-tree state, MCP servers, pinned messages and
context sources were scattered across the header, the composer and the
context panel — a blocked subagent was reported nowhere at all. The
panel reads them from live channels rather than persisted history, and
becomes an overlay where the chat is too narrow to seat a column.

It is on by default, including for existing installs. Because it now
carries these readouts, the desktop header and composer drop the ones it
duplicates: todo and changed-files chips, usage and MCP tabs. VS Code
and mobile keep theirs — neither hosts the panel.

Fixes MCP authorization, which was broken from the panel, invalidated by
a directory switch through a redirect URI that encoded the working
directory, and left the desktop app in the background because browsers
will not follow a custom-protocol link without a user gesture. The
settings page no longer asks the user to understand the MCP spec before
adding a server: one field takes the command or the link, with the kind
inferred and a visible override, and client-registration fields appear
only when a server actually asks for its own credentials.

Also: skills load from the panel instead of only when the composer's
slash autocomplete opens; the header button names the current instance
rather than falling through to the word "Instance" for relay hosts.

Three new optional UI settings keys, all migrated. No change to stored
MCP server configuration.
2026-08-09 19:30:25 +03:00
17f1b24709 Standardize Settings layout and save feedback (#2122)
* Group settings navigation menu

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Revert "Group settings navigation menu"

This reverts commit 5983a4e82074b8dab1084af1cadd803ba28ea65d.

* Standardize settings layout feedback

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Fix settings save status timer typing

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Retain settings save status

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Report color mode save state

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Match Appearance settings to two-column layout

Rebuild Appearance into Color mode & Theme, Localization, and Density & type sections with responsive two-column grids, consistent section headers, page description, and green save status.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Migrate settings pages to SettingsPageLayout and SettingsSection

Replace ScrollableOverlay/max-w-3xl shells with the shared settings
layout primitives across entity and static settings pages, normalize
section headers, and add settings.page.behavior.description locales.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Unify remaining settings pages on shared section chrome

Wire Appearance, Projects, and Remote Instances through SettingsSection/SettingsPageLayout so every settings surface shares the same header, divider, and page shell treatment.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Align settings UI with OpenChamber design system kit

Apply kit geometry and control specs: 840px content width, 32/48 padding, fixed 260/280 sidebars, radius/spacing tokens, settings select height, stepper dimensions, and shared field/link typography.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Migrate OpenChamberVisualSettings to shared settings primitives

Replace ad-hoc radio/checkbox/chip/field layouts with SettingsSection
shared chrome for Appearance, Density, Navigation, Chat/behavior, and
Privacy while preserving handlers and data-settings-item anchors.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Migrate settings pages to shared field/checkbox/radio primitives

Unify Defaults, Notifications, Behavior, Git, Session Retention, Passkeys,
OpenCode CLI, Commands, About, Keyboard Shortcuts, and Desktop Network on
SettingsFieldRow / SettingsCheckboxRow / SettingsRadioGroup / SettingsChipGroup
for consistent grid, spacing, and DRY layout. Also remove the GitPage double
SettingsSection wrap around GitHubSettings.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Unify remaining settings pages onto shared field primitives

Migrate Agents, Snippets, Skills, Usage, MCP identity rows and selects to
SettingsFieldRow/CheckboxRow/ChipGroup and SETTINGS_SELECT_*; align page
titles; light-touch Voice/Tunnel/Providers/Plugins without rewriting
complex OAuth, permissions, or tunnel flows.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Polish settings section dividers and transport helper text

Slightly stronger section borders for clearer group separation, and keep
message-stream transport description under the chip control.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Fix settings grid alignment, control heights, and Chat section titles

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Tighten settings grid: flat Chat 2x2 and full-width stacked selects

Message options use a flat two-column grid so row headers share a baseline.
Stacked selects fill their column; field-row selects keep a fixed sm:w-56 width.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Align mixed settings controls to shared FieldRow/CheckboxRow grid

Separate FieldRows from CheckboxRows with SettingsInset, move enum
radios into ControlGroups, and convert misplaced StackedFields to
full-width FieldRows so left edges no longer clash.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Translate Behavior response-style preset labels for es and pl

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Group settings nav into categories and improve icons/order

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Fix settings layout: fewer hrs, shared control widths, chat transport section

Remove SettingsInset top borders, align App install/Density controls to full cluster width, give Message Stream Transport its own Chat section, and fold Sessions Small Model into the first section to cut extra dividers.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Align Voice and MCP settings labels with shared heading classes

Swap form field labels to SETTINGS_FIELD_LABEL_CLASS and use
SettingsGroupTitle for MCP control-group headings (manual auth fallback,
request headers).

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Differentiate settings heading levels by context

Page titles are larger and quieter than section titles; group and field
labels use dedicated shared classes so hierarchy is consistent across
settings surfaces without ad-hoc typography mixes.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Use shared settings title classes in SettingsView home

Wire home and unavailable headings through the shared L1/L2 class
constants so they stay aligned with SettingsPageLayout.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Keep settings number steppers from stretching +/- buttons

Density & type NumberInputs no longer flex-grow across the row, and
NumberInput locks minus/plus to fixed width so the plus side cannot
inflate when the control is placed in a full-width cluster.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Place spacing and input-bar offset on their own row

Density & type now lays out as font families, then font sizes, then
Spacing Density / Input Bar Offset on the row below.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Regroup settings nav and improve Voice layout

Drop Advanced/Usage/Git nav categories: Voice and About under
Interface, Usage under OpenCode, Git under Workspace. Voice provider
chips and STT model cards use shared settings primitives with roomier
spacing and a two-column model grid.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* Space out chat feature groups in visual settings

* Polish settings: save-state wiring, container-query layouts, unified dropdown triggers

- Wire shared save indicator into Behavior page saves and git identity CRUD
- Convert settings layout primitives and page rows from viewport to container
  queries so narrow panes stack instead of clipping
- Unify custom dropdown triggers (model/agent/provider pickers) and remaining
  page selects on the settings control size
- Bump global radius scale by 1px; align variant input with select size
- Migrate stray raw controls (MCP OAuth checkbox, worktree remove button,
  git identity dialog rows, skills dialog labels) onto shared primitives
- Make settings nav items span full sidebar width; trim nav to 240px

* Add General settings page, regroup nav, cap control widths, promote chat feature headers

* Mobile settings nav: plain background and touch-sized rows

* Hide secondary settings descriptions behind clickable info hints

* Move quota credentials to Usage, navigation settings to General, rename External Tunnel

* Quiet settings save indicator: silent success, delayed spinner, visible errors

* Rewrite settings-ui-patterns skill around shared primitives and refactored conventions

* Remove settings starter page; open last visited page, defaulting to General

* Settings polish: spacing, control sizes, trigger widths, readable model names, device dates

* Centralize dropdown trigger chrome, settings nav polish, mobile-only input bar offset

* Fix global line-height regression, auto-hide first-section divider, shortcut row spacing

* Global line-height 1.45, align scheduled tasks header button with select

* Hide editor toolbar and About in VS Code, animate chat render preview outside desktop dialog

* Rebuild agent tool permissions on source-of-truth model

Edit the agent's own permission map verbatim (inherit vs explicit actions, pattern rules only for pattern-capable keys), save permission-only, drop the server-side non-wildcard re-merge that resurrected deleted rules, and surface session-granted rules as read-only.

* Agents model parameters polish: row spacing, variant dropdown, unified widths, dash for unset numbers

---------

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-07-18 00:11:05 +03:00
Leonidandbashrusakh e5bba59a75 fix(worktree): restore last source branch reliably (#2030)
* fix(worktree): restore last source branch reliably

* chore: retrigger review

---------

Co-authored-by: bashrusakh <bashrusakh@users.noreply.github.com>
2026-07-11 16:01:30 +03:00
Leonidandbashrusakh d01126cf3c fix(ui): pass sourceRepo to PR/issue context calls for fork workflows (#2090) (#2091)
Fixes GitHub PR/issue context endpoints returning 404 when working
from a fork because they resolved the repo from origin remote only.

- Pass sourceRepo: status?.repo ?? null to all prContext() calls in
  PullRequestSection.tsx (5 call sites)
- Pass sourceRepo: args.pr.sourceRepo ?? null / args.issue.sourceRepo
  ?? null to NewWorktreeDialog.tsx (3 call sites: prContext, issueGet,
  issueComments)
- Add status?.repo to dependency arrays to prevent stale closures

The prStatus endpoint already resolves the correct repo through the
fork network; this change wires it through to the downstream API calls.

Closes #2090

Co-authored-by: bashrusakh <bashrusakh@users.noreply.github.com>
2026-07-11 14:37:33 +03:00
8c1a24089d fix(worktree): gate sessions on bootstrap readiness (#1762)
Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-06-26 12:16:42 +03:00
Leonid Skorobogatyy 20f2a2635b chore(worktree): drop dead otherBranches labels after search guard
The mobile and desktop (cmdk) pickers in NewWorktreeDialog now hide
non-matching branches during search via a !hasExistingBranchQuery /
!hasSourceBranchQuery outer guard. That makes the inner heading
ternaries (which switched between 'localBranches' and
'otherLocalBranches' depending on the search state) unreachable: they
always resolve to the non-query label, and the
'hasExistingBranchQuery && <CommandSeparator />' lines were dead code.

This commit:

- Replaces 8 heading ternaries with static non-query labels
  (mobile and desktop, existing/source, local/remote).
- Drops the two unreachable <CommandSeparator /> renders in the
  otherLocal blocks.
- Simplifies the otherRemote separator conditions by removing the
  always-false '|| hasExistingBranchQuery' / '|| hasSourceBranchQuery'
  terms.
- Removes the now-unused 'session.newWorktree.otherLocalBranches' and
  'session.newWorktree.otherRemoteBranches' keys from all 9 locales.

Behavior is unchanged; this is dead-branch cleanup only.
2026-06-24 03:51:56 +11:00
Leonid Skorobogatyy 1ad470cead fix(worktree): hide non-matching branches during search 2026-06-24 03:26:26 +11:00
Tom RochetteandReproduction Bot e74ce993d5 fix: auto-fetch branches when opening create worktree dialog (#1572)
* repro: add test reproducing issue #1564 - empty branch list in create worktree dialog

The NewWorktreeDialog does not auto-fetch branches when it opens
if branches haven't been cached yet. Branches only get populated
if the Git tab has been opened or a draft session has been started,
both of which call fetchBranches/fetchAll independently.

The test demonstrates:
1. Branches are null (empty list) for directories not yet fetched
2. Branches are available after fetchBranches is called
3. The ChatInput's draft session branch fetch explains why the
   issue says 'list gets populated after starting a draft session'
4. The missing auto-fetch call when dialog opens

* fix: auto-fetch branches when opening create worktree dialog

---------

Co-authored-by: Reproduction Bot <repro-bot@openchamber.dev>
2026-06-13 08:50:11 +03:00
Bohdan Triapitsyn e0113c637d feat: support fast worktree-backed session flows
Add a directory-created fast path for worktree creation so session and send flows can continue once the target directory exists while Git attachment and bootstrap finish in the background.

Track bootstrap status explicitly in shared UI contracts, including pending, ready, and failed states. Background watchers now surface failures and timeouts, update stored worktree metadata, and keep web and VS Code runtime behavior in parity.

Move GitHub issue/PR worktree sessions and assistant-answer fork sessions onto the unified send path so provider, model, agent, and variant selections are preserved. The assistant-answer fork dialog can optionally create a worktree outside VS Code.

Make worktree deletion dialogs close after linked-session cleanup while removing the worktree in the background, and clean up failed fast-create artifacts safely without recursively deleting user or agent-written files.

Validation: bun test packages/ui/src/lib/worktrees/worktreeBootstrap.test.ts packages/ui/src/lib/worktrees/worktreeManager.test.ts; bun run type-check; bun run lint.
2026-06-06 23:25:39 +03:00
Bohdan Triapitsyn 2031e3b4a8 Decouple bundled UI from runtime API and add remote instance tooling (#1228)
Add a packaged-client runtime boundary so the shared UI can talk to local,
desktop, remote, and VS Code runtimes through the right transport instead of
assuming one same-origin web server.

Centralize OpenChamber-owned API access behind RuntimeAPIs, runtimeFetch, and
runtime URL helpers, while keeping official OpenCode traffic on the SDK path.
Support runtime switching, remote host selection, desktop client credentials,
and headless connection links for pairing packaged clients with remote
OpenChamber servers.

Harden the new auth model by moving long-lived client tokens out of browser
URLs, introducing short-lived scoped URL tokens for browser-owned transports,
restricting URL-token access to explicit readable/realtime routes, and making
client-token management session-scoped or self-scoped as appropriate.

Update browser-owned assets and preview proxy flows to work with the split
runtime model, including authenticated project icons, preview token propagation,
CSP-safe preview bridge injection, and preview proxy auth that survives
short-lived URL-token expiry.

Tighten Electron security boundaries for packaged clients by gating privileged
preload state to trusted origins and requiring explicit confirmation before
connect deep-links import or switch remote runtimes.

Also refresh agent guidance and project skills so future runtime/API, auth,
preview, UI, CLI, settings, locale, and drag-to-reorder work follows the new
architecture.
2026-06-02 00:43:05 +03:00
Bohdan Triapitsyn 5c1333a139 fix: restore typing in branch dropdown search fields
Prevent menu typeahead from intercepting search input keystrokes
Fix branch search in worktree and Git sidebar dropdowns
Cover related directory tree and integrate branch inputs
2026-05-17 23:48:05 +03:00
Bohdan Triapitsyn 14357257ae perf(ui): migrate icons to SVG sprite system
Replace @remixicon/react with a shared Icon component that renders
via <use href> references to a single hidden SVG sprite. This reduces
DOM node count by replacing inline SVGs with lightweight references.

- Create Icon component with sprite injection (packages/ui/src/components/icon/)
- Migrate all 164 files from @remixicon/react to Icon component
- Auto-generate sprite data from remixicon bundle (scripts/generate-icon-sprite.mjs)
- Add bun run icons:generate to package.json
- Move @remixicon/react to devDependencies
- Add icon usage instructions to theme-system skill
2026-05-13 13:26:35 +03:00
Bohdan Triapitsyn 1a0f3bea8e fix: allow PR worktrees from existing local branches
Use existing local PR branches instead of blocking worktree creation
Only block PR branches already checked out in another worktree
Clarify local branch validation messages across locales
2026-05-08 16:59:31 +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 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
Shyamalan KannanandBohdan Triapitsyn 6ab363f8df fix(ui): keep settings sidebar open, center content, and fix worktree refresh (#1058)
* fix(ui): keep settings sidebar open, center content, and fix worktree refresh

- Prevent settings nav from collapsing on split pages (Magic Prompts, Agents, etc.)

- Center settings content with max-w-3xl for better layout on wide screens

- Widen settings window from 960px to 1200px max-width

- Fix source branch dropdown overflow with max-height and scrolling

- Add worktreeRefreshNonce to trigger immediate sidebar refresh after worktree creation

* ref(ui): address Greptile review — remove dead isNavCollapsed code and split worktree effect

- Remove isNavCollapsed variable and all dead branches (resize handle, renderSettingsNav param, SETTINGS_NAV_RAIL_WIDTH)

- Split SessionSidebar effect so worktreeRefreshNonce only triggers discoverWorktrees, not refreshGlobalSessions

- Both type-check and lint pass

* fix(ui): refine settings and worktree dropdown behavior

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-04-28 14:52:57 +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
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
Bohdan Triapitsyn 4506c18f53 UI refresh: Base UI migration + flat tinted button language + mobile polish (#960)
* deps: bump @opencode-ai/sdk to 1.4.6 and add @base-ui/react

* build: align vite configs with Base UI and packages update

* feat(ui): migrate primitives to Base UI

Replace Radix-based wrappers (button, checkbox, collapsible, command,
dialog, dropdown-menu, input, radio, scroll-area, select, separator,
sonner, switch, textarea, toggle, tooltip) with @base-ui/react
equivalents and a refreshed visual language: flexoki theme tokens,
typography utilities, and a redesigned CommandPalette. Add new
fancy-button variant set and a local slot helper for asChild composition.

* refactor(ui consumers): adopt Base UI primitives across the app

Update chat, layout, git views, settings, multirun, session and agent
management screens to use the new Base UI-backed primitives (anchor
width token rename, dropdown/select/dialog usage adjustments, new
component APIs).

* fix(ScrollableOverlay): forward `disabled` to inner element

Add `disabled` to the type so it propagates via rest spread to the
underlying element (e.g. textarea in simple mode). Resolves a type
error reported after the main merge.

* refactor(dropdown consumers): migrate preventDefault sites to closeOnClick

Base UI's Menu.Item does not check `event.defaultPrevented` before
closing the menu, so the Radix-era `onSelect={(e) => e.preventDefault()}`
pattern no longer keeps the menu open. Replace those sites with the
Base UI `closeOnClick={false}` prop (VSCodeLayout rate-limit info
rows; AgentGroupDetail worktree actions).

* docs(AGENTS): note Base UI as primary source for UI primitives

* fix(ContextPanel): center expand/close buttons in tab header

Header (h-8) had the action row pinned with items-end + pb-1.5, which
placed the h-7 buttons ~2px above the header top and made them look
cropped. Switch to items-center so they sit on the same baseline as
the tabs.

* style(tabs): drop transitions on default variant, soften active pill

Default (underline) variant now switches instantly — no transform/width
transition on the underline indicator, no color transition on tab
buttons. Active pill bg is color-mixed down to 55% of
--interactive-selection so light-theme selection doesn't scream.

* style(button): pill-style elevation and introduce chip variant

Solid variants (default/destructive/neutral/primary/basic) now use the
active-pill shadow stack — hairline navy outer ring + stacked tight/soft
drops — instead of the pure-black inset/border approach. Outline
variant (and FancyButton basic) gets the same elevation without the
white top highlight (no fill to catch light).

Add a new chip variant: flat `border border-border/60` button without
elevation, intended for one-of-N toggle groups where a raised
button would read as the wrong affordance.

* style(header): apply pill-style shadow to OpenIn and Project Actions

Both split-button wrappers previously used a 1px CSS border for
definition. Replace that with the pill elevation stack so the header
buttons match the new Button variants and feel less hard-edged.

* refactor(toggles): migrate one-of-N segmented groups to chip variant

Color Mode, When-sessions-expire, voice/STT providers, auth method,
agent mode, MCP transport, tunnel type and terminal Ctrl/Cmd modifier
toggles all read as flat chip selectors rather than raised buttons.
Switch them from variant="outline" to the new chip variant so the
raised pill-style elevation doesn't fight the selected-state tint.

* fix(elevation): theme-adaptive pill-style shadow for dark mode

The navy (rgba(14,18,27,*)) outer ring was invisible on dark surfaces,
erasing the button/pill edge. Add dark: variants that use a subtle
white outer ring (rgba(255,255,255,0.10)) and darker drop shadows
(rgba(0,0,0,0.30/0.20)) so the elevation reads on both themes.

Applies to Button solid/outline variants, FancyButton equivalents,
the OpenIn and Project Actions header split-buttons, and the active
pill indicator in SortableTabsStrip.

* style(button): flat tinted variants with squircle corner-shape

Replace elevated pill-shadow design with a flat tinted language: pale tinted
fill + hairline tinted border + saturated tinted text for default/destructive.
Add CSS corner-shape: squircle with @supports gate so Chrome 136+ gets the
softened superellipse corners at 50px, Safari falls back to a slightly larger
round radius.

* style(tabs): replace active pill shadow with tinted border

* style(header): flat border and squircle corners for OpenIn / Actions

Drop pill-style shadow stacks in favor of a plain hairline border and apply
the same squircle corner-shape treatment as Button so header controls match
the new visual language.

* style(checkbox): drop filled background for tinted checkmark

Checked / indeterminate states now keep the transparent box, show a
primary-base check/minus glyph, and use a softened primary-tinted inset
border (50% mix) so the control reads as selected without a solid swatch.

* style(layout): widen sidebar-to-chat corner radius to 10px

* style(button): retune tinted variants and unify outline fill

- Bump primary fill toward noticeable but still soft (light 10/16/22,
  dark 16/22/30) and weaken both primary and destructive borders
  (12% light / 20% dark) so the tone reads without shouting.
- Further calm destructive across fill and border so revert-style
  actions match the muted reference.
- Align the outline variant fill with --surface-elevated so git-view
  generate/commit buttons share the header action button surface.

* style(tabs): align pill variant with new button language

Active pill now uses --surface-elevated fill and border-border/60 to
match the outline button. Track and tab/pill geometry adopt the same
squircle treatment as Button: 9-10px fallback with 50px radius under
@supports(corner-shape:squircle). Also tighten track padding (inline
2px, block 2px) and grow active tab height so the indicator fills
the track instead of floating inside it.

* refactor(theme): hardcode radius scale, drop per-theme override

Radius values are now fixed in the design system (flexoki-dark's scale
becomes the app default). Themes can no longer override --radius-* — the
config.radius block is removed from every theme JSON, dropped from the
Theme type, and no longer emitted by the CSS generator.

* chore(ui): lint/type cleanup across Base UI wrappers

* style(tabs): dial pill track background down to near-invisible

* refactor(chip): aria-pressed drives the selected tinted state

The chip variant now carries the tinted primary palette (same as the
default filled button) under aria-pressed, so consumers toggle selection
via a single aria attribute instead of repeating the border/text/bg
override classes. All existing chip consumers migrate to aria-pressed
and drop their hand-rolled selected className.

* chore(defaults): humanize variant label in thinking selector

* fix(diff): render all lines in single-file mode

Single-file mode wrapped PierreDiffViewer in its own layout=fill which
creates a nested virtual-root; combined with the outer CSS containment
on the wrapper, Pierre's virtualizer measured a clamped viewport and
stopped rendering lines past ~150. Mirror the stacked-mode approach:
hoist a ScrollableOverlay as the sole virtual root/content and render
PierreDiffViewer inline, so the shared virtualizer sees the real scroll
viewport and paints every line.

Also drops an unused cn import in SessionRetentionSettings.

* feat(files): collapse floating editor controls behind icon button

The floating file editor toolbar now starts as a single pill icon button
and expands into the full control row on hover or click. Clicking outside
or moving the pointer away collapses it back, keeping the editor surface
unobstructed while the file is being read/edited. Fullscreen overlay
keeps its always-visible toolbar.

* fix(chat): align mobile session status bar radius with chat input

* fix(mobile): keep radio/checkbox/switch at their native size

Mobile CSS forced min 36x36px on every <button>, which swallowed our
Radio and Checkbox primitives (also buttons with role=radio/checkbox)
and ballooned them on touch devices. Exclude role=radio/checkbox/switch
from the touch-target rule and pin the Radio/Checkbox box size with
arbitrary px so padding-scale overrides cannot stretch them either.

* chore(mobile): drop unused cornerRadius prop plumbing

The status bar now hardcodes var(--radius-lg) on both views, so the
cornerRadius prop and the useUIStore selector that sourced it in
ChatInput are dead weight.
2026-04-19 15:52:54 +03:00
Bohdan Triapitsyn 5f0d1623ae feat: expand magic prompts coverage and split generation prompts (#835)
- Add configurable visible/instructions prompt families for commit/PR generation, PR checks/comments flows, and git conflict resolution helpers.
- Refactor prompt sending to explicit visible + synthetic parts instead of newline-based splitting, with legacy override migration for old keys.
- Polish Magic Prompts settings UX with grouped sidebar entries, tooltip-based descriptions, AI icon, and validation that visible prompts cannot be empty across web and VS Code runtimes.
2026-04-07 22:51:14 +03:00
Bohdan Triapitsyn d982171689 refactor: centralize git and pr store refresh 2026-04-03 19:47:01 +03:00
Bohdan TriapitsynandIuliia Ivashko c9e31a0e6c perf: harden sync architecture and modularize runtimes (#803)
* fix: added desktop app background throttling

* perf: add streaming debug metrics panel

- Show streaming performance metrics in the debug panel
- Auto-enable stream profiling while the panel is open
- Add JSON export for sharing UI and VS Code metrics

* perf: batch streaming updates more aggressively

- Buffer message deltas and metadata updates to cut render churn
- Skip no-op part updates before they touch the message store
- Fix the desktop debug panel shortcut binding

* perf: split streaming event handling and coalesce deltas

- Move streaming content events onto a dedicated fast path
- Defer non-critical stream side effects off the hot path
- Merge repeated message delta events before they reach the UI

* perf: isolate streaming rows from chat rerenders

- Memoize chat rows against render-relevant message changes only
- Read live assistant text directly from store to narrow streaming updates
- Split the active streaming entry from the stable message list path

* perf: streamline chat streaming and SSE proxying

- Reduce chat rerenders around the active streaming path
- Simplify server SSE forwarding to avoid duplicate proxy work

* fix: preserve the first streaming text chunk

- Show the initial text chunk immediately before batched deltas arrive
- Bypass batching for the first text or reasoning part update
- Keep later streaming updates buffered for performance

* perf: align streaming/render hot paths with opencode parity

* perf: harden turn/cache stability and stale delta suppression

* fix: stabilize chat rendering and disable timeline interactions

- Disabled timeline dialog access from shortcuts, commands, and chat input
- Reduced chat render churn by simplifying message list and turn staging behavior
- Improved session-switch stability to prevent update-depth crashes

* perf: track static message rerenders during streaming

* perf: reduce sorted-mode activity rerender fanout

* perf: reduce chat rerender fanout and add active-turn metrics

- Reduced sorted-mode rerender coupling by tightening turn context propagation
- Added a metric for static rerenders outside the active turn during streaming
- Exposed new chat render counters in the debug panel for parity tracking

* fix: keep sorted activity mounted while stream grows

* fix: stabilize session and history scroll rendering

* refactor: decouple server routes from index

* refactor: extract fs module from server index

* refactor: move opencode route ownership into module

* refactor: extract notification route registration

* refactor: extract opencode and notification runtimes from index

* refactor: extract settings runtime and complete server modularization pass

* refactor: modularize server config, skills, icons, and tunnel routes

* refactor: extract server modules from monolithic index.js

Split proxy, routes, runtime helpers, and notification emitter
into dedicated modules under packages/web/server/lib/.

* refactor: replace session/message stores with SSE-driven sync layer

Delete ~9200 lines of old architecture (useEventStream, messageStore,
sessionStore, useSessionStore, questionStore, useTodoStore, client SSE).

New sync layer: event pipeline with coalescing + 16ms flush, pure event
reducer, per-directory child stores with LRU eviction, cursor pagination,
optimistic updates, deferred timeline staging, text throttle.

Migrate all UI consumers to sync hooks (useSessionMessages,
useSessionMessageRecords, useSessionStatus, useSessionPermissions, etc).

Strip session-ui-store to UI-only state, delegate SDK ops to
session-actions with abort-if-busy, optimistic store updates, and
response merging for revert/fork/archive/delete.

Add notification-store for SSE-driven session attention tracking,
cross-directory GlobalSessionStatusStore for sidebar indicators,
client-side diff snapshot sanitization to prevent memory bloat,
and revert message filtering via useVisibleSessionMessages.

* feat: notification store, session actions, activity detection

Add notification-store.ts for SSE-driven attention tracking.
Add sanitize.ts to strip diff snapshot memory bloat.
Add session-actions.ts with optimistic revert/fork/archive/delete.
Improve useSessionActivity with incomplete-message fallback.
Delete useServerSessionStatus polling hook.

* fix: add directory param to all SDK calls, fix command/shell/abort routing

All SDK calls in session-actions.ts now pass directory parameter —
required by OpenCode server to scope session operations. Without it,
abort, commands, revert, fork, and other operations returned 500.

Add routeMessage() in session-ui-store for shell mode (session.shell),
slash commands (session.command), and normal prompts. Command lookup
checks both sync child store and useCommandsStore. Handle /compact
locally via session.summarize().

Implement getContextUsage() to restore header context usage display —
reads token counts from last assistant message in sync store.

* refactor: replace custom API proxy with http-proxy-middleware

Remove ~280 lines of custom proxy code: forwardSseRequest,
forwardGenericApiRequest, collectRequestBodyBuffer, header
manipulation, hop-by-hop filtering, SSE block buffering.

Replace with single createProxyMiddleware() call that handles
SSE streaming, large bodies, and timeouts out of the box.
Dynamic router for OpenCode port changes after restarts.
Auth headers injected via proxyReq hook.

Keep: readiness gate, Windows session merge, API prefix detection.

* perf: targeted event draft cloning to fix streaming render cascade

Event handler was eagerly cloning all state slices on every event,
breaking Zustand selector referential equality. During streaming
(~60 events/sec), this caused every subscriber to re-render regardless
of which slice actually changed.

Now only clones fields the specific event type mutates. Also extracts
StatusRowContainer to isolate high-frequency useAssistantStatus
subscription, removes dead messageStreamStatesMap subscription from
ChatContainer, and narrows useAssistantStatus to only track last
assistant message parts.

MessageList renders: 1972 → 296 per streaming session (-85%).

* fix: null safety for sync state slices

Add defensive ?? {} guards on permission, question, session_status,
and message record access. Prevents crashes when child store state
is partially initialized during bootstrap.

* perf: dedup inflight SDK calls, extract concurrency util, delay PR tracking

Extract mapWithConcurrency to shared lib/concurrency.ts. Add in-flight
dedup for loadProviders/loadAgents to prevent concurrent duplicate SDK
calls. Delay initial PR background tracking by 5s to reduce startup
CPU burst.

* fix: header session lookup across all child stores

Session title and context panel click failed when session belonged to
a different directory than the current child store. Fall back to
getAllSyncSessions() to search all initialized stores.

* chore: bump @opencode-ai/sdk to 1.3.5

* docs: add sync event handling guide

* Optimize session prefetch and improve delete/archive UX

- Add settlement delay to session prefetch to avoid race conditions on
  rapid session switches
- Reduce git diff prefetch and session cache limits for better performance
- Implement optimistic UI updates for session delete/archive operations
  with proper rollback on failure
- Wire session prefetch hook into SessionSidebar with sync integration

* Add file content cache and sync optimizations

- Wrap FilesAPI with in-memory LRU cache for file content with dual
  constraints (entry count and byte size)
- Optimize chat timeline scroll restoration using useLayoutEffect
- Preserve React references in message and part arrays to prevent
  unnecessary re-renders when prepending history
- Add session prefetch TTL cache to prevent redundant fetches
- Integrate session prefetch cache clearing with eviction flow

* Improve session sidebar error handling and add diff prefetch filtering

Load active and archived sessions independently using Promise.allSettled
to prevent one failure from blocking the other. Add retry logic to session
API calls and skip large files during diff prefetch to improve performance.

* Replace sendMessage with optimisticSend wrapper

Introduces optimistic UI updates for normal chat messages to provide
instant feedback. Messages appear immediately in the UI while the API
call executes in the background, with automatic rollback on errors.

* perf: split stores, proper optimistic send, fix revert/directory bugs

- split session-ui-store into voice/input/selection/viewport stores
  to reduce subscriber re-evaluation during streaming
- wire optimisticSend through useSync shadow Map infrastructure
  matching OpenCode's pattern (no heuristic part detection)
- port OpenCode Identifier.ascending ID format for correct sorting
- pass messageID to promptAsync to prevent duplicate messages
- fix worktree directory not propagating to session actions
  (dynamic dir() via opencodeClient.getDirectory)
- fix setCurrentSession accepting directoryHint for new sessions
- fix revert not hiding messages (session limit was 5, bumped to match loaded count)
- fix revert optimistic message removal from store
- fix load-more flicker (useLayoutEffect scroll compensation)
- add prefetch TTL cache, file content LRU cache
- add session prefetch for adjacent sessions
- add instant archive/delete (optimistic before SDK call)
- migrate legacy window.__zustand_session_store__ to session-ui-store
- add retry + independent error handling for archived sessions
- add AGENTS.md performance rules

* perf: startup optimization — dedup, caching, light git status, diff rendering gates

- defer diff prefetch to git tab open, reduce concurrency 4→2, skip >500 changed lines
- cap project git checks concurrency (2), directory status probe (3)
- dedup provider/agent loading, github auth, worktree list (in-flight + TTL caches)
- delay PR tracking 5s, cache 403 search failures per-repo
- coalesce settings PUT (200ms debounce), cache settings GET (2s TTL)
- cache canonical directory resolution (60s TTL)
- persist missing directory status to localStorage (10min TTL)
- light/heavy git status: polling skips numstat+line counting+rev-list
- large diff rendering gate (>500 lines → "render anyway" button)
- tokenization degradation for >500KB files in Pierre
- parallelize main.tsx pre-render awaits
- batch sidebar file tree expanded paths restoration (3 at a time)
- remove bare useConfigStore() subscription in AgentsPage
- sync worktree sandboxes to OpenCode SQLite DB
- fix RightSidebarTabs ternary → explicit tab matching
- defensive guards on sync state (session_status, permission, question, message)

* fix: add defensive guards on remaining sync state field accesses

guard session_status, permission, message, todo, part, config with ?? {}
in useDirectorySync selectors, session-cache, and bootstrap

* fix: add missing directory dep to useCallback in use-sync.ts

* fix: preserve diffStats when light-mode polling overwrites status

* perf: optimize startup git status polling and diff rendering

Preserves diff stats when lightweight polling updates repository status
Reduces startup overhead with smarter git polling and store updates
Adds detailed optimization and migration docs for next performance steps

* fix: keep chat diff stats stable during git status updates

Prevents lightweight git polling from dropping diff statistics
Keeps MessageList diff indicators consistent while status refreshes
Improves reliability of git-aware chat rendering

* fix: user animation replay, queued message variant, startup provider loading

- consume animation ID after first play to prevent re-animation
  on neighbor assistant message completion
- capture send config (model/agent/variant) at queue time matching
  OpenCode's FollowupDraft pattern instead of re-resolving at send time
- replace one-shot startup recovery effect with polling interval
  that retries every 2s until providers and agents load
- fix optimistic bridge to avoid re-render loop (stable ref wrappers)

* chore: update tauri to 2.10.3 and all plugins to latest

- tauri 2.9.4 → 2.10.3
- tauri-build 2.5.3 → 2.5.6
- tauri-plugin-dialog 2.4.2 → 2.6.0
- tauri-plugin-log 2.7.1 → 2.8.0
- tauri-plugin-shell 2.3.3 → 2.3.5
- tauri-plugin-updater 2 (floating) → 2.10.0 (pinned)
- @tauri-apps/api ^2.9.0 → ^2.10.1
- wry 0.53.5 → 0.54.4 (transitive)

* refactor: decouple web server index orchestration runtimes

* fix: align VS Code runtime behavior with web and reduce draft view CPU load

- Queue VS Code bridge and SSE startup requests until API readiness to avoid false bootstrap failures
- Make agent manager actions directory-aware and remove real worktrees with safer partial-failure handling
- Replace heavy logo animation path with a lightweight pulse to cut draft-session CPU usage

* fix: restore auto-selected file sending in chat input

- Send server-selected files as proper file URLs in the message payload
- Include server-backed attachments in submit flow instead of dropping them
- Restore queued-message attachments through the refactored input store

* fix: restore session model selection consistently on session switch

- Restore agent, model, and variant from the latest loaded user message for each session
- Wait for session messages before applying restored selections to avoid stale or missing state
- Remove legacy session-choice inference paths that caused overlap and instability

* fix: restore permission replies and auto-accept across sessions

- Scope permission and question replies to the target session directory so answers take effect reliably
- Make permission auto-accept immediately handle pending requests and react to new permission prompts
- Keep parent-session handling working for child-session requests through the shared response path

* feat: add reusable fuzzy branch fuzzy-search helper and dialog integration (#798)

* feat: add reusable fuzzy branch search for worktrees

* chore: drop planning docs from feature branch

* feat: make worktree branch refresh manual

* feat: add configurable session retention action

* refactor: centralize global session state in ui store

* fix: cancel debounced permission push after reply

* docs: clarify global and directory session store architecture

* docs: refine agent development rules and session activity guidance

- Clarify agent code of conduct and durable development patterns
- Add explicit shared-store rerender and live-state guidance
- Narrow session activity fallback to avoid stale working state

* chore: updated .gitignore

---------

Co-authored-by: Iuliia Ivashko <yulia.ivashko@gmail.com>
2026-03-31 18:47:00 +03:00
Bohdan Triapitsyn 53c2a0d919 feat: instant draft-first worktree creation and multi-run launcher redesign (#741)
## Summary

- **Instant worktree creation from chat draft**: selecting "+ New worktree" in the draft branch selector immediately creates a session draft and bootstraps the worktree in the background — no modal interruption
- **Redesigned multi-run launcher**: compact 2-column grid layout in a right-sized dialog with scroll shadow, sticky footer, tooltips replacing verbose descriptions, and project icons in the selector
- **Branch selector aligned across surfaces**: multi-run and agent manager branch pickers now use the shared git store and match NewWorktreeDialog behavior (same default resolution cascade, no synthetic HEAD option, all branches shown)
- **Opaque model multi-select dropdown**: fixes text bleed-through on translucent backgrounds by compositing `--surface-elevated` over `--surface-background`
- **"+ New" inline button in sidebar worktree headers** for faster worktree creation

## Why

Worktree creation was behind modal flow that interrupted the user's train of thought. The draft-first approach lets users start typing immediately while the worktree bootstraps. The multi-run launcher had an oversized form layout with redundant explanations, and its branch picker behaved differently from the main worktree dialog - causing confusion about which branches were available and what the default was.
2026-03-22 22:31:29 +02:00
Bohdan Triapitsyn 79143bff4c feat: massive chat reliability + UX pass (web/desktop/mobile/vscode) (#593)
## Added Features
- Add VS Code save-as-image flow for assistant messages via webview bridge + native save dialog.
- Add hourly desktop update checks after startup.
- Add new tool output display mode: `Changes` (auto-expand edit/write/patch only; keep activity expanded; mode guidance text).
- Add GitHub PR attachment flow in chat input with PR picker + attached PR chip/details.
- Add mobile overlay presentation for GitHub Issue and PR pickers (shared with desktop picker content).

## Fixes
- Save-gate project icon updates until explicit Save; allow icon removal with same save-gated behavior.
- Restore clickable chat action buttons in sticky header mode (desktop + Firefox hit-target issue).
- Clamp sticky user messages to bounded chat height and allow internal scrolling.
- Prevent drawer context crash during iPad/tablet orientation switching.
- Improve text-selection action menu placement on narrow screens.
- Move assistant message time into clock tooltip; keep duration display clean.
- Hide `Link GitHub Issue` row in VS Code chat input area (GitHub flow is not yet ready there).
- Remove laggy close animation in text-selection popover; keep open motion/positioning behavior.
- Fetch branches when picker opens and cache empty; show loading state instead of false “No branches found”.
- Fix share-image export metadata rendering (theme background resolution, timestamp rendering, footer alignment).
- Scope MCP services status/toggles to active directory to avoid cross-project leakage.
- Improve long user-message clamp behavior (40% cap variant, hidden scrollbar, scroll shadows, expansion detection).
- Fix desktop `Check for Updates` menu handler; prevent duplicate checks; show clear success/error toasts.
- Stabilize long user-message scrolling behavior (follow-up hardening).
- Avoid premature web update failure on slower servers.
- Restore user message image previews + fullscreen gallery navigation payload.
- Repair desktop chat drag-and-drop image attachments when native drop coords are missing.
- Move GitHub issue linking entry into Add attachment menu.
- Align header context usage percentage visuals with context panel.
- Align `@` file search with active project in all runtimes.
- Route `@` file discovery through OpenCode SDK `find.files`; remove legacy `/api/fs/search` reliance.
- Make chat `@` mention behavior consistent with files-style behavior.
- Keep status-row todos in stable order after status changes; add compact status icons; replace noisy priority labels.

## Refactors / UX Consistency
- Simplify chat attachment model and remove project file picker path.
- Keep composer focused on `@` mention file flow.
- Use direct `Attach files` action in VS Code instead of attachment dropdown path.
- Unify issue/PR picker behavior between desktop and mobile overlays.
2026-03-04 01:41:01 +02:00
Bohdan Triapitsyn 604ac682c2 fix: restore correct worktree source resolution for branches and PRs (#578)
Prevent local branches with slashes from being treated as remotes during worktree creation
Use PR head refs (including fork remotes) when creating PR-linked worktrees
Keep upstream tracking aligned so pushes target the intended PR branch
2026-03-03 00:52:36 +02:00
Bohdan Triapitsyn bdad912ea5 feat: streamline worktree session flow (#577)
* feat: add unified worktree creation dialog with GitHub integration

Unified dialog for creating worktrees with branch selection and GitHub integration
New GitHub picker dialog for selecting issues and PRs with validation
Persistent state when switching between new branch and existing branch modes

* refactor: minimalistic compact design for worktree dialogs

Redesigned dialogs with minimalistic compact layout
Moved mode tabs inline with dialog header
Inline validation errors with action buttons

* fix: maintain consistent dialog height in all states

Fixed dialog height fluctuations when loading or switching tabs
Consistent 300px height for loading, empty, and populated states
Nested container structure prevents layout shifts

* refactor: minimalistic compact UI redesign for worktree dialogs

Minimalistic compact layouts with reduced spacing and inline tabs
Standardized SortableTabsStrip with pills variant for mode selection
Two-row linked item display and inline validation with action buttons

* feat: mobile overlays for worktree dialogs with branch pickers

Mobile overlay panels replace dropdowns for branch selection
Fixed footer button overlapping with proper flex layout
Responsive design with conditional mobile/desktop rendering

* refactor: relocate selected item badge to footer with proper alignment

Selected item badge moved to footer left side on desktop
Proper alignment with Cancel/Select buttons using h-8 height
Cleaner header layout removing the selected item display

* perf: use cached branches from git store for instant branch selection

Branch list now loads instantly from git store cache
No more loading delay when opening new worktree dialog
Removes redundant API calls by using existing cached data

* refactor: improve branch selection UI with grouped headers and compact layout

Group branches into Local and Remote sections with clear headers
Compact dropdowns with better sizing and text wrapping
Bold labels and smaller issue/PR titles for cleaner layout

* feat: add prefilled slugs and improve branch selection UX in new worktree dialog

Prefilled unique branch name when opening new worktree dialog
Group branches into Local and Remote sections with bold headers
Instant branch list from cache with compact, wrapped dropdowns

* feat: update GitHub button text to be more descriptive

* feat: add GitHub issue linking to draft sessions

Link GitHub issues to draft sessions with full context including body, labels and comments
View issue author avatar, number and title in session input area
Open linked issues in browser or remove link with inline action buttons

* style: redesign queued messages with text-only row layout

* style: redesign attached files with text-only row layout and file type icons

Redesigned attached files with clean text-only rows matching linked issue style
Split display into two rows: image previews and file list with proper type icons
Removed internal Server/Local indicators to reduce visual clutter

* refactor: remove New from PR and Manage branches from sessions UI

Remove New from PR and Manage branches buttons from sessions sidebar
Delete GitHubPullRequestPickerDialog component and related code
Simplify GitView to sidebar-only mode with no mode prop

* fix: simplify new session shortcuts and remove worktree toggle

* fix: restore issue and PR context when creating worktree sessions

Worktrees created from GitHub issues and PRs now seed the new session with visible and synthetic context messages
Sidebar now opens the created linked session directly instead of creating an extra draft
Visible prompts are cleaner and include issue or PR numbers for clarity
2026-03-03 00:20:15 +02:00