Non-modal operation-feedback toasts, bottom-right: a ClideToast card per
severity (success/warning/error/info), auto-dismiss (errors linger), queue
with a visible cap, slide+fade in, manual dismiss, live-region a11y.
ToastService is a MessageBus consumer — components raise a toast by publishing
to the 'toast' channel (publishToast helper), so emitters stay decoupled from
the UI. GitController's push/pull are the first emitters. ToastOverlay mounts
in the app-root Stack.
Also adds comprehensive GitController coverage: importing it for the toast
emitter test first pulled the whole file into the coverage denominator, so the
controller is now tested end to end (status/stage/commit/stash/push/pull).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
test / dart doc (lib API) (push) Failing after 28s
ClidePane.didChangeDependencies/didUpdateWidget run in the build phase and
called FocusTracker.setStatusWidget -> notifyListeners() synchronously,
rebuilding the focus-listening status-bar item mid-build — Flutter threw
"markNeedsBuild called during build" on every frame once a Claude pane was
focused. The convey now defers to a post-frame callback when mid-build
(re-checking focus then) and applies immediately otherwise. The T-150
widget tests missed this because no focus listener was in their tree;
added a regression test with PaneContextStatusItem present.
T-159.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the MessageBus-based pane-context slot with a focus-driven one.
Panes keep their status widget locally; the FocusTracker holds the
focused pane's widget (activeStatusWidget) and ClidePane conveys it to
the shared slot only while its contribution is focused, re-conveying on
change and clearing on blur. The status-bar item just renders
focus.activeStatusWidget, height-clamped and marquee-scrolled when it
overflows. Removes the publish/subscribe race the bus version had.
T-150.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Foundation for rendering the Claude conversation natively (T-132) with
the cross-widget select+copy the terminal gives today. Converts the raw
RichText in clide_markdown + clide_code_block to Text.rich, which
registers with a Flutter SelectionArea's selection machinery (raw
RichText does not). Adds a selectionBackground surface token
(globalFocus at ~40% alpha, matching the terminal's selection tint) via
tokens + resolver default; bundled palettes are untouched (D-69).
Text and code blocks now select across each other under a SelectionArea;
tables and tappable link-spans remain non-selectable islands for now.
The selection contrast pair is intentionally not added to the WCAG gate:
the tint is semi-transparent and the gate's neutral-grey compositor would
false-fail it (documented in contrast.dart); deferred to the -hc/-cb pass.
Co-Authored-By: Claude <noreply@anthropic.com>
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
test / dart doc (lib API) (push) Failing after 1m5s
Plug widgets into the keymap layer landed in T-117.
ClideTappable:
- Wrap in `Actions(ActivateIntent → onTap)` outside a `Focus` so
dispatch from the focused context walks up and hits the action.
- Add a focus ring via `tokens.globalFocus` (DecoratedBox foreground
overlay, transparent border when unfocused, no layout shift).
- Disabled (`onTap == null`) skips focus traversal and shows the
forbidden cursor.
ClidePalette:
- Register Actions for the four palette intents
(selectNext / selectPrev / accept / dismiss).
- Publish `palette.open` scope flag via `KeymapService.setScopeFlag`
so when-clauses can scope future bindings to "palette only".
- Highlight the selected row with `listItemSelectedBackground`;
scroll it into view on nav.
- `PaletteController` grows `selectedIndex` + `selectNext` /
`selectPrevious` / `acceptSelected`; index resets on open /
filter change.
Intents.dart drops the `ClideIntent` base — `ActivateIntent` and
`DismissIntent` come from Flutter; clide owns the palette and text-
scale and command-bridge subclasses. `parseIntentId('activate')` →
Flutter's class; same for dismiss. Widget code uses the canonical
Flutter Intent types where they fit.
App root grows a PaletteOpenIntent action that calls
`services.palette.open()`, completing the ctrl/cmd+shift+p path
end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add tests for `keybindings.dart` (KeyEvent → Keybinding mapping,
parse-error edges, resolver entries view), `toolchain_paths.dart`
(the Flutter-free `ToolchainView.resolved` static view), and several
`widgets/src/` primitives: tooltip hover/overlay, palette filter +
submit, multitab controller `copyWith` + size getters, and additional
markdown branches (h3–h6 headings, tables, strikethrough, default
block fallback, record-link tap).
Unfreezes the pre-push coverage floor that was held at 90 on
2026-05-14 by mistake and ratchets to 93. Tidies eight test files
that had accumulated unused imports flagged by `unnecessary_import`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single Flutter package at the repo root. All code, tests, assets,
and platform directories moved from app/ to root. Package renamed
from clide_app to clide — all imports rewritten. Merged pubspec
combines core (ffi) and app (flutter, yaml, xterm) dependencies.
Makefile simplified: no APP_PRESENT conditionals, no cd, no daemon
lifecycle. 317 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>