refactor(settings): ClideSettings — one live-preferences facade (D-101)
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>
This commit is contained in:
@@ -36,8 +36,15 @@ These apply across every reference and every surface:
|
|||||||
- Never use `Material*` or `Cupertino*` widgets or color constants — clide
|
- Never use `Material*` or `Cupertino*` widgets or color constants — clide
|
||||||
is `WidgetsApp` only (D-7).
|
is `WidgetsApp` only (D-7).
|
||||||
- Use `ClideText` for themed text; never bare `Text` in production widgets.
|
- Use `ClideText` for themed text; never bare `Text` in production widgets.
|
||||||
- Typography: `clideFontMono` for code/paths/IDs, `clideFontCaption` for
|
- Typography sizes: `clideFontMono` for code/paths/IDs, `clideFontCaption` for
|
||||||
status/section headers, body inherits from `DefaultTextStyle`.
|
status/section headers, body inherits from `DefaultTextStyle`.
|
||||||
|
- Font *family* comes from the user-selectable facade, not a const: a
|
||||||
|
monospace surface uses `fontFamily: ClideSettings.fonts.monoOf(context)`
|
||||||
|
(and `fontFamilyFallback: clideMonoFamilyFallback`); the UI face is inherited
|
||||||
|
via the root `DefaultTextStyle`, or `ClideSettings.fonts.uiOf(context)` when a
|
||||||
|
widget must set it explicitly. `clideMonoFamily` / `clideUiFamily` are the
|
||||||
|
facade's defaults — don't read them directly in new widgets (D-101). Same
|
||||||
|
facade exposes `ClideSettings.theme.of(context)` and `.i18n.of(context)`.
|
||||||
|
|
||||||
## Conversation-panel cards (T-305)
|
## Conversation-panel cards (T-305)
|
||||||
|
|
||||||
|
|||||||
@@ -297,3 +297,4 @@ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_
|
|||||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FDA10DRQE3SED10JAW2CZDCR', 'T-469', '2026-06-17 09:57:53.221', '2026-06-17 09:57:53.221', NULL, '2a72e3e647afc1b5b235ab1a97d298f8', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FDA10DRQE3SED10JAW2CZDCR', 'T-469', '2026-06-17 09:57:53.221', '2026-06-17 09:57:53.221', NULL, '2a72e3e647afc1b5b235ab1a97d298f8', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FDB1VFADV4QK1YG29T658T8C', 'T-470', '2026-06-17 12:21:23.411', '2026-06-17 12:21:23.411', NULL, '0f95e5a1faa7a7e902c765f449cceb15', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FDB1VFADV4QK1YG29T658T8C', 'T-470', '2026-06-17 12:21:23.411', '2026-06-17 12:21:23.411', NULL, '0f95e5a1faa7a7e902c765f449cceb15', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FDDJ17C3GZWNE98NRVXP189C', 'T-471', '2026-06-17 18:11:42.049', '2026-06-17 18:11:42.049', NULL, '10a9a957d647a63ce69dc4b6b564f6aa', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FDDJ17C3GZWNE98NRVXP189C', 'T-471', '2026-06-17 18:11:42.049', '2026-06-17 18:11:42.049', NULL, '10a9a957d647a63ce69dc4b6b564f6aa', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||||
|
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FDDTCGG5Z0KNQKF89W1VZSQ8', 'T-472', '2026-06-17 18:48:11.649', '2026-06-17 18:48:11.649', NULL, '097120cff851cb2dac9f54937f4c7b17', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||||
|
|||||||
@@ -7237,3 +7237,5 @@ INSERT INTO tickets (record_id, type, parent_record_id, title, description, stat
|
|||||||
**Files:** `pubspec.yaml` (fonts:), `assets/fonts/inter/`, `assets/licenses.yaml`, `lib/widgets/src/typography.dart`, `lib/src/shell/root_shell.dart` (DefaultTextStyle), mono consumers (editor / terminal / markdown / code-block), the Appearance settings category (T-452) + schema registration.
|
**Files:** `pubspec.yaml` (fonts:), `assets/fonts/inter/`, `assets/licenses.yaml`, `lib/widgets/src/typography.dart`, `lib/src/shell/root_shell.dart` (DefaultTextStyle), mono consumers (editor / terminal / markdown / code-block), the Appearance settings category (T-452) + schema registration.
|
||||||
|
|
||||||
**Related:** T-444 (epic), T-452 (Appearance category — where these render), T-448 (field renderer), T-449 (scope), ui-design `theme.md` (typography section: clideUiFamily / clideMonoFamily / clideFont* sizes), D-42 (bundled-dep documentation), D-44 (theming).', 'done', 'medium', NULL, NULL, NULL, '2026-06-17 07:39:25.019', '2026-06-17 18:14:38.272', NULL, '138f95c597eebc2db37c9e0c4bc49c91', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
**Related:** T-444 (epic), T-452 (Appearance category — where these render), T-448 (field renderer), T-449 (scope), ui-design `theme.md` (typography section: clideUiFamily / clideMonoFamily / clideFont* sizes), D-42 (bundled-dep documentation), D-44 (theming).', 'done', 'medium', NULL, NULL, NULL, '2026-06-17 07:39:25.019', '2026-06-17 18:14:38.272', NULL, '138f95c597eebc2db37c9e0c4bc49c91', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||||
|
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FDDJ17C3GZWNE98NRVXP189C', 'task', '06FD07ECS3GK2V7Z2WYYPJHJYC', 'Settings → Appearance: monospace font picker (needs context-driven mono family)', 'Follow-up to T-460. T-460 shipped the UI-font picker (live via the root DefaultTextStyle, single seam) + bundled Inter as default. The MONOSPACE picker is deferred because clideMonoFamily is referenced as a const in ~38 sites (terminal, diff, code blocks, ClideText fontFamily) — not inherited. To make the mono picker apply live, route the mono family through context (a small FontScope InheritedWidget or the theme) read by those sites, then add a ''Monospace font'' select in Appearance writing app.mono.font (kMonoFontSettingKey already defined in typography.dart). Default JetBrains Mono.', 'in_progress', 'medium', NULL, NULL, NULL, '2026-06-17 18:11:42.049', '2026-06-17 18:18:13.607', NULL, 'c401f1c31a8bd9f8cb252f718696d07e', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||||
|
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FDDTCGG5Z0KNQKF89W1VZSQ8', 'task', '06FD07ECS3GK2V7Z2WYYPJHJYC', 'Migrate 11 context-less mono-font sites onto ClideSettings.fonts (thread family through helpers)', NULL, 'backlog', 'medium', NULL, NULL, NULL, '2026-06-17 18:48:11.649', '2026-06-17 18:48:11.649', NULL, '267cb357e1a5532db0a4bb0f9312ccf2', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ You might also want, project-permitting:
|
|||||||
- [D-98: Remote-tool contract + connect preflight](decisions/architecture.md#d-98-remote-tool-contract--connect-preflight) — _architecture_
|
- [D-98: Remote-tool contract + connect preflight](decisions/architecture.md#d-98-remote-tool-contract--connect-preflight) — _architecture_
|
||||||
- [D-99: Remote session identity keyed on (host, workspace)](decisions/architecture.md#d-99-remote-session-identity-keyed-on-host-workspace) — _architecture_
|
- [D-99: Remote session identity keyed on (host, workspace)](decisions/architecture.md#d-99-remote-session-identity-keyed-on-host-workspace) — _architecture_
|
||||||
- [D-100: Fence `dart:ffi` behind conditional imports + web stubs to keep the web/WASM target compiling](decisions/tooling.md#d-100-fence-dartffi-behind-conditional-imports--web-stubs-to-keep-the-webwasm-target-compiling) — _tooling_
|
- [D-100: Fence `dart:ffi` behind conditional imports + web stubs to keep the web/WASM target compiling](decisions/tooling.md#d-100-fence-dartffi-behind-conditional-imports--web-stubs-to-keep-the-webwasm-target-compiling) — _tooling_
|
||||||
|
- [D-101: ClideSettings — one live-preferences access facade](decisions/architecture.md#d-101-clidesettings--one-live-preferences-access-facade) — _architecture_
|
||||||
|
|
||||||
## Open questions
|
## Open questions
|
||||||
|
|
||||||
|
|||||||
@@ -534,4 +534,12 @@ Core, rendering, IPC, kernel, panel manager.
|
|||||||
- **Cross-reference:** [D-41](#d-41-claude-panes-one-primary-per-repo-tmux-backed), [D-70](#d-70-ipc-socket-path-is-per-workspace-deterministic), [D-77](#d-77-drive-claude-via-the-stream-json-control-protocol-teams-become-a-clide-owned-coordination-layer), [D-93](#d-93-clide-writes-no-directories-of-its-own-into-the-workspace), [D-96](#d-96-remote-execution-footprint--no-install-ssh-exec), [D-97](#d-97-ssh-workspace-uri--system-ssh-auth). Implemented across T-332 (identity carrier) and T-333 (session re-key).
|
- **Cross-reference:** [D-41](#d-41-claude-panes-one-primary-per-repo-tmux-backed), [D-70](#d-70-ipc-socket-path-is-per-workspace-deterministic), [D-77](#d-77-drive-claude-via-the-stream-json-control-protocol-teams-become-a-clide-owned-coordination-layer), [D-93](#d-93-clide-writes-no-directories-of-its-own-into-the-workspace), [D-96](#d-96-remote-execution-footprint--no-install-ssh-exec), [D-97](#d-97-ssh-workspace-uri--system-ssh-auth). Implemented across T-332 (identity carrier) and T-333 (session re-key).
|
||||||
- **Raised by:** 2026-06-12 — T-330 spike artifacts ("session identity keyed on (host, repo) amending D-41/D-77").
|
- **Raised by:** 2026-06-12 — T-330 spike artifacts ("session identity keyed on (host, repo) amending D-41/D-77").
|
||||||
|
|
||||||
|
### D-101: ClideSettings — one live-preferences access facade
|
||||||
|
- **Date:** 2026-06-17
|
||||||
|
- **Decision:** Live, user-selectable preferences are read through a single widget-facing facade, `ClideSettings`, namespaced by concern: `ClideSettings.fonts.monoOf(context)` / `.fonts.uiOf(context)`, `ClideSettings.theme.of(context)`, `ClideSettings.i18n.of(context)`. Values originate in the kernel `SettingsStore`; the app root (`root_shell`) resolves them and provides a `ClideSettingsScope` InheritedWidget (carrying the font families), rebuilding it on a settings change so dependents re-read live. Theme and i18n **delegate** to their existing live providers (`ClideTheme` / the `I18n` service) rather than being duplicated — one source of truth. Reads outside a scope fall back to the bundled font defaults, so a widget renders without a provider (isolated tests). "Plumb once, use many."
|
||||||
|
- **Rationale:** Before this, each live setting had its own ad-hoc read path — theme via `ClideTheme.of`, i18n via `ClideKernel.of(context).i18n`, fonts as a compile-time `const` that couldn't change at runtime at all. A new preference meant inventing another path. One facade gives every current and future preference a uniform, discoverable read site and a single root resolution point — without a mutable global (the shortcut rejected during the T-471 font-flow design) and without a big-bang rewrite of the established theme/i18n providers (they delegate, so their consumers migrate incrementally).
|
||||||
|
- **Cost:** Two read paths coexist during migration — `ClideTheme.of` / `i18n.string` still work (the facade delegates to them), so their hundreds of call sites move to `ClideSettings.theme` / `.i18n` incrementally rather than at once. The font consts (`clideMonoFamily` / `clideUiFamily`) remain as the facade's defaults; ~11 context-less helper sites still read the const directly, migrated in a follow-up (T-472). The facade lives in `widgets` and reaches into `kernel` for the theme/i18n delegates (a dependency already present).
|
||||||
|
- **Cross-reference:** Fonts landed it: T-460 (Inter default + UI picker) and T-471 (mono picker) migrated ~93 sites onto `ClideSettings.fonts`. Consumer migration of theme + i18n onto the facade, and the context-less font stragglers (T-472), are staged follow-ups. Values live in the kernel `SettingsStore`.
|
||||||
|
- **Raised by:** 2026-06-17 — user, during T-471 font-flow design: "I do see reason in nesting them all in one settings object that dynamically loads so we can extend it in the future… plumb once, use many."
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -37,10 +37,13 @@ class ClaudeBanner extends StatelessWidget {
|
|||||||
const SizedBox(height: 18),
|
const SizedBox(height: 18),
|
||||||
ClideText('Claude', fontSize: clideFontDialogTitle, color: claudeAccent, fontWeight: FontWeight.w500),
|
ClideText('Claude', fontSize: clideFontDialogTitle, color: claudeAccent, fontWeight: FontWeight.w500),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
ClideText(role, fontSize: clideFontSmall, muted: true, fontFamily: clideMonoFamily),
|
ClideText(role, fontSize: clideFontSmall, muted: true, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
if (ws != null) ClideText(ws, fontSize: clideFontCaption, muted: true),
|
if (ws != null) ClideText(ws, fontSize: clideFontCaption, muted: true),
|
||||||
if (statusLine != null) ...[const SizedBox(height: 2), ClideText(statusLine!, fontSize: clideFontSmall, muted: true, fontFamily: clideMonoFamily)],
|
if (statusLine != null) ...[
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
ClideText(statusLine!, fontSize: clideFontSmall, muted: true, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
|
],
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
const ClideText('Warming up — your conversation will appear here.', fontSize: clideFontSmall, muted: true),
|
const ClideText('Warming up — your conversation will appear here.', fontSize: clideFontSmall, muted: true),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -132,12 +132,13 @@ class _ClaudePaneState extends State<ClaudePane> {
|
|||||||
final seg = statusSegmentsAroundMode(_status);
|
final seg = statusSegmentsAroundMode(_status);
|
||||||
final mode = _status.permissionMode;
|
final mode = _status.permissionMode;
|
||||||
|
|
||||||
Widget text(String t) => ClideText(t, fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: tokens.statusBarForeground, maxLines: 1);
|
Widget text(String t) =>
|
||||||
|
ClideText(t, fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.statusBarForeground, maxLines: 1);
|
||||||
|
|
||||||
final children = <Widget>[];
|
final children = <Widget>[];
|
||||||
void add(Widget w) {
|
void add(Widget w) {
|
||||||
if (children.isNotEmpty) {
|
if (children.isNotEmpty) {
|
||||||
children.add(ClideText(' · ', fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: tokens.globalTextMuted, maxLines: 1));
|
children.add(ClideText(' · ', fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.globalTextMuted, maxLines: 1));
|
||||||
}
|
}
|
||||||
children.add(w);
|
children.add(w);
|
||||||
}
|
}
|
||||||
@@ -857,7 +858,13 @@ class _ModeBadge extends StatelessWidget {
|
|||||||
return Semantics(
|
return Semantics(
|
||||||
label: 'permission mode: ${permissionModeLabel(mode)}',
|
label: 'permission mode: ${permissionModeLabel(mode)}',
|
||||||
excludeSemantics: true,
|
excludeSemantics: true,
|
||||||
child: ClideText(permissionModeLabel(mode), fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: permissionModeColor(mode, tokens), maxLines: 1),
|
child: ClideText(
|
||||||
|
permissionModeLabel(mode),
|
||||||
|
fontSize: clideFontSmall,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
color: permissionModeColor(mode, tokens),
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,13 +241,13 @@ class _ConversationCardState extends State<ConversationCard> {
|
|||||||
if (widget.collapsible) _caret(tokens),
|
if (widget.collapsible) _caret(tokens),
|
||||||
// Header label size matches ClideCollapserCard (clideFontCaption) so
|
// Header label size matches ClideCollapserCard (clideFontCaption) so
|
||||||
// neighbouring cards in the conversation stream align (T-344).
|
// neighbouring cards in the conversation stream align (T-344).
|
||||||
ClideText(widget.label, fontSize: clideFontCaption, color: widget.accent, fontFamily: clideMonoFamily),
|
ClideText(widget.label, fontSize: clideFontCaption, color: widget.accent, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
// While collapsed, show a one-line gist next to the label so the card
|
// While collapsed, show a one-line gist next to the label so the card
|
||||||
// still says what it holds.
|
// still says what it holds.
|
||||||
if (_collapsed && summary != null) ...[
|
if (_collapsed && summary != null) ...[
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ClideText(summary, fontSize: clideFontMeta, color: tokens.globalTextMuted, fontFamily: clideMonoFamily, maxLines: 1),
|
child: ClideText(summary, fontSize: clideFontMeta, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context), maxLines: 1),
|
||||||
),
|
),
|
||||||
] else
|
] else
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
@@ -322,7 +322,7 @@ class _ConversationCardState extends State<ConversationCard> {
|
|||||||
padding: const EdgeInsets.only(top: 8, bottom: 4),
|
padding: const EdgeInsets.only(top: 8, bottom: 4),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
ClideText(label, fontSize: clideFontMeta, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
ClideText(label, fontSize: clideFontMeta, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(child: Container(height: 1, color: tokens.panelBorder)),
|
Expanded(child: Container(height: 1, color: tokens.panelBorder)),
|
||||||
],
|
],
|
||||||
@@ -349,7 +349,7 @@ class _ConversationCardState extends State<ConversationCard> {
|
|||||||
onTap: items[i].onTap,
|
onTap: items[i].onTap,
|
||||||
builder: (_, hovered, pressed) => Padding(
|
builder: (_, hovered, pressed) => Padding(
|
||||||
padding: const EdgeInsets.only(left: 10),
|
padding: const EdgeInsets.only(left: 10),
|
||||||
child: ClideText(items[i].label, fontSize: clideFontMeta, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
child: ClideText(items[i].label, fontSize: clideFontMeta, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -147,9 +147,14 @@ class _ModelPickerCardState extends State<ModelPickerCard> {
|
|||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
ClideText(widget.title, fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: tokens.statusInfo),
|
ClideText(widget.title, fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.statusInfo),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
ClideText('↑↓ · 1-${widget.models.length} · Enter · Esc', fontSize: clideFontMeta, fontFamily: clideMonoFamily, color: tokens.globalTextMuted),
|
ClideText(
|
||||||
|
'↑↓ · 1-${widget.models.length} · Enter · Esc',
|
||||||
|
fontSize: clideFontMeta,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
color: tokens.globalTextMuted,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class _ToolPromptCardState extends State<ToolPromptCard> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ClideText(label, fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: accent),
|
ClideText(label, fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context), color: accent),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
...children,
|
...children,
|
||||||
],
|
],
|
||||||
@@ -362,14 +362,21 @@ class _ToolPromptCardState extends State<ToolPromptCard> {
|
|||||||
border: Border.all(color: tokens.statusInfo),
|
border: Border.all(color: tokens.statusInfo),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: ClideText(text, fontSize: clideFontMeta, fontFamily: clideMonoFamily, color: tokens.globalForeground),
|
child: ClideText(text, fontSize: clideFontMeta, fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.globalForeground),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
chips.add(ClideText(text, fontSize: clideFontMeta, fontFamily: clideMonoFamily, color: done ? tokens.statusSuccess : tokens.globalTextMuted));
|
chips.add(
|
||||||
|
ClideText(
|
||||||
|
text,
|
||||||
|
fontSize: clideFontMeta,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
color: done ? tokens.statusSuccess : tokens.globalTextMuted,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chips.add(ClideText('Review', fontSize: clideFontMeta, fontFamily: clideMonoFamily, color: tokens.globalTextMuted));
|
chips.add(ClideText('Review', fontSize: clideFontMeta, fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.globalTextMuted));
|
||||||
chips.add(ClideText('›', color: tokens.globalTextMuted, fontSize: 15));
|
chips.add(ClideText('›', color: tokens.globalTextMuted, fontSize: 15));
|
||||||
return Wrap(spacing: 10, runSpacing: 6, crossAxisAlignment: WrapCrossAlignment.center, children: chips);
|
return Wrap(spacing: 10, runSpacing: 6, crossAxisAlignment: WrapCrossAlignment.center, children: chips);
|
||||||
}
|
}
|
||||||
@@ -399,7 +406,8 @@ class _ToolPromptCardState extends State<ToolPromptCard> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (q.header.isNotEmpty) ClideText(q.header.toUpperCase(), fontSize: clideFontMeta, fontFamily: clideMonoFamily, color: tokens.globalTextMuted),
|
if (q.header.isNotEmpty)
|
||||||
|
ClideText(q.header.toUpperCase(), fontSize: clideFontMeta, fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.globalTextMuted),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 2, bottom: 6),
|
padding: const EdgeInsets.only(top: 2, bottom: 6),
|
||||||
child: ClideText(q.question, color: tokens.globalForeground),
|
child: ClideText(q.question, color: tokens.globalForeground),
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ class _TeammateTile extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Container(width: 3, height: 13, color: accent),
|
Container(width: 3, height: 13, color: accent),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
ClideText(member.name, fontSize: clideFontSmall, color: accent, fontFamily: clideMonoFamily),
|
ClideText(member.name, fontSize: clideFontSmall, color: accent, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ClideText(
|
child: ClideText(
|
||||||
|
|||||||
@@ -148,19 +148,22 @@ class _DecisionDetailViewState extends State<DecisionDetailView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ClideText(id, fontSize: clideFontSmall, color: typeColor, fontFamily: clideMonoFamily),
|
ClideText(id, fontSize: clideFontSmall, color: typeColor, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (domain != null)
|
if (domain != null)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
decoration: BoxDecoration(color: tokens.panelBorder, borderRadius: BorderRadius.circular(3)),
|
decoration: BoxDecoration(color: tokens.panelBorder, borderRadius: BorderRadius.circular(3)),
|
||||||
child: ClideText(domain, fontSize: clideFontBadge, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
child: ClideText(domain, fontSize: clideFontBadge, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
ClideText(title, fontSize: 15, fontWeight: FontWeight.w500),
|
ClideText(title, fontSize: 15, fontWeight: FontWeight.w500),
|
||||||
if (date != null) ...[const SizedBox(height: 6), ClideText(date, muted: true, fontSize: clideFontSmall, fontFamily: clideMonoFamily)],
|
if (date != null) ...[
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
ClideText(date, muted: true, fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
|
],
|
||||||
if (status != null && status != 'active') ...[const SizedBox(height: 8), _StatusBadge(status: status, tokens: tokens)],
|
if (status != null && status != 'active') ...[const SizedBox(height: 8), _StatusBadge(status: status, tokens: tokens)],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -168,7 +171,7 @@ class _DecisionDetailViewState extends State<DecisionDetailView> {
|
|||||||
if (body != null && body.isNotEmpty) ...[const SizedBox(height: 12), ClideMarkdown(body, onRecordTap: (id) => _navigateToRecord(context, id))],
|
if (body != null && body.isNotEmpty) ...[const SizedBox(height: 12), ClideMarkdown(body, onRecordTap: (id) => _navigateToRecord(context, id))],
|
||||||
if (refs.isNotEmpty) ...[
|
if (refs.isNotEmpty) ...[
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
ClideText('CROSS-REFERENCES', fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: clideMonoFamily),
|
ClideText('CROSS-REFERENCES', fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
for (final ref in refs) _RefCard(ref: ref, tokens: tokens),
|
for (final ref in refs) _RefCard(ref: ref, tokens: tokens),
|
||||||
],
|
],
|
||||||
@@ -201,7 +204,7 @@ class _RefCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
ClideText(targetId, fontSize: clideFontSmall, color: tokens.globalFocus, fontFamily: clideMonoFamily),
|
ClideText(targetId, fontSize: clideFontSmall, color: tokens.globalFocus, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ClideText(refType, fontSize: clideFontSmall, color: tokens.globalTextMuted),
|
ClideText(refType, fontSize: clideFontSmall, color: tokens.globalTextMuted),
|
||||||
],
|
],
|
||||||
@@ -227,7 +230,7 @@ class _StatusBadge extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
decoration: BoxDecoration(color: color.withAlpha(0x30), borderRadius: BorderRadius.circular(3)),
|
decoration: BoxDecoration(color: color.withAlpha(0x30), borderRadius: BorderRadius.circular(3)),
|
||||||
child: ClideText(status.toUpperCase(), fontSize: clideFontBadge, color: color, fontFamily: clideMonoFamily),
|
child: ClideText(status.toUpperCase(), fontSize: clideFontBadge, color: color, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,9 +309,10 @@ class _DecisionCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
ClideText(entry.id, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
ClideText(entry.id, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (entry.domain != null) ClideText(entry.domain!, fontSize: clideFontBadge, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
if (entry.domain != null)
|
||||||
|
ClideText(entry.domain!, fontSize: clideFontBadge, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
@@ -321,7 +322,7 @@ class _DecisionCard extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
decoration: BoxDecoration(color: tokens.statusSuccess.withAlpha(0x30), borderRadius: BorderRadius.circular(3)),
|
decoration: BoxDecoration(color: tokens.statusSuccess.withAlpha(0x30), borderRadius: BorderRadius.circular(3)),
|
||||||
child: ClideText('resolved', fontSize: clideFontBadge, color: tokens.statusSuccess, fontFamily: clideMonoFamily),
|
child: ClideText('resolved', fontSize: clideFontBadge, color: tokens.statusSuccess, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class _DeepLinkConfirmDialog extends StatelessWidget {
|
|||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
ClideText('A clide:// link from outside the app is asking to:', muted: true, fontSize: clideFontSmall),
|
ClideText('A clide:// link from outside the app is asking to:', muted: true, fontSize: clideFontSmall),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
ClideText(action.describe, fontFamily: clideMonoFamily, fontSize: clideFontSmall, color: t.globalForeground),
|
ClideText(action.describe, fontFamily: ClideSettings.fonts.monoOf(context), fontSize: clideFontSmall, color: t.globalForeground),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
ClideText('Only allow this if you trust where the link came from.', fontSize: clideFontMeta, color: t.statusWarning),
|
ClideText('Only allow this if you trust where the link came from.', fontSize: clideFontMeta, color: t.statusWarning),
|
||||||
const SizedBox(height: 14),
|
const SizedBox(height: 14),
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ class _HunkView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),
|
||||||
child: ClideText(header, fontSize: clideFontMono, muted: true, fontFamily: clideMonoFamily),
|
child: ClideText(header, fontSize: clideFontMono, muted: true, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
),
|
),
|
||||||
for (final lineObj in lines) _DiffLineRow(line: (lineObj as Map).cast<String, Object?>()),
|
for (final lineObj in lines) _DiffLineRow(line: (lineObj as Map).cast<String, Object?>()),
|
||||||
],
|
],
|
||||||
@@ -294,7 +294,7 @@ class _DiffLineRow extends StatelessWidget {
|
|||||||
oldLineNo != null ? '${oldLineNo.toInt()}' : '',
|
oldLineNo != null ? '${oldLineNo.toInt()}' : '',
|
||||||
fontSize: clideFontMono,
|
fontSize: clideFontMono,
|
||||||
muted: true,
|
muted: true,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -305,15 +305,22 @@ class _DiffLineRow extends StatelessWidget {
|
|||||||
newLineNo != null ? '${newLineNo.toInt()}' : '',
|
newLineNo != null ? '${newLineNo.toInt()}' : '',
|
||||||
fontSize: clideFontMono,
|
fontSize: clideFontMono,
|
||||||
muted: true,
|
muted: true,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
ClideText(prefix, fontSize: clideFontMono, color: fg, fontFamily: clideMonoFamily),
|
ClideText(prefix, fontSize: clideFontMono, color: fg, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ClideText(text, fontSize: clideFontMono, color: fg, fontFamily: clideMonoFamily, maxLines: 1, overflow: TextOverflow.clip),
|
child: ClideText(
|
||||||
|
text,
|
||||||
|
fontSize: clideFontMono,
|
||||||
|
color: fg,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.clip,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -371,7 +371,12 @@ class _TextBody extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final style = TextStyle(color: foreground, fontSize: clideFontMono, fontFamily: clideMonoFamily, fontFamilyFallback: clideMonoFamilyFallback);
|
final style = TextStyle(
|
||||||
|
color: foreground,
|
||||||
|
fontSize: clideFontMono,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
fontFamilyFallback: clideMonoFamilyFallback,
|
||||||
|
);
|
||||||
final editable = EditableText(
|
final editable = EditableText(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
focusNode: focus,
|
focusNode: focus,
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class ExtensionsNotice extends StatelessWidget {
|
|||||||
i.string('notice.tracked', namespace: ns, placeholder: 'Tracked in T-8 (Tier 6) · D-16'),
|
i.string('notice.tracked', namespace: ns, placeholder: 'Tracked in T-8 (Tier 6) · D-16'),
|
||||||
color: tokens.globalTextMuted,
|
color: tokens.globalTextMuted,
|
||||||
fontSize: clideFontCaption,
|
fontSize: clideFontCaption,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -164,7 +164,12 @@ class _BranchPickerState extends State<_BranchPicker> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ClideText('Switch branch', fontSize: clideFontCaption, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
child: ClideText(
|
||||||
|
'Switch branch',
|
||||||
|
fontSize: clideFontCaption,
|
||||||
|
color: tokens.globalTextMuted,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => widget.onDismiss(),
|
onTap: () => widget.onDismiss(),
|
||||||
@@ -229,7 +234,7 @@ class _BranchRow extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: ClideText(
|
child: ClideText(
|
||||||
name,
|
name,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
fontSize: clideFontMono,
|
fontSize: clideFontMono,
|
||||||
color: current ? tokens.globalForeground : tokens.listItemForeground,
|
color: current ? tokens.globalForeground : tokens.listItemForeground,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class _Kv extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(width: 90, child: ClideText(label, fontSize: 13, color: tokens.globalTextMuted)),
|
SizedBox(width: 90, child: ClideText(label, fontSize: 13, color: tokens.globalTextMuted)),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ClideText(value, fontSize: 13, fontFamily: clideMonoFamily, maxLines: 1, overflow: TextOverflow.ellipsis),
|
child: ClideText(value, fontSize: 13, fontFamily: ClideSettings.fonts.monoOf(context), maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -106,7 +106,7 @@ class _Licenses extends StatelessWidget {
|
|||||||
child: ClideText(
|
child: ClideText(
|
||||||
'${d.name} ${d.version} · ${d.license}',
|
'${d.name} ${d.version} · ${d.license}',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
color: tokens.globalTextMuted,
|
color: tokens.globalTextMuted,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
|||||||
@@ -137,7 +137,12 @@ class _OpenFolderDialogState extends State<OpenFolderDialog> {
|
|||||||
child: EditableText(
|
child: EditableText(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
focusNode: _focus,
|
focusNode: _focus,
|
||||||
style: TextStyle(color: tokens.globalForeground, fontSize: 14, fontFamily: clideMonoFamily, fontFamilyFallback: clideMonoFamilyFallback),
|
style: TextStyle(
|
||||||
|
color: tokens.globalForeground,
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
fontFamilyFallback: clideMonoFamilyFallback,
|
||||||
|
),
|
||||||
cursorColor: tokens.globalForeground,
|
cursorColor: tokens.globalForeground,
|
||||||
backgroundCursorColor: tokens.globalTextMuted,
|
backgroundCursorColor: tokens.globalTextMuted,
|
||||||
onSubmitted: (_) => unawaited(_submit()),
|
onSubmitted: (_) => unawaited(_submit()),
|
||||||
|
|||||||
@@ -162,7 +162,12 @@ class _TopMenuButtonState extends State<_TopMenuButton> {
|
|||||||
label: node.title,
|
label: node.title,
|
||||||
enabled: node.enabled,
|
enabled: node.enabled,
|
||||||
trailing: node.keybinding != null
|
trailing: node.keybinding != null
|
||||||
? ClideText(node.keybinding!, fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: ClideTheme.of(ctx).surface.globalTextMuted)
|
? ClideText(
|
||||||
|
node.keybinding!,
|
||||||
|
fontSize: clideFontSmall,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
color: ClideTheme.of(ctx).surface.globalTextMuted,
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
onSelect: () => unawaited(widget.kernel.commands.execute(node.commandId)),
|
onSelect: () => unawaited(widget.kernel.commands.execute(node.commandId)),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -217,11 +217,11 @@ class _LogRow extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ClideText('$hh:$mm:$ss', fontSize: clideFontMono, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
ClideText('$hh:$mm:$ss', fontSize: clideFontMono, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 48,
|
width: 48,
|
||||||
child: ClideText(record.level.name.toUpperCase(), fontSize: clideFontMono, color: fg, fontFamily: clideMonoFamily),
|
child: ClideText(record.level.name.toUpperCase(), fontSize: clideFontMono, color: fg, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@@ -230,14 +230,14 @@ class _LogRow extends StatelessWidget {
|
|||||||
record.source,
|
record.source,
|
||||||
fontSize: clideFontMono,
|
fontSize: clideFontMono,
|
||||||
color: tokens.globalTextMuted,
|
color: tokens.globalTextMuted,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.clip,
|
overflow: TextOverflow.clip,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ClideText(record.message, fontSize: clideFontMono, color: fg, fontFamily: clideMonoFamily),
|
child: ClideText(record.message, fontSize: clideFontMono, color: fg, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ class _ScoreBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
ClideText('${(score * 100).round()}%', fontSize: clideFontBadge, muted: true, fontFamily: clideMonoFamily),
|
ClideText('${(score * 100).round()}%', fontSize: clideFontBadge, muted: true, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class _ProblemRow extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
ClideText(problem.source, fontSize: clideFontMono, color: tokens.statusWarning, fontFamily: clideMonoFamily),
|
ClideText(problem.source, fontSize: clideFontMono, color: tokens.statusWarning, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
Expanded(child: ClideText(problem.message, color: tokens.sidebarForeground, maxLines: 2)),
|
Expanded(child: ClideText(problem.message, color: tokens.sidebarForeground, maxLines: 2)),
|
||||||
],
|
],
|
||||||
@@ -121,7 +121,7 @@ class _ProblemRow extends StatelessWidget {
|
|||||||
if (problem.hint != null)
|
if (problem.hint != null)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 44, top: 2),
|
padding: const EdgeInsets.only(left: 44, top: 2),
|
||||||
child: ClideText(problem.hint!, fontSize: clideFontMono, muted: true, fontFamily: clideMonoFamily),
|
child: ClideText(problem.hint!, fontSize: clideFontMono, muted: true, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ class _Toggle extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: ClideText(
|
child: ClideText(
|
||||||
label,
|
label,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
fontSize: clideFontCaption,
|
fontSize: clideFontCaption,
|
||||||
color: active ? tokens.listItemSelectedForeground : tokens.sidebarForeground,
|
color: active ? tokens.listItemSelectedForeground : tokens.sidebarForeground,
|
||||||
),
|
),
|
||||||
@@ -344,7 +344,7 @@ class _MatchRow extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 36,
|
width: 36,
|
||||||
child: ClideText('${match.line}', fontSize: clideFontCaption, fontFamily: clideMonoFamily, color: tokens.globalTextMuted),
|
child: ClideText('${match.line}', fontSize: clideFontCaption, fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.globalTextMuted),
|
||||||
),
|
),
|
||||||
Expanded(child: replacement.isEmpty ? _highlighted() : _preview()),
|
Expanded(child: replacement.isEmpty ? _highlighted() : _preview()),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -128,7 +128,12 @@ class _SectionCard extends StatelessWidget {
|
|||||||
if (section.label.isNotEmpty)
|
if (section.label.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 2, bottom: 6),
|
padding: const EdgeInsets.only(left: 2, bottom: 6),
|
||||||
child: ClideText(section.label.toUpperCase(), fontSize: clideFontCaption, color: tokens.sidebarSectionHeader, fontFamily: clideMonoFamily),
|
child: ClideText(
|
||||||
|
section.label.toUpperCase(),
|
||||||
|
fontSize: clideFontCaption,
|
||||||
|
color: tokens.sidebarSectionHeader,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
ClideSurface(
|
ClideSurface(
|
||||||
// Card surface (surface.md): panelHeader resolves to the `surface`
|
// Card surface (surface.md): panelHeader resolves to the `surface`
|
||||||
@@ -440,7 +445,7 @@ class _EditControlState extends State<_EditControl> {
|
|||||||
child: EditableText(
|
child: EditableText(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
focusNode: _focus,
|
focusNode: _focus,
|
||||||
style: TextStyle(fontFamily: clideMonoFamily, fontSize: clideFontMono, color: tokens.globalForeground),
|
style: TextStyle(fontFamily: ClideSettings.fonts.monoOf(context), fontSize: clideFontMono, color: tokens.globalForeground),
|
||||||
cursorColor: tokens.globalFocus,
|
cursorColor: tokens.globalFocus,
|
||||||
backgroundCursorColor: tokens.globalTextMuted,
|
backgroundCursorColor: tokens.globalTextMuted,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ class _CategoryRail extends StatelessWidget {
|
|||||||
i.string('rail.header', namespace: SettingsModal.ns, placeholder: 'Categories'),
|
i.string('rail.header', namespace: SettingsModal.ns, placeholder: 'Categories'),
|
||||||
fontSize: clideFontCaption,
|
fontSize: clideFontCaption,
|
||||||
color: tokens.sidebarSectionHeader,
|
color: tokens.sidebarSectionHeader,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class _SettingsViewState extends State<SettingsView> {
|
|||||||
i.string('section.appearance', namespace: SettingsView.ns, placeholder: 'Appearance'),
|
i.string('section.appearance', namespace: SettingsView.ns, placeholder: 'Appearance'),
|
||||||
fontSize: clideFontCaption,
|
fontSize: clideFontCaption,
|
||||||
color: tokens.sidebarSectionHeader,
|
color: tokens.sidebarSectionHeader,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_HighContrastToggle(
|
_HighContrastToggle(
|
||||||
|
|||||||
@@ -148,16 +148,17 @@ class _TicketHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ClideText(detail.id, fontSize: clideFontSmall, color: typeColor, fontFamily: clideMonoFamily),
|
ClideText(detail.id, fontSize: clideFontSmall, color: typeColor, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (detail.priority != null) ClideText(detail.priority!, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
if (detail.priority != null)
|
||||||
|
ClideText(detail.priority!, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
ClideText(detail.title, fontSize: 15, fontWeight: FontWeight.w500),
|
ClideText(detail.title, fontSize: 15, fontWeight: FontWeight.w500),
|
||||||
if (detail.assignedTo != null) ...[
|
if (detail.assignedTo != null) ...[
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
ClideText('assigned: ${detail.assignedTo}', muted: true, fontSize: clideFontSmall, fontFamily: clideMonoFamily),
|
ClideText('assigned: ${detail.assignedTo}', muted: true, fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -206,7 +207,7 @@ class _StatusControls extends StatelessWidget {
|
|||||||
border: Border.all(color: active ? tokens.statusInfo : tokens.panelBorder),
|
border: Border.all(color: active ? tokens.statusInfo : tokens.panelBorder),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: ClideText(_shortLabel(s), fontSize: clideFontBadge, color: color, fontFamily: clideMonoFamily),
|
child: ClideText(_shortLabel(s), fontSize: clideFontBadge, color: color, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -234,7 +235,7 @@ class _SectionLabel extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ClideText(label, fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: clideMonoFamily);
|
return ClideText(label, fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: ClideSettings.fonts.monoOf(context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +271,7 @@ class _CompactCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(color: typeColor, shape: BoxShape.circle),
|
decoration: BoxDecoration(color: typeColor, shape: BoxShape.circle),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
ClideText(id, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
ClideText(id, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(child: ClideText(title, fontSize: clideFontSmall)),
|
Expanded(child: ClideText(title, fontSize: clideFontSmall)),
|
||||||
],
|
],
|
||||||
@@ -320,9 +321,10 @@ class _DecisionRefCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
|
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
ClideText(id, fontSize: clideFontSmall, color: color, fontFamily: clideMonoFamily),
|
ClideText(id, fontSize: clideFontSmall, color: color, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (domain != null) ClideText(domain, fontSize: clideFontBadge, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
if (domain != null)
|
||||||
|
ClideText(domain, fontSize: clideFontBadge, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 3),
|
const SizedBox(height: 3),
|
||||||
|
|||||||
@@ -314,7 +314,12 @@ class _TypeChip extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(color: dotColor, shape: BoxShape.circle),
|
decoration: BoxDecoration(color: dotColor, shape: BoxShape.circle),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
ClideText(label, fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: active ? tokens.globalForeground : tokens.globalTextMuted),
|
ClideText(
|
||||||
|
label,
|
||||||
|
fontSize: clideFontSmall,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
color: active ? tokens.globalForeground : tokens.globalTextMuted,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -386,7 +391,7 @@ class _TicketCard extends StatelessWidget {
|
|||||||
entry.parentId!,
|
entry.parentId!,
|
||||||
fontSize: clideFontSmall,
|
fontSize: clideFontSmall,
|
||||||
color: hovered ? tokens.globalForeground : tokens.globalTextMuted,
|
color: hovered ? tokens.globalForeground : tokens.globalTextMuted,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -402,7 +407,13 @@ class _TicketCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
ClideText(entry.id, fontSize: clideFontSmall, color: tokens.globalForeground, fontFamily: clideMonoFamily, fontWeight: FontWeight.w600),
|
ClideText(
|
||||||
|
entry.id,
|
||||||
|
fontSize: clideFontSmall,
|
||||||
|
color: tokens.globalForeground,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
@@ -480,7 +491,7 @@ class _StatusBadge extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
decoration: BoxDecoration(color: color.withAlpha(0x30), borderRadius: BorderRadius.circular(3)),
|
decoration: BoxDecoration(color: color.withAlpha(0x30), borderRadius: BorderRadius.circular(3)),
|
||||||
child: ClideText(label, fontSize: clideFontBadge, color: color, fontFamily: clideMonoFamily),
|
child: ClideText(label, fontSize: clideFontBadge, color: color, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,12 @@ class VimModeIndicator extends StatelessWidget {
|
|||||||
final tokens = ClideTheme.of(context).surface;
|
final tokens = ClideTheme.of(context).surface;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
child: ClideText('-- ${service.mode.label} --', fontFamily: clideMonoFamily, fontSize: clideFontCaption, color: tokens.statusBarForeground),
|
child: ClideText(
|
||||||
|
'-- ${service.mode.label} --',
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
fontSize: clideFontCaption,
|
||||||
|
color: tokens.statusBarForeground,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class _TipsCard extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ClideText('TIPS', fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: clideMonoFamily),
|
ClideText('TIPS', fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(height: 14),
|
const SizedBox(height: 14),
|
||||||
_tipRow(firstRow),
|
_tipRow(firstRow),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
@@ -170,7 +170,7 @@ class _StartColumn extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ClideText('START', fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: clideMonoFamily),
|
ClideText('START', fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
// Only flows that exist get a tile — the old Clone-from-git and
|
// Only flows that exist get a tile — the old Clone-from-git and
|
||||||
// Start-a-Claude-session rows were inert and advertised shortcuts
|
// Start-a-Claude-session rows were inert and advertised shortcuts
|
||||||
@@ -233,7 +233,7 @@ class _ActionRow extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: ClideText(label, fontSize: clideFontBody, color: tokens.globalForeground),
|
child: ClideText(label, fontSize: clideFontBody, color: tokens.globalForeground),
|
||||||
),
|
),
|
||||||
if (shortcut != null) ClideText(shortcut!, fontSize: clideFontMeta, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
if (shortcut != null) ClideText(shortcut!, fontSize: clideFontMeta, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -255,7 +255,7 @@ class _RecentColumn extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ClideText('RECENT', fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: clideMonoFamily),
|
ClideText('RECENT', fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (recents.isEmpty)
|
if (recents.isEmpty)
|
||||||
const ClideText('No recent projects.', muted: true, fontSize: clideFontCaption)
|
const ClideText('No recent projects.', muted: true, fontSize: clideFontCaption)
|
||||||
@@ -309,7 +309,7 @@ class _RecentRow extends StatelessWidget {
|
|||||||
project.relativePath,
|
project.relativePath,
|
||||||
muted: true,
|
muted: true,
|
||||||
fontSize: clideFontMeta,
|
fontSize: clideFontMeta,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -323,7 +323,7 @@ class _RecentRow extends StatelessWidget {
|
|||||||
project.branch!,
|
project.branch!,
|
||||||
muted: true,
|
muted: true,
|
||||||
fontSize: clideFontMeta,
|
fontSize: clideFontMeta,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -399,17 +399,17 @@ class _StatusLine extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ClideText('$clideName $clideVersion', muted: true, fontSize: clideFontSmall, fontFamily: clideMonoFamily),
|
ClideText('$clideName $clideVersion', muted: true, fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
ClideText(' · ', muted: true, fontSize: clideFontSmall),
|
ClideText(' · ', muted: true, fontSize: clideFontSmall),
|
||||||
if (!tc.resolved)
|
if (!tc.resolved)
|
||||||
ClideText('checking…', muted: true, fontSize: clideFontSmall, fontFamily: clideMonoFamily)
|
ClideText('checking…', muted: true, fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context))
|
||||||
else if (tc.allOk)
|
else if (tc.allOk)
|
||||||
ClideText('application ok', fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: tokens.statusSuccess)
|
ClideText('application ok', fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.statusSuccess)
|
||||||
else
|
else
|
||||||
ClideText(
|
ClideText(
|
||||||
tc.missing.map((t) => '$t not found').join(' · '),
|
tc.missing.map((t) => '$t not found').join(' · '),
|
||||||
fontSize: clideFontSmall,
|
fontSize: clideFontSmall,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
color: tokens.statusWarning,
|
color: tokens.statusWarning,
|
||||||
),
|
),
|
||||||
ClideText(' · ', muted: true, fontSize: clideFontSmall),
|
ClideText(' · ', muted: true, fontSize: clideFontSmall),
|
||||||
@@ -435,8 +435,13 @@ class _ThemeLink extends StatelessWidget {
|
|||||||
builder: (ctx, hovered, _) => Row(
|
builder: (ctx, hovered, _) => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ClideText('theme: ', muted: true, fontSize: clideFontSmall, fontFamily: clideMonoFamily),
|
ClideText('theme: ', muted: true, fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
ClideText(themeName, fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: hovered ? tokens.globalForeground : tokens.globalFocus),
|
ClideText(
|
||||||
|
themeName,
|
||||||
|
fontSize: clideFontSmall,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
color: hovered ? tokens.globalForeground : tokens.globalFocus,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -518,7 +523,7 @@ class _OpenProjectDialogState extends State<_OpenProjectDialog> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: tokens.globalForeground,
|
color: tokens.globalForeground,
|
||||||
fontSize: clideFontCaption,
|
fontSize: clideFontCaption,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
fontFamilyFallback: clideMonoFamilyFallback,
|
fontFamilyFallback: clideMonoFamilyFallback,
|
||||||
),
|
),
|
||||||
cursorColor: tokens.globalForeground,
|
cursorColor: tokens.globalForeground,
|
||||||
|
|||||||
@@ -34,7 +34,12 @@ class ProjectSwitcherButton extends StatelessWidget {
|
|||||||
builder: (context, hovered, _) => Row(
|
builder: (context, hovered, _) => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ClideText(label, fontSize: 12, color: hovered ? tokens.globalForeground : tokens.chromeForeground, fontFamily: clideMonoFamily),
|
ClideText(
|
||||||
|
label,
|
||||||
|
fontSize: 12,
|
||||||
|
color: hovered ? tokens.globalForeground : tokens.chromeForeground,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
ClideIcon(PhosphorIcons.byName('caret-down'), size: 8, color: tokens.chromeForeground),
|
ClideIcon(PhosphorIcons.byName('caret-down'), size: 8, color: tokens.chromeForeground),
|
||||||
],
|
],
|
||||||
@@ -195,7 +200,7 @@ class _RecentProjectRow extends StatelessWidget {
|
|||||||
project.relativePath,
|
project.relativePath,
|
||||||
muted: true,
|
muted: true,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -203,11 +208,18 @@ class _RecentProjectRow extends StatelessWidget {
|
|||||||
ClideText(' · ', muted: true, fontSize: 12),
|
ClideText(' · ', muted: true, fontSize: 12),
|
||||||
ClideIcon(PhosphorIcons.byName('git-branch'), size: 10, color: tokens.globalTextMuted),
|
ClideIcon(PhosphorIcons.byName('git-branch'), size: 10, color: tokens.globalTextMuted),
|
||||||
const SizedBox(width: 3),
|
const SizedBox(width: 3),
|
||||||
ClideText(project.branch!, muted: true, fontSize: 12, fontFamily: clideMonoFamily),
|
ClideText(project.branch!, muted: true, fontSize: 12, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
ClideText(project.relativePath, muted: true, fontSize: 12, fontFamily: clideMonoFamily, maxLines: 1, overflow: TextOverflow.ellipsis),
|
ClideText(
|
||||||
|
project.relativePath,
|
||||||
|
muted: true,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -236,7 +248,8 @@ class _ActionRow extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: ClideText(label, fontSize: 14)),
|
Expanded(child: ClideText(label, fontSize: 14)),
|
||||||
if (shortcut != null && shortcut!.isNotEmpty) ClideText(shortcut!, fontSize: 12, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
|
if (shortcut != null && shortcut!.isNotEmpty)
|
||||||
|
ClideText(shortcut!, fontSize: 12, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
+119
-110
@@ -74,118 +74,127 @@ class RootShellState extends State<RootShell> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final tokens = ClideTheme.of(context).surface;
|
final tokens = ClideTheme.of(context).surface;
|
||||||
return DefaultTextStyle(
|
// Resolve the user-selected fonts once (Settings → Appearance, T-460/T-471)
|
||||||
style: TextStyle(
|
// and publish them via ClideFonts; the settings listener rebuilds this on a
|
||||||
color: tokens.globalForeground,
|
// change so descendants re-read live. The UI font flows through the
|
||||||
fontSize: 15,
|
// DefaultTextStyle below; mono sites read ClideFonts.monoOf(context).
|
||||||
height: clideLineHeight,
|
final settings = widget.services.settings;
|
||||||
fontWeight: clideUiDefaultWeight,
|
final uiFont = settings.get<String>(kUiFontSettingKey) ?? clideUiFamily;
|
||||||
// User-selected UI font (Settings → Appearance, T-460), else the
|
final monoFont = settings.get<String>(kMonoFontSettingKey) ?? clideMonoFamily;
|
||||||
// bundled default. The fallback chain still covers a missing asset.
|
return ClideSettingsScope(
|
||||||
fontFamily: widget.services.settings.get<String>(kUiFontSettingKey) ?? clideUiFamily,
|
ui: uiFont,
|
||||||
fontFamilyFallback: clideUiFamilyFallback,
|
mono: monoFont,
|
||||||
),
|
child: DefaultTextStyle(
|
||||||
child: MediaQuery(
|
style: TextStyle(
|
||||||
data: MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(widget.services.textZoom.scale)),
|
color: tokens.globalForeground,
|
||||||
child: Actions(
|
fontSize: 15,
|
||||||
actions: <Type, Action<Intent>>{
|
height: clideLineHeight,
|
||||||
TextScaleIncreaseIntent: CallbackAction<TextScaleIncreaseIntent>(
|
fontWeight: clideUiDefaultWeight,
|
||||||
onInvoke: (_) {
|
fontFamily: uiFont,
|
||||||
widget.services.textZoom.increase();
|
fontFamilyFallback: clideUiFamilyFallback,
|
||||||
return null;
|
),
|
||||||
},
|
child: MediaQuery(
|
||||||
),
|
data: MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(widget.services.textZoom.scale)),
|
||||||
TextScaleDecreaseIntent: CallbackAction<TextScaleDecreaseIntent>(
|
child: Actions(
|
||||||
onInvoke: (_) {
|
actions: <Type, Action<Intent>>{
|
||||||
widget.services.textZoom.decrease();
|
TextScaleIncreaseIntent: CallbackAction<TextScaleIncreaseIntent>(
|
||||||
return null;
|
onInvoke: (_) {
|
||||||
},
|
widget.services.textZoom.increase();
|
||||||
),
|
return null;
|
||||||
TextScaleResetIntent: CallbackAction<TextScaleResetIntent>(
|
},
|
||||||
onInvoke: (_) {
|
),
|
||||||
widget.services.textZoom.reset();
|
TextScaleDecreaseIntent: CallbackAction<TextScaleDecreaseIntent>(
|
||||||
return null;
|
onInvoke: (_) {
|
||||||
},
|
widget.services.textZoom.decrease();
|
||||||
),
|
return null;
|
||||||
InvokeCommandIntent: CallbackAction<InvokeCommandIntent>(
|
},
|
||||||
onInvoke: (intent) {
|
),
|
||||||
widget.services.commands.execute(intent.commandId);
|
TextScaleResetIntent: CallbackAction<TextScaleResetIntent>(
|
||||||
return null;
|
onInvoke: (_) {
|
||||||
},
|
widget.services.textZoom.reset();
|
||||||
),
|
return null;
|
||||||
PaletteOpenIntent: CallbackAction<PaletteOpenIntent>(
|
},
|
||||||
onInvoke: (_) {
|
),
|
||||||
widget.services.palette.open();
|
InvokeCommandIntent: CallbackAction<InvokeCommandIntent>(
|
||||||
return null;
|
onInvoke: (intent) {
|
||||||
},
|
widget.services.commands.execute(intent.commandId);
|
||||||
),
|
return null;
|
||||||
QuickOpenIntent: CallbackAction<QuickOpenIntent>(
|
},
|
||||||
onInvoke: (_) {
|
),
|
||||||
widget.services.quickOpen.open();
|
PaletteOpenIntent: CallbackAction<PaletteOpenIntent>(
|
||||||
return null;
|
onInvoke: (_) {
|
||||||
},
|
widget.services.palette.open();
|
||||||
),
|
return null;
|
||||||
ExLineOpenIntent: CallbackAction<ExLineOpenIntent>(
|
},
|
||||||
onInvoke: (_) {
|
),
|
||||||
widget.services.exLine.open();
|
QuickOpenIntent: CallbackAction<QuickOpenIntent>(
|
||||||
return null;
|
onInvoke: (_) {
|
||||||
},
|
widget.services.quickOpen.open();
|
||||||
),
|
return null;
|
||||||
ExLineWriteQuitIntent: CallbackAction<ExLineWriteQuitIntent>(
|
},
|
||||||
onInvoke: (_) {
|
),
|
||||||
// ZZ — save+close the active tab without opening the overlay.
|
ExLineOpenIntent: CallbackAction<ExLineOpenIntent>(
|
||||||
unawaited(exWriteQuitActive(widget.services.ipc));
|
onInvoke: (_) {
|
||||||
return null;
|
widget.services.exLine.open();
|
||||||
},
|
return null;
|
||||||
),
|
},
|
||||||
FindInFilesIntent: CallbackAction<FindInFilesIntent>(
|
),
|
||||||
onInvoke: (_) {
|
ExLineWriteQuitIntent: CallbackAction<ExLineWriteQuitIntent>(
|
||||||
widget.services.arrangement.setVisible(Slots.sidebar, true);
|
onInvoke: (_) {
|
||||||
widget.services.arrangement.setCollapsed(Slots.sidebar, false);
|
// ZZ — save+close the active tab without opening the overlay.
|
||||||
widget.services.panels.activateTab(Slots.sidebar, 'search.findInFiles');
|
unawaited(exWriteQuitActive(widget.services.ipc));
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
FocusNextPanelIntent: CallbackAction<FocusNextPanelIntent>(
|
FindInFilesIntent: CallbackAction<FindInFilesIntent>(
|
||||||
onInvoke: (_) {
|
onInvoke: (_) {
|
||||||
widget.services.focus.focusNextSlot();
|
widget.services.arrangement.setVisible(Slots.sidebar, true);
|
||||||
return null;
|
widget.services.arrangement.setCollapsed(Slots.sidebar, false);
|
||||||
},
|
widget.services.panels.activateTab(Slots.sidebar, 'search.findInFiles');
|
||||||
),
|
return null;
|
||||||
FocusPreviousPanelIntent: CallbackAction<FocusPreviousPanelIntent>(
|
},
|
||||||
onInvoke: (_) {
|
),
|
||||||
widget.services.focus.focusPreviousSlot();
|
FocusNextPanelIntent: CallbackAction<FocusNextPanelIntent>(
|
||||||
return null;
|
onInvoke: (_) {
|
||||||
},
|
widget.services.focus.focusNextSlot();
|
||||||
),
|
return null;
|
||||||
},
|
},
|
||||||
child: KeyboardListener(
|
),
|
||||||
focusNode: _keyFocus,
|
FocusPreviousPanelIntent: CallbackAction<FocusPreviousPanelIntent>(
|
||||||
autofocus: true,
|
onInvoke: (_) {
|
||||||
onKeyEvent: _onKey,
|
widget.services.focus.focusPreviousSlot();
|
||||||
child: ColoredBox(
|
return null;
|
||||||
color: tokens.globalBackground,
|
},
|
||||||
child: ClideResizeBorder(
|
),
|
||||||
windowControls: widget.services.window,
|
},
|
||||||
child: Column(
|
child: KeyboardListener(
|
||||||
children: [
|
focusNode: _keyFocus,
|
||||||
HatBar(kernel: widget.services, menuBar: _menuBar),
|
autofocus: true,
|
||||||
Expanded(
|
onKeyEvent: _onKey,
|
||||||
child: DialogHost(
|
child: ColoredBox(
|
||||||
router: widget.services.dialog,
|
color: tokens.globalBackground,
|
||||||
child: Stack(
|
child: ClideResizeBorder(
|
||||||
children: [
|
windowControls: widget.services.window,
|
||||||
const Positioned.fill(child: RootLayout()),
|
child: Column(
|
||||||
const ClidePalette(),
|
children: [
|
||||||
const QuickOpenOverlay(),
|
HatBar(kernel: widget.services, menuBar: _menuBar),
|
||||||
const ExLineOverlay(),
|
Expanded(
|
||||||
const Positioned.fill(child: _WelcomeOverlay()),
|
child: DialogHost(
|
||||||
const ToastOverlay(),
|
router: widget.services.dialog,
|
||||||
],
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
const Positioned.fill(child: RootLayout()),
|
||||||
|
const ClidePalette(),
|
||||||
|
const QuickOpenOverlay(),
|
||||||
|
const ExLineOverlay(),
|
||||||
|
const Positioned.fill(child: _WelcomeOverlay()),
|
||||||
|
const ToastOverlay(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:clide/kernel/src/theme/controller.dart';
|
import 'package:clide/kernel/src/theme/controller.dart';
|
||||||
import 'package:clide/widgets/src/clide_icon.dart';
|
import 'package:clide/widgets/src/clide_icon.dart';
|
||||||
|
import 'package:clide/widgets/src/clide_settings.dart';
|
||||||
import 'package:clide/widgets/src/clide_tappable.dart';
|
import 'package:clide/widgets/src/clide_tappable.dart';
|
||||||
import 'package:clide/widgets/src/clide_text.dart';
|
import 'package:clide/widgets/src/clide_text.dart';
|
||||||
import 'package:clide/widgets/src/icons/phosphor.dart';
|
import 'package:clide/widgets/src/icons/phosphor.dart';
|
||||||
@@ -43,7 +44,7 @@ class ClideAccordion extends StatelessWidget {
|
|||||||
'$label · $count',
|
'$label · $count',
|
||||||
fontSize: clideFontSmall,
|
fontSize: clideFontSmall,
|
||||||
color: hovered ? tokens.globalForeground : tokens.sidebarSectionHeader,
|
color: hovered ? tokens.globalForeground : tokens.sidebarSectionHeader,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:clide/kernel/src/theme/controller.dart';
|
|||||||
import 'package:clide/widgets/src/typography.dart';
|
import 'package:clide/widgets/src/typography.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'clide_settings.dart';
|
||||||
|
|
||||||
class ClideCodeBlock extends StatefulWidget {
|
class ClideCodeBlock extends StatefulWidget {
|
||||||
const ClideCodeBlock({super.key, required this.source, this.language});
|
const ClideCodeBlock({super.key, required this.source, this.language});
|
||||||
|
|
||||||
@@ -50,7 +52,12 @@ class _ClideCodeBlockState extends State<ClideCodeBlock> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final tokens = ClideTheme.of(context).surface;
|
final tokens = ClideTheme.of(context).surface;
|
||||||
final style = TextStyle(fontFamily: clideMonoFamily, fontFamilyFallback: clideMonoFamilyFallback, fontSize: clideFontMono, color: tokens.globalForeground);
|
final style = TextStyle(
|
||||||
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
|
fontFamilyFallback: clideMonoFamilyFallback,
|
||||||
|
fontSize: clideFontMono,
|
||||||
|
color: tokens.globalForeground,
|
||||||
|
);
|
||||||
|
|
||||||
final spans = _spans;
|
final spans = _spans;
|
||||||
TextSpan textSpan;
|
TextSpan textSpan;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import 'package:clide/kernel/src/theme/controller.dart';
|
|||||||
import 'package:clide/kernel/src/theme/tokens.dart';
|
import 'package:clide/kernel/src/theme/tokens.dart';
|
||||||
import 'package:clide/widgets/src/clide_card_metrics.dart';
|
import 'package:clide/widgets/src/clide_card_metrics.dart';
|
||||||
import 'package:clide/widgets/src/clide_icon.dart';
|
import 'package:clide/widgets/src/clide_icon.dart';
|
||||||
|
import 'package:clide/widgets/src/clide_settings.dart';
|
||||||
import 'package:clide/widgets/src/clide_status_indicator.dart';
|
import 'package:clide/widgets/src/clide_status_indicator.dart';
|
||||||
import 'package:clide/widgets/src/clide_tappable.dart';
|
import 'package:clide/widgets/src/clide_tappable.dart';
|
||||||
import 'package:clide/widgets/src/clide_text.dart';
|
import 'package:clide/widgets/src/clide_text.dart';
|
||||||
@@ -119,7 +120,7 @@ class _ClideCollapserCardState extends State<ClideCollapserCard> {
|
|||||||
// Chevron — hard against the left edge; the toggle.
|
// Chevron — hard against the left edge; the toggle.
|
||||||
ClideIcon(expanded ? const ChevronDownIcon() : const ChevronRightIcon(), size: 12, color: accent),
|
ClideIcon(expanded ? const ChevronDownIcon() : const ChevronRightIcon(), size: 12, color: accent),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ClideText(widget.label, fontSize: clideFontCaption, fontFamily: clideMonoFamily, color: accent),
|
ClideText(widget.label, fontSize: clideFontCaption, fontFamily: ClideSettings.fonts.monoOf(context), color: accent),
|
||||||
if (showSummary) ...[
|
if (showSummary) ...[
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -128,7 +129,7 @@ class _ClideCollapserCardState extends State<ClideCollapserCard> {
|
|||||||
// Summary one step below the label, matching ConversationCard so
|
// Summary one step below the label, matching ConversationCard so
|
||||||
// collapser + tool cards read consistently in the stream (T-344).
|
// collapser + tool cards read consistently in the stream (T-344).
|
||||||
fontSize: clideFontMeta,
|
fontSize: clideFontMeta,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
color: tokens.globalTextMuted,
|
color: tokens.globalTextMuted,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:clide/kernel/kernel.dart';
|
import 'package:clide/kernel/kernel.dart';
|
||||||
|
import 'package:clide/widgets/src/clide_settings.dart';
|
||||||
import 'package:clide/widgets/src/clide_text.dart';
|
import 'package:clide/widgets/src/clide_text.dart';
|
||||||
import 'package:clide/widgets/src/typography.dart';
|
import 'package:clide/widgets/src/typography.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
@@ -139,7 +140,7 @@ class _ClidePaletteState extends State<ClidePalette> {
|
|||||||
child: EditableText(
|
child: EditableText(
|
||||||
controller: _input,
|
controller: _input,
|
||||||
focusNode: _focus,
|
focusNode: _focus,
|
||||||
style: TextStyle(fontFamily: clideMonoFamily, fontSize: clideFontMono, color: tokens.dropdownForeground),
|
style: TextStyle(fontFamily: ClideSettings.fonts.monoOf(context), fontSize: clideFontMono, color: tokens.dropdownForeground),
|
||||||
cursorColor: tokens.globalFocus,
|
cursorColor: tokens.globalFocus,
|
||||||
backgroundCursorColor: tokens.globalFocus,
|
backgroundCursorColor: tokens.globalFocus,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@@ -222,7 +223,8 @@ class _PaletteItemState extends State<_PaletteItem> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: ClideText(widget.title, color: selected ? tokens.listItemSelectedForeground : tokens.listItemForeground)),
|
Expanded(child: ClideText(widget.title, color: selected ? tokens.listItemSelectedForeground : tokens.listItemForeground)),
|
||||||
if (widget.binding != null) ClideText(widget.binding!, fontSize: clideFontCaption, fontFamily: clideMonoFamily, color: tokens.globalTextMuted),
|
if (widget.binding != null)
|
||||||
|
ClideText(widget.binding!, fontSize: clideFontCaption, fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.globalTextMuted),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import 'package:clide/widgets/src/typography.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:clide/src/terminal/terminal.dart';
|
import 'package:clide/src/terminal/terminal.dart';
|
||||||
|
|
||||||
|
import 'clide_settings.dart';
|
||||||
|
|
||||||
/// Theme-aware terminal view. Wraps xterm.dart's [TerminalView] with
|
/// Theme-aware terminal view. Wraps xterm.dart's [TerminalView] with
|
||||||
/// clide token bindings, JetBrainsMono as the face, and a Semantics
|
/// clide token bindings, JetBrainsMono as the face, and a Semantics
|
||||||
/// wrapper that exposes the pane as a live region with the terminal's
|
/// wrapper that exposes the pane as a live region with the terminal's
|
||||||
@@ -42,7 +44,7 @@ class ClidePtyView extends StatelessWidget {
|
|||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
autofocus: autofocus,
|
autofocus: autofocus,
|
||||||
theme: _buildTheme(tokens),
|
theme: _buildTheme(tokens),
|
||||||
textStyle: TerminalStyle(fontSize: fontSize, fontFamily: clideMonoFamily, fontFamilyFallback: clideMonoFamilyFallback),
|
textStyle: TerminalStyle(fontSize: fontSize, fontFamily: ClideSettings.fonts.monoOf(context), fontFamilyFallback: clideMonoFamilyFallback),
|
||||||
padding: const EdgeInsets.all(2),
|
padding: const EdgeInsets.all(2),
|
||||||
backgroundOpacity: 1,
|
backgroundOpacity: 1,
|
||||||
cursorType: TerminalCursorType.block,
|
cursorType: TerminalCursorType.block,
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import 'package:clide/kernel/src/facade.dart' show ClideKernel;
|
||||||
|
import 'package:clide/kernel/src/i18n/i18n.dart' show I18n;
|
||||||
|
import 'package:clide/kernel/src/theme/controller.dart' show ClideTheme, ClideThemeData;
|
||||||
|
import 'package:clide/widgets/src/typography.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
/// One facade for the app's live user preferences — fonts, theme, and i18n —
|
||||||
|
/// read uniformly as `ClideSettings.fonts.monoOf(context)`,
|
||||||
|
/// `ClideSettings.theme.of(context)`, `ClideSettings.i18n.of(context)`. Plumb
|
||||||
|
/// once at the root, use many (D-101).
|
||||||
|
///
|
||||||
|
/// The values all originate in the kernel `SettingsStore`; this is the
|
||||||
|
/// widget-facing read side. Fonts are carried by [ClideSettingsScope] (the root
|
||||||
|
/// resolves them from the font settings and rebuilds on change, so dependents
|
||||||
|
/// re-read live). Theme and i18n **delegate** to their existing live providers
|
||||||
|
/// ([ClideTheme] / the `I18n` service) rather than duplicating them — one source
|
||||||
|
/// of truth, and consumers migrate onto this facade incrementally. Reads
|
||||||
|
/// outside a [ClideSettingsScope] fall back to the bundled font defaults, so a
|
||||||
|
/// widget never needs the scope to render (isolated tests).
|
||||||
|
abstract final class ClideSettings {
|
||||||
|
static const fonts = _Fonts();
|
||||||
|
static const theme = _Theme();
|
||||||
|
static const i18n = _I18n();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Fonts {
|
||||||
|
const _Fonts();
|
||||||
|
|
||||||
|
/// The active monospace family in scope, else the bundled default.
|
||||||
|
String monoOf(BuildContext context) => ClideSettingsScope.of(context)?.mono ?? clideMonoFamily;
|
||||||
|
|
||||||
|
/// The active UI family in scope, else the bundled default.
|
||||||
|
String uiOf(BuildContext context) => ClideSettingsScope.of(context)?.ui ?? clideUiFamily;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Theme {
|
||||||
|
const _Theme();
|
||||||
|
|
||||||
|
/// Resolved theme data for [context] — delegates to the [ClideTheme] provider.
|
||||||
|
ClideThemeData of(BuildContext context) => ClideTheme.of(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _I18n {
|
||||||
|
const _I18n();
|
||||||
|
|
||||||
|
/// The i18n service for [context] — delegates to the kernel `I18n` service.
|
||||||
|
I18n of(BuildContext context) => ClideKernel.of(context).i18n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Root-provided InheritedWidget carrying the live font families (D-101). The
|
||||||
|
/// root rebuilds it from the font settings on change; font dependents re-read
|
||||||
|
/// via [ClideSettings.fonts]. Theme/i18n have their own providers, so they are
|
||||||
|
/// not duplicated here.
|
||||||
|
class ClideSettingsScope extends InheritedWidget {
|
||||||
|
const ClideSettingsScope({super.key, required this.ui, required this.mono, required super.child});
|
||||||
|
|
||||||
|
final String ui;
|
||||||
|
final String mono;
|
||||||
|
|
||||||
|
static ClideSettingsScope? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<ClideSettingsScope>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(ClideSettingsScope old) => ui != old.ui || mono != old.mono;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:clide/kernel/kernel.dart';
|
import 'package:clide/kernel/kernel.dart';
|
||||||
|
import 'package:clide/widgets/src/clide_settings.dart';
|
||||||
import 'package:clide/widgets/src/clide_text.dart';
|
import 'package:clide/widgets/src/clide_text.dart';
|
||||||
import 'package:clide/widgets/src/typography.dart';
|
import 'package:clide/widgets/src/typography.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
@@ -160,13 +161,13 @@ class _ExLineOverlayState extends State<ExLineOverlay> {
|
|||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
ClideText(':', fontFamily: clideMonoFamily, color: tokens.globalTextMuted),
|
ClideText(':', fontFamily: ClideSettings.fonts.monoOf(context), color: tokens.globalTextMuted),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: EditableText(
|
child: EditableText(
|
||||||
controller: _input,
|
controller: _input,
|
||||||
focusNode: _focus,
|
focusNode: _focus,
|
||||||
style: TextStyle(fontFamily: clideMonoFamily, fontSize: clideFontMono, color: tokens.dropdownForeground),
|
style: TextStyle(fontFamily: ClideSettings.fonts.monoOf(context), fontSize: clideFontMono, color: tokens.dropdownForeground),
|
||||||
cursorColor: tokens.globalFocus,
|
cursorColor: tokens.globalFocus,
|
||||||
backgroundCursorColor: tokens.globalFocus,
|
backgroundCursorColor: tokens.globalFocus,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:clide/kernel/kernel.dart';
|
import 'package:clide/kernel/kernel.dart';
|
||||||
|
import 'package:clide/widgets/src/clide_settings.dart';
|
||||||
import 'package:clide/widgets/src/clide_text.dart';
|
import 'package:clide/widgets/src/clide_text.dart';
|
||||||
import 'package:clide/widgets/src/typography.dart';
|
import 'package:clide/widgets/src/typography.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
@@ -187,7 +188,7 @@ class _QuickOpenOverlayState extends State<QuickOpenOverlay> {
|
|||||||
child: EditableText(
|
child: EditableText(
|
||||||
controller: _input,
|
controller: _input,
|
||||||
focusNode: _focus,
|
focusNode: _focus,
|
||||||
style: TextStyle(fontFamily: clideMonoFamily, fontSize: clideFontMono, color: tokens.dropdownForeground),
|
style: TextStyle(fontFamily: ClideSettings.fonts.monoOf(context), fontSize: clideFontMono, color: tokens.dropdownForeground),
|
||||||
cursorColor: tokens.globalFocus,
|
cursorColor: tokens.globalFocus,
|
||||||
backgroundCursorColor: tokens.globalFocus,
|
backgroundCursorColor: tokens.globalFocus,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@@ -288,7 +289,7 @@ class _QuickOpenItemState extends State<_QuickOpenItem> {
|
|||||||
child: ClideText(
|
child: ClideText(
|
||||||
dir,
|
dir,
|
||||||
fontSize: clideFontCaption,
|
fontSize: clideFontCaption,
|
||||||
fontFamily: clideMonoFamily,
|
fontFamily: ClideSettings.fonts.monoOf(context),
|
||||||
color: tokens.globalTextMuted,
|
color: tokens.globalTextMuted,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export 'src/clide_card_metrics.dart';
|
|||||||
export 'src/clide_collapser_card.dart';
|
export 'src/clide_collapser_card.dart';
|
||||||
export 'src/clide_code_block.dart';
|
export 'src/clide_code_block.dart';
|
||||||
export 'src/clide_divider.dart';
|
export 'src/clide_divider.dart';
|
||||||
|
export 'src/clide_settings.dart';
|
||||||
export 'src/clide_file_image.dart';
|
export 'src/clide_file_image.dart';
|
||||||
export 'src/ex_line_overlay.dart';
|
export 'src/ex_line_overlay.dart';
|
||||||
export 'src/clide_filter_box.dart';
|
export 'src/clide_filter_box.dart';
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:clide/widgets/widgets.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
/// The ClideSettings.fonts facade (D-101): reads the active families from a
|
||||||
|
/// [ClideSettingsScope], or the bundled defaults outside one.
|
||||||
|
void main() {
|
||||||
|
testWidgets('fonts fall back to the bundled defaults without a scope', (tester) async {
|
||||||
|
late String mono;
|
||||||
|
late String ui;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
mono = ClideSettings.fonts.monoOf(context);
|
||||||
|
ui = ClideSettings.fonts.uiOf(context);
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(mono, clideMonoFamily);
|
||||||
|
expect(ui, clideUiFamily);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('a scope provides the active families, read live', (tester) async {
|
||||||
|
late String mono;
|
||||||
|
late String ui;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ClideSettingsScope(
|
||||||
|
ui: 'Inter',
|
||||||
|
mono: 'FiraMono',
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
mono = ClideSettings.fonts.monoOf(context);
|
||||||
|
ui = ClideSettings.fonts.uiOf(context);
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(mono, 'FiraMono');
|
||||||
|
expect(ui, 'Inter');
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user