Apply the manifest-i18n foundation across every extension: each
CommandContribution (45, command-palette + menu) gains titleKey + i18nNamespace,
and each SettingsCategory/section/field/option gains its key — with the English
text added to the owning extension's catalog. The settings renderer
(settings_category_view + settings_modal) threads the category's i18nNamespace
down and resolves every label/help/option through it; new catalogs created for
view, cli-install, keybindings-ui. No en_US behaviour change — the command
palette, menus, and settings panel now localize from the catalog (D-21).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lets manifest labels (command-palette/menu titles, settings labels) localize,
not just displayed widget strings. Adds optional titleKey/i18nNamespace to
CommandContribution and labelKey/helpKey/titleKey + a category i18nNamespace to
the settings schema. The command palette and menu bar now resolve titles via a
shared localizedCommandTitle helper — and the palette's fuzzy search matches
the localized title too (PaletteController.titleResolver). No behaviour change
until the per-extension keys + catalog entries land (placeholder == English).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migrate the user-facing strings in menubar, output, search, deeplink, editor,
files, terminal, markdown, and vim to ClideSettings.i18n.string/.interpolated;
extend the existing catalogs and create the missing ones (deeplink, output,
search, markdown, menubar, vim). vim mode labels now resolve through
builtin.vim (default still `-- NORMAL --`, now localizable). A dock-status test
seeds builtin.output so the widget's own i18n lookups resolve instead of
warning into the logRing it counts. 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>
Lay the foundation for the schema-driven settings UI (epic T-444). A new
`settings.open` command (⌘`,`, plus a File-menu and command-palette entry)
opens a centered Settings modal over the dimmed app via the dialog router,
built from the modalSurface* tokens (D-7, no Material). The shell frames the
two regions later tickets fill in — the category rail (T-447) and the
scrolling carded panel (T-448) — and dismisses on ✕, Esc, or barrier tap.
With no category registered yet it shows its empty state, which is the
correct runtime state.
Flesh out the `builtin.settings-ui` stub (was 0.0.0-stub) into a real
extension; ship its en-US i18n catalog. Relabel the theme picker's
`theme.pick` command title from "Settings…" to "Theme…" so the two no
longer collide in the palette (the picker folds into the new panel's
Appearance category in T-452).
Tests: command + ⌘`,` binding registered, shell renders, Esc and close
both dismiss.
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 per-button hand-rolled LayerLink/OverlayEntry/barrier and the
bespoke MenuDropdown + MenuItemRow with the shared popover primitive (D-88).
A small _MenuOverlayAdapter bridges the single-open MenuBarController to each
button's ClideOverlayController, so one source of truth drives open/close,
hover-switch, Alt mnemonics, and Left/Right menu switching (onArrowLeft/Right).
menu_dropdown.dart and menu_item_row.dart are deleted (absorbed by ClideMenu).
Behaviour and a11y are unchanged; menu_bar_test + app_test stay green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A custom in-window menu bar in the hat (no native menu, D-7), built from
the command registry so it stays in sync and satisfies D-6 parity.
- Menu model + hybrid resolver (menu_model.dart): a curated File/View/Help
tree where a MenuAutoFill node sweeps in unplaced view.* commands; titles
+ keybindings come from the registry/keymap; unregistered or
enabledWhen-false items render disabled (greyed), never hidden.
- Widgets: MenuBar row in the hat (chrome tokens), anchored MenuDropdown
overlay (dropdown tokens), two-column MenuItemRow with inline keybinding.
- Full keyboard: Alt+mnemonic opens (hook in _RootShell._onKey), arrows
navigate, Enter activates, Esc closes, Left/Right switch menus.
- Commands: file.openFolder / file.newWindow / file.closeWorkspace /
help.about, registered by MenuBarExtension(services:). File logic lifted
out of the project switcher into FileActions (one source of truth; the
switcher now dispatches the commands). Ctrl+O / Ctrl+Shift+N are now real
keybindings in default.yaml.
- Help → About: version/commit/date/repo from build-info + the bundled
dependency licenses parsed from assets/licenses.yaml.
Edit/Selection menus are deferred to T-271/T-272 (need focused-surface
command routing).
Tests: resolver + controller + licenses parse (pure); menu-bar widget
(open/close/execute/disabled/Esc/arrow/Enter/Left-Right); FileActions +
Open dialog; app-level Alt+F, non-repo dialog, and closeWorkspace.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>