@pellux/goodvibes-tui 2.0.20 → 2.1.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 +70 -0
- package/README.md +16 -16
- package/docs/README.md +4 -4
- package/docs/foundation-artifacts/operator-contract.json +59 -4
- package/package.json +16 -6
- package/src/cli/management.ts +5 -2
- package/src/cli/tui-startup.ts +34 -39
- package/src/core/agent-view-render.ts +157 -0
- package/src/core/approval-alert.ts +49 -11
- package/src/core/bookmark-navigation.ts +7 -1
- package/src/core/budget-breach-notifier.ts +27 -8
- package/src/core/compaction-receipt.ts +1 -1
- package/src/core/context-usage.ts +1 -1
- package/src/core/conversation-compaction-render.ts +3 -3
- package/src/core/conversation-display-only.ts +86 -0
- package/src/core/conversation-event-navigation.ts +27 -0
- package/src/core/conversation-line-cache.ts +216 -447
- package/src/core/conversation-render-context.ts +20 -27
- package/src/core/conversation-rendering.ts +157 -575
- package/src/core/conversation-splash-state.ts +6 -1
- package/src/core/conversation-types.ts +35 -0
- package/src/core/conversation-usage.ts +12 -0
- package/src/core/conversation.ts +130 -131
- package/src/core/long-task-notifier.ts +100 -31
- package/src/core/notices.ts +75 -0
- package/src/core/pairing-tailscale-gateway.ts +1 -1
- package/src/core/process-output-log.ts +143 -0
- package/src/core/rewind-receipt.ts +1 -1
- package/src/core/session-focus.ts +190 -0
- package/src/core/session-resume-core.ts +23 -81
- package/src/core/stream-event-wiring.ts +31 -5
- package/src/core/system-message-noise.ts +2 -2
- package/src/core/system-message-router.ts +16 -18
- package/src/core/turn-event-wiring.ts +272 -33
- package/src/core/work-tree-focus.ts +290 -0
- package/src/core/work-tree-fold-store.ts +143 -0
- package/src/core/work-tree-model.ts +799 -0
- package/src/core/work-tree-render.ts +384 -0
- package/src/core/work-tree-sources.ts +184 -0
- package/src/core/work-tree-wiring.ts +176 -0
- package/src/core/workstream-notification.ts +36 -6
- package/src/core/wrfc-notice-titles.ts +37 -0
- package/src/input/agents-modal-text.ts +119 -0
- package/src/input/agents-modal.ts +699 -0
- package/src/input/bookmark-modal.ts +33 -9
- package/src/input/changes-git.ts +251 -0
- package/src/input/changes-modal.ts +629 -0
- package/src/input/changes-model.ts +125 -0
- package/src/input/command-palette-types.ts +41 -0
- package/src/input/command-palette.ts +272 -0
- package/src/input/command-registry.ts +77 -35
- package/src/input/commands/acp-runtime.ts +19 -9
- package/src/input/commands/channel-runtime.ts +2 -2
- package/src/input/commands/ci-runtime.ts +1 -1
- package/src/input/commands/codebase-runtime.ts +1 -1
- package/src/input/commands/config.ts +7 -1
- package/src/input/commands/context-window.ts +25 -19
- package/src/input/commands/control-room-runtime.ts +18 -18
- package/src/input/commands/conversation-runtime.ts +0 -1
- package/src/input/commands/cost-runtime.ts +29 -20
- package/src/input/commands/diff-runtime.ts +35 -181
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/experience-runtime.ts +3 -7
- package/src/input/commands/fresh-conversation.ts +86 -0
- package/src/input/commands/git-runtime.ts +5 -22
- package/src/input/commands/graph-runtime.ts +1 -1
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +8 -9
- package/src/input/commands/hooks-runtime.ts +2 -2
- package/src/input/commands/hosted-runtime.ts +3 -3
- package/src/input/commands/incident-runtime.ts +3 -3
- package/src/input/commands/intelligence-runtime.ts +6 -9
- package/src/input/commands/knowledge.ts +2 -2
- package/src/input/commands/legacy-panel-command.ts +57 -0
- package/src/input/commands/local-auth-runtime.ts +8 -9
- package/src/input/commands/local-provider-runtime.ts +7 -4
- package/src/input/commands/local-runtime.ts +12 -15
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/local-setup.ts +13 -19
- package/src/input/commands/memory.ts +1 -1
- package/src/input/commands/notify-runtime.ts +9 -0
- package/src/input/commands/operator-runtime.ts +10 -12
- package/src/input/commands/permissions-provenance.ts +3 -3
- package/src/input/commands/planning-runtime.ts +7 -7
- package/src/input/commands/platform-sandbox-runtime.ts +2 -2
- package/src/input/commands/platform-services-runtime.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +2 -2
- package/src/input/commands/policy.ts +3 -3
- package/src/input/commands/provider.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/review-runtime.ts +39 -94
- package/src/input/commands/rewind-runtime.ts +81 -108
- package/src/input/commands/runtime-services.ts +15 -17
- package/src/input/commands/services-runtime.ts +2 -2
- package/src/input/commands/session-content.ts +21 -5
- package/src/input/commands/session-workflow.ts +13 -38
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/shell-core.ts +19 -4
- package/src/input/commands/skills-runtime.ts +2 -2
- package/src/input/commands/status-runtime.ts +13 -0
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/tasks-runtime.ts +2 -8
- package/src/input/commands/work-plan-runtime.ts +4 -4
- package/src/input/commands/workstream-attempts.ts +24 -47
- package/src/input/commands/workstream-runtime.ts +2 -2
- package/src/input/commands/worktree-runtime.ts +3 -3
- package/src/input/commands.ts +3 -1
- package/src/input/composer-editor.ts +44 -0
- package/src/input/concealed-input.ts +1 -1
- package/src/input/config-modal-types.ts +20 -8
- package/src/input/config-modal.ts +106 -105
- package/src/input/confirm-dialog-types.ts +28 -0
- package/src/input/confirm-dialog.ts +70 -0
- package/src/input/delete-key-policy.ts +3 -3
- package/src/input/feed-context-factory.ts +15 -16
- package/src/input/handler-command-route.ts +7 -42
- package/src/input/handler-content-actions.ts +16 -15
- package/src/input/handler-feed-routes.ts +26 -211
- package/src/input/handler-feed.ts +68 -95
- package/src/input/handler-interactions.ts +5 -3
- package/src/input/handler-modal-routes.ts +215 -216
- package/src/input/handler-modal-stack.ts +81 -11
- package/src/input/handler-modal-token-routes.ts +4 -0
- package/src/input/handler-onboarding.ts +0 -1
- package/src/input/handler-picker-routes.ts +65 -107
- package/src/input/handler-session-view-route.ts +149 -0
- package/src/input/handler-shortcuts.ts +16 -154
- package/src/input/handler-types.ts +4 -2
- package/src/input/handler-ui-state.ts +45 -15
- package/src/input/handler-work-tree-route.ts +123 -0
- package/src/input/handler.ts +25 -70
- package/src/input/keybindings.ts +16 -71
- package/src/input/masked-entry-modal.ts +117 -0
- package/src/input/mcp-workspace.ts +48 -17
- package/src/input/model-picker-filter.ts +8 -0
- package/src/input/model-picker-open.ts +99 -0
- package/src/input/model-picker-types.ts +1 -1
- package/src/input/model-picker.ts +92 -9
- package/src/input/notifications-modal.ts +123 -0
- package/src/input/overlay-filter.ts +26 -0
- package/src/input/profile-picker-modal.ts +37 -8
- package/src/input/search.ts +3 -3
- package/src/input/selection-modal-queue.ts +2 -0
- package/src/input/selection-modal.ts +28 -16
- package/src/input/session-picker-modal.ts +33 -8
- package/src/input/settings-modal-activation.ts +7 -2
- package/src/input/settings-modal-data.ts +11 -6
- package/src/input/settings-modal-types.ts +1 -1
- package/src/input/settings-modal.ts +20 -20
- package/src/input/submission-intent.ts +1 -1
- package/src/input/submission-router.ts +4 -9
- package/src/input/surface-modal-host.ts +110 -0
- package/src/input/theme-settings-actions.ts +82 -0
- package/src/input/usage-modal.ts +183 -0
- package/src/input/views.ts +97 -0
- package/src/main.ts +175 -165
- package/src/permissions/hunk-selection.ts +1 -1
- package/src/permissions/prompt-card.ts +511 -0
- package/src/permissions/prompt.ts +59 -622
- package/src/renderer/agents-modal.ts +310 -0
- package/src/renderer/autocomplete-overlay.ts +42 -220
- package/src/renderer/back-to-bottom.ts +80 -0
- package/src/renderer/block-actions-overlay.ts +30 -48
- package/src/renderer/bookmark-modal.ts +48 -79
- package/src/renderer/changes-modal.ts +325 -0
- package/src/renderer/code-block.ts +184 -106
- package/src/renderer/command-palette.ts +93 -0
- package/src/renderer/composer-fragments.ts +4 -4
- package/src/renderer/composer.ts +146 -0
- package/src/renderer/compositor.ts +26 -160
- package/src/renderer/config-modal.ts +185 -81
- package/src/renderer/confirm-dialog.ts +50 -0
- package/src/renderer/context-inspector.ts +116 -154
- package/src/renderer/context-status-hint.ts +4 -4
- package/src/renderer/conversation-layout.ts +20 -0
- package/src/renderer/conversation-overlays.ts +94 -84
- package/src/renderer/conversation-surface.ts +19 -38
- package/src/renderer/diff-tint.ts +98 -0
- package/src/renderer/file-picker-overlay.ts +63 -90
- package/src/renderer/footer-targets.ts +38 -0
- package/src/renderer/git-status.ts +3 -4
- package/src/renderer/header-line.ts +140 -117
- package/src/renderer/help-overlay.ts +133 -219
- package/src/renderer/history-search-overlay.ts +15 -12
- package/src/renderer/lane-graph/bead.ts +800 -0
- package/src/renderer/lane-graph/glyphs.ts +134 -0
- package/src/renderer/lane-graph/layout.ts +248 -0
- package/src/renderer/lane-graph/paint.ts +537 -0
- package/src/renderer/lane-graph/semantic-memo.ts +77 -0
- package/src/renderer/layout-engine.ts +2 -57
- package/src/renderer/layout.ts +7 -13
- package/src/renderer/markdown-inline.ts +2 -1
- package/src/renderer/markdown-table.ts +17 -12
- package/src/renderer/markdown.ts +92 -34
- package/src/renderer/masked-entry-modal.ts +78 -0
- package/src/renderer/mcp-workspace.ts +166 -203
- package/src/renderer/model-workspace.ts +381 -436
- package/src/renderer/notifications-modal.ts +116 -0
- package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
- package/src/renderer/process-view.ts +192 -0
- package/src/renderer/profile-picker-modal.ts +58 -110
- package/src/renderer/search-overlay.ts +6 -3
- package/src/renderer/selection-modal-overlay.ts +87 -327
- package/src/renderer/semantic-diff.ts +2 -2
- package/src/renderer/session-chips.ts +102 -0
- package/src/renderer/session-picker-modal.ts +177 -297
- package/src/renderer/settings-modal-connections.ts +14 -23
- package/src/renderer/settings-modal-context.ts +367 -0
- package/src/renderer/settings-modal-helpers.ts +6 -11
- package/src/renderer/settings-modal.ts +322 -726
- package/src/renderer/shell-surface.ts +277 -175
- package/src/renderer/shortcuts-overlay.ts +217 -0
- package/src/renderer/status-line.ts +475 -0
- package/src/renderer/surface-compose.ts +174 -0
- package/src/renderer/surface-kit-extra.ts +303 -0
- package/src/renderer/surface-kit-list.ts +366 -0
- package/src/renderer/surface-kit-parts.ts +360 -0
- package/src/renderer/surface-kit.ts +568 -0
- package/src/renderer/syntax-highlighter.ts +280 -197
- package/src/renderer/syntax-theme.ts +51 -0
- package/src/renderer/system-message.ts +0 -1
- package/src/renderer/term-caps.ts +42 -1
- package/src/renderer/terminal-bg-probe.ts +116 -19
- package/src/renderer/terminal-palette-probe.ts +322 -0
- package/src/renderer/terminal-palette-reader.ts +183 -0
- package/src/renderer/terminal-palette.ts +58 -0
- package/src/renderer/theme-mode-config.ts +16 -1
- package/src/renderer/theme.ts +234 -207
- package/src/renderer/thinking.ts +0 -1
- package/src/renderer/throbber.ts +188 -0
- package/src/renderer/toast-center.ts +119 -0
- package/src/renderer/ui-factory.ts +39 -602
- package/src/renderer/ui-primitives.ts +8 -25
- package/src/renderer/usage-modal.ts +353 -0
- package/src/renderer/user-message.ts +58 -0
- package/src/renderer/voice-capture-chip.ts +53 -0
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +38 -89
- package/src/runtime/bootstrap-core.ts +13 -15
- package/src/runtime/bootstrap-hook-bridge.ts +12 -21
- package/src/runtime/bootstrap-notifier-sync.ts +17 -1
- package/src/runtime/bootstrap-shell.ts +16 -50
- package/src/runtime/bootstrap.ts +14 -14
- package/src/runtime/client/fleet-union.ts +5 -5
- package/src/runtime/client/hosted-exit.ts +2 -2
- package/src/runtime/client/hosted-sessions.ts +1 -1
- package/src/runtime/client/spine-adoption.ts +1 -1
- package/src/runtime/context-accounting-source.ts +5 -4
- package/src/runtime/index.ts +1 -0
- package/src/runtime/notification-dispatch.ts +102 -29
- package/src/runtime/recovery-offer-wiring.ts +4 -1
- package/src/runtime/recovery-prompt.ts +31 -15
- package/src/runtime/runtime-services-types.ts +10 -6
- package/src/runtime/services.ts +17 -12
- package/src/runtime/session-continuity-hints.ts +2 -5
- package/src/runtime/session-inbound-inputs.ts +1 -1
- package/src/runtime/session-resume-liveness-confirm.ts +14 -2
- package/src/runtime/startup-typeahead-gate.ts +103 -0
- package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
- package/src/runtime/store/domains/index.ts +0 -8
- package/src/runtime/store/selectors/index.ts +0 -28
- package/src/runtime/store/state.ts +0 -8
- package/src/runtime/ui-services.ts +4 -6
- package/src/runtime/usage-tracker.ts +348 -0
- package/src/runtime/wrfc-persistence.ts +4 -4
- package/src/shell/blocking-input.ts +92 -2
- package/src/shell/recovery-input-helpers.ts +1 -1
- package/src/shell/session-views.ts +539 -0
- package/src/shell/status-report.ts +86 -0
- package/src/shell/transcript-scroll.ts +68 -0
- package/src/shell/ui-openers.ts +84 -172
- package/src/shell/view-openers.ts +205 -0
- package/src/tools/wrfc-agent-guard.ts +22 -2
- package/src/utils/format-duration.ts +4 -8
- package/src/utils/splash-lines.ts +9 -1
- package/src/utils/terminal-width.ts +56 -0
- package/src/verification/verification-ledger.ts +18 -16
- package/src/{panels → views}/agent-inspector-shared.ts +2 -2
- package/src/{panels → views}/builtin-modals.ts +22 -21
- package/src/views/builtin-views.ts +135 -0
- package/src/{panels → views}/confirm-state.ts +9 -9
- package/src/{panels → views}/diff-review-model.ts +10 -3
- package/src/{panels → views}/eval-registry.ts +1 -1
- package/src/{panels → views}/fleet-acts.ts +49 -85
- package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
- package/src/{panels → views}/fleet-gateway.ts +5 -5
- package/src/{panels → views}/fleet-observed-render.ts +16 -16
- package/src/{panels → views}/fleet-read-model.ts +9 -9
- package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
- package/src/{panels → views}/fleet-spawn.ts +33 -49
- package/src/{panels → views}/fleet-steer.ts +12 -12
- package/src/{panels → views}/fleet-stop.ts +4 -4
- package/src/{panels → views}/fleet-transcript.ts +26 -27
- package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
- package/src/{panels → views}/hosted-session-feed.ts +6 -6
- package/src/views/modal-surface-registry.ts +41 -0
- package/src/{panels → views}/modals/devices-modal.ts +2 -2
- package/src/{panels → views}/modals/hooks-modal.ts +13 -13
- package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
- package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
- package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
- package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
- package/src/{panels → views}/modals/memory-modal.ts +8 -8
- package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
- package/src/{panels → views}/modals/pairing-modal.ts +9 -9
- package/src/{panels → views}/modals/planning-modal.ts +15 -20
- package/src/{panels → views}/modals/plugins-modal.ts +6 -6
- package/src/{panels → views}/modals/policy-modal.ts +13 -5
- package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
- package/src/{panels → views}/modals/remote-modal.ts +3 -3
- package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
- package/src/{panels → views}/modals/security-modal.ts +22 -16
- package/src/{panels → views}/modals/services-modal.ts +3 -3
- package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
- package/src/{panels → views}/modals/skills-modal.ts +9 -9
- package/src/{panels → views}/modals/subscription-modal.ts +3 -3
- package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
- package/src/views/notifications-feed.ts +314 -0
- package/src/{panels → views}/polish-core.ts +38 -36
- package/src/{panels → views}/polish-tables.ts +35 -26
- package/src/{panels → views}/polish.ts +101 -95
- package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
- package/src/{panels → views}/provider-health-routes.ts +14 -14
- package/src/{panels → views}/search-focus.ts +8 -8
- package/src/{panels → views}/session-maintenance.ts +11 -11
- package/src/views/skills-discovery.ts +159 -0
- package/src/views/view-deps.ts +80 -0
- package/src/views/view-panel-adapter.ts +53 -0
- package/src/{panels → views}/workstream-graph-render.ts +1 -1
- package/src/core/conversation-fold.ts +0 -101
- package/src/core/conversation-turn-structure.ts +0 -464
- package/src/core/conversation-user-receipts.ts +0 -42
- package/src/input/commands/operator-panel-runtime.ts +0 -186
- package/src/input/panel-integration-actions.ts +0 -33
- package/src/input/panel-mouse-geometry.ts +0 -97
- package/src/input/panel-paste-flood-guard.ts +0 -86
- package/src/panels/base-panel.ts +0 -301
- package/src/panels/builtin/agent.ts +0 -27
- package/src/panels/builtin/development.ts +0 -92
- package/src/panels/builtin/knowledge.ts +0 -12
- package/src/panels/builtin/operations.ts +0 -177
- package/src/panels/builtin/session.ts +0 -81
- package/src/panels/builtin/shared.ts +0 -357
- package/src/panels/builtin-panels.ts +0 -28
- package/src/panels/cost-tracker-panel.ts +0 -746
- package/src/panels/diff-panel.ts +0 -797
- package/src/panels/diff-review-panel.ts +0 -395
- package/src/panels/expandable-list-panel.ts +0 -189
- package/src/panels/fleet-deep-link.ts +0 -31
- package/src/panels/fleet-panel.ts +0 -799
- package/src/panels/git-panel.ts +0 -800
- package/src/panels/hosted-session-panel.ts +0 -221
- package/src/panels/index.ts +0 -21
- package/src/panels/local-auth-panel.ts +0 -478
- package/src/panels/notifications-feed.ts +0 -127
- package/src/panels/notifications-panel.ts +0 -160
- package/src/panels/panel-confirm-overlay.ts +0 -72
- package/src/panels/panel-manager.ts +0 -791
- package/src/panels/plugins-panel.ts +0 -371
- package/src/panels/scrollable-list-panel.ts +0 -498
- package/src/panels/skills-panel.ts +0 -454
- package/src/panels/token-budget-panel.ts +0 -688
- package/src/panels/types.ts +0 -191
- package/src/panels/wrfc-panel-format.ts +0 -133
- package/src/renderer/diff-view.ts +0 -134
- package/src/renderer/fleet-tab-strip.ts +0 -56
- package/src/renderer/footer-tips.ts +0 -53
- package/src/renderer/fullscreen-primitives.ts +0 -146
- package/src/renderer/fullscreen-workspace.ts +0 -194
- package/src/renderer/hint-grammar.ts +0 -52
- package/src/renderer/modal-factory.ts +0 -480
- package/src/renderer/overlay-box.ts +0 -154
- package/src/renderer/overlay-viewport.ts +0 -104
- package/src/renderer/panel-composite.ts +0 -145
- package/src/renderer/panel-workspace-bar.ts +0 -54
- package/src/renderer/process-indicator.ts +0 -163
- package/src/renderer/tab-strip.ts +0 -222
- package/src/renderer/text-layout.ts +0 -31
- package/src/renderer/tool-call.ts +0 -276
- package/src/renderer/tool-result-expanded-lines.ts +0 -48
- package/src/renderer/tool-result-summary.ts +0 -219
- package/src/runtime/diagnostics/panels/index.ts +0 -21
- package/src/runtime/diagnostics/panels/ops.ts +0 -156
- package/src/runtime/perf/panel-contracts.ts +0 -32
- package/src/runtime/store/domains/panels.ts +0 -117
- /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
- /package/src/{panels → views}/modals/modal-theme.ts +0 -0
- /package/src/{panels → views}/provider-health-domains.ts +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminal-palette-reader, reads the terminal's colours until it has them.
|
|
3
|
+
*
|
|
4
|
+
* One collection round (TerminalPaletteProbe) at startup is not enough:
|
|
5
|
+
* - tmux drops a query written while no client is attached (a TUI started
|
|
6
|
+
* in a detached session, or before the terminal window attached), and
|
|
7
|
+
* never answers it later;
|
|
8
|
+
* - a reply slower than the window (ssh, a busy multiplexer) arrives after
|
|
9
|
+
* the round closed.
|
|
10
|
+
* So when a round ends without a single colour, the reader asks again on the
|
|
11
|
+
* next sign a terminal is there: a timed retry after the first frame, a
|
|
12
|
+
* focus-in report (tmux sends one on attach), a resize (attach usually
|
|
13
|
+
* resizes), or input from the user. Rounds are capped (maxRounds) and spaced
|
|
14
|
+
* (minRoundSpacingMs), so a terminal that never answers costs a few queries,
|
|
15
|
+
* not one per keystroke. A reply that arrives outside any round is still
|
|
16
|
+
* removed from stdin and, when it carries colours, published: the theme is
|
|
17
|
+
* regenerated and repainted when the palette arrives, however late.
|
|
18
|
+
*
|
|
19
|
+
* Nothing here blocks: first paint never waits on the terminal.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { TerminalPaletteProbe, sweepPaletteReplies, type PaletteProbeResolution, type PaletteReply } from './terminal-palette-probe.ts';
|
|
23
|
+
import { emptyTerminalPalette, type TerminalPalette } from './terminal-palette.ts';
|
|
24
|
+
|
|
25
|
+
/** Timed retry after a round that got no colours (after the first frame). */
|
|
26
|
+
const PALETTE_RETRY_DELAY_MS = 1500;
|
|
27
|
+
/** Rounds per run, the startup round included. */
|
|
28
|
+
const PALETTE_MAX_ROUNDS = 6;
|
|
29
|
+
/** Minimum time between two round starts. */
|
|
30
|
+
const PALETTE_MIN_ROUND_SPACING_MS = 1000;
|
|
31
|
+
|
|
32
|
+
export interface TerminalPaletteReaderOptions {
|
|
33
|
+
/** Writes one batch of queries (OSC 11 + OSC 10 + OSC 4;0..15). */
|
|
34
|
+
readonly writeBatch: () => void;
|
|
35
|
+
/** A palette with at least one colour arrived, or the startup round ended. */
|
|
36
|
+
readonly onPalette: (palette: TerminalPalette) => void;
|
|
37
|
+
/** Receives Esc / Alt+] held at a round's close (see TerminalPaletteProbe onFlush). */
|
|
38
|
+
readonly onFlush?: (bytes: string) => void;
|
|
39
|
+
/** Observer for each round's resolution (tests, diagnostics). */
|
|
40
|
+
readonly onRoundResolve?: (result: PaletteProbeResolution, round: number) => void;
|
|
41
|
+
readonly timeoutMs?: number;
|
|
42
|
+
readonly retryDelayMs?: number;
|
|
43
|
+
readonly maxRounds?: number;
|
|
44
|
+
readonly minRoundSpacingMs?: number;
|
|
45
|
+
readonly now?: () => number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function hasColours(palette: TerminalPalette): boolean {
|
|
49
|
+
return palette.background !== undefined || palette.foreground !== undefined || palette.ansi.some((slot) => slot !== undefined);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function samePalette(a: TerminalPalette, b: TerminalPalette | null): boolean {
|
|
53
|
+
return b !== null && a.background === b.background && a.foreground === b.foreground
|
|
54
|
+
&& a.ansi.every((slot, i) => slot === b.ansi[i]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class TerminalPaletteReader {
|
|
58
|
+
private probe: TerminalPaletteProbe | null = null;
|
|
59
|
+
private rounds = 0;
|
|
60
|
+
private lastRoundAt = Number.NEGATIVE_INFINITY;
|
|
61
|
+
private settled = false;
|
|
62
|
+
private published: TerminalPalette | null = null;
|
|
63
|
+
private retryTimer: ReturnType<typeof setTimeout> | null = null;
|
|
64
|
+
private readonly now: () => number;
|
|
65
|
+
|
|
66
|
+
constructor(private readonly options: TerminalPaletteReaderOptions) {
|
|
67
|
+
this.now = options.now ?? Date.now;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** True once some round (or late reply) produced at least one colour. */
|
|
71
|
+
get hasPalette(): boolean {
|
|
72
|
+
return this.settled;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Rounds started so far. */
|
|
76
|
+
get roundCount(): number {
|
|
77
|
+
return this.rounds;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Start the startup round. The caller may have written the batch itself (skipWrite). */
|
|
81
|
+
start(skipWrite = false): void {
|
|
82
|
+
this.beginRound(skipWrite);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** An OSC 11 body consumed upstream by the background probe (startup round). */
|
|
86
|
+
noteBackgroundSpec(spec: string): void {
|
|
87
|
+
this.probe?.noteBackgroundSpec(spec);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Filter a stdin chunk: strips our replies, returns the rest. */
|
|
91
|
+
feed(chunk: string): string {
|
|
92
|
+
let out: string;
|
|
93
|
+
if (this.probe?.active) {
|
|
94
|
+
out = this.probe.feed(chunk);
|
|
95
|
+
} else {
|
|
96
|
+
const swept = sweepPaletteReplies(chunk);
|
|
97
|
+
out = swept.out;
|
|
98
|
+
if (swept.replies.length > 0) this.acceptLate(swept.replies);
|
|
99
|
+
}
|
|
100
|
+
// Input of any kind (a focus-in on attach, a key) means a terminal is
|
|
101
|
+
// there to answer now.
|
|
102
|
+
if (out.length > 0) this.retry();
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The terminal resized (a client attached, or the window changed). */
|
|
107
|
+
noteResize(): void {
|
|
108
|
+
this.retry();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Stop timers (tests, teardown). */
|
|
112
|
+
dispose(): void {
|
|
113
|
+
if (this.retryTimer !== null) clearTimeout(this.retryTimer);
|
|
114
|
+
this.retryTimer = null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Start another round when one is due. Returns true when it started one. */
|
|
118
|
+
retry(): boolean {
|
|
119
|
+
if (this.settled || this.probe?.active) return false;
|
|
120
|
+
if (this.capped()) return false;
|
|
121
|
+
if (this.now() - this.lastRoundAt < (this.options.minRoundSpacingMs ?? PALETTE_MIN_ROUND_SPACING_MS)) return false;
|
|
122
|
+
this.beginRound(false);
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private capped(): boolean {
|
|
127
|
+
return this.rounds >= (this.options.maxRounds ?? PALETTE_MAX_ROUNDS);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private beginRound(skipWrite: boolean): void {
|
|
131
|
+
this.rounds += 1;
|
|
132
|
+
this.lastRoundAt = this.now();
|
|
133
|
+
const round = this.rounds;
|
|
134
|
+
this.probe = new TerminalPaletteProbe({
|
|
135
|
+
timeoutMs: this.options.timeoutMs,
|
|
136
|
+
onFlush: this.options.onFlush,
|
|
137
|
+
onResolve: (result) => this.roundResolved(result, round),
|
|
138
|
+
});
|
|
139
|
+
if (!skipWrite) this.options.writeBatch();
|
|
140
|
+
this.probe.startTimeout();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private roundResolved(result: PaletteProbeResolution, round: number): void {
|
|
144
|
+
if (hasColours(result.palette)) {
|
|
145
|
+
this.settled = true;
|
|
146
|
+
this.publish(result.palette);
|
|
147
|
+
} else if (this.published === null) {
|
|
148
|
+
// The startup round always publishes, so readers see the probe finished.
|
|
149
|
+
this.publish(result.palette);
|
|
150
|
+
}
|
|
151
|
+
this.options.onRoundResolve?.(result, round);
|
|
152
|
+
if (!this.settled) this.scheduleRetry();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private scheduleRetry(): void {
|
|
156
|
+
if (this.retryTimer !== null) return;
|
|
157
|
+
this.retryTimer = setTimeout(() => {
|
|
158
|
+
this.retryTimer = null;
|
|
159
|
+
// Spacing can defer the timed retry (a round just ran); the cap ends it.
|
|
160
|
+
if (!this.retry() && !this.settled && !this.probe?.active && !this.capped()) this.scheduleRetry();
|
|
161
|
+
}, this.options.retryDelayMs ?? PALETTE_RETRY_DELAY_MS);
|
|
162
|
+
(this.retryTimer as unknown as { unref?: () => void }).unref?.();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private acceptLate(replies: readonly PaletteReply[]): void {
|
|
166
|
+
const base = this.published ?? emptyTerminalPalette();
|
|
167
|
+
const next: TerminalPalette = { ...base, ansi: [...base.ansi] };
|
|
168
|
+
for (const reply of replies) {
|
|
169
|
+
if (reply.hex === undefined) continue;
|
|
170
|
+
if (reply.slot === 'background') next.background = reply.hex;
|
|
171
|
+
else if (reply.slot === 'foreground') next.foreground = reply.hex;
|
|
172
|
+
else next.ansi[reply.slot] = reply.hex;
|
|
173
|
+
}
|
|
174
|
+
if (!hasColours(next) || samePalette(next, this.published)) return;
|
|
175
|
+
this.settled = true;
|
|
176
|
+
this.publish(next);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private publish(palette: TerminalPalette): void {
|
|
180
|
+
this.published = palette;
|
|
181
|
+
this.options.onPalette(palette);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminal-palette.ts, the terminal's own colours as reported at startup.
|
|
3
|
+
*
|
|
4
|
+
* `TerminalPalette` mirrors the SDK type of the same name (the theme engine's
|
|
5
|
+
* input for "use the terminal's palette" themes). Keep the two shapes identical:
|
|
6
|
+
* `background` from OSC 11, `foreground` from OSC 10, and `ansi[0..15]` from
|
|
7
|
+
* OSC 4;N. Every value is `#rrggbb`; a slot the terminal did not answer is
|
|
8
|
+
* `undefined`.
|
|
9
|
+
*
|
|
10
|
+
* The store beside the type holds the result of the startup probe
|
|
11
|
+
* (terminal-palette-probe.ts). It stays `null` until the probe finishes
|
|
12
|
+
* (all replies in, or the window closes). Nothing renders from it yet.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Mirrors the SDK `TerminalPalette` type. `ansi` always has 16 entries. */
|
|
16
|
+
export type TerminalPalette = {
|
|
17
|
+
background?: string;
|
|
18
|
+
foreground?: string;
|
|
19
|
+
ansi: Array<string | undefined>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Number of ANSI palette slots queried (OSC 4;0 .. OSC 4;15). */
|
|
23
|
+
export const TERMINAL_PALETTE_ANSI_SLOTS = 16;
|
|
24
|
+
|
|
25
|
+
/** A palette with every slot unanswered. */
|
|
26
|
+
export function emptyTerminalPalette(): TerminalPalette {
|
|
27
|
+
return { ansi: new Array<string | undefined>(TERMINAL_PALETTE_ANSI_SLOTS).fill(undefined) };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let probedPalette: TerminalPalette | null = null;
|
|
31
|
+
const listeners = new Set<(palette: TerminalPalette) => void>();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The palette reported by the terminal at startup, or null when the probe has
|
|
35
|
+
* not finished (or never ran: non-TTY). A finished probe with no replies yields
|
|
36
|
+
* a palette whose slots are all undefined.
|
|
37
|
+
*/
|
|
38
|
+
export function getTerminalPalette(): TerminalPalette | null {
|
|
39
|
+
return probedPalette;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Record the probe result and notify listeners. Called by the probe once. */
|
|
43
|
+
export function setTerminalPalette(palette: TerminalPalette): void {
|
|
44
|
+
probedPalette = palette;
|
|
45
|
+
for (const listener of listeners) listener(palette);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Subscribe to the probe result. Returns an unsubscribe function. */
|
|
49
|
+
export function onTerminalPalette(listener: (palette: TerminalPalette) => void): () => void {
|
|
50
|
+
listeners.add(listener);
|
|
51
|
+
return () => { listeners.delete(listener); };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Test hook: forget the stored palette and listeners. */
|
|
55
|
+
export function resetTerminalPaletteForTests(): void {
|
|
56
|
+
probedPalette = null;
|
|
57
|
+
listeners.clear();
|
|
58
|
+
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* without pulling in the stateful probe class.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import type
|
|
22
|
+
import { normalizeThemeName, type ThemeModeSetting } from './theme.ts';
|
|
23
23
|
import type { ConfigKey, ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
24
24
|
|
|
25
25
|
/** Config key backing the appearance/theme-mode preference (see module doc). */
|
|
@@ -53,3 +53,18 @@ export function resolveConfiguredThemeMode(
|
|
|
53
53
|
return THEME_MODE_DEFAULT;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
/** Config key backing the color theme (bundled name, 'system', or legacy 'vaporwave'). */
|
|
58
|
+
export const THEME_NAME_CONFIG_KEY: ConfigKey = 'display.theme';
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Read the configured theme name, normalized: 'vaporwave' maps to
|
|
62
|
+
* 'goodvibes-neon' and an unset or unknown value maps to the default theme.
|
|
63
|
+
*/
|
|
64
|
+
export function resolveConfiguredThemeName(configManager: Pick<ConfigManager, 'get'>): string {
|
|
65
|
+
try {
|
|
66
|
+
return normalizeThemeName(configManager.get(THEME_NAME_CONFIG_KEY));
|
|
67
|
+
} catch {
|
|
68
|
+
return normalizeThemeName(undefined);
|
|
69
|
+
}
|
|
70
|
+
}
|