Skip to content

feat(desktop): React frontend + Tauri shell + bridge rewrite#684

Open
abraxas914 wants to merge 81 commits into
lsdefine:mainfrom
abraxas914:main
Open

feat(desktop): React frontend + Tauri shell + bridge rewrite#684
abraxas914 wants to merge 81 commits into
lsdefine:mainfrom
abraxas914:main

Conversation

@abraxas914

Copy link
Copy Markdown
Contributor

Summary

Complete desktop client implementation for GenericAgent — a React + Tauri hybrid app providing native GUI access to the agent system.

What's included

Frontend (frontends/desktop/)

  • React scaffold: Vite + Zustand + Semi UI, bilingual i18n (zh/en), dark/light theme
  • Chat UI: Composer, MessageList, SessionList, streaming partial updates via WebSocket
  • Thread rendering: Markdown pipeline, thinking disclosure, tool call rows, progressive disclosure (L1/L2/L3)
  • Settings & model management: CRUD model profiles, provider presets, per-session model binding
  • Token stats: Per-prompt cost tracking, time filters, cache rate formula
  • Notifications & connection status: Toast system, offline overlay, bridge status panel
  • Data maintenance: Local repo connection, memory import, key configuration
  • Platform adaptation: macOS traffic lights, Windows frameless chrome, event-delegation drag

Tauri shell (frontends/desktop/src-tauri/)

  • Tauri v2 config, native commands, window management
  • macOS DMG repackage script with Finder layout
  • Windows UCRT bundling, shortcut prompt, tray lifecycle

Bridge & backend (frontends/)

  • Full desktop bridge rewrite: session persistence, image/file injection, streaming
  • Conductor enhanced orchestration with error feedback
  • Model routing isolation (session-level binding)

CI (.github/workflows/)

  • Desktop release workflow: 3-platform build matrix (macOS/Windows/Linux)
  • Frontend test workflow (vitest)

Docs & specs (frontends/desktop/spec/, DESIGN.md)

  • Product design spec: feature architecture, progressive disclosure, microcopy, visual language
  • Spec docs: model selection, microcopy reference, local repo connection, memory import

Testing

  • 328 frontend unit tests (vitest) — all passing
  • 114 bridge integration tests (pytest) — all passing
  • Manual testing on macOS and Windows

Notes

  • Desktop layer is fully additive — no modifications to upstream agent core (agentmain, llmcore, agent_loop)
  • Bridge communicates with agent via existing Python API; frontend connects to bridge over localhost HTTP + WS

abraxas914 and others added 30 commits July 9, 2026 15:02
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e/file injection, streaming

Major rewrite of desktop_bridge.py for the React desktop frontend:
- Per-session file persistence (individual JSON files, O(1) write)
- submit_prompt: file path injection into agent_prompt for file_read access
- _patch_chat_for_images: monkey-patch backend.ask for base64 image injection
- Vision MIME whitelist (png/jpeg/gif/webp); SVG falls back to path reference
- Thread-safe run_agent_turn with sess.id scoping (no cross-thread sid leak)
- Ring buffer self-log capture (bridge_print → deque(maxlen=500))
- turn_segs streaming: per-turn structured output for frontend rendering
- Model profile CRUD: /model-profiles endpoints
- Session model binding (llm_no per session)
- Service lifecycle management via /services/* endpoints
- Bridge exit endpoint with graceful shutdown
- WebSocket hub for real-time state sync
- plan_state.py: plan mode path utilities for desktop sessions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd lifecycle hooks

- Improved error classification and productized error messages
- Graceful start/stop with proper signal handling
- Bridge-visible status transitions for frontend service panel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… routing

Complete React + TypeScript desktop frontend built on Vite:
- App shell: layout system (TopBar, LeftPanel, RightPanel, Resizer)
- State management: Zustand stores (bridge connection, services, UI state)
- Bridge HTTP/WS client with auto-reconnect and offline detection
- Plan mode UI (PlanCard, QuickPlan components)
- Session CRUD (new/delete/rename/pin/drag-sort)
- Font, appearance, and log modal infrastructure
- Dev tooling: vitest config, tsconfig, ESLint
- Asset pipeline: provider logos, channel icons, app icons
- Loading screen with i18n-aware progress messages
- Documentation: adaptation notes, white-screen debug, gap analysis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Complete chat experience for the React desktop:
- Composer: rich editor with IME handling, file/image upload, model selector
- MessageList: user/assistant messages with action bars
- SessionList: search, pin, rename, drag-sort, contextual actions
- ChannelList: multi-channel routing (WeChat, DingTalk, Discord, etc.)
- Plan mode: QuickPlan widget, auto/manual toggle
- AttachmentStrip: file preview with remove action
- Chat service layer: prompt submission, cancel, message polling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…serTurnRail

Structured multi-turn rendering with hardened markdown processing:
- Thread component: turn_segs-based rendering with stale-part fallback
- Markdown pipeline: scrubBacktickNoise, normalizeFenceBlocks, KaTeX LRU memo
- DiffLines renderer for ```diff fenced blocks
- Prism.js syntax highlighting integration
- UserTurnRail: smooth scroll navigation between user turns
- AssistantActionBar: copy, retry, model badge
- Streaming: opacity layering for in-progress turns
- Tests: markdown preprocessing, streaming, thread grouping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…profiles

Complete settings system for the React desktop:
- Settings modal: appearance, language, font size sections
- Model CRUD: add/edit/delete with protocol-aware validation
- Provider presets: Claude, GPT, DeepSeek, Qwen, Gemini, etc.
- Model profiles: named configurations saved to bridge
- ModelSelector dropdown in Composer with per-session binding
- mykey.py integration: read/write via bridge /services/mykey endpoints
- Stress tests for settings store

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…pagination

Token usage analytics UI:
- TokenPage with windowed pagination for large histories
- TokenFilter: time-range selector with date normalization
- TokenTable: model-aware cost estimation display
- ConductorTab: conductor-specific token breakdown
- IME composition guard on filter inputs
- Tests: token filter date normalization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ine overlay

Real-time feedback layer:
- NotificationStack: queued toast with auto-dismiss and severity levels
- ConnectingOverlay: bridge disconnect detection with retry countdown
- Notification store (Zustand) with queue drain and dedup
- Stress tests for notification store edge cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…h/en)

Visual identity and localization:
- CSS custom properties (--bg, --fg, --accent, --divider, etc.)
- Dark/light theme toggle persisted in settings
- i18n: complete zh/en translation files
- Navigator.language-aware loading screen text
- Legacy i18n.js for non-React loading pages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s, installers

Native desktop application layer:
- Tauri v2 configuration: capabilities, permissions, bridge command bindings
- src/lib.rs: Tauri command handlers for bridge lifecycle
- Packaging scripts: macOS (DMG), Linux (AppImage + install.sh), Windows (portable + PS1)
- install/uninstall scripts for all three platforms
- Cargo static-link config for MSVC CRT (Windows portability)
- Conductor store + stress tests
- titleBarStyle fix for Tauri v2 (camelCase required)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automated cross-platform build and release pipeline:
- Trigger: tag push (desktop-portable-*) or manual workflow_dispatch
- Matrix: Windows (MSVC static CRT), macOS (universal), Linux (AppImage)
- Rust/Node caching for fast rebuilds
- upload-artifact v7 for CI artifacts
- GitHub Release creation on tag push with platform-specific assets
- Platform-selective manual dispatch for testing individual targets

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tch, utils

Comprehensive pytest test suite for desktop_bridge.py:
- test_bridge_submit: scope isolation guards (sid vs sess.id), image patch existence
- test_bridge_sessions: session CRUD, persistence, concurrent access
- test_bridge_uploads: file/image upload flow, path injection verification
- test_bridge_model_switch: model binding, profile CRUD
- test_bridge_utils: normalize_prompt, strip_final_info_marker, utility functions
- Source-parsing guards to prevent regression on critical code patterns

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… layout

Tauri's create-dmg leaves .VolumeIcon.icns, .background/, and .fseventsd
visible when users resize the Finder window. Instead of trying to hide
these files (SetFile/chflags don't work reliably), repackage the DMG from
scratch: extract .app, generate .DS_Store with ds_store lib (540x380
window, 128px icons, centered positions), and create a fresh UDZO DMG.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lay with Tauri startDragging

The previous `::before` full-width `-webkit-app-region: drag` overlay
was swallowing click events for titlebar buttons on macOS/WKWebView.
CSS drag regions proved unreliable in Tauri v2 (both full-width and
carve-out strip approaches failed in packaged builds).

New approach: Hermes-style carve-out drag strips that trigger
`window.getCurrentWindow().startDragging()` on mousedown. This keeps
drag and click on separate channels — DOM events for buttons, Tauri IPC
for window movement — eliminating compositor-level hit conflicts.

Changes:
- Delete `::before` full-width drag overlay
- Add shell geometry CSS variables (--ga-titlebar-height, etc.)
- Add two scoped drag strip divs (left gap + main gap)
- Programmatic drag via Tauri window API on mousedown
- Add `core:window:allow-start-dragging` capability
- Clear native window title (remove redundant "GenericAgent" text)
- Align fold button with traffic lights (top: 5px)

Verified: build passes, 248 tests pass, packaged .app drag works.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r panel click

- Adjust --ga-titlebar-controls-top from 5px to 6px for vertical center
  alignment with macOS traffic lights
- Add position: relative + z-index: 60 to .collab-worker-panel-head so
  the Agents panel collapse button sits above the drag strip (z:50) and
  receives click events

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rag on Conductor top bar

Panel no longer unmounts on collapse; it shrinks to 2.5rem with the toggle
button staying in the same DOM position. This prevents the button from
relocating to WorkerRail (which required z-index elevation that blocked drag).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…llapse border cleanup

- Add trafficLightPosition {x:13, y:22} to align traffic lights with fold buttons
- Add position:relative + z-index:1 on .ga-app-body to isolate Semi's internal
  z-indexes from competing with TitlebarControls (fixes intermittent click failures)
- Change TitlebarControls from fixed to absolute (same visual effect, better
  stacking behavior in WKWebView)
- Unify panel-head/rail height to --ga-titlebar-height for vertical alignment
- Match collapse button size (28x28) with sidebar toggle

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lebar clearance

Drag strips (absolute divs with z-index) caused cascading stacking context
conflicts with panel buttons. Replace with mousedown handler on .ga-app-layout
that checks y < 38px and filters interactive elements — eliminates all z-index
contention between drag and click.

- Remove .ga-drag-strip divs and CSS
- useDragWindow() now delegates on layout root with hit-test filtering
- Remove z-index hacks from panel-head and rail-toggle
- Add titlebar clearance padding only to .ga-main-full in collapsed sidebar
  (services/token pages), not globally

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… conflict

@lobehub/fluent-emoji@4 requires react@^19, but we use react@18.
Local installs work because node_modules already exist; CI fails on
fresh `npm install` without this override.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The post-dmg.sh script uses the ds_store package to set Finder icon
positions in the DMG. CI runners don't have it pre-installed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ismatch)

pip3 installs to Homebrew site-packages but post-dmg.sh calls python3
which may resolve to a different interpreter on GitHub Actions runners.
Using python3 -m pip ensures the package lands in the correct env.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
python-build-standalone dynamically links the Universal CRT but does not
bundle it. Win10/11 have UCRT in-box, but stripped/older images lack it,
causing python.exe to fail on launch. Copy the redistributable UCRT DLLs
from the Windows SDK on the runner into the portable python/ directory.

Ported from dd3xp/GenericAgent_Desktop workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…itecture

Replace drag-strip-based spec with event delegation pattern. Document the
z-index war that made strips untenable and why delegation eliminates it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(desktop): React + Tauri desktop application
selectedModelNo was a global Zustand state persisted to localStorage,
causing Session 1 to display Session 2's model after switching.

- Rename selectedModelNo → defaultModelNo (new-session default only)
- Add sessionModelNo to chat store, populated from poll model.llmNo
- ModelSelector reads sessionModelNo ?? defaultModelNo
- Model mutation goes through POST /session/{sid}/model with
  optimistic update + rollback
- Remove unused llmNo param from sendPrompt (bridge ignores it)
- Add 8 acceptance tests for session-binding logic

Bridge backend unchanged — it already implements correct per-session
model binding via sess.llm_no.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GaSourceBlock passed { path: picked } but the Rust command expects
{ dir: picked }. Tauri deserializes by param name, so the call
always failed with a missing-argument error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add DataSection with import/export mykey (browser), import memory and
connect GA source directory (Tauri-only). Extract mykey operations from
FeatureSection, fix modal.features i18n key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove abstraction leaks: mykey → 密钥配置, 源目录 → 本地仓库
- Replace inline descriptions with tooltip (Layer 2: explanation-level)
- Rename all data.* i18n keys to match new naming convention
- Remove .ga-data-row-desc CSS (no longer rendered)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
abraxas914 and others added 30 commits July 10, 2026 00:04
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ResizeObserver was re-evaluating clamped state while expanded, causing
the collapse button to vanish when content fit within 40dvh. Use a ref
to skip measurement while expanded.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
macOS .app bundles are discoverable via Spotlight and Launchpad,
so the desktop-shortcut prompt is only useful on Windows/Linux.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
….app

- Remove config-level `decorations: false` (killed traffic lights on
  macOS); apply it at runtime on Windows only via set_decorations()
- Replace the blanket `trust_settings = false` for .app bundles with a
  validation check: settings are trusted when the saved project_dir
  still contains frontends/desktop_bridge.py. This fixes the DMG "stuck
  on loading" bug — the app couldn't find the project path, so bridge
  never started.
- Remove unused `running_inside_app_bundle()` function

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The function was called unconditionally in get_or_discover_config() but
gated to macOS only — a leftover from the deleted running_inside_app_bundle().
This broke Linux and Windows builds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cycle gaps

Bridge _live_model now uses sess.llm_no as SSOT instead of agent.llm_no
which is stale during agent initialization. Frontend poll callback no
longer overwrites sessionModelNo during active turns. Model selection
in empty state (no session) is preserved and synced on session creation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…estart

run_agent_turn previously created a fresh agent without restoring the
persisted conversation history, causing LLM "amnesia" after bridge or
app restarts. Now injects sess.llm_history immediately after make_agent,
matching the existing restore_context logic.

Adds 5 tests covering history injection, empty/null cases, model
preservation, and persist/reload roundtrip.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce shared log components inspired by Hermes' dual-component pattern:
- LogTail: follow-the-tail scrolling, hover copy, three-state (loading/empty/data)
- LogView: lightweight static log container

Refactor BridgeMenuPanel to use LogView with noise filtering and timestamp trimming.
Refactor ChannelLogModal to use LogTail, replacing raw <pre> rendering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…opy button on clamped messages

- ChannelLogModal: give LogTail a bounded max-height so overflow-y produces a scrollbar
- BridgeMenuPanel: replace event-summary with real process log tabs (bridge/conductor/scheduler),
  poll from bridge HTTP API, show truncation count + "View all" link
- ConductorMenuPanel: use LogView component, remove manual scroll logic
- UserMessage: change bubble overflow-y from auto to visible, fixing copy button
  hit-testing on clamped messages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…losure, and visual language

Consolidates established patterns from code and existing trellis specs into a
single DESIGN.md for team and agent reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: isolate Session and Conductor model routing
- Remove internal tool/project references from DESIGN.md
- Copy microcopy, local-repo-connection, and memory-import specs to docs/spec/
- Move HANDOFF-white-screen-debug.md into docs/ for tidiness

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ntend/backend separation

Documents the authoritative model selection contract: frontend reads and
requests, bridge owns truth, per-session binding with global fallback,
conductor independently configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move specs from docs/spec/ to spec/ (the canonical location)
- Delete docs/hermes-reference/ (4 files, all Hermes-saturated)
- Delete docs/ADAPTATION-NOTES.md and docs/session-row-action-menu.md
- Update DESIGN.md appendix paths to spec/
- Retain clean debug docs (THREAD-BLEED, WHITE-SCREEN, HANDOFF) in docs/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ode)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.2.0 is a stable release, not pre-release.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(desktop): add isolated e2e control interfaces

* test(desktop): add WebdriverIO automation harness

* ci(desktop): gate deterministic desktop journeys

* ci(desktop): harden release gates

* fix(ci): install Tauri libraries for Rust contracts

* fix(e2e): wait past stale Windows retry state

* fix(packaging): retry transient DMG creation

* fix(release): emit portable Windows checksums
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant