-
Notifications
You must be signed in to change notification settings - Fork 0
Developer Architecture
BLXCode is a local-first Tauri desktop app with a Leptos/WASM frontend and a Rust backend. The frontend owns UI state and rendering. The backend owns native capabilities such as PTYs, filesystem access, app config storage, keyring access, browser host integration, provider HTTP calls, memory, and tasks.
Leptos UI
-> tauri_bridge.rs invoke wrappers
-> Tauri commands registered in src-tauri/src/lib.rs
-> Backend modules and managed state
-> Serialized responses/events back to the UI
-
src/main.rs: mounts the Leptos app. -
src/app.rs: sets up i18n, ThemeService, EULA gating, and rendersWorkbenchShell. -
src/workbench/mod.rs: workbench context, state hydration, auto-save, embedded browser event handling. -
src/workbench/state.rs: workspace state, snapshots, workspace creation draft, layout and browser state. -
src/tauri_bridge.rs: typed wrappers around Tauriinvoke()calls. -
src/agent_wire.rs: frontend mirror of backend agent protocol types.
-
src-tauri/src/main.rs: thin binary entry point. -
src-tauri/src/lib.rs: Tauri builder, managed state, plugin setup, command registration. -
src-tauri/src/commands.rs: general app commands, agent command shims, browser commands, directory picker helpers, PTY command wrappers, and git helpers. -
src-tauri/src/workbench_state.rs: persisted workbench snapshot/session storage. -
src-tauri/src/pty_host.rs: terminal session lifecycle and PTY IO. -
src-tauri/src/browser_host.rs: native or iframe browser embedding support. -
src-tauri/src/voice/: microphone recording, voice settings, STT, TTS, and voice catalog.
The agent subsystem lives under src-tauri/src/agent/. See Agent Harness for Better Harness; Subagents for coordinated parallel runs.
-
state.rs: shared event queue, busy/cancel flags, provider environment status, and conversation state. -
protocol.rs:UserTurnandAgentEventtypes (including subagent events). -
session_orchestrator.rs: loads provider settings/key,note_workspace_change, dispatches the turn. -
openrouter.rs/anthropic.rs: streaming tool-call loops viatool_dispatch.rs. -
tools.rs: full tool registry and sandboxedexecute_server_tool. -
system_prompt.rs: slim shared prompt (checklist + tool name index; docs in core skills).
-
harness_skills/*.md: eleven embedded core skill documents. -
tool_groups.rs/tool_dispatch.rs: filtered catalogs for coordinator vs subagents. -
environment.rs,shell_exec.rs,git_agent.rs,workspace_agent.rs: server tools. -
web_settings.rs,web_tools.rs,web_commands.rs: Tavily/Brave keys and search. -
subagents.rs,subagent_runner.rs,subagent_prompts.rs: parallel subagent runs — Subagents.
The frontend submits turns through agent_submit_turn and polls agent_poll_events. Subagent timeline updates are debounced 50 ms (Subagents). Tool results that need client execution are returned through agent_submit_tool_result.
Voice-originated turns set voice_input=true. After the provider turn finishes, the session orchestrator can synthesize the final assistant text and emit AgentEvent::VoiceReady for frontend playback.
When UserTurn.image_generate is true, the orchestrator takes an early exit: calls src-tauri/src/image/generate.rs, saves to <workspace>/.blxcode/generated/, emits AgentEvent::ImageGenerated, and skips the tool loop. Image settings live in the image envelope of agent_provider_settings.json (src-tauri/src/image/settings.rs).
sequenceDiagram
participant UI as AgentPanel
participant IPC as Tauri
participant Orch as session_orchestrator
participant API as Provider
UI->>IPC: agent_submit_turn
IPC->>Orch: dispatch_user_turn
alt image_generate
Orch->>API: image API
Orch-->>UI: ImageGenerated + Done
else chat turn
Orch->>API: stream + tools
loop poll
UI->>IPC: agent_poll_events
IPC-->>UI: AgentEvent stream
end
end
Client-only tools (context attach, plan context, image context list) execute in the frontend; results return via agent_submit_tool_result.
The voice subsystem lives under src-tauri/src/voice/ with frontend support in src/workbench/agent_panel/voice_orb/ and src/workbench/harness_voice_pane/.
It captures microphone audio with cpal, writes temporary mono WAV files with hound, sends STT requests to OpenAI or OpenRouter, and sends TTS requests to OpenAI. Voice settings are persisted as a voice sub-object inside agent_provider_settings.json and reuse the existing provider keyring entries.
See Voice Architecture for the detailed flow.
Workbench snapshots are serialized from frontend state and saved through backend commands. The snapshot version is defined by WORKBENCH_SNAPSHOT_VERSION in src/workbench/state.rs.
The state model includes workspaces, active workspace ID, recent workspaces, sidebar/right-panel layout, browser tabs, agent timeline, and terminal pane layout.
Memory lives in src-tauri/src/memory.rs and src-tauri/src/agents_layout.rs. Notes are stored under <workspace>/.agents/memory/ and learnings under <workspace>/.agents/learnings/ (API paths learnings/…). Legacy .blxcode/memory/ is migrated on workspace bootstrap via workspace_ensure_agents.
flowchart LR
subgraph frontend [Frontend]
Panel[MemoryPanel]
PlansPanel[PlansPanel]
Bridge[tauri_bridge.rs]
end
subgraph backend [Backend]
Ensure[workspace_ensure_agents]
MemCmd[memory_* commands]
PlanCmd[plan_* commands]
Layout[agents_layout.rs]
MemMod[memory.rs]
PlansMod[plans.rs]
end
subgraph storage [Workspace]
MemDir[".agents/memory"]
LearnDir[".agents/learnings"]
PlansDir[".agents/plans"]
Legacy[".blxcode/memory"]
TasksJson[".blxcode/tasks"]
end
Panel --> Bridge
PlansPanel --> Bridge
Bridge --> Ensure
Bridge --> MemCmd
Bridge --> PlanCmd
Ensure --> Layout
MemCmd --> MemMod
PlanCmd --> PlansMod
Layout --> MemDir
Layout --> LearnDir
Layout --> PlansDir
Layout -.->|migrate if empty| Legacy
MemMod --> MemDir
MemMod --> LearnDir
PlansMod --> PlansDir
PlansMod --> TasksJson
Tasks live in src-tauri/src/tasks.rs and store JSON under <workspace>/.blxcode/tasks/index.json. plan_load replaces tasks matching a plan path; tasks_update can write status markers back into plan Markdown.
flowchart LR
PlanMd["plan.md ## Tasks"]
PlanLoad[plan_load]
TasksMod[tasks.rs]
TaskUpdate[tasks_update]
PlanMd --> PlanLoad
PlanLoad --> TasksMod
TasksMod --> TaskUpdate
TaskUpdate --> PlanMd
src-tauri/src/plans.rs parses and writes the canonical ## Tasks / ## Todos section. PLANS.md is protected. Path traversal is rejected relative to the plans root.
src-tauri/src/skills_rules/ implements list/read/write, enable flags in index.json, and install staging (git, npm, local). Core skills are embedded via CORE_SKILLS in store.rs (SkillSourceKind::Core) and merged into skills_list on every workspace. skills_rules_bootstrap runs on workspace open via workspace_ensure_agents / layout helpers.
-
src-tauri/src/fs_entries.rs—list_path_entries(sandboxed directory listing),read_workspace_text_file(UTF-8 text preview, 512 KiB cap), and the file-preview trio:-
stat_workspace_file→FileMeta { name, relPath, byteLen, modifiedMs, kind, mime, policyKind? }withFileKind(Image/Video/Markdown/Mermaid/Code/Text/Binary).Codecovers source languages (Rust/TS/JS/Py/Go/HTML/CSS/JSON/YAML/shell/SQL/…);Textis reserved for plain text (txt/log/ini/conf/env/csv/…) that should still get gutter+selection but no syntax highlighting. -
Repository policy classification:
classify_policy(stem)runs afterclassify_kind(ext)and inspects the lowercased filename stem. When it matches a well-known stem (license/licence/copying/copyright/unlicense,contributing/contribution(s),contributors/contributer(s),code_of_conduct/code-of-conduct/codeofconduct,security/security-policy/security_policy,authors/maintainers/owners/codeowners,changelog/changes/history/release_notes,readme) it returnsSome(PolicyKind)andstat_workspace_fileforceskind = FileKind::Markdown(regardless of extension) and falls back totext/markdownfor the MIME guess. Effect: a bareLICENSE(no extension) renders identically toLICENSE.md. The optionalpolicy_kind: Option<PolicyKind>field onFileMetais#[serde(skip_serializing_if = "Option::is_none")]so older snapshots and non-policy files stay unchanged on the wire. -
read_workspace_image_file→ base64 + MIME, 16 MiB cap (MAX_IMAGE_PREVIEW_BYTES). -
read_workspace_video_file→ base64 + MIME, 64 MiB cap (MAX_VIDEO_PREVIEW_BYTES). All four commands reuse the samecanonical_root/resolve_under_rootsandbox so traversal-out-of-root, missing files, and non-files behave identically.
-
-
src-tauri/src/git_graph.rs—git_is_repository,git_commit_graph(lane layout, unit-tested).
Frontend:
-
src/workbench/sidebar_view_section/— explorer and graph panels in the workbench sidebar. -
src/workbench/file_preview/— center-tab preview dispatcher:-
mod.rsloadsFileMetaonce and routes toImageView/VideoView/MarkdownView/MermaidView/CodeView(used for bothFileKind::CodeandFileKind::Text) /UnsupportedView. -
header.rsrenders the topbar (icon, name, path, size, mtime, Copy path, Refresh). -
code_view.rstwo-column layout:<div class="code-view__row" data-line="N">rows with a line-number gutter on the left andinner_htmlhighlighted code on the right. Selection is stored asRwSignal<Option<(usize, usize)>>(1-based, inclusive range, always ordered low..=high).mousedowncaptures adrag_anchorand seedsSome((N, N));mousemoveextends the range while the anchor is set; a window-levelmouseuplistener (installed once, cleaned up viaon_cleanup) ends drags even when the cursor leaves the gutter. A pure click without drag still toggles the single-line case.on:contextmenuopens the right-click menu (seecode_context_menu.rs) at the click position; if the click lands outside the current range the selection is first replaced with that single line. Window-levelmousedownandkeydown=Escapelisteners close the menu. The component also caches aplain_lines: Arc<Vec<String>>(raw\n-split source) parallel to the HTML lines so snippet builders never have to re-read the file. Plain-text files share the same layout but skip the highlight call. Lines are pre-rendered into aVec<View>(no per-line<For>clone cost on large files)..code-viewgetsuser-select: noneso the drag never fights native text selection. -
code_context_menu.rsrenders the four-section right-click menu (Snippet → Insert into terminal,Full context block → Insert into terminal,Snippet → Attach to agent,Clipboard). Terminal sections list every workspace with at least one live PTY session — grouped by workspace, with the preview's own workspace pinned to the top and tagged with a localized current badge. The menu is purely view-layer: the parent ownsRwSignal<Option<CodeContextMenuState>>and aCallback<CodeMenuAction>that runs the actual side effects (pty_write,upsert_workspace_agent_context,navigator.clipboard.writeText). -
hljs_glue.rslazy-loads the vendored highlight.js 11 common bundlepublic/vendor/highlight/highlight.min.js, pollsglobalThis.hljsfor up to 5 s, and exposeshighlight(code, language)overhljs.highlight(code, { language, ignoreIllegals: true }). Same lazy-script pattern asmermaid_glue.rs. -
markdown_view.rsrunspulldown-cmark(tables, strikethrough, task lists, footnotes, smart-punctuation), detects```mermaidfences and replaces them with<pre class="mermaid">sentinels that the post-mount effect hands tomermaid.run({ nodes }). Accepts an optionalpolicy_kind: Option<PolicyKind>prop; when set, apolicy_hero(kind)lookup table chooses the icon (LuScale/LuGitPullRequest/LuUsers/LuShieldCheck/LuLock/LuUserRound/LuHistory/LuBookOpen), theFilePreviewPolicy{Kind}{Title,Subtitle}i18n keys, and a CSS modifier (license/contributing/security/ …). The component renders a<header class="file-preview__policy-hero file-preview__policy-hero--<modifier>">above the markdown body; per-modifier--policy-accentoverrides instyles.cssretint the left bar and icon (e.g.Security→var(--danger),License→var(--success)) while staying fully theme-aware. -
mermaid_glue.rslazy-loads the vendored bundlepublic/vendor/mermaid/mermaid.min.js, callsmermaid.initialize({ startOnLoad: false, securityLevel: 'strict', theme: 'dark' }), and exposesrun_mermaid_on(&[HtmlElement]). -
util.rsshipsformat_bytes,format_mtime(js_sys::Date.to_locale_string),icon_for_kind,hljs_lang_for_ext(extension → highlight.js alias map),html_escape,split_highlighted_into_lines(UTF-8-safe HTML splitter that balances open<span>s across\n),build_file_snippet_block(rel_path, language, plain_lines, range, source_workspace_for_header)(fenced markdown emitter — clamps out-of-range indices, prefixes the header with the source workspace when crossing workspaces), allowlist-basedsanitize_svg+sanitize_markdown_html(strips<script>/<style>/<iframe>/<object>/<embed>/<foreignObject>blocks,on*=event handlers, andjavascript:/vbscript:URIs while preserving multi-byte UTF-8), plus a sharedFilePreviewErrorenum (NoTauri/WorkspaceNotFound/TooLarge(u64)/Failed(String)) andrender_load_error(i18n, failed_label, error)helper used by every renderer for consistent localized banners.
-
flowchart LR
Click[Sidebar file click]
Tab[CenterTabKind::FilePreview]
Dock[FilePreviewDock]
Stat[stat_workspace_file]
Disp{FileKind}
Img[read_workspace_image_file]
Vid[read_workspace_video_file]
Txt[read_workspace_text_file]
Mer[Mermaid bundle]
Hjs[highlight.js bundle]
Click --> Tab --> Dock --> Stat
Dock --> Disp
Disp -->|Image| Img
Disp -->|Video| Vid
Disp -->|Markdown / Mermaid / Code / Text| Txt
Disp -->|Markdown / Mermaid| Mer
Disp -->|Code| Hjs
- Frontend:
src/workbench/agent_context_handoff.rs— full-block path:render_agent_context_block,HandoffMenu,perform_handoff(single renderer for tool and UI). Lightweight path for file-preview snippets:render_file_snippet_envelopeemits the same⟪ BLXCode attached context ⟫delimiters with just a Session header + File snippet section. - Cross-workspace terminal enumeration:
list_terminal_targets_all_workspaces(&wb, Some(preferred_workspace_id))iterates every workspace (filtering shell workspaces viastate::is_shell_workspace), groups live PTY sessions per workspace, and moves the preferred (preview-owning) workspace to the front.WorkspaceTerminalGroupcarries the workspace id + label + the per-workspaceWorkspaceTerminalTargetlist. -
AgentContextItem(mirrored insrc/agent_wire.rsandsrc-tauri/src/agent/protocol.rs) has an optionalcontent: Option<String>field and aFileSnippetkind.file_snippet_context_item(rel_path, start, end, language, label, snippet, source_workspace)is the canonical constructor used by the file preview's "Attach to agent" action. - Backend prompt renderer
render_context_promptinsrc-tauri/src/agent/session_orchestrator.rspartitionsFileSnippetitems into a dedicatedAttached file snippets (verbatim, line-numbered headers):section and embeds each item's inlinecontentdirectly.render_agent_context_blockmirrors this with a## Attached file snippetssection (memory/plans filters skip snippet items). - Backend:
agent_export_context_imageswrites<workspace>/.blxcode/agent-context/images/plus manifest JSON (full-block path only — file-preview snippets ride entirely inline). - PTY env:
BLX_AGENT_CONTEXT_DIR,BLX_AGENT_CONTEXT_MANIFEST.
flowchart LR
UI[HandoffMenu]
CodeMenu[CodeContextMenu]
Bridge[tauri_bridge]
Export[agent_export_context_images]
RenderFull[render_agent_context_block]
RenderSnip[render_file_snippet_envelope]
BuildSnip[build_file_snippet_block]
AgentCtx[upsert_workspace_agent_context FileSnippet]
Pty[pty_write]
UI --> Bridge
Bridge --> Export
Bridge --> RenderFull
Export --> RenderFull
RenderFull --> Pty
CodeMenu --> BuildSnip
CodeMenu --> AgentCtx
BuildSnip --> RenderSnip
BuildSnip --> Pty
RenderSnip --> Pty
Both memory and plan modules validate workspace paths and sandbox file operations to workspace-local directories.
The browser host supports native child webviews on platforms where Tauri's unstable child-webview API works well. Linux currently uses iframe fallback. The frontend stores the detected embedding kind in BrowserEmbedSurface.
The i18n service lives under src/i18n/ and src/service/. Locale tables are Rust source files, while EULA source content is Markdown under content/eula/.
Themes are frontend-only. ThemeService (src/workbench/theme_service.rs) sets html[data-theme] from themes/tokens.css and persists to localStorage. The Appearance settings pane reads the catalog from src/theme/catalog.rs. JavaScript subsystems (xterm, 3D memory graph) listen for blxcode-theme-changed and read computed CSS variables.
See Themes and Theme exceptions.
- UI code should not perform native filesystem or keyring operations directly.
- Backend modules should not depend on Leptos signals or DOM concepts.
-
src-tauri/src/lib.rsshould register and wire modules, not accumulate feature implementation. - Shared protocol types should be mirrored intentionally, as with
agent_wire.rsandagent/protocol.rs.
- User-Agent-Harness
- User-Agent-Providers
- User-Appearance-Themes
- User-Building
- User-File-Preview
- User-Getting-Started
- User-Image
- User-Keyboard-Shortcuts
- User-Language
- User-Memory-And-Tasks
- User-Plans
- User-Rules-And-Skills
- User-Settings
- User-Subagents
- User-Troubleshooting
- User-Voice
- User-Workspaces
- Developer-Agent-Harness
- Developer-Architecture
- Developer-Contributing
- Developer-I18n
- Developer-Setup
- Developer-Subagents
- Developer-Tauri-Ipc
- Developer-Themes
- Developer-Voice