Skip to content

Commit 7ba49ae

Browse files
author
verseles
committed
docs: document Windows terminal keyboard fallback
Document the embedded terminal's Windows printable hardware-key fallback and AltGr support in ADR-027, BEHAVIOR.md, and CODEBASE.md. Sync AGENTS.md ADR quick-reference line ranges after the ADR-027 update.
1 parent 11342ca commit 7ba49ae

4 files changed

Lines changed: 16 additions & 14 deletions

File tree

ADR.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,7 @@ ADR-026 specified a local PTY shell spawned on the client device using `flutter_
13131313
5. **Composer auto-hide on compact/mobile**: On compact and mobile layouts, the composer input area is hidden while the terminal panel is open. The composer reappears when the terminal is minimized or closed.
13141314
6. **Project directory integration**: The server-side PTY launches in the active project's working directory (the `scopeId` from the current `serverId::scopeId` context), ensuring the shell operates in the same workspace the chat conversation is about.
13151315
7. **No server API contract changes**: The terminal transport reuses existing OpenCode streaming infrastructure (WebSocket or SSE). No new dedicated terminal endpoints are introduced — the server exposes PTY data through the established event stream contract.
1316+
8. **Windows printable hardware-key fallback and AltGr support**: The vendored `xterm` `TerminalView` includes a Windows-only fallback that handles printable hardware-key events (raw scan codes) and AltGr key composition. This guards against input regression on international keyboard layouts where AltGr produces alternate characters (e.g. European layouts). The fallback is gated behind `TargetPlatform.windows` (Flutter platform gate) and does not affect other platforms.
13161317

13171318
### Rationale
13181319

@@ -1324,7 +1325,7 @@ ADR-026 specified a local PTY shell spawned on the client device using `flutter_
13241325

13251326
### Consequences
13261327

1327-
- ✅ Terminal works identically on all client platforms (desktop, mobile, web) with no platform-specific native dependencies.
1328+
- ✅ Terminal works identically on all client platforms (desktop, mobile, web) with no platform-specific native dependencies. Windows AltGr and hardware-key fallback preserves input parity for international keyboard layouts.
13281329
- ✅ Server-side PTY runs in the correct project environment with full toolchain access.
13291330
- ✅ Removes `flutter_pty` native compilation complexity from the client build pipeline.
13301331
- ✅ Close/minimize/maximize semantics and composer auto-hide on compact/mobile are preserved.

AGENTS.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,17 @@ This rule is **supreme** for any app behavior change and overrides conflicting l
5656
| 024 | Modal Enter keyboard policy for safe dialogs — speed up keyboard confirmation without enabling destructive or ambiguous modal flows | 1106–1180 |
5757
| 025 | Settled Assistant-Work Disclosure Ownership — client-side architectural ownership to prevent open/close thrash, scroll jumps on session return | 1181–1232 |
5858
| 026 | ⚠️ SUPERSEDED — Local PTY shell replaced by server-hosted PTY (ADR-027) | 1233–1291 |
59-
| 027 | Server-hosted PTY terminal with embedded client rendering — runs on OpenCode host in active project directory, client renders via streaming transport, local flutter_pty removed, close/minimize/maximize semantics preserved, composer hides on compact/mobile | 1292–1353 |
60-
| 028 | Unified scroll ownership via `_ScrollOwner` enum — eliminate scroll jumping across send/return/pagination triggers, user drag priority, force scroll bypass; additive guardrails cover passive provider scroll suppression, manual follow pause near bottom, response-settle shrink-snap suppression, duplicate return-to-chat scoping, queued cached restore targets for settled-vs-active session return, active-turn/global-fallback guards against passive background settle, a single reading-mode final reveal path for long answers, deferred tool-only merge until settlement to prevent active-turn structural shrink, and a narrow active-turn shrink heal while passive follow remains enabled | 1354–1434 |
61-
| 029 | Host-discovered quota and rate-limit monitoring for OpenChamber parity — server-host quota ownership, strategy-chain transport (REST/Shell), popup-only UI (compact-first), grouped providers with pace/progress, explicit parity opt-in; narrow `opencode-go` exception for dashboard credential opt-in (workspace ID + auth cookie, scoped by serverId, quota-probe only, removable via UI); Codex `providerId` guard prevents single-window label collapse | 1435–1514 |
62-
| 030 | OpenChamber-driven realtime hardening and permission continuity — atomic refresh consolidation, mutation guard during reconnect failures, authoritative pruning delay, and bounded reconnect helpers | 1515–1557 |
63-
| 031 | Historical inline revert through the official session revert endpoint — `revertToTurn`, duplicate-revert guard, `local_user_*` validation, composer draft restoration, distinct inline rewind action for server-confirmed user messages, and permission `remember: true` companion fix for `always` replies | 1560–1624 |
64-
| 032 | LaTeX math rendering with `flutter_math_fork` (pure-Dart KaTeX port), custom `$…$`/`$$…$$` Markdown delimiters, `MathExpressionWidget` with styled fallback, and `showMathRendering` toggle in `ExperienceSettings` — typeset math in chat without WebView | 1629–1679 |
65-
| 033 | Cloudflare Managed OAuth as optional desktop + Android reverse-proxy auth (ADR-023 exception) — `ServerProfile.oauthEnabled`, `OAuthService` conditional export (io/stub), auth code + PKCE S256, DCR, `OAuthTokenStorage` in `flutter_secure_storage` scoped by profileId+serverUrl, Bearer token for matching origin only, OAuth/Basic Auth mutually exclusive per profile, desktop (local HTTP redirect) and Android (flutter_appauth) via `AppProvider.supportsCloudflareAccessOAuth`, `/oauth/callback` with state/duplicate rejection, health checks record OAuth challenges, rollback by disabling `oauthEnabled` + clearing credentials | 1683–1792 |
66-
| 034 | Density-aware spacing tokens via `AppDensitySpacing` static helper — 5-tier `AppDensity` switch expressions for horizontal/vertical padding, gaps, content insets, chrome/composer convenience builders, replacing ~25 hardcoded EdgeInsets/SizedBox magic numbers | 1794–1849 |
67-
| 035 | Message-derived selection fallback with explicit-override precedence — 3-tier restoration (explicit override → message scan → global defaults), LRU cache-first backward scan for `providerId`/`modelId`/`mode`/`variant`, neutral-message filtering, isExplicit flag, override promotion on fallback success; OpenChamber parity for `restoreSessionStateFromMessages()` | 1852–1901 |
68-
| 036 | Userspace Tailscale transport with vendored `package:tailscale`, `hook/build.dart` no-op on Windows, `ServerProfile.tailscaleEnabled`, one node per process, active-profile-only transport, inactive health returns unknown, custom Dio HttpClientAdapter preserving SSE + cancellation, interactive auth UX via `AppProvider` reactive state + `authenticateTailscale()`, auth panels in onboarding/settings, no auto-launch of auth URLs, no Web/Windows | 1903–1960 |
69-
| 037 | Chat Viewport and Scroll/Follow Synchronization Revamp — consolidation of viewport flags to `_ScrollFollowMode`, turn-scoped reveal guard, time-windowed REST status guard, build-phase sync extraction, no entrance motion on timeline entries (passive auto-follow uses non-animated `jumpTo` with bounded retry), and reader-owned viewport preservation during active incoming response growth | 1964–2011 |
59+
| 027 | Server-hosted PTY terminal with embedded client rendering — runs on OpenCode host in active project directory, client renders via streaming transport, local flutter_pty removed, close/minimize/maximize semantics preserved, composer hides on compact/mobile, Windows AltGr/hardware-key fallback for international keyboards | 1294–1356 |
60+
| 028 | Unified scroll ownership via `_ScrollOwner` enum — eliminate scroll jumping across send/return/pagination triggers, user drag priority, force scroll bypass; additive guardrails cover passive provider scroll suppression, manual follow pause near bottom, response-settle shrink-snap suppression, duplicate return-to-chat scoping, queued cached restore targets for settled-vs-active session return, active-turn/global-fallback guards against passive background settle, a single reading-mode final reveal path for long answers, deferred tool-only merge until settlement to prevent active-turn structural shrink, and a narrow active-turn shrink heal while passive follow remains enabled | 1357–1437 |
61+
| 029 | Host-discovered quota and rate-limit monitoring for OpenChamber parity — server-host quota ownership, strategy-chain transport (REST/Shell), popup-only UI (compact-first), grouped providers with pace/progress, explicit parity opt-in; narrow `opencode-go` exception for dashboard credential opt-in (workspace ID + auth cookie, scoped by serverId, quota-probe only, removable via UI); Codex `providerId` guard prevents single-window label collapse | 1438–1517 |
62+
| 030 | OpenChamber-driven realtime hardening and permission continuity — atomic refresh consolidation, mutation guard during reconnect failures, authoritative pruning delay, and bounded reconnect helpers | 1518–1561 |
63+
| 031 | Historical inline revert through the official session revert endpoint — `revertToTurn`, duplicate-revert guard, `local_user_*` validation, composer draft restoration, distinct inline rewind action for server-confirmed user messages, and permission `remember: true` companion fix for `always` replies | 1562–1629 |
64+
| 032 | LaTeX math rendering with `flutter_math_fork` (pure-Dart KaTeX port), custom `$…$`/`$$…$$` Markdown delimiters, `MathExpressionWidget` with styled fallback, and `showMathRendering` toggle in `ExperienceSettings` — typeset math in chat without WebView | 1630–1683 |
65+
| 033 | Cloudflare Managed OAuth as optional desktop + Android reverse-proxy auth (ADR-023 exception) — `ServerProfile.oauthEnabled`, `OAuthService` conditional export (io/stub), auth code + PKCE S256, DCR, `OAuthTokenStorage` in `flutter_secure_storage` scoped by profileId+serverUrl, Bearer token for matching origin only, OAuth/Basic Auth mutually exclusive per profile, desktop (local HTTP redirect) and Android (flutter_appauth) via `AppProvider.supportsCloudflareAccessOAuth`, `/oauth/callback` with state/duplicate rejection, health checks record OAuth challenges, rollback by disabling `oauthEnabled` + clearing credentials | 1684–1796 |
66+
| 034 | Density-aware spacing tokens via `AppDensitySpacing` static helper — 5-tier `AppDensity` switch expressions for horizontal/vertical padding, gaps, content insets, chrome/composer convenience builders, replacing ~25 hardcoded EdgeInsets/SizedBox magic numbers | 1797–1853 |
67+
| 035 | Message-derived selection fallback with explicit-override precedence — 3-tier restoration (explicit override → message scan → global defaults), LRU cache-first backward scan for `providerId`/`modelId`/`mode`/`variant`, neutral-message filtering, isExplicit flag, override promotion on fallback success; OpenChamber parity for `restoreSessionStateFromMessages()` | 1854–1904 |
68+
| 036 | Userspace Tailscale transport with vendored `package:tailscale`, `hook/build.dart` no-op on Windows, `ServerProfile.tailscaleEnabled`, one node per process, active-profile-only transport, inactive health returns unknown, custom Dio HttpClientAdapter preserving SSE + cancellation, interactive auth UX via `AppProvider` reactive state + `authenticateTailscale()`, auth panels in onboarding/settings, no auto-launch of auth URLs, no Web/Windows | 1905–1964 |
69+
| 037 | Chat Viewport and Scroll/Follow Synchronization Revamp — consolidation of viewport flags to `_ScrollFollowMode`, turn-scoped reveal guard, time-windowed REST status guard, build-phase sync extraction, no entrance motion on timeline entries (passive auto-follow uses non-animated `jumpTo` with bounded retry), and reader-owned viewport preservation during active incoming response growth | 1965–2012 |
7070

7171
## 🗺 CODEBASE Quick Reference (details in `CODEBASE.md`)
7272

BEHAVIOR.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,7 @@ Additional commands may be provided by the connected OpenCode server and merged
819819
- **Then** CodeWalk creates or reconnects to a server-hosted PTY terminal rooted in the active project directory on the OpenCode host and renders it inside the embedded panel
820820
- **Then** `Close terminal` fully closes the panel and terminates the active server PTY session, while `Minimize terminal` hides the panel without stopping that session
821821
- **Then** `Maximize terminal` expands the panel to a larger workspace view and `Restore terminal size` returns it to the saved panel height
822+
- **Then** on Windows, printable hardware keyboard input, including AltGr characters from international layouts, is forwarded to the terminal session instead of being dropped after focus
822823
- **Given** the user is on a compact/mobile chat layout
823824
- **When** the embedded terminal is open
824825
- **Then** CodeWalk hides the composer input area until the terminal is minimized or closed so the terminal can use the available screen space

CODEBASE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ codewalk/
6262
├── linux/runner/resources/ # Linux launcher icon + desktop entry icon metadata
6363
├── third_party/ # Vendored Dart packages (path dependencies)
6464
│ ├── tailscale/ # Userspace Tailscale networking; Go native build hook via `hook/build.dart`
65-
│ └── xterm/ # xterm.js terminal emulator Dart port
65+
│ └── xterm/ # xterm.js terminal emulator Dart port (customized with Windows-only printable hardware-key fallback and AltGr support)
6666
└── Makefile # Main development and validation commands
6767
```
6868

@@ -358,7 +358,7 @@ test/unit/services/ # Platform and runtime service unit test
358358
codewalk_terminal_controller_test.dart # Terminal controller: server-side PTY lifecycle, WebSocket connectivity, resize debouncing, cursor tracking
359359
codewalk_terminal_url_test.dart # WebSocket terminal URL construction
360360
read_aloud_service_test.dart # Text-to-speech service lifecycle, options (pitch/rate/voice), and message tracking
361-
test/widget/ # Widget tests (includes icon assertions with Symbols.*, explicit compact/mobile collapsed-copy coverage for chat message and session todo surfaces, historical rewind action coverage, desktop/mobile spacing for ChatSessionList, toolbar undo/redo, slash-command parity, and terminal mobile backspace simulation coverage)
361+
test/widget/ # Widget tests (includes icon assertions with Symbols.*, explicit compact/mobile collapsed-copy coverage for chat message and session todo surfaces, historical rewind action coverage, desktop/mobile spacing for ChatSessionList, toolbar undo/redo, slash-command parity, terminal mobile backspace simulation, Windows printable hardware key forwarding, and Windows AltGr printable forwarding)
362362
test/integration/ # Integration tests; includes data-usage optimization and permission `remember` contract coverage in `opencode_server_integration_test.dart`
363363
test/presentation/ # Presentation-focused tests (incl. window_size_class_test.dart)
364364
test/support/ # Test helpers/fakes; `mock_opencode_server.dart` includes extra counters for usage optimization tracking; `pump_localized_app.dart` wraps widgets with all l10n delegates for locale-aware tests

0 commit comments

Comments
 (0)