@pellux/goodvibes-tui 1.16.1 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -9
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +5490 -1806
- package/package.json +7 -7
- package/src/cli/completions/generate.ts +5 -4
- package/src/cli/config-overrides.ts +51 -11
- package/src/cli/doctor.ts +4 -5
- package/src/cli/entrypoint.ts +13 -8
- package/src/cli/help.ts +7 -6
- package/src/cli/launch-auto-update.ts +214 -0
- package/src/cli/management-commands.ts +82 -15
- package/src/cli/management.ts +0 -2
- package/src/cli/parser.ts +5 -0
- package/src/cli/status.ts +13 -0
- package/src/cli/surface-command.ts +5 -5
- package/src/cli/tui-startup.ts +60 -97
- package/src/cli/types.ts +2 -0
- package/src/config/index.ts +1 -1
- package/src/config/tui-extension-settings.ts +33 -0
- package/src/core/composer-state.ts +1 -1
- package/src/core/consolidation-receipt.ts +41 -0
- package/src/core/conversation-line-cache.ts +3 -3
- package/src/core/conversation-rendering.ts +18 -9
- package/src/core/conversation.ts +2 -2
- package/src/core/long-task-notifier.ts +7 -7
- package/src/core/memory-provenance.ts +67 -0
- package/src/core/pairing-devices.ts +49 -0
- package/src/core/pairing-handoff.ts +101 -0
- package/src/core/pairing-offers.ts +83 -0
- package/src/core/pairing-origin.ts +78 -0
- package/src/core/pairing-tailscale-gateway.ts +56 -0
- package/src/core/power-chip-source.ts +91 -0
- package/src/core/power-status.ts +95 -0
- package/src/core/stable-host.ts +171 -0
- package/src/core/stream-event-wiring.ts +29 -6
- package/src/core/system-message-noise.ts +1 -1
- package/src/core/system-message-router.ts +6 -6
- package/src/core/turn-budget-outcome.ts +60 -0
- package/src/core/turn-cancellation.ts +40 -1
- package/src/core/turn-event-wiring.ts +14 -8
- package/src/daemon/cli.ts +64 -58
- package/src/daemon/lifecycle.ts +41 -0
- package/src/daemon/service-commands.ts +5 -5
- package/src/export/cost-utils.ts +128 -6
- package/src/input/autocomplete.ts +2 -2
- package/src/input/command-registry.ts +55 -9
- package/src/input/commands/acp-runtime.ts +72 -0
- package/src/input/commands/ci-runtime.ts +5 -1
- package/src/input/commands/codebase-runtime.ts +6 -6
- package/src/input/commands/cost-runtime.ts +1 -1
- package/src/input/commands/devices-runtime.ts +109 -0
- package/src/input/commands/git-runtime.ts +28 -4
- package/src/input/commands/graph-runtime.ts +45 -0
- package/src/input/commands/health-runtime.ts +2 -2
- package/src/input/commands/intelligence-runtime.ts +1 -1
- package/src/input/commands/local-auth-runtime.ts +1 -1
- package/src/input/commands/local-provider-runtime.ts +49 -10
- package/src/input/commands/local-runtime.ts +53 -5
- package/src/input/commands/memory.ts +2 -2
- package/src/input/commands/operator-panel-runtime.ts +5 -5
- package/src/input/commands/operator-rpc.ts +32 -11
- package/src/input/commands/permissions-provenance.ts +1 -1
- package/src/input/commands/permissions-runtime.ts +99 -8
- package/src/input/commands/planning-runtime.ts +3 -3
- package/src/input/commands/platform-sandbox-runtime.ts +1 -1
- package/src/input/commands/power-runtime.ts +56 -0
- package/src/input/commands/provider-accounts-runtime.ts +71 -6
- package/src/input/commands/provider.ts +7 -5
- package/src/input/commands/qrcode-runtime.ts +14 -19
- package/src/input/commands/queue-runtime.ts +70 -0
- package/src/input/commands/recall-files-git.ts +13 -51
- package/src/input/commands/recall-files.ts +8 -11
- package/src/input/commands/recall-review.ts +2 -2
- package/src/input/commands/relay-runtime.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/runtime-services.ts +4 -4
- package/src/input/commands/schedule-runtime.ts +14 -7
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-workflow.ts +2 -2
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/share-runtime.ts +1 -1
- package/src/input/commands/shell-core.ts +46 -68
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/update-runtime.ts +153 -96
- package/src/input/commands/work-plan-runtime.ts +1 -1
- package/src/input/commands/workstream-attempts.ts +6 -1
- package/src/input/commands/workstream-runtime.ts +46 -27
- package/src/input/commands/worktree-runtime.ts +35 -8
- package/src/input/commands.ts +10 -2
- package/src/input/config-modal-types.ts +14 -3
- package/src/input/config-modal.ts +75 -13
- package/src/input/feature-unit-layout.ts +93 -82
- package/src/input/feed-context-factory.ts +2 -2
- package/src/input/handler-command-route.ts +2 -2
- package/src/input/handler-content-actions.ts +1 -1
- package/src/input/handler-feed-routes.ts +7 -7
- package/src/input/handler-feed.ts +4 -4
- package/src/input/handler-interactions.ts +3 -2
- package/src/input/handler-modal-routes.ts +12 -4
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-onboarding-daemon-adopt.ts +2 -2
- package/src/input/handler-onboarding.ts +3 -2
- package/src/input/handler-picker-routes.ts +26 -13
- package/src/input/handler-shortcuts.ts +29 -6
- package/src/input/handler.ts +2 -2
- package/src/input/keybindings.ts +21 -3
- package/src/input/onboarding/onboarding-feature-units.ts +34 -27
- package/src/input/onboarding/onboarding-provider-key-fields.ts +112 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +22 -21
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +17 -0
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +2 -2
- package/src/input/onboarding/onboarding-wizard-steps.ts +30 -42
- package/src/input/panel-integration-actions.ts +2 -2
- package/src/input/panel-mouse-geometry.ts +1 -1
- package/src/input/panel-paste-flood-guard.ts +1 -1
- package/src/input/provider-key-intake.ts +167 -0
- package/src/input/sandbox-exec-config.ts +2 -2
- package/src/input/selection-modal.ts +1 -1
- package/src/input/settings-modal-activation.ts +5 -0
- package/src/input/settings-modal-adjustment.ts +4 -31
- package/src/input/settings-modal-data.ts +52 -57
- package/src/input/settings-modal-mutations.ts +12 -68
- package/src/input/settings-modal-types.ts +27 -17
- package/src/input/settings-modal.ts +74 -55
- package/src/main.ts +90 -90
- package/src/panels/agent-inspector-shared.ts +4 -4
- package/src/panels/base-panel.ts +2 -2
- package/src/panels/builtin/agent.ts +4 -4
- package/src/panels/builtin/development.ts +4 -4
- package/src/panels/builtin/knowledge.ts +1 -1
- package/src/panels/builtin/operations.ts +83 -11
- package/src/panels/builtin/session.ts +5 -5
- package/src/panels/builtin/shared.ts +16 -4
- package/src/panels/builtin-modals.ts +59 -42
- package/src/panels/builtin-panels.ts +1 -1
- package/src/panels/cost-tracker-panel.ts +124 -7
- package/src/panels/diff-panel.ts +39 -6
- package/src/panels/eval-registry.ts +2 -2
- package/src/panels/fleet-acts.ts +446 -0
- package/src/panels/fleet-deep-link.ts +1 -1
- package/src/panels/fleet-gateway.ts +117 -0
- package/src/panels/fleet-observed-render.ts +128 -0
- package/src/panels/fleet-panel-format.ts +163 -11
- package/src/panels/fleet-panel-worktree-detail.ts +14 -0
- package/src/panels/fleet-panel.ts +73 -57
- package/src/panels/fleet-read-model.ts +84 -14
- package/src/panels/fleet-spawn.ts +266 -0
- package/src/panels/fleet-steer.ts +2 -2
- package/src/panels/fleet-stop.ts +41 -5
- package/src/panels/fleet-tabs.ts +15 -16
- package/src/panels/fleet-transcript.ts +5 -5
- package/src/panels/git-panel.ts +2 -2
- package/src/panels/index.ts +4 -1
- package/src/panels/local-auth-panel.ts +1 -1
- package/src/panels/memory-consolidation-gateway.ts +94 -0
- package/src/panels/modals/devices-modal.ts +161 -0
- package/src/panels/modals/hooks-modal.ts +1 -1
- package/src/panels/modals/keybindings-modal.ts +2 -2
- package/src/panels/modals/knowledge-modal.ts +1 -1
- package/src/panels/modals/marketplace-modal.ts +4 -4
- package/src/panels/modals/memory-modal.ts +153 -9
- package/src/panels/modals/modal-surface-helpers.ts +1 -1
- package/src/panels/modals/modal-theme.ts +1 -1
- package/src/panels/modals/pairing-modal.ts +198 -68
- package/src/panels/modals/planning-modal.ts +2 -2
- package/src/panels/modals/plugins-modal.ts +1 -1
- package/src/panels/modals/policy-modal.ts +1 -1
- package/src/panels/modals/security-modal.ts +7 -1
- package/src/panels/modals/skills-modal.ts +1 -1
- package/src/panels/modals/work-plan-modal.ts +3 -3
- package/src/panels/notifications-feed.ts +127 -0
- package/src/panels/notifications-panel.ts +160 -0
- package/src/panels/panel-manager.ts +9 -9
- package/src/panels/plugins-panel.ts +4 -4
- package/src/panels/project-planning-answer-actions.ts +1 -1
- package/src/panels/provider-health-routes.ts +2 -2
- package/src/panels/skills-panel.ts +4 -5
- package/src/panels/token-budget-panel.ts +11 -3
- package/src/panels/types.ts +5 -5
- package/src/panels/workstream-graph-render.ts +123 -0
- package/src/permissions/broker-approval-card.ts +201 -0
- package/src/permissions/hunk-selection.ts +28 -4
- package/src/permissions/prompt.ts +244 -29
- package/src/renderer/autocomplete-overlay.ts +107 -54
- package/src/renderer/code-block.ts +2 -2
- package/src/renderer/compaction-history-modal.ts +1 -1
- package/src/renderer/compaction-preview.ts +1 -1
- package/src/renderer/compaction-quality.ts +1 -1
- package/src/renderer/composer-fragments.ts +69 -0
- package/src/renderer/compositor.ts +2 -2
- package/src/renderer/config-modal.ts +1 -1
- package/src/renderer/conversation-overlays.ts +1 -1
- package/src/renderer/footer-tips.ts +3 -3
- package/src/renderer/hint-grammar.ts +1 -1
- package/src/renderer/modal-factory.ts +1 -1
- package/src/renderer/model-picker-overlay.ts +1 -1
- package/src/renderer/model-workspace.ts +7 -2
- package/src/renderer/panel-composite.ts +2 -2
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +1 -1
- package/src/renderer/selection-modal-overlay.ts +1 -1
- package/src/renderer/session-picker-modal.ts +2 -2
- package/src/renderer/settings-modal-helpers.ts +23 -5
- package/src/renderer/settings-modal.ts +129 -39
- package/src/renderer/shell-surface.ts +6 -0
- package/src/renderer/terminal-bg-probe.ts +1 -1
- package/src/renderer/theme.ts +3 -3
- package/src/renderer/tool-call.ts +13 -7
- package/src/renderer/tool-result-summary.ts +1 -1
- package/src/renderer/turn-injection.ts +7 -6
- package/src/renderer/ui-factory.ts +38 -1
- package/src/runtime/bootstrap-command-context.ts +7 -2
- package/src/runtime/bootstrap-command-parts.ts +9 -5
- package/src/runtime/bootstrap-core.ts +41 -41
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap-shell.ts +13 -5
- package/src/runtime/bootstrap.ts +34 -32
- package/src/runtime/channel-composition.ts +46 -0
- package/src/runtime/code-index-services.ts +3 -4
- package/src/runtime/daemon-attach-notices.ts +120 -0
- package/src/runtime/durability-services.ts +90 -0
- package/src/runtime/embedded-service-factories.ts +40 -0
- package/src/runtime/feature-settings.ts +147 -0
- package/src/runtime/fleet-services.ts +41 -0
- package/src/runtime/idle-power-services.ts +85 -0
- package/src/runtime/index.ts +6 -21
- package/src/runtime/interaction-seams.ts +109 -0
- package/src/runtime/legacy-daemon-migration.ts +29 -19
- package/src/runtime/memory-fold.ts +1 -1
- package/src/runtime/notification-dispatch.ts +110 -0
- package/src/runtime/onboarding/apply.ts +3 -29
- package/src/runtime/onboarding/fast-path.ts +39 -6
- package/src/runtime/onboarding/provider-key-capture.ts +126 -0
- package/src/runtime/onboarding/snapshot.ts +0 -1
- package/src/runtime/onboarding/types.ts +9 -3
- package/src/runtime/power-keepawake-remote.ts +75 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/provider-fallback.ts +55 -0
- package/src/runtime/provider-optimizer-wiring.ts +55 -0
- package/src/runtime/release-artifacts.ts +19 -103
- package/src/runtime/resume-notice.ts +1 -1
- package/src/runtime/services.ts +113 -113
- package/src/runtime/session-continuity-hints.ts +38 -0
- package/src/runtime/surface-feature-flags.ts +40 -9
- package/src/runtime/terminal-output-guard.ts +2 -2
- package/src/runtime/trust/workspace-registration.ts +10 -2
- package/src/runtime/trust/workspace-trust.ts +58 -31
- package/src/runtime/ui-services.ts +6 -1
- package/src/runtime/update-check.ts +15 -69
- package/src/runtime/workstream-services.ts +2 -2
- package/src/shell/blocking-input.ts +108 -132
- package/src/shell/recovery-input-helpers.ts +53 -166
- package/src/shell/ui-openers.ts +38 -6
- package/src/utils/format-duration.ts +1 -1
- package/src/verification/verification-ledger.ts +21 -4
- package/src/version.ts +1 -1
- package/src/input/commands/flags-runtime.ts +0 -328
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { MODAL_TONES } from './modal-theme.ts';
|
|
2
|
+
import { infoRow } from './modal-surface-helpers.ts';
|
|
3
|
+
import type {
|
|
4
|
+
ConfigModalActionContext,
|
|
5
|
+
ConfigModalRow,
|
|
6
|
+
ConfigModalSurface,
|
|
7
|
+
ConfigModalView,
|
|
8
|
+
} from '../../input/config-modal-types.ts';
|
|
9
|
+
import type { MintedPairingToken, PublicPairingToken } from '@pellux/goodvibes-sdk/platform/pairing';
|
|
10
|
+
import { formatDeviceLine, shortTokenId } from '../../core/pairing-devices.ts';
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Paired Devices — the settings security-domain device/token management surface.
|
|
14
|
+
// Lists every per-device pairing token (name · created · last-seen · id) and
|
|
15
|
+
// mirrors the pairing.tokens.* gateway verbs: revoke a device, migrate off the
|
|
16
|
+
// legacy shared token, revoke the shared token. Rename needs a name, which the
|
|
17
|
+
// stable-layout modal cannot capture inline, so it routes to the /devices rename
|
|
18
|
+
// command with the row's id pre-filled in the guidance. buildView reads list()
|
|
19
|
+
// fresh every render, so a revoke/migrate reflects immediately.
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/** The token-store capability this surface needs — satisfied by PairingTokenManager. */
|
|
23
|
+
export interface DevicesModalPairingTokens {
|
|
24
|
+
list(): PublicPairingToken[];
|
|
25
|
+
rename(id: string, name: string): boolean;
|
|
26
|
+
revoke(id: string): boolean;
|
|
27
|
+
mintForMigration(input: { readonly name: string }): MintedPairingToken;
|
|
28
|
+
isLegacyRevoked(): boolean;
|
|
29
|
+
revokeLegacyShared(): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DevicesModalDeps {
|
|
33
|
+
readonly pairingTokens: DevicesModalPairingTokens;
|
|
34
|
+
/** Injectable clock so last-seen formatting is deterministic in tests. */
|
|
35
|
+
readonly now?: () => number;
|
|
36
|
+
readonly copyToClipboard?: (text: string) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DEVICE_ROW_PREFIX = 'device:';
|
|
40
|
+
|
|
41
|
+
function deviceIdOf(row: ConfigModalRow | null): string | null {
|
|
42
|
+
return row && row.id.startsWith(DEVICE_ROW_PREFIX) ? row.id.slice(DEVICE_ROW_PREFIX.length) : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class DevicesModalSurface implements ConfigModalSurface {
|
|
46
|
+
readonly name = 'devices-modal';
|
|
47
|
+
readonly title = 'Paired Devices';
|
|
48
|
+
private requestRender: () => void = () => {};
|
|
49
|
+
|
|
50
|
+
constructor(private readonly deps: DevicesModalDeps) {}
|
|
51
|
+
|
|
52
|
+
readonly actions = [
|
|
53
|
+
{ key: 'r', id: 'rename', label: 'rename', enabledFor: (row: ConfigModalRow | null) => deviceIdOf(row) !== null },
|
|
54
|
+
{ key: 'x', id: 'revoke', label: 'revoke', confirm: true, enabledFor: (row: ConfigModalRow | null) => deviceIdOf(row) !== null },
|
|
55
|
+
{ key: 'm', id: 'migrateShared', label: 'migrate off shared token' },
|
|
56
|
+
{ key: 's', id: 'revokeShared', label: 'revoke shared token', confirm: true, enabledFor: () => !this.deps.pairingTokens.isLegacyRevoked() },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
onOpen(requestRender: () => void): void { this.requestRender = requestRender; }
|
|
60
|
+
|
|
61
|
+
buildView(): ConfigModalView {
|
|
62
|
+
const now = this.deps.now?.() ?? Date.now();
|
|
63
|
+
const tokens = this.deps.pairingTokens.list();
|
|
64
|
+
const rows: ConfigModalRow[] = [];
|
|
65
|
+
rows.push(infoRow('intro', 'Devices paired to this daemon. Each has its own token — revoke one without affecting the others.', { dim: true }));
|
|
66
|
+
|
|
67
|
+
if (tokens.length === 0) {
|
|
68
|
+
rows.push(infoRow('empty', 'No devices paired yet — run /pair (or open the pairing QR) to add one.'));
|
|
69
|
+
} else {
|
|
70
|
+
for (const token of tokens) {
|
|
71
|
+
rows.push({ id: `${DEVICE_ROW_PREFIX}${token.id}`, label: formatDeviceLine(token, now) });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const legacyRevoked = this.deps.pairingTokens.isLegacyRevoked();
|
|
76
|
+
rows.push(infoRow(
|
|
77
|
+
'legacy',
|
|
78
|
+
legacyRevoked
|
|
79
|
+
? 'Legacy shared token: revoked.'
|
|
80
|
+
: 'Legacy shared token: active — migrate a device off it (m), then revoke it (s).',
|
|
81
|
+
legacyRevoked ? { dim: true } : { fg: MODAL_TONES.reasoning },
|
|
82
|
+
));
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
title: 'Paired Devices',
|
|
86
|
+
tabs: [{ id: 'devices', label: 'Devices', rows, hints: ['r rename', 'x revoke', 'm migrate off shared', 's revoke shared'] }],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
onAction(id: string, ctx: ConfigModalActionContext): void {
|
|
91
|
+
const tokens = this.deps.pairingTokens;
|
|
92
|
+
|
|
93
|
+
if (id === 'rename') {
|
|
94
|
+
const deviceId = deviceIdOf(ctx.row);
|
|
95
|
+
if (!deviceId) { ctx.setStatus('Select a device row first.'); return; }
|
|
96
|
+
// The stable-layout modal cannot capture a name inline — route to the
|
|
97
|
+
// command with the id pre-filled so the user only types the new name.
|
|
98
|
+
ctx.print(`To rename this device, run: /devices rename ${shortTokenId(deviceId)} <new name>`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (id === 'revoke') {
|
|
103
|
+
const deviceId = deviceIdOf(ctx.row);
|
|
104
|
+
if (!deviceId) { ctx.setStatus('Select a device row first.'); return; }
|
|
105
|
+
const record = tokens.list().find((t) => t.id === deviceId);
|
|
106
|
+
const revoked = tokens.revoke(deviceId);
|
|
107
|
+
ctx.setStatus(revoked ? `Revoked "${record?.name ?? shortTokenId(deviceId)}" — its token stops working now.` : 'That device was already absent.');
|
|
108
|
+
this.requestRender();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (id === 'migrateShared') {
|
|
113
|
+
const minted = tokens.mintForMigration({ name: 'migrated device' });
|
|
114
|
+
if (this.deps.copyToClipboard) {
|
|
115
|
+
this.deps.copyToClipboard(minted.token);
|
|
116
|
+
ctx.print(`Minted "${minted.name}" and copied its one-time token to the clipboard. Pair the device, then revoke the shared token (s).`);
|
|
117
|
+
} else {
|
|
118
|
+
ctx.print(`Minted "${minted.name}" (one-time token): ${minted.token}\nPair the device with it, then revoke the shared token (s).`);
|
|
119
|
+
}
|
|
120
|
+
this.requestRender();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (id === 'revokeShared') {
|
|
125
|
+
if (tokens.isLegacyRevoked()) { ctx.setStatus('The legacy shared token is already revoked.'); return; }
|
|
126
|
+
tokens.revokeLegacyShared();
|
|
127
|
+
ctx.setStatus('Revoked the legacy shared token — devices on it must re-pair with their own token.');
|
|
128
|
+
this.requestRender();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function createDevicesModalSurface(deps: DevicesModalDeps): ConfigModalSurface {
|
|
135
|
+
return new DevicesModalSurface(deps);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Deterministic golden/render fixture: frozen token list + clock so last-seen
|
|
140
|
+
* formatting is stable.
|
|
141
|
+
*/
|
|
142
|
+
export function devicesModalGoldenSurface(): ConfigModalSurface {
|
|
143
|
+
const now = Date.UTC(2026, 6, 13, 12, 0, 0);
|
|
144
|
+
const tokens: PublicPairingToken[] = [
|
|
145
|
+
{ id: 'aaaa1111bbbb2222', name: 'my laptop', createdAt: Date.UTC(2026, 6, 10, 9, 0, 0), lastSeenAt: now - 3 * 60_000 },
|
|
146
|
+
{ id: 'cccc3333dddd4444', name: 'kitchen tablet', createdAt: Date.UTC(2026, 6, 1, 9, 0, 0), lastSeenAt: now - 5 * 60 * 60_000 },
|
|
147
|
+
{ id: 'eeee5555ffff6666', name: 'phone (never scanned)', createdAt: Date.UTC(2026, 6, 13, 8, 0, 0) },
|
|
148
|
+
];
|
|
149
|
+
let legacyRevoked = false;
|
|
150
|
+
return createDevicesModalSurface({
|
|
151
|
+
now: () => now,
|
|
152
|
+
pairingTokens: {
|
|
153
|
+
list: () => tokens,
|
|
154
|
+
rename: () => true,
|
|
155
|
+
revoke: () => true,
|
|
156
|
+
mintForMigration: () => ({ id: 'new', name: 'migrated device', token: 'tok', createdAt: now }),
|
|
157
|
+
isLegacyRevoked: () => legacyRevoked,
|
|
158
|
+
revokeLegacyShared: () => { legacyRevoked = true; },
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
} from '../../input/config-modal-types.ts';
|
|
17
17
|
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
|
-
// Hooks → config-modal surface (
|
|
19
|
+
// Hooks → config-modal surface (group-B port). Two tabs: 'Hooks' (active
|
|
20
20
|
// registrations + contract posture) and 'Activity' (recent runtime activity +
|
|
21
21
|
// managed authoring + last simulation — the panel's expand-activity view, now a
|
|
22
22
|
// dedicated tab). All sources are in-memory (dispatcher/workbench/tracker hold
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
import { KeybindingsManager } from '../../input/keybindings.ts';
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
|
-
// Docs + Shortcuts → 'keybindings' config-modal surface (
|
|
12
|
+
// Docs + Shortcuts → 'keybindings' config-modal surface (group-B port).
|
|
13
13
|
// A MERGE of the retired DocsPanel (tools/models/shortcuts) and the
|
|
14
14
|
// shortcuts-overlay reference. Three tabs: 'Tools', 'Models', and 'Shortcuts'
|
|
15
15
|
// (the categorized keyboard reference — driven live off keybindingsManager so
|
|
@@ -112,7 +112,7 @@ class KeybindingsModalSurface implements ConfigModalSurface {
|
|
|
112
112
|
if (id === 'refresh') { ctx.setStatus('Docs & shortcuts are read live.'); ctx.requestRender(); return; }
|
|
113
113
|
if (id !== 'activate') return;
|
|
114
114
|
if (ctx.tabId === 'tools') {
|
|
115
|
-
//
|
|
115
|
+
// item 4: pass the tool name through as a deep-link target.
|
|
116
116
|
// Honest caveat (documented, not a bug to silently paper over): fleet's
|
|
117
117
|
// ProcessKind set has no 'tool' node — a docs Tools row names a static
|
|
118
118
|
// tool DEFINITION, not a live process, so FleetPanel.receiveDeepLink
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
|
-
// Knowledge (graph) → config-modal surface (
|
|
12
|
+
// Knowledge (graph) → config-modal surface (group-B port). Two tabs:
|
|
13
13
|
// 'Browse' (sources/nodes/issues + schedules) and 'Review' (the open-issue
|
|
14
14
|
// queue) — the panel's browse/review toggle, now host tabs. Issue review
|
|
15
15
|
// mutations (accept/reject/resolve/reopen) route to the `/knowledge
|
|
@@ -38,14 +38,14 @@ interface MarketplaceRow {
|
|
|
38
38
|
const KINDS: readonly EcosystemEntryKind[] = ['plugin', 'skill', 'hook-pack', 'policy-pack'];
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Marketplace → config-modal surface (
|
|
41
|
+
* Marketplace → config-modal surface (group-B port). Local-first
|
|
42
42
|
* publish/import catalog (NOT a remote store) with provenance, compatibility,
|
|
43
43
|
* and install posture. Disk catalog loads happen in refresh() (never in
|
|
44
44
|
* buildView), mirroring the panel's explicit render()/refresh() split.
|
|
45
45
|
* Install/uninstall route to the `/marketplace` command path (charter: no
|
|
46
46
|
* destructive confirm folded into a modal).
|
|
47
47
|
*
|
|
48
|
-
*
|
|
48
|
+
* Honest empty-state: the panel's original "No curated marketplace
|
|
49
49
|
* entries found yet." copy implied a curated remote catalog. It isn't one —
|
|
50
50
|
* loadEcosystemCatalog only ever reads .goodvibes/ecosystem/<kind>s.json under
|
|
51
51
|
* the project/home, populated solely by `/marketplace publish` and
|
|
@@ -118,7 +118,7 @@ class MarketplaceModalSurface implements ConfigModalSurface {
|
|
|
118
118
|
const snapshot = this.deps.readModel?.getSnapshot();
|
|
119
119
|
const rows: ConfigModalRow[] = [];
|
|
120
120
|
|
|
121
|
-
// Honest empty / degraded state
|
|
121
|
+
// Honest empty / degraded state. loadError → degraded banner.
|
|
122
122
|
if (this.loadError) {
|
|
123
123
|
return { title: 'Marketplace', degraded: this.loadError, tabs: [{ id: 'catalog', label: 'Catalog', rows: [] }] };
|
|
124
124
|
}
|
|
@@ -205,7 +205,7 @@ export function createMarketplaceModalSurface(deps: MarketplaceModalDeps): Confi
|
|
|
205
205
|
* Deterministic golden fixture: catalog roots wired at a fresh tmp path that is
|
|
206
206
|
* removed immediately (loadEcosystemCatalog/listInstalledEcosystemEntries guard
|
|
207
207
|
* missing paths with existsSync → [], so refresh() finds nothing and renders the
|
|
208
|
-
*
|
|
208
|
+
* honest empty-state copy). The random tmp path never appears in the
|
|
209
209
|
* rendered lines (the empty-state copy is static), so the golden is byte-stable.
|
|
210
210
|
*/
|
|
211
211
|
export function marketplaceModalGoldenSurface(): ConfigModalSurface {
|
|
@@ -7,9 +7,11 @@ import type {
|
|
|
7
7
|
ConfigModalView,
|
|
8
8
|
} from '../../input/config-modal-types.ts';
|
|
9
9
|
import { memoryRecordTemporalStatus } from '@pellux/goodvibes-sdk/platform/state';
|
|
10
|
+
import type { MemoryConsolidationGatewayResolution, MemoryConsolidationProposal } from '../memory-consolidation-gateway.ts';
|
|
11
|
+
import { classifyConsolidationFetchError } from '../memory-consolidation-gateway.ts';
|
|
10
12
|
|
|
11
13
|
// ---------------------------------------------------------------------------
|
|
12
|
-
// Memory → config-modal surface (
|
|
14
|
+
// Memory → config-modal surface (group-B port). Two tabs: 'All Records'
|
|
13
15
|
// (the full record list) and 'Review Queue' (the operator review queue) — the
|
|
14
16
|
// same split the panel toggled with Tab, now real host tabs. Review-state
|
|
15
17
|
// mutations (reviewed/stale/contradicted/fresh) and record delete route to the
|
|
@@ -63,8 +65,23 @@ export interface MemoryModalDeps {
|
|
|
63
65
|
readonly indexUnavailableReason?: string | null | undefined;
|
|
64
66
|
}>;
|
|
65
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the memory-consolidation-receipts gateway (memory-consolidation-gateway.ts),
|
|
70
|
+
* lazily — called fresh each time the Proposals tab (re)fetches, exactly like
|
|
71
|
+
* the Fleet panel's `resolveGateway` (fleet-gateway.ts / fleet-acts.ts). Absent
|
|
72
|
+
* in a session with no daemon-backed control plane wired: the Proposals tab
|
|
73
|
+
* then renders its "unavailable" honest state without ever attempting a fetch.
|
|
74
|
+
*/
|
|
75
|
+
readonly resolveConsolidationGateway?: () => MemoryConsolidationGatewayResolution;
|
|
66
76
|
}
|
|
67
77
|
|
|
78
|
+
/** The Proposals tab's fetch state — one honest rendering per state, never a blend. */
|
|
79
|
+
type ProposalsStatus =
|
|
80
|
+
| { readonly kind: 'loading' }
|
|
81
|
+
| { readonly kind: 'unavailable'; readonly reason: string }
|
|
82
|
+
| { readonly kind: 'error'; readonly message: string }
|
|
83
|
+
| { readonly kind: 'ready'; readonly proposals: readonly MemoryConsolidationProposal[] };
|
|
84
|
+
|
|
68
85
|
/**
|
|
69
86
|
* Client-side port of the SDK's MemoryStore.reviewQueue ranking
|
|
70
87
|
* (memory-store-helpers.ts reviewQueueScore/isReviewCandidate): all four
|
|
@@ -104,11 +121,14 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
104
121
|
private reviewRecords: MemoryRecordLike[] = [];
|
|
105
122
|
/** Honest note on the last read: a wire failure (client mode) or the index-unavailable fallback reason — never silently dropped. */
|
|
106
123
|
private loadNote: string | null = null;
|
|
124
|
+
/** The Proposals tab's fetch state — starts 'loading' whenever a gateway is wired (see refreshProposals). */
|
|
125
|
+
private proposalsStatus: ProposalsStatus = { kind: 'loading' };
|
|
107
126
|
private requestRender: (() => void) | null = null;
|
|
108
127
|
|
|
109
128
|
constructor(private readonly deps: MemoryModalDeps) {}
|
|
110
129
|
|
|
111
130
|
private readonly reviewGate = (row: ConfigModalRow | null, tabId: string): boolean => tabId === 'review' && row !== null;
|
|
131
|
+
private readonly proposalsGate = (row: ConfigModalRow | null, tabId: string): boolean => tabId === 'proposals' && row !== null;
|
|
112
132
|
|
|
113
133
|
readonly actions = [
|
|
114
134
|
{ key: 'enter', id: 'markReviewed', label: 'reviewed', enabledFor: this.reviewGate },
|
|
@@ -117,6 +137,13 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
117
137
|
{ key: 'f', id: 'markFresh', label: 'fresh', enabledFor: this.reviewGate },
|
|
118
138
|
{ key: 'd', id: 'remove', label: 'delete', enabledFor: (row: ConfigModalRow | null) => row !== null },
|
|
119
139
|
{ key: 'r', id: 'refresh', label: 'refresh' },
|
|
140
|
+
// Jump from a selected proposal to one of its affected records in the
|
|
141
|
+
// Review Queue tab. A DISTINCT key from 'enter' (not shared with
|
|
142
|
+
// markReviewed): ConfigModal.resolveAction matches the FIRST action bound
|
|
143
|
+
// to a key and returns null if THAT one's enabledFor fails, rather than
|
|
144
|
+
// trying the next action bound to the same key — so gating two different
|
|
145
|
+
// 'enter' actions by tabId alone would silently break one of them.
|
|
146
|
+
{ key: 'v', id: 'jumpToReviewQueue', label: 'view in review queue', enabledFor: this.proposalsGate },
|
|
120
147
|
];
|
|
121
148
|
|
|
122
149
|
onOpen(requestRender: () => void): void {
|
|
@@ -129,6 +156,10 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
129
156
|
}
|
|
130
157
|
|
|
131
158
|
private async refresh(): Promise<void> {
|
|
159
|
+
await Promise.all([this.refreshRecords(), this.refreshProposals()]);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private async refreshRecords(): Promise<void> {
|
|
132
163
|
if (!this.deps.memoryRegistry) { this.allRecords = []; this.reviewRecords = []; this.loadNote = null; return; }
|
|
133
164
|
try {
|
|
134
165
|
const result = await this.deps.memoryRegistry.honestSearch({ limit: 100 });
|
|
@@ -142,10 +173,50 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
142
173
|
this.requestRender?.();
|
|
143
174
|
}
|
|
144
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Lazy fetch of memory.consolidation.receipts (memory-consolidation-gateway.ts)
|
|
178
|
+
* — only attempted when this surface opens or refreshes, never polled. No
|
|
179
|
+
* gateway resolver wired at all (session has no daemon-backed control
|
|
180
|
+
* plane) renders the same honest "unavailable" state as a resolved-but-
|
|
181
|
+
* refused gateway (daemon disabled / no control-plane URL) or a 501/404
|
|
182
|
+
* from an older daemon with no consolidation scheduler — three different
|
|
183
|
+
* causes, one bucket, because from the operator's seat they mean the same
|
|
184
|
+
* thing: nothing to show, and no fault of theirs.
|
|
185
|
+
*/
|
|
186
|
+
private async refreshProposals(): Promise<void> {
|
|
187
|
+
if (!this.deps.resolveConsolidationGateway) {
|
|
188
|
+
this.proposalsStatus = { kind: 'unavailable', reason: 'No consolidation gateway wired for this session.' };
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
this.proposalsStatus = { kind: 'loading' };
|
|
192
|
+
this.requestRender?.();
|
|
193
|
+
const resolution = this.deps.resolveConsolidationGateway();
|
|
194
|
+
if (!resolution.available) {
|
|
195
|
+
this.proposalsStatus = { kind: 'unavailable', reason: resolution.reason };
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
const result = await resolution.gateway.fetchReceipts();
|
|
200
|
+
this.proposalsStatus = { kind: 'ready', proposals: result.pendingProposals };
|
|
201
|
+
} catch (error) {
|
|
202
|
+
this.proposalsStatus = classifyConsolidationFetchError(error);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
145
206
|
private recordFrom(id: string): MemoryRecordLike | undefined {
|
|
146
207
|
return this.allRecords.find((r) => r.id === id) ?? this.reviewRecords.find((r) => r.id === id);
|
|
147
208
|
}
|
|
148
209
|
|
|
210
|
+
/** First pending proposal that names a given record id, keyed for the Review Queue's reason correlation (item 3). */
|
|
211
|
+
private proposalsByRecordId(): Map<string, MemoryConsolidationProposal> {
|
|
212
|
+
const map = new Map<string, MemoryConsolidationProposal>();
|
|
213
|
+
if (this.proposalsStatus.kind !== 'ready') return map;
|
|
214
|
+
for (const proposal of this.proposalsStatus.proposals) {
|
|
215
|
+
for (const id of proposal.ids) if (!map.has(id)) map.set(id, proposal);
|
|
216
|
+
}
|
|
217
|
+
return map;
|
|
218
|
+
}
|
|
219
|
+
|
|
149
220
|
private allTab(): ConfigModalTab {
|
|
150
221
|
const rows: ConfigModalRow[] = this.allRecords.map((record) => ({
|
|
151
222
|
id: record.id,
|
|
@@ -162,10 +233,25 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
162
233
|
}
|
|
163
234
|
|
|
164
235
|
private reviewTab(): ConfigModalTab {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
236
|
+
const proposalByRecord = this.proposalsByRecordId();
|
|
237
|
+
const rows: ConfigModalRow[] = this.reviewRecords.map((record) => {
|
|
238
|
+
// A contradiction proposal already carries its reason on the record
|
|
239
|
+
// itself (the daemon sets reviewState 'contradicted' + staleReason); a
|
|
240
|
+
// cross-scope-duplicate proposal marks the record 'fresh' with NO
|
|
241
|
+
// staleReason, so without this correlation those rows are bare and
|
|
242
|
+
// unexplained (item 3) — fall back to the fetched proposal's own
|
|
243
|
+
// reason only when the record didn't already carry one.
|
|
244
|
+
const proposal = record.staleReason ? undefined : proposalByRecord.get(record.id);
|
|
245
|
+
const reasonSuffix = record.staleReason
|
|
246
|
+
? ` (stale: ${record.staleReason})`
|
|
247
|
+
: proposal
|
|
248
|
+
? ` (${proposal.kind}: ${proposal.reason})`
|
|
249
|
+
: '';
|
|
250
|
+
return {
|
|
251
|
+
id: record.id,
|
|
252
|
+
label: `${record.reviewState.padEnd(13)} ${String(record.confidence).padStart(3)}% ${record.summary}${reasonSuffix}${temporalSuffix(record)}`,
|
|
253
|
+
};
|
|
254
|
+
});
|
|
169
255
|
return {
|
|
170
256
|
id: 'review',
|
|
171
257
|
label: 'Review Queue',
|
|
@@ -176,6 +262,40 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
176
262
|
};
|
|
177
263
|
}
|
|
178
264
|
|
|
265
|
+
/**
|
|
266
|
+
* The Proposals tab — WHAT the daemon's idle-time consolidation pass
|
|
267
|
+
* proposed (contradiction / cross-scope-duplicate / stale-delete) and is
|
|
268
|
+
* holding for human judgment, read lazily over memory.consolidation.receipts
|
|
269
|
+
* (memory-consolidation-gateway.ts). Three distinct honest states render as
|
|
270
|
+
* three distinct lines — 'loading' / 'unavailable' / 'error' never collapse
|
|
271
|
+
* into a shared "nothing to show" — plus the true empty state ('ready' with
|
|
272
|
+
* zero proposals) which is a fourth, equally honest line.
|
|
273
|
+
*/
|
|
274
|
+
private proposalsTab(): ConfigModalTab {
|
|
275
|
+
const status = this.proposalsStatus;
|
|
276
|
+
const header = [`pending proposals ${status.kind === 'ready' ? status.proposals.length : 0}`];
|
|
277
|
+
if (status.kind === 'loading') {
|
|
278
|
+
return { id: 'proposals', label: 'Proposals', header, rows: [], emptyText: 'Loading pending proposals…' };
|
|
279
|
+
}
|
|
280
|
+
if (status.kind === 'unavailable') {
|
|
281
|
+
return { id: 'proposals', label: 'Proposals', header, rows: [], emptyText: `Pending proposals unavailable: ${status.reason}` };
|
|
282
|
+
}
|
|
283
|
+
if (status.kind === 'error') {
|
|
284
|
+
return { id: 'proposals', label: 'Proposals', header, rows: [], emptyText: `Could not fetch pending proposals: ${status.message}` };
|
|
285
|
+
}
|
|
286
|
+
const rows: ConfigModalRow[] = status.proposals.map((proposal, index) => ({
|
|
287
|
+
id: `proposal:${index}`,
|
|
288
|
+
label: `[${proposal.kind}] ${proposal.reason} — ${proposal.ids.length} record${proposal.ids.length === 1 ? '' : 's'}: ${proposal.ids.join(', ')}`,
|
|
289
|
+
}));
|
|
290
|
+
return {
|
|
291
|
+
id: 'proposals',
|
|
292
|
+
label: 'Proposals',
|
|
293
|
+
header,
|
|
294
|
+
rows,
|
|
295
|
+
emptyText: 'No pending proposals — nothing awaiting judgment right now.',
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
179
299
|
buildView(): ConfigModalView {
|
|
180
300
|
if (!this.deps.memoryRegistry) {
|
|
181
301
|
return {
|
|
@@ -187,12 +307,13 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
187
307
|
return {
|
|
188
308
|
title: 'Memory',
|
|
189
309
|
...(this.loadNote ? { degraded: this.loadNote } : {}),
|
|
190
|
-
tabs: [this.allTab(), this.reviewTab()],
|
|
310
|
+
tabs: [this.allTab(), this.reviewTab(), this.proposalsTab()],
|
|
191
311
|
};
|
|
192
312
|
}
|
|
193
313
|
|
|
194
314
|
onAction(id: string, ctx: ConfigModalActionContext): void {
|
|
195
315
|
if (id === 'refresh') { void this.refresh(); ctx.setStatus('Reloading memory records...'); return; }
|
|
316
|
+
if (id === 'jumpToReviewQueue') { this.handleJumpToReviewQueue(ctx); return; }
|
|
196
317
|
const record = ctx.row ? this.recordFrom(ctx.row.id) : undefined;
|
|
197
318
|
if (!record) return;
|
|
198
319
|
const review = (state: string, confidence: number, reason?: string): void => {
|
|
@@ -209,6 +330,29 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
209
330
|
case 'remove': void ctx.executeCommand?.('recall', ['remove', record.id]); ctx.setStatus(`Dispatched /recall remove ${record.id}.`); break;
|
|
210
331
|
}
|
|
211
332
|
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* The Proposals tab's "jump": move to the Review Queue tab with one of the
|
|
336
|
+
* selected proposal's affected records selected — the same host mechanism
|
|
337
|
+
* (`ctx.jumpToRow`) any future in-surface jump would use. A proposal's
|
|
338
|
+
* `ids` are not guaranteed to all be inside the review queue's top-24
|
|
339
|
+
* window, so this picks the first id that IS present there rather than
|
|
340
|
+
* assuming the first id always is; when none are, it says so honestly
|
|
341
|
+
* instead of jumping nowhere and leaving the user to wonder why.
|
|
342
|
+
*/
|
|
343
|
+
private handleJumpToReviewQueue(ctx: ConfigModalActionContext): void {
|
|
344
|
+
if (this.proposalsStatus.kind !== 'ready' || !ctx.row) return;
|
|
345
|
+
const index = Number(ctx.row.id.slice('proposal:'.length));
|
|
346
|
+
const proposal = this.proposalsStatus.proposals[index];
|
|
347
|
+
if (!proposal) return;
|
|
348
|
+
const target = proposal.ids.find((id) => this.reviewRecords.some((r) => r.id === id));
|
|
349
|
+
if (!target) {
|
|
350
|
+
ctx.setStatus(`None of this proposal's ${proposal.ids.length} record(s) are in the current review-queue window.`);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
ctx.jumpToRow?.('review', target);
|
|
354
|
+
ctx.setStatus(`Jumped to ${target} in the Review Queue.`);
|
|
355
|
+
}
|
|
212
356
|
}
|
|
213
357
|
|
|
214
358
|
export function createMemoryModalSurface(deps: MemoryModalDeps): ConfigModalSurface {
|
|
@@ -227,9 +371,9 @@ export async function memoryModalGoldenSurface(): Promise<ConfigModalSurface> {
|
|
|
227
371
|
const records: readonly MemoryRecordLike[] = [
|
|
228
372
|
{
|
|
229
373
|
id: 'mem-0000001a', scope: 'project', cls: 'decision',
|
|
230
|
-
summary: '
|
|
231
|
-
detail: 'Applies to
|
|
232
|
-
tags: ['
|
|
374
|
+
summary: 'Panel retirements are batched behind modal builders.',
|
|
375
|
+
detail: 'Applies to the knowledge, memory, and work-plan modals.',
|
|
376
|
+
tags: ['panels', 'modals'], reviewState: 'reviewed', confidence: 90,
|
|
233
377
|
reviewedAt: FIXED_CREATED_AT + 3600000, reviewedBy: 'operator', createdAt: FIXED_CREATED_AT,
|
|
234
378
|
provenance: [{ kind: 'session', ref: 'session-fixed-1' }],
|
|
235
379
|
},
|
|
@@ -3,7 +3,7 @@ import type { ModalSectionStyle } from '../../renderer/modal-factory.ts';
|
|
|
3
3
|
import type { ConfigModalRow } from '../../input/config-modal-types.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Shared formatting helpers for
|
|
6
|
+
* Shared formatting helpers for config-modal surfaces. Row styling reuses
|
|
7
7
|
* DEFAULT_PANEL_PALETTE (the retired panels' palette) so a migrated modal reads
|
|
8
8
|
* as the same surface it replaced — the goldens stay panel-consistent.
|
|
9
9
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { activeUiTones, registerThemeRefresh } from '../../renderer/theme.ts';
|
|
2
2
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
|
-
// Semantic tone tokens for the
|
|
4
|
+
// Semantic tone tokens for the group-B config modals.
|
|
5
5
|
//
|
|
6
6
|
// Derived entirely from the chrome token set so the builders carry NO raw hex
|
|
7
7
|
// color literals (architecture: no-raw-hex-literal-growth). Rebuilt IN PLACE
|