bots-garden/sidekickspublic Fork
⑂ v0.1.0
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/sidekicks.git
git clone ssh://git@rickub.com/bots-garden/sidekicks.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. 74ac365 · on v0.1.0 · k33g · 4d ago
0002-ui-stack.md · 75 lines · 5.5 KBmarkdown
Blame HistoryOpen raw

ADR 0002 — UI stack: Wails v3, Svelte 5 and their components

Context

The draft imposes Go and Wails. Wails leaves the frontend open, and Sidekicks needs four non-trivial UI parts: a Zed-like layout of resizable panels, a Markdown conversation that streams, a code editor (Settings, workspace files) and a terminal. The target machine is a MacBook Air; the users are not developers.

A prototype (spikes/wails-ui) assembles the candidates below in one window.

Decisions

1. Wails v3.0.0

Wails v2.17 Wails v3.0.0
Status stable, maintenance stable since 2026-10-26 (after a long alpha/beta, beta.34)
Backend model one bound struct, context-based runtime services with startup/shutdown hooks; managers for windows, events, dialogs
Events untyped typed events registered in Go (application.RegisterEvent[T]), generated TypeScript constants
Bindings generated generated by static analysis, Go doc comments carried to TypeScript
Windows one several (useful later: detach a channel, a terminal)
Streaming to the UI events events, plus streams and raw messages
macOS packaging app bundle app bundle, DMG, signing, notarisation, in-app updater
Build opaque wails build a readable Taskfile
Go ≥ 1.22 ≥ 1.26 (we use 1.27.3)

v3 maps directly onto Sidekicks: agents and the terminal are services, ACP updates are typed events, and #36 (packaging) is largely covered. The risk — a .0 release three days old — is limited by the length of its beta and by our timeline (v0.1 in February 2027). If a blocking v3 bug appears, the fix is to pin a later v3.0.x, not to go back to v2.

2. Svelte 5 + TypeScript, with Vite

Svelte 5 is the lightest of the official Wails templates (no virtual DOM, fine-grained reactivity through runes), which suits a UI updated many times per second by streaming. React 19 or Vue 3 would work too, but nothing in our needs requires their ecosystems, and the components below are framework-agnostic.

3. Components

Need Choice Licence Why, and rejected alternatives
Panels paneforge 1.0 MIT Svelte 5 native, nested groups, collapsible panes, autoSaveId persists the layout (issue #12). A home-made splitter would re-implement keyboard and persistence handling.
Markdown markdown-it 15, html: false MIT Mature, CommonMark, renders an unclosed code fence as a block — streaming-friendly. marked is close; markdown-it's plugin model is richer.
Code colouring Shiki 4, JavaScript regex engine, bundled languages only MIT VS Code-quality colouring, dual light/dark themes through CSS variables. The JS engine avoids shipping the Oniguruma WebAssembly. highlight.js is lighter but visibly poorer.
Sanitising DOMPurify 3 MPL-2.0 / Apache-2.0 Model output is untrusted: even with html: false, the rendered HTML goes through DOMPurify.
Editor CodeMirror 6 + @codemirror/lang-yaml MIT Modular and light, YAML mode, lint API for live schema validation (issue #18). Monaco is the VS Code editor but heavy (several MB, web workers) for a YAML file.
Terminal xterm.js 6 + addon-fit, Go side creack/pty MIT The de-facto web terminal; with a real PTY htop/vim and resizing work.

4. Rendering rules that came out of the prototype

  • Streamed chunks are appended to a string, and the Markdown is rendered at most once per animation frame, not once per chunk. Measured in the sandbox (software rendering): 219 chunks at 50/s in 4.6 s (4.4 s of sleep), and at 500/s in 0.6 s — the UI keeps up.
  • Shiki loads asynchronously; the message is rendered again when it is ready.
  • Theme: design tokens as CSS variables, dark and light following the system (prefers-color-scheme).

Measured cost

Size
frontend/dist (all assets) 1.9 MB
Main JS chunk (CodeMirror, xterm, markdown-it, Shiki core) 1.16 MB, 368 kB gzipped
Shiki grammars and themes (lazy chunks) ≈ 420 kB
Linux arm64 binary with assets embedded 17.9 MB

Assets are embedded in the binary and never downloaded, so the size matters little for a desktop app.

Verification

  • npm run build and svelte-check (0 errors, 0 warnings), go vet.
  • Built and run on Linux arm64 (GTK4 + WebKitGTK 6.0, under Xvfb): layout, resizing, streaming, colouring, sanitising and a real shell in the terminal, driven with xdotool.
  • Run on macOS by the project owner (wails3 dev, wails3 package), following the macOS test guide: everything as expected; the traffic lights covered the left dock heading, fixed with a --titlebar-inset padding (38 px on macOS, equal to InvisibleTitleBarHeight).

Consequences

  • Issue #8 starts from wails3 init -t svelte (v3.0.0) and keeps the Taskfile that Wails generates; the project Makefile stays the single entry point and delegates to it.
  • Third-party licences (Apache-2.0, MIT, MPL-2.0) are listed in a THIRD_PARTY_NOTICES file shipped with the binary (issue #36).
  • Building on Linux needs pkg-config, libgtk-4-dev and libwebkitgtk-6.0-dev; on macOS, the Xcode command line tools.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# ADR 0002 — UI stack: Wails v3, Svelte 5 and their components

- **Status**: accepted — prototype verified on macOS by the project owner on 2026-10-29
- **Date**: 2026-10-29
- **Issue**: [#3 — Spike : choisir la stack Wails et frontend](../../.tickets/issues/0003-spike-choisir-la-stack-wails-et-frontend.yaml)

## Context

The draft imposes Go and [Wails](https://wails.io). Wails leaves the frontend open, and Sidekicks needs four non-trivial UI parts: a Zed-like layout of resizable panels, a Markdown conversation that streams, a code editor (Settings, workspace files) and a terminal. The target machine is a MacBook Air; the users are not developers.

A prototype ([spikes/wails-ui](../../spikes/wails-ui)) assembles the candidates below in one window.

![Prototype — streamed answer, YAML editor, terminal](../spikes/ui/prototype-linux.png)

## Decisions

### 1. Wails v3.0.0

| | Wails v2.17 | Wails v3.0.0 |
| --- | --- | --- |
| Status | stable, maintenance | **stable since 2026-10-26** (after a long alpha/beta, beta.34) |
| Backend model | one bound struct, context-based runtime | **services** with startup/shutdown hooks; managers for windows, events, dialogs |
| Events | untyped | **typed events** registered in Go (`application.RegisterEvent[T]`), generated TypeScript constants |
| Bindings | generated | generated by static analysis, **Go doc comments carried to TypeScript** |
| Windows | one | **several** (useful later: detach a channel, a terminal) |
| Streaming to the UI | events | events, plus streams and raw messages |
| macOS packaging | app bundle | app bundle, **DMG, signing, notarisation, in-app updater** |
| Build | opaque `wails build` | a readable Taskfile |
| Go | ≥ 1.22 | **≥ 1.26** (we use 1.27.3) |

v3 maps directly onto Sidekicks: agents and the terminal are services, ACP updates are typed events, and #36 (packaging) is largely covered. The risk — a `.0` release three days old — is limited by the length of its beta and by our timeline (v0.1 in February 2027). If a blocking v3 bug appears, the fix is to pin a later v3.0.x, not to go back to v2.

### 2. Svelte 5 + TypeScript, with Vite

Svelte 5 is the lightest of the official Wails templates (no virtual DOM, fine-grained reactivity through runes), which suits a UI updated many times per second by streaming. React 19 or Vue 3 would work too, but nothing in our needs requires their ecosystems, and the components below are framework-agnostic.

### 3. Components

| Need | Choice | Licence | Why, and rejected alternatives |
| --- | --- | --- | --- |
| Panels | **paneforge** 1.0 | MIT | Svelte 5 native, nested groups, collapsible panes, `autoSaveId` persists the layout (issue #12). A home-made splitter would re-implement keyboard and persistence handling. |
| Markdown | **markdown-it** 15, `html: false` | MIT | Mature, CommonMark, renders an unclosed code fence as a block — streaming-friendly. `marked` is close; markdown-it's plugin model is richer. |
| Code colouring | **Shiki** 4, JavaScript regex engine, bundled languages only | MIT | VS Code-quality colouring, dual light/dark themes through CSS variables. The JS engine avoids shipping the Oniguruma WebAssembly. highlight.js is lighter but visibly poorer. |
| Sanitising | **DOMPurify** 3 | MPL-2.0 / Apache-2.0 | Model output is untrusted: even with `html: false`, the rendered HTML goes through DOMPurify. |
| Editor | **CodeMirror 6** + `@codemirror/lang-yaml` | MIT | Modular and light, YAML mode, lint API for live schema validation (issue #18). Monaco is the VS Code editor but heavy (several MB, web workers) for a YAML file. |
| Terminal | **xterm.js** 6 + `addon-fit`, Go side **creack/pty** | MIT | The de-facto web terminal; with a real PTY `htop`/`vim` and resizing work. |

### 4. Rendering rules that came out of the prototype

- Streamed chunks are appended to a string, and the Markdown is **rendered at most once per animation frame**, not once per chunk. Measured in the sandbox (software rendering): 219 chunks at 50/s in 4.6 s (4.4 s of sleep), and at 500/s in 0.6 s — the UI keeps up.
- Shiki loads asynchronously; the message is rendered again when it is ready.
- Theme: design tokens as CSS variables, dark and light following the system (`prefers-color-scheme`).

## Measured cost

| | Size |
| --- | --- |
| `frontend/dist` (all assets) | 1.9 MB |
| Main JS chunk (CodeMirror, xterm, markdown-it, Shiki core) | 1.16 MB, 368 kB gzipped |
| Shiki grammars and themes (lazy chunks) | ≈ 420 kB |
| Linux arm64 binary with assets embedded | 17.9 MB |

Assets are embedded in the binary and never downloaded, so the size matters little for a desktop app.

## Verification

- `npm run build` and `svelte-check` (0 errors, 0 warnings), `go vet`.
- Built and run on Linux arm64 (GTK4 + WebKitGTK 6.0, under Xvfb): layout, resizing, streaming, colouring, sanitising and a real shell in the terminal, driven with `xdotool`.
- Run on macOS by the project owner (`wails3 dev`, `wails3 package`), following the macOS test guide: everything as expected; the traffic lights covered the left dock heading, fixed with a `--titlebar-inset` padding (38 px on macOS, equal to `InvisibleTitleBarHeight`).

## Consequences

- Issue #8 starts from `wails3 init -t svelte` (v3.0.0) and keeps the Taskfile that Wails generates; the project `Makefile` stays the single entry point and delegates to it.
- Third-party licences (Apache-2.0, MIT, MPL-2.0) are listed in a `THIRD_PARTY_NOTICES` file shipped with the binary (issue #36).
- Building on Linux needs `pkg-config`, `libgtk-4-dev` and `libwebkitgtk-6.0-dev`; on macOS, the Xcode command line tools.