* feat: add draw.io diagram editor integration
Embed draw.io editor via react-drawio (MIT, zero deps) for inline
editing of .drawio files. Changes auto-save to disk. Includes
inline editor in FilesView with Visual/Source toggle, dark mode
support, template picker for new files, and chat file attachment
integration.
* fix: debounce diagram autosave to prevent reload loop
* fix: ignore watcher-triggered xml prop changes to prevent reload loop
* fix: remove auto-save-to-disk, add manual save button for diagrams
Autosave writes triggered file watcher cascade that reloaded the
draw.io iframe and reset zoom. Replaced with explicit Save button
in the toolbar (floppy disk icon). Editor XML is stable on mount
and ignores watcher-triggered prop changes.
* fix: remove auto-save write from DiagramView, add save button
* fix: hide draw.io save/exit buttons in editor
* fix: also hide save-and-exit button
* fix: brighten save button styling, add saved confirmation
* fix: remove autoSaveStatus toggle on diagram save to prevent toolbar collapse
* fix: add local save confirmation state for diagram button
* fix: remount drawio iframe on theme change, persisting XML across mounts
* fix: clear persisted xml on mount to prevent leaking between files
* fix: initialize dark mode synchronously, preserve edits across theme remount
* fix: auto-focus drawio iframe on mount/theme-change for keyboard shortcuts
* fix: add diagram i18n keys to Traditional Chinese locale
* fix: restore upstream HMR host and LAN address support
* fix: load sub-agent sessions on bootstrap for sidebar visibility
Two-phase session load: first fetch root sessions (for accurate
sessionTotal), then fetch all sessions and include child sessions
(sub-agent delegations). This ensures sub-agent sessions appear
in the sidebar immediately instead of relying on the async global
session store.
* remove opencode-drawio from PR branch
* fix: atomic file writes to prevent concurrent read/write truncation
Three-layer defense against the O_TRUNC race:
1. Write side (server): replace direct writeFile with write-to-temp-
then-rename. fs.rename is atomic on POSIX.
2. Read side (server): retry up to 3 times with 50ms backoff when
readFile returns empty but stat reported non-zero size.
3. FilesView client: refuse to save empty draftContent when the
original fileContent was non-empty.
* fix(dev): clean up orphaned OpenCode processes on Ctrl+C
* fix: allow empty file saves, log warning instead of blocking
Replaces the hard block on saving empty content with a console.warn.
The atomic write + read retry on the server side handle the O_TRUNC
race properly. The previous guard caused a UX regression by silently
preventing users from clearing a file and saving.
* fix: remove time window from sub-agent fallback for live tasks
While a task tool is active, the fallback now matches any session
with the correct parentID regardless of creation time. This allows
late-appearing child sessions to be found when the OpenCode server
is slow or the SSE event pipeline is delayed. The time window is
still applied once the task tool has completed, as a final sanity
check.
* fix: three diagram editor bugs from Greptile review
1. stableXmlRef now resets when xml prop changes — switching
between .drawio files renders the correct content.
2. Focus effect only runs on mount, not on isDark changes —
theme toggle no longer steals keyboard focus 600ms later.
3. saveDiagram updates xml state after writing — dirty-check
guard works correctly for subsequent saves.
* fix: route session.created SSE events to correct directory
Three-layer fix for sub-agent sessions not appearing in sidebar and
inline chat:
1. protocol.js: parseSseEventEnvelope now extracts directory from
properties.info.directory (where session.created/updated events
carry it) in addition to properties.directory. WS frames relayed
to the browser now carry the real directory instead of 'global',
so child sessions routed to the correct directory store.
2. event-pipeline.ts: same fallback in resolveEventDirectory for
defense-in-depth when SSE events bypass the WS relay.
3. resolveFallbackTaskSessionId.ts: time window lower bound now
allows 2s grace before taskStartTime to accommodate server timing
jitter (child session creation timestamps consistently precede the
tool's recorded start by ~6-9ms), fixing the 'Open subtask'
button not rendering in OpenChamber's inline chat.
* fix: sub-agent sidebar visibility, file zeroing guard, inline badge fallback
- Sync watchdog: periodic child session discovery poll (every 15s) detects
sessions created by other OpenCode instances, triggers parent materialization
- protocol.js: parseSseEventEnvelope extracts directory from
properties.info.directory for session.created/updated events
- event-pipeline.ts: same fallback in resolveEventDirectory for defense-in-depth
- resolveFallbackTaskSessionId: don't require taskStartTime (cross-OpenCode);
pick most recent child when multiple idle candidates exist
- readTaskSessionIdFromOutput: parse <task id="ses_xxx"> format from output
- FilesView: reinstate empty-draft guard (block save when draftContent='' but
fileContent had content) to prevent file zeroing on tab switch
* Fix diagram autosave reload loop
* Highlight drawio files as XML
* Use diff-compatible highlighting for drawio files
* Restore drawio file icon mapping
* Stabilize drawio source preview toggle
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Preserves session directory metadata across live updates
Keeps desktop and mobile session lists in project groups
Adds regression coverage for session grouping
Electron updater now uses Electron release metadata only
Removed legacy Tauri package and migration workflow
Replaced Tauri shim usage with the desktop bridge
Capture the preview/browser iframe DOM with snapDOM (html-to-image
fallback) so web annotation screenshots match the visible viewport,
without a headless Chromium dependency.
- Preserve document scroll via viewport crop and re-bake nested scroll
(e.g. the Starlight sidebar) deterministically on the clone
- Pin position:fixed elements to their measured viewport rect so headers
and sidebars land correctly in the crop
- Extract preview capture/proxy helpers into
lib/preview/screenshot-capture.ts to slim down ContextPanel
- Guard the external preview proxy against SSRF to private, loopback and
reserved/link-local addresses (incl. cloud metadata)
- Fully validate preview bridge messages before formatting/use
- Warn on the empty browser tab that pages run with full access, so
users browse untrusted sites knowingly
* fix: make upstream sync actions target the selected remote
Ensure fetch and pull actually honor upstream selection so fork maintenance works from the Git sidebar, and surface upstream branch status alongside the primary origin-tracking indicators.
* feat: add Windows Electron desktop foundation
* fix(electron): stabilize Windows desktop packaging
* fix(electron): stabilize Windows desktop chrome
Use native Windows titlebar behavior with an Alt-accessible hidden menu, and harden Windows dev command launching so the desktop app follows platform conventions.
* fix(electron): stabilize Windows dev startup
* fix(electron): clarify desktop artifact names
* fix(electron): harden Windows desktop release and launch
* fix(electron): address Windows release review
* fix(electron): point updater and release links to org repo
* Fix Windows settings persistence fallback
* Fix Windows Electron dev startup
* Add Windows Electron window controls
* Fix Windows Electron install and opencode launch
* fix: resolve git status for repositories without upstream
Fixes repository detection stuck on Checking repository
Handles git status when no upstream is configured
Adds regression coverage for git status loading
* Add Windows app menu button
* fix: preserve file editor line endings
* ci: add desktop release smoke workflow
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Replace server-enforced structured output with local JSON parsing for Git generation
Render generated commit and PR JSON responses as compact chat cards
Tighten generation prompts while preserving active session context
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
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
* 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.
* feat: implement LaTeX math rendering via KaTeX
- Add katex, remark-math, and rehype-katex dependencies
- Integrate KaTeX CSS with light/dark theme color overrides
- Wire remark-math and rehype-katex plugins into MarkdownRenderer
- Support inline ($...$) and display ($$...$$) math delimiters
- Add plan and task documentation
* fix(katex): display inline error messages for invalid LaTeX
- Configure rehypeKatex with throwOnError: false to render errors inline
- Use destructive color var for error text
- Update feature documentation with verification results
* chore: remove latex PR artifacts and hardcoded color fallback
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Add reflect-metadata bootstrap so desktop sidecar no longer crashes on startup.
Update SDK v1.4 compatibility for model variant and diff payload handling.
Unify write/edit/apply patch expanded previews and hide write success output noise.
- Adjust package metadata and lockfiles across the workspace
- Refine chat sidebar and auto-scroll behavior in the UI
- Update PWA install handling alongside runtime package changes