Commit Graph
80 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 4.7 8fd251ac48 add file/image paste to the Claude composer via native clipboard
Flutter's clipboard is text-only and tmux/send-keys carry text only, so
a pasted file or image must reach Claude as an @path reference (per the
T-134 spike). A native clide/clipboard MethodChannel reads the non-text
clipboard: GTK (gtk_clipboard_wait_for_image/uris) on Linux, NSPasteboard
on macOS. The composer overrides PasteTextIntent — Ctrl/Cmd+V resolves a
file path or writes a clipboard image to a cache dir, inserts the @path,
and falls back to plain-text paste otherwise. No new package dependency.

macOS handler is written but unverified on this Linux box — needs a build
on a Mac. Linux path builds and is covered by tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:36:18 +02:00
jpmschweitzerandClaude Opus 4.7 b3fa69c982 add native composer to the Claude pane (T-138)
A no-Material EditableText below the ConversationView. Enter submits,
Shift+Enter inserts a newline (a Shortcuts override maps plain Enter to
a submit intent; shifted Enter falls through to the default newline).

Submitted text goes to Claude over pane.write — the same input verb the
terminal pane uses, so D-6 parity holds and there's no Claude-only path.
Multi-line input is wrapped in bracketed-paste markers so the embedded
newlines submit as a single message instead of a stream of submits.

Paste handling is stubbed via an injectable resolver (file/image @path
support lands in the next commit); plain-text paste falls through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:30:33 +02:00
jpmschweitzerandClaude Opus 4.7 0b942db251 route Claude transcript content through the MessageBus
test / unit + widget + golden + a11y (push) Failing after 26s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 23s
The Claude pane owned a TranscriptReader directly via the controller.
Insert a TranscriptPublisher that tails the transcript and republishes
each ConversationItem onto the kernel MessageBus; the view's controller
subscribes through ConversationController.fromBus. The subscription is
attached before the reader's first poll, so the initial tail isn't
missed on the broadcast bus.

This decouples reading from rendering: the team work (T-139/T-140) can
run one observer that publishes per-agent channels while the lead tile
and each teammate tile subscribe independently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:03:17 +02:00
jpmschweitzerandClaude Opus 4.7 a6ed22405f fix Claude pane hang: cap tail, parse off-isolate, coalesce notifies
test / unit + widget + golden + a11y (push) Failing after 27s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 27s
The native Claude pane (T-137) picks the newest transcript JSONL by
mtime — which on a live session is this multi-MB active file. On attach
it read and parsed the whole file synchronously on the UI isolate and
fired notifyListeners per item, freezing the app.

Three fixes: cap the initial read to a recent tail (256KB, injectable
for tests); run JSON parsing in a background isolate via Isolate.run;
coalesce the controller's notifications with a zero-duration Timer so a
burst collapses into one rebuild (a microtask-scheduled notify wouldn't
— stream events deliver one per microtask and the notify interleaves).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 20:30:30 +02:00
jpmschweitzerandClaude 76c1d24904 render the Claude pane natively from the transcript (T-137)
test / unit + widget + golden + a11y (push) Failing after 27s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 27s
The Phase-1 wedge of epic T-132 (D-75): the Claude pane no longer
renders the PTY's TUI. It runs claude in tmux as before (so the
transcript is written) but displays the conversation as native cards
read from the transcript via TranscriptReader — user / assistant
markdown / thinking / tool-use / tool-result. The whole list sits under
a new no-Material ClideSelectionArea (SelectableRegion-based, since
Flutter's SelectionArea is Material and D-7 bans it), so text selects
and copies across cards — recovering the terminal's one real advantage.

ClaudePane drops its Terminal model and the resize-driven spawn trigger
(spawn now fires once on didChangeDependencies with a fixed tmux size,
since the TUI isn't shown); pane.output is no longer consumed. The
terminal builtin is untouched and still available as a general tool.
Input/composer is the next ticket (T-138).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-22 20:12:30 +02:00
jpmschweitzerandClaude 0eee456426 add TranscriptReader: tail + parse Claude transcript JSONL (T-136)
test / unit + widget + golden + a11y (push) Failing after 27s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 30s
Data layer for native Claude rendering (T-132). Resolves the munged
project dir, picks the newest session .jsonl by mtime (Claude doesn't
expose its session id), tails it append-only via a dart:io byte cursor,
and emits a sealed ConversationItem stream (user / tool-result /
assistant text / thinking / tool-use). Skips bookkeeping record types
and degrades gracefully on an unfamiliar transcript `version`. Pure
dart:io + dart:convert, Flutter-free, zero new deps.

The parser is a pure public parseLine(line) -> List<ConversationItem>
so tests exercise the real code (an injectable projectsBase lets the
streaming tests point the real reader at a temp dir) — no shadow
re-implementation. 31 tests under dart test.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-22 19:36:53 +02:00
jpmschweitzerandClaude f1f12e7d79 add multi-file editor tabs
The editor pane showed a single buffer — opening a second file
replaced the first, even though the daemon's EditorRegistry has
always been multi-buffer (editor.list / activate / close). This wires
the UI up to that: EditorController now tracks the full open-buffer
list (via editor.list on hydrate, kept in sync by editor.opened /
closed / saved / edited events), and EditorView renders the buffers
as tabs through the shared MultitabPane — the same strip the Claude
pane uses. The daemon stays the source of truth: the local tab
controller is reconciled from it, and tab select / close route back
as editor.activate / editor.close. Reorder is disabled for now
(daemon order is authoritative).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-22 12:24:35 +02:00
jpmschweitzerandClaude 74f9a45539 extend build-info bake to name + tagline + repository
test / unit + widget + golden + a11y (push) Failing after 32s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s
User asked for the rest of the pubspec-derived strings to share the
same path the version went down. gen-build-info now also writes
`clideName`, `clideTagline`, `clideRepository` to
lib/src/build_info.g.dart from pubspec.yaml. Added a `tagline:`
field to pubspec for the short user-facing line (the welcome
subtitle, future web meta) — pubspec stays the single source of
truth for every name/tagline/version/repository string the app
shows.

Consumers swept:
* welcome banner ('clide' / 'IDE for Claude Code CLI') and status
  line version label read from the constants.
* app.dart WidgetsApp title + project-switcher label use clideName.
* clide_column_hat uses clideName for the empty-projects fallback.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-18 13:32:15 +02:00
jpmschweitzerandClaude 7c2eae42f1 fix theme picker integration test; one bake for build-time facts (T-116)
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m1s
The test was awaiting services.commands.execute('theme.pick') whose
Future doesn't complete until the dialog is dismissed — deadlock.
Fire-and-forget around pumpAndSettle, then tap Cancel, then await
the original future. Also tear the widget tree down before
services.dispose() so listening widgets unsubscribe first.

Pre-existing layout overflow in the welcome _StatusLine surfaced
when running the test at narrower viewports. Switched to a whole-
row FittedBox(scaleDown) — uniform shrink on narrow screens, no-op
at standard widths.

User flagged the hardcoded 'clide 2.0.0-dev' string. Replaced with
one generated lib/src/build_info.g.dart (gitignored, regenerated
by `make gen-build-info` from pubspec.yaml + git short SHA + UTC
clock). The same target re-syncs assets/licenses.yaml self.version
in place — no second source. Every make build/run/test depends on
it implicitly. Welcome status line now reads `clideVersion`. Stale
fontSize literals in welcome_view swept to typography constants;
clideFontMeta=13, clideFontDialogTitle=16, clideFontWelcomeBanner=52
added to fill gaps in the scale.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-18 13:22:55 +02:00
jpmschweitzerandClaude 78b38e389d T-115 finishing touches + D-66 amendment for justified floor drops
test / unit + widget + golden + a11y (push) Failing after 29s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s
* Adds `make t T=...` and `make verify` (no-tests gate sweep), plus a
  gitignored test/.test-output/ that the new tee target writes to.
* loadRecents() now notifies listeners so the welcome view reflects
  recents loaded on cold boot.
* _StickyToggle gets a ValueKey('welcome.sticky.<path>') for testing.
* D-66 amended: a downward floor change is allowed iff (a) the commit
  explains the drop, (b) a follow-up ticket is filed in the same
  commit, (c) the new floor rounds down to the nearest whole percent
  of current actual coverage.
* coverage_floor: 95 -> 94. T-115's new _StickyToggle widget is
  uncovered because pumpWidget(WelcomeView) with a non-empty recents
  list strands the test until the 10-min Flutter timeout — even after
  ruling out ClideTooltip and tap shape. Tracked as T-122; next
  test-adding commit re-bumps the floor.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-18 11:29:41 +02:00
jpmschweitzerandClaude 7046bf9c70 picker-first startup with per-project sticky override (T-115)
Boot used to auto-open app.lastProject and fall back to the CWD; new
default is the welcome screen as the project picker. Sticky-open is
opt-in: a checkbox on each recent-projects row toggles a
startupSticky flag, and clide auto-opens iff exactly one row has it.
Two-or-more, or none, ⇒ picker (unambiguous user intent).

RecentProject gains the boolean (persisted in app.recentProjects);
ProjectManager exposes stickyProjectPath, openStickyOrNothing,
setStickyStartup, isStickyStartup, and preserves the flag across
reopens.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-18 10:26:12 +02:00
jpmschweitzerandClaude 044d1b2ff1 lift text-zoom into kernel, surface it in the palette (T-114)
test / unit + widget + golden + a11y (push) Failing after 30s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m1s
Workspace text-zoom (Ctrl +/-/0) was local state on _RootShellState,
reachable only via the keymap intent path. Lifted to a kernel TextZoom
ChangeNotifier so the new `view.zoomIn/Out/Reset` palette commands
mutate the same number the keymap does — closing T-114's "discoverable
in the palette" item.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-18 09:04:43 +02:00
jpmschweitzerandClaude Opus 4.7 d4f8f89016 code-quality batch (T-112)
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m7s
Seven small consultant findings, one commit:

1. TreeSitterLib stores last dlopen error + path in static fields
   instead of swallowing them. Callers that observe a null instance
   can now read the diagnostic.

2. Drop the Cmsghdr alias in libc.dart — back-compat shim with no
   callers; CLAUDE.md forbids those in a solo repo.

3. Drop EditorController._events field + the unused_field
   suppression. The constructor still subscribes via `events.on<...>`
   for _eventSub; the field itself was speculative retention.

4. Replace inline hex / errno literals in native_pty.dart with
   PosixErrno.{eintr,ebadf,epipe} and new libc.{pollin, pollAnyErr,
   sighup, sigkill, sigwinch}. PosixErrno gains eintr.

5. ExtensionManager records activate/deactivate exceptions in a
   `_failed` map exposed as `failedExtensions` + `didFail(id)`.
   Listeners are notified on entry/exit; cleared on a clean
   activate. UI surfaces the degraded state instead of pretending
   everything is fine.

6. file_tree_view imports FileEntry via the clide.dart barrel
   instead of `package:clide/src/files/listing.dart` directly — the
   leak the consultant flagged (barrel already re-exports it).

7. test_app branch in main.dart wrapped in `if (kDebugMode)` so
   release tree-shaker elides the test harness from shipping
   binaries. Source import stays; tree-shake handles the rest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 22:11:23 +02:00
jpmschweitzerandClaude f90ddc345f update decisions/ → governance/ refs after D-21 migration
test / unit + widget + golden + a11y (push) Failing after 29s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m1s
Stale path references from the move in 63195d1:
- CLAUDE.md: 8 D-record links, the layout tree, the open-questions
  pointer, all rewritten to governance/.
- docs/design/multitab-pane.md + docs/claude-design/README.md:
  cross-references updated.
- lib/clide.dart: doc-comment refs.
- lib/builtin/problems: user-facing message string.
- Makefile: decisions-validate target docstring.

Note: lib/builtin/decisions/ (the in-app decisions panel package)
keeps its name — it's a feature name, not a filesystem-path mirror.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-11 18:38:39 +02:00
Jeroen SchweitzerandClaude Opus 4.6 a6eca2561b remove dissolved daemon, retire ptyc, fix golden cross-platform
Complete three overdue cleanups discovered during macOS health check:

D-56 daemon dissolution: delete bin/clide.dart, DaemonServer,
and orphaned tests (test/cli/, subprocess_test, in_process_test).
Update stale "clide --daemon" references in i18n catalogs, error
messages, editor_commands, CI scripts, and decision records.

ptyc retirement: delete ptyc/ source tree, PtySession, scm_rights.
Remove from Toolchain resolution, ToolCheck gate, backend
serialization, testmode harness, Makefile, CI, and sandbox
entitlements. PTY spawning uses NativePty (Dart FFI forkpty) since
the terminal was absorbed in-tree. D-5 amended.

Golden tests: wire the existing but never-applied clideGoldenConfig
via flutter_test_config.dart. Disable CI goldens (Skia anti-aliasing
differs between macOS/Linux even with Ahem). Keep platform-keyed
goldens only — goldens/linux/ and goldens/macos/ each run on their
own OS.

Test suite: 826 pass, 0 fail on macOS (was 829 pass, 11 fail).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 18:40:01 +02:00
jpmschweitzerandClaude 1fb4786f18 sweep remaining analyze infos to zero
test / unit + widget + golden + a11y (push) Failing after 35s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m0s
Clears the 19 misc lint hits left after the test_app print sweep
+ libc.dart suppression. By rule:

- `withOpacity(α)` → `withValues(alpha: α)` (deprecated_member_use)
  in `painter.dart:187` and `terminal_view.dart:318`.
- `Pointer.elementAt(n)` → `Pointer + n` (deprecated_member_use)
  in `native_pty.dart:306` and `session.dart:187`.
- Brace single-statement for/if bodies in `native_pty.dart`
  (×3) and `decisions_view.dart` (curly_braces_in_flow_control_
  structures).
- `IsolateClient` and `InProcessClient` constructors switched to
  `super.log` / `super.events` parameters (use_super_parameters);
  associated unused imports of `kernel/src/log.dart` and
  `kernel/src/events/bus.dart` removed in the same files.
- `InProcessClient._dispatcher` field + getter/setter pair folded
  into a single mutable public `dispatcher` field
  (unnecessary_getters_setters).
- `_buildDispatcher` local in `lib/main.dart` renamed to
  `buildDispatcher` (no_leading_underscores_for_local_identifiers).
- `_onTapDown(_)` in `terminal_view.dart` typed as
  `TapDownDetails _` (strict_top_level_inference).
- `operator []=(...)` in `circular_buffer.dart` given an explicit
  `void` return type (strict_top_level_inference).
- `CustomKeyboardListener` and `TerminalGestureDetector` callsites
  reordered so `child:` lands last (sort_child_properties_last).
- `CustomTextEdit` constructor declared `const`
  (prefer_const_constructors_in_immutables).
- `LinkedHashMap<K, V>()` in `paragraph_cache.dart` collapsed to a
  `<K, V>{}` literal (prefer_collection_literals); the now-unused
  `dart:collection` import dropped.

Project analyze: 19 → 0 issues. `make test` stays green; coverage
unchanged at 52.72%.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 23:51:46 +02:00
jpmschweitzerandClaude 26cc1b3154 drop redundant single-symbol imports across the tree
12 imports flagged by `unnecessary_import` because the symbols
they bring in are also re-exported by the umbrella import already
present in the same file:

- bin/clide.dart: src/git/client.dart, src/pql/client.dart
  (covered by package:clide/clide.dart).
- lib/builtin/decisions/, lib/builtin/tickets/ (4 files):
  kernel/src/events/message_bus.dart (covered by kernel.dart).
- lib/kernel/src/ipc/in_process.dart: src/daemon/dispatcher.dart
  (covered by clide.dart).
- lib/main.dart: kernel/src/toolchain.dart (covered by kernel.dart).
- test/builtin/ipc_status/widget_test.dart:
  builtin/ipc_status/src/status_item.dart (covered by
  ipc_status.dart).
- test/daemon/{git,pql}_commands_test.dart: src/git/client.dart and
  src/pql/client.dart (covered by clide.dart).
- test/widgets/multitab_pane_test.dart: widgets/src/icons/x.dart
  (covered by widgets.dart).

Mechanical change — every removed line was already a no-op for
symbol resolution; the umbrella imports define the public surface
each file is actually using.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 23:46:24 +02:00
jpmschweitzerandClaude f0bb2ffcce format the tree to current dart format spec
Mechanical `dart format` sweep across files that drifted from the
formatter's output (mostly trailing-comma and line-wrap differences
from a Dart SDK / formatter version bump). No semantic changes.

Caught because the pre-push gate now actually fires.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 22:28:51 +02:00
jpmschweitzerandClaude 5ee759d0fd clear dart doc validate-links warnings
Eight unresolved doc references and broken README-rewritten links
that surfaced under `dart doc --validate-links`:

- Library-scope refs `[spawn]`, `[openProject]` qualified to
  `[Backend.spawn]` / `[Backend.openProject]`; same treatment for
  `[resolvePaths]` / `[applyResolved]` on Toolchain.
- `[D-41]` was a decision ID, not a Dart symbol — drop the brackets.
- `[from]` from I18n.interpolated qualified to `[I18nReplacer.from]`.
- `[DefaultSurfaceMap]` was a stale name (private `_defaultSurfaceMap`
  in resolver.dart); switch to backticked path reference since
  dartdoc can't link private members.
- `[icons/]` was a directory, not a symbol; backticked path.
- README links to `legacy/`, `docs/initial-plan.md`, `decisions/`,
  `LICENSE` rewritten as absolute github.com/postmeridiem/clide URLs
  so dartdoc stops re-rooting them into the doc tree.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 17:58:18 +02:00
jpmschweitzerandClaude 6601056972 finish T-87: cold-start reap, kill-all-sessions, helper tests
Three remaining acceptance criteria for T-87:

1. Cold-start reap. The Claude extension's activate() now kills
   every leftover secondary tmux session for the current repo
   before any new spawn. activate runs before any UI mounts, so
   _nextSecondary's starting value of 1 is correct even when a
   previous run died abruptly (kill -9, OOM, force-quit). The
   deactivate() hook also calls reapSecondaries as a courtesy on
   explicit extension teardown — but Flutter's deactivate doesn't
   fire on app quit, so activate is the load-bearing path.

2. claude.kill-all-sessions actually kills server-side. The
   command previously called pane.close on every claude pane,
   which only kills the tmux client. It now also calls
   tmux.killAllForRepo to kill the sessions on the clide socket.

3. Tests. test/builtin/claude/tmux_session_test.dart covers
   killSession, listClideSessions, reapSecondaries, and
   killAllForRepo via the TmuxRunner override — no real shell-out
   in tests.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 16:48:06 +02:00
jpmschweitzerandClaude 1c424f26e7 kill secondary Claude tmux sessions on tab close (T-87)
Adds lib/builtin/claude/src/tmux_session.dart with helpers for the
clide-socket tmux server: killSession, listClideSessions,
reapSecondaries, killAllForRepo. The runner is overrideable via a
TmuxRunner typedef so tests don't shell out for real.

Wires ClaudePane.dispose() to call killSession(sessionName) for
secondary panes. Primary panes are left alone — D-41 keeps the
primary's tmux session alive across clide restarts so the next
launch re-attaches via `tmux new-session -A`.

Imports the helpers in the Claude extension as groundwork for the
app-shutdown reap and the existing claude.kill-all-sessions
command — wiring those uses lands separately.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 16:41:33 +02:00
jpmschweitzerandClaude 71b21e1974 wire Claude pane to MultitabPane (T-24)
ClaudeSessionHost replaces its bespoke tab strip / add button /
close handler with a MultitabPane<_Session> in keepAlive mode.
The primary tab is seeded as non-closeable and non-reorderable
per D-41; secondaries spawn via the existing addSecondary()
entry point and gain drag-to-reorder for free.

Drops ~100 lines of custom _TabRow / _Tab / _AddButton code in
favour of the shared widget. Behaviour is preserved: primary
persists across clide restarts, secondaries can be closed, and
PTY state survives tab switches because IndexedStack keeps every
ClaudePane mounted.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 15:21:08 +02:00
jpmschweitzerandClaude 2d64740007 welcome screen: add Tips card below START / RECENT row
Six common keybindings (Quick open, Command palette, Toggle
sidebar, Toggle context, Switch theme, New Claude session) shown
as a 3x2 grid card spanning the same 850px content column as the
two action columns above. LayoutBuilder gates the card on viewport
height (>640px) so on shorter windows the centered START / RECENT
columns stay the focus and the tips drop out cleanly.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 11:10:17 +02:00
jpmschweitzerandClaude 5f162a6668 rename tagline to "IDE for Claude Code CLI"
clide is an IDE for the Claude Code CLI. The previous tagline
"Flutter desktop IDE for Claude Code" overemphasized the host
toolkit (Flutter is implementation detail, immediately obvious to
contributors) and was ambiguous about whether the integration
target is the CLI specifically.

Updates the welcome subtitle (i18n catalog + widget test + view),
the project description in pubspec.yaml, README and CLAUDE.md, the
CLI banner, and the web manifest/index.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 11:06:41 +02:00
jpmschweitzerandClaude c378503e26 spawn Claude in fullscreen mode and route mouse-wheel to PgUp/PgDown
Spawn `claude` directly as the tmux command with
CLAUDE_CODE_NO_FLICKER=1 so Claude Code runs in its fullscreen TUI
mode (input box pinned at bottom, owns its own scrollback). Mouse
wheel events are converted to PgUp/PgDown key input — universal
scroll signal that Claude, less, vim normal mode all respect, and
sidesteps the mouse-mode-but-no-scroll dead end where TUI apps
capture mouse without binding the wheel.

Drops the 1000-row tmux canvas + SingleChildScrollView experiment
in favor of viewport-sized tmux and Claude's native bottom-pinning.

Makefile install target now kills the clide tmux server so the
new config takes effect immediately. Marked TEMP — to be removed
once we no longer need the rapid-iteration loop.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 13:08:47 +02:00
jpmschweitzerandClaude 85e275a362 wire Claude pane to clide tmux config and isolated socket
Extract bundled tmux.conf to ~/.config/clide/tmux.conf on first
spawn and pass via -f. Use -L clide for a dedicated tmux server
so clide sessions don't inherit the user's tmux settings.
Terminal maxLines bumped from 5k to 50k.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 08:50:50 +02:00
jpmschweitzerandClaude 9c1597a450 inline terminal emulator, drop xterm.dart dependency
Replace the xterm pub.dev package with owned code under
lib/src/terminal/. Based on xterm.dart v4.0.0 by xuty (MIT).
Quiver LRU replaced with hand-rolled LinkedHashMap cache.
Scrollable removed from TerminalView — scroll events are forwarded
via Listener.onPointerSignal instead. zmodem, debugger, and
suggestion modules stripped as unused.

Also: bundle clide.tmux.conf (no status bar, 50k scrollback,
mouse on, zero escape delay, isolated -L clide socket), bump PTY
read buffer to 64KB, add 2px terminal padding, drop bold
JetBrains Mono registration.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 08:50:38 +02:00
jpmschweitzerandClaude 864a1062a6 remove dead code and fix analyzer warnings
Unused _resolvePtycPath (bin/clide.dart), _TrafficDot (app.dart
and clide_column_hat.dart), stale @override on _spawned field
(claude_pane.dart), duplicate import and unused local in
test_app.dart, unnecessary non-null assertions in project.dart.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-03 21:52:26 +02:00
jpmschweitzerandClaude 9c7ec008dc dart format whole tree
Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-03 21:51:59 +02:00
Jeroen SchweitzerandClaude Opus 4.6 56d13c71ef use tmux resize-window for explicit window size control
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
tmux determines window size from its client, not the PTY winsize.
TIOCSWINSZ + SIGWINCH on the master fd has no effect on tmux's
internal dimensions. Use tmux resize-window -t <session> -x -y
to explicitly set the window size on resize.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 13:17:56 +02:00
Jeroen SchweitzerandClaude Opus 4.6 956279eca4 tell tmux to refresh-client on resize
test / unit + widget + golden + a11y (push) Failing after 27s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
tmux doesn't re-read the PTY winsize on SIGWINCH. After the ioctl
resize, call tmux refresh-client -C cols,rows to update tmux's
internal window dimensions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 13:11:26 +02:00
Jeroen SchweitzerandClaude Opus 4.6 a028eb7ad3 pass -x/-y to tmux new-session for correct initial size
test / unit + widget + golden + a11y (push) Failing after 27s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
tmux running inside a PTY without a traditional terminal client
defaults to a huge window size (2000+ cols, 10000 rows). Pass
explicit -x and -y flags matching the TerminalView dimensions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 13:03:51 +02:00
Jeroen SchweitzerandClaude Opus 4.6 0b3c6bc1f8 restore tmux session in Claude pane
test / unit + widget + golden + a11y (push) Failing after 34s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
Reverts the bare-shell diagnostic. Spawns tmux new-session -A -s
<sessionName> for persistence. Claude command will be added back
after tmux + resize are stable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 12:20:03 +02:00
Jeroen SchweitzerandClaude Opus 4.6 2921adc9cb add TERMINFO env for top/ncurses, tighten output flush
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
top fails with "Error opening terminal: xterm-256color" because the
PTY child can't find terminfo. Set TERMINFO=/usr/share/terminfo
explicitly in the pane environment.

Output flush changed from 16ms Timer to Duration.zero (next event
loop turn) for tighter batching of escape sequences.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 12:08:01 +02:00
Jeroen SchweitzerandClaude Opus 4.6 3e6c0279ef batch terminal output at 60fps, increase read buffer to 64KB
test / unit + widget + golden + a11y (push) Failing after 29s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
Split escape sequences caused rendering artifacts (visible % prompt
mark, garbled lines between commands). Two fixes from the legacy
Python implementation:

1. Read buffer increased from 4096 to 65536 bytes. Larger reads mean
   fewer chunk boundaries that can split multi-byte escape sequences.

2. Output writes throttled to ~60fps via a 16ms Timer. Multiple PTY
   reads within one frame window are batched into a single
   terminal.write() call, so the xterm parser sees complete sequences
   instead of fragments that render as garbage between frames.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 12:01:22 +02:00
Jeroen SchweitzerandClaude Opus 4.6 2f7435f271 debounce terminal resize, add SIGWINCH, remove padding
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
Rapid resize during window drag sends many SIGWINCH signals causing
the shell to redraw repeatedly, corrupting xterm's render. Debounce
to 150ms so only the final size is sent.

NativePty.resize now explicitly sends SIGWINCH (signal 28) after
TIOCSWINSZ, matching the legacy Python implementation.

Removed 8px padding from ClidePtyView to eliminate potential
dimension mismatch between widget size and PTY cols/rows.

Default window size increased to 1600x900.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 11:40:12 +02:00
Jeroen SchweitzerandClaude Opus 4.6 6f12c884cd defer PTY spawn until first TerminalView resize
test / unit + widget + golden + a11y (push) Failing after 1m57s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
The PTY was spawning at 80x24 defaults before TerminalView had laid
out, then the real resize caused visual artifacts (ghost lines, broken
reflow). Now spawn triggers on the first onResize callback when real
dimensions are known — matching the pty-spike's proven pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 09:24:10 +02:00
Jeroen SchweitzerandClaude Opus 4.6 acc7407fa5 show context panel on decision/ticket selection
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
The context panel was hidden or collapsed when a decision or ticket
was selected. activateTab alone doesn't make the panel visible.
Now both extensions explicitly setVisible + setCollapsed before
activating the detail tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 08:23:54 +02:00
Jeroen SchweitzerandClaude Opus 4.6 baf90270d1 share DaemonBus between backend and kernel, fix terminal spawn timing
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
The IsolateClient and KernelServices now share the same DaemonBus.
Previously, backend events (pane.output, git.changed) went to a
separate bus that widgets couldn't see.

ClaudePane._spawnWhenReady waits for ProjectOpened before sending
pane.spawn, preventing "No project active" errors.

Recents loaded before runApp so the welcome screen shows them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 17:17:17 +02:00
Jeroen SchweitzerandClaude Opus 4.6 73e80a55a6 add Toolchain, GitClient, native directory picker
Toolchain centralizes binary resolution — replaces five ad-hoc
mechanisms (expandedPath, _resolveGit, _resolve, _resolvePtyc,
_existsOnPath). Resolves via Future.delayed after runApp to avoid
blocking the merged UI/platform thread on macOS.

GitClient wraps all git operations with a typed API. Every subprocess
call goes through _run() using toolchain.git + toolchain.gitEnv.
Replaces free functions in operations.dart.

Native directory picker: NSOpenPanel on macOS (method channel in
AppDelegate), GtkFileChooserDialog on Linux. Falls back to text-input
dialog on web or MissingPluginException. Shows "No git repo found"
dialog when the selected directory is not a git repository.

PqlClient and pane commands updated to use Toolchain. ToolCheck
replaced by Toolchain.missing/allOk. All IPC handlers now catch
GitException to prevent unhandled exceptions on the merged thread.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 13:18:39 +02:00
jpmschweitzerandClaude b8c8826b9e use pql --with-context for ticket detail loading
test / unit + widget + golden + a11y (push) Failing after 26s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
Replaces the N+1 parent-chain walk (one IPC call per ancestor, one
per decision ref) with a single pql ticket show --with-context call.
pql now returns ancestors and decisions arrays inline.

Updates PqlClient.ticketShow to accept withContext/withChildren flags,
removes the obsolete withDecision flag.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-24 10:54:17 +02:00
jpmschweitzerandClaude ad1261fdc2 auto-refresh sidebar panels, pin/focus accordion logic
Decisions: file-change subscription for decisions/*.md plus 1-minute
scheduler tick. Tickets: 1-minute scheduler tick plus changed-event
subscription from status button clicks. PQL markdown tab: file-change
subscription for *.md files. All panels get a manual refresh button.

Accordion sections use pin/focus logic: manually toggled sections are
pinned and stay open; the focused item's section auto-opens; unpinned
sections without focus auto-collapse. Both decisions and tickets views
use ClideAccordion with this pattern.

Ticket sidebar split into six status sections (IN PROGRESS, REVIEW,
READY, BACKLOG, DONE, CANCELLED) matching pql's actual statuses.
Status changes scroll the ticket into its new section.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-24 10:50:25 +02:00
jpmschweitzerandClaude 8a0ca92a1f wire ticket status buttons to pql ticket status
Status buttons in the detail view now call pql.tickets.status IPC,
publish a changed event to refresh the sidebar list, and reload the
detail. Disabled for the current status. IPC handler accepts ids as
a list for batch transitions. PqlClient.ticketSetStatus takes
List<String> and joins with comma per pql CLI contract.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-24 10:50:05 +02:00
jpmschweitzerandClaude 226520cfce add ranked search to pql sidebar with DSL toggle
test / unit + widget + golden + a11y (push) Failing after 33s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
Search tab defaults to ranked text search via pql search — debounced
300ms, scored results with visual score bar.  DSL toggle switches to
raw PQL query mode for SQL-like queries.  Clicking a search result
opens it in the markdown context viewer.

Adds pql.search IPC command and PqlClient.search method.  Controller
gains SearchMode enum and search() method alongside existing runQuery.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-23 22:22:16 +02:00
jpmschweitzerandClaude bfd304a3f9 restructure pql sidebar: Search default, Markdown tab with viewer
Search (PQL DSL query) is now the default left tab; Markdown (filtered
to .md files) on the right.  Clicking a markdown file publishes on
the message bus; the markdown extension activates the context panel
viewer and re-publishes on a load channel after the frame so the
viewer is guaranteed to be mounted.  Viewer subscribes to load channel
and editor buffer events, publishes focus for sidebar highlighting.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-23 21:25:52 +02:00
jpmschweitzerandClaude b236ec439f make TreeSitterService a shared singleton
Each ClideCodeBlock and EditorView created its own TreeSitterService,
each allocating a WASM engine, store, parser, and cursor via FFI.
When widgets were disposed, multiple instances freeing the same
underlying native resources caused double-free corruption on startup.

Single shared instance for the app lifetime.  Also fixes overlapping
tree-sitter spans that caused duplicated text in code blocks — spans
are now clipped to avoid re-emitting already-rendered characters.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-23 21:25:22 +02:00
jpmschweitzerandClaude 04d133f4ed sidebar focus indicators, bus navigation, font constants
All sidebar and detail panes use clideFontSmall/clideFontBadge/
clideFontCaption instead of hardcoded sizes — single place to tune.

Decision detail view subscribes to the message bus for selection
events (same pattern as ticket controller) and publishes focus after
loading.  Both decisions and tickets sidebar views subscribe to the
focus channel, expand the accordion section, highlight the active
card, and scroll it into view.

Clickable DQRT links wired in decision detail, ticket detail, and
markdown viewer via onRecordTap.  Ticket descriptions now render
through ClideMarkdown instead of plain text.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-23 18:53:53 +02:00
jpmschweitzerandClaude Opus 4.6 227d787bc6 add ClideMarkdown, ClideCodeBlock, ClideSvgView primitives
Three reusable rendering widgets in lib/widgets/:

- ClideMarkdown: walks markdown AST (GFM tables, task lists,
  fenced code), renders headings, lists, blockquotes, tables,
  inline bold/italic/code/links. Delegates code blocks to
  ClideCodeBlock.

- ClideCodeBlock: syntax-highlighted code via TreeSitterService.
  Async highlight, byte-to-char offset mapping, theme-aware
  colors from SurfaceTokens.

- ClideSvgView: wraps jovial_svg with asset/string constructors
  and optional sizing.

Retrofitted: decision detail body renders as markdown instead of
raw text. Markdown viewer uses ClideMarkdown. Welcome logo uses
ClideSvgView.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 17:36:40 +02:00
jpmschweitzerandClaude Opus 4.6 5fd31247da re-contribute detail tabs with initialId on each selection
Both tickets and decisions extensions now uncontribute and
re-contribute the detail tab on every selection, passing the
selected ID via initialId. This guarantees the view loads
immediately — no first-click miss from the widget not being
built when the message fires.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 17:23:08 +02:00
jpmschweitzerandClaude Opus 4.6 2345125172 decision detail shows full markdown body via pql decisions read
New pql.decisions.read IPC verb returns the full markdown section
body. Decision detail view now shows: title card, full decision
text (rationale, cost, cross-references), and clickable ref cards
for cross-reference navigation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 17:19:15 +02:00