Migrate the user-facing strings in builtin.tickets, builtin.pql,
builtin.decisions, and builtin.problems (list/detail views, section headers,
filters, empty/loading/error states, type chips) to ClideSettings.i18n; extend
each extension's catalog. The detail/links tabs that had a bare title now wire
titleKey + i18nNamespace so the tab title resolves too. Context threaded into
the static label helpers. No en_US behaviour change (D-21).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migrate ClideTheme.of(context) reads across the widget, feature, and shell
layers to the unified ClideSettings.theme.of(context) facade (D-101), so
theme/i18n/fonts/settings share one widget-facing entry. The facade delegates
straight to ClideTheme, so behaviour is unchanged — goldens are unmoved.
The low-level theme provider keeps its direct ClideTheme.of: the facade is
built on it, and the two kernel sites (ClideTheme's own definition + the
panels drag-resize widget) stay direct to avoid a widgets→kernel import cycle.
Dead controller.dart/kernel.dart imports left by the sweep removed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce ClideSettings, a single widget-facing facade for the app's live
user preferences, namespaced by concern: ClideSettings.fonts.monoOf(context)
/ .fonts.uiOf(context), ClideSettings.theme.of(context),
ClideSettings.i18n.of(context). "Plumb once, use many."
Fonts are carried by a new root-provided ClideSettingsScope (resolved from the
font settings in root_shell, rebuilt on change); theme and i18n delegate to
their existing live providers (ClideTheme / the I18n service) so there's one
source of truth and their many consumers migrate incrementally rather than in
a big-bang. Reads outside a scope fall back to the bundled font defaults, so a
widget renders without a provider (isolated tests).
Migrate ~93 monospace-font call sites across 33 files from the clideMonoFamily
const to ClideSettings.fonts.monoOf(context) — pure refactor, identical family
when no override is set. 11 context-less helper sites (markdown static spans,
a few top-level/static builders) keep the const for now and are tracked in
T-472. Records D-101; updates the ui-design skill's font-family rule.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Raise the declared minimums in pubspec.yaml to what our deps already
require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist
0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is
the binding floor. Pin the exact build toolchain in .fvmrc (Flutter
3.44.1).
Moving to the Dart 3.9 language level switches `dart format` to the new
"tall" style and enables two new lints. This commit is the resulting
mechanical churn, isolated from any behaviour change:
- whole-tree `dart format` reformat (tall style)
- `dart fix` for unnecessary_underscores + use_null_aware_elements
No runtime behaviour change; `make test` green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the 49 hand-maintained named consts with one generated
label→codepoint map (phosphor_glyphs.g.dart, 1512 glyphs from the glyph
table via tool/gen_phosphor_glyphs.dart). Feature code now references
glyphs by their exact kebab-case name — PhosphorIcons.byName('folder') —
with no raw codepoints; this also lets a Lua extension name an icon
without crossing the FFI boundary with a codepoint.
byName is total: an unknown name degrades to the `placeholder` box so the
bug is visible (it's a real error), while phosphor_glyphs_test asserts
every byName('...') literal in lib/ resolves — recovering the typo check a
const gave. Migrated the 89 call sites. Adds EmptyIconPainter for an
intentional blank that still reserves the icon box; ClideFilterBox gains
showIcon to keep the slot aligned when blank.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sidebar/dock filter fields (the shared ClideFilterBox) had no CLI
peer — a one-way, UI-only affordance that broke D-6 parity. Add the
drive+observe verb `clide ui filter <address> [<text>]`, routed entirely
through the kernel MessageBus pub/sub so a box reacts to a published
message identically whether the trigger was a UI keystroke or the CLI —
keeping extensions first-class (no dispatcher→widget wiring).
- ClideFilterBox gains an `address`; when set it listens on `filter.set`
for its address and republishes its value on `filter.state`. Null
address keeps the box a kernel-free UI widget.
- FilterStateCache (new kernel service) caches the latest `filter.state`
per address — the bus has no retention, so this backs the observe-half.
- ui.filter: with text → publishes `filter.set` (drive); without →
reads the cache (observe). Honest toolError when there is no live UI.
- Address every box: decisions/tickets/files/git/output/problems panes,
the four search boxes, and the pql search/query/markdown inputs.
Addresses are the ids from `clide pane list` (e.g. decisions.panel).
settings.json: allow the `clide` CLI + relevant skills.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pql sidebar panel and the find-in-files tab were duplicate search
surfaces. Consolidate into one Search tab with a mode switch: Find
(content grep), Vault (pql ranked search), Query (PQL DSL), Markdown
(the synced markdown-file listing, keeping focus-highlight + live
refresh). SearchPanelView holds both FindInFilesController and
PqlController; the pql body + result rows move into a reusable
PqlSearchBody. The standalone builtin.pql sidebar tab is removed (one
fewer tab — eases the rail); the pql extension keeps the Backlinks
context panel. No D-79 conflict — grep vs ranked search remain distinct
backends, this is UI consolidation.
Adds the pql builtin's first widget/controller tests (it was untested,
so folding it into the tested Search panel required covering the
Vault/Query/Markdown modes + PqlController).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
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>
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>
Icons now declared on TabContribution via the icon field — the
hardcoded switch in _BottomRail is a fallback only. All sidebar
and context extensions set their Phosphor icon explicitly.
PanelRegistry supports setTabOrder(slot, List<String>) — tabs
render in the order specified by project.layout.sidebar.order /
context.order in settings.yaml. Falls back to registration order.
Priority field removed from all contributions.
Context tabs are static again — persist with their last content,
selection just loads new data and switches focus. No spawn/despawn.
Tickets sidebar priority set to -200 (leftmost, default open).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Context panel tabs are no longer static. Extensions spawn them
dynamically when relevant and despawn on deactivate:
- Tickets: detail tab spawns on builtin.tickets/selection
- Decisions: detail tab spawns on builtin.decisions/selection
- Backlinks: spawns when editor opens a file
- Markdown viewer: spawns when editor opens a .md file
- Graph: removed from static context (will be command-spawnable)
The icon rail only shows what's active — no empty tabs sitting
there waiting for content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DaemonBus (was EventBus): typed events for system/IPC layer.
MessageBus: channel-based pub/sub for UI/extension coordination.
Messages carry publisher (auto-stamped from extension ID),
channel (required), timestamp, and payload. Subscribe by
publisher, channel, or both — zero collision across extensions.
Extension context gains publish() and subscribe() convenience
methods that auto-stamp the extension's ID as publisher.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Decisions and tickets are now top-level sidebar sections with
their own extensions and filter boxes. The pql panel keeps only
Files and Query — its own surfaces. Removed loadDecisions,
loadTickets, _DecisionRow, _TicketColumn from pql controller
and view.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shared filter box with search icon, debounced input, and clear
button. Applied to all six left-panel panes for consistent
filtering: Files (flat path results when filtering), Git (filter
staged/unstaged/untracked by path), Decisions (by ID/title/domain),
Tickets (by ID/title/status), Problems (by source/message), and
pql Query (replaces custom _QueryInput).
Co-Authored-By: Claude Opus 4.6 (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>