@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,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* renderShortcutsOverlay, the keyboard shortcuts modal (/shortcuts), drawn
|
|
3
|
+
* like the concept's "keys" screen: columns of key / action pairs, keys bold,
|
|
4
|
+
* actions muted, grouped under ✦ headers, with the always-live search row
|
|
5
|
+
* filtering by key or action. Reflects the live keybindings table (user
|
|
6
|
+
* overrides included).
|
|
7
|
+
*
|
|
8
|
+
* Two columns below a 170-column screen, three from 170 up (one on a narrow
|
|
9
|
+
* modal). The modal takes 86% of the screen without the usual 124-column cap,
|
|
10
|
+
* so each description gets room to stay on one line; the key column is as wide
|
|
11
|
+
* as the longest key (up to 18). Keys and actions that still do not fit wrap
|
|
12
|
+
* inside their column; the grid scrolls when it is taller than the modal.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { KeybindingsManager } from '../input/keybindings.ts';
|
|
16
|
+
import type { OverlayFilter } from '../input/overlay-filter.ts';
|
|
17
|
+
import { getDisplayWidth } from '../utils/terminal-width.ts';
|
|
18
|
+
import { activeTokens } from './theme.ts';
|
|
19
|
+
import {
|
|
20
|
+
NARROW_MODAL_BREAKPOINT,
|
|
21
|
+
beginModal,
|
|
22
|
+
finishModal,
|
|
23
|
+
searchRow,
|
|
24
|
+
scrollCountText,
|
|
25
|
+
standardModalWidth,
|
|
26
|
+
wrapLines,
|
|
27
|
+
MODAL_MARK_INSET,
|
|
28
|
+
type KitHint,
|
|
29
|
+
type SurfaceCanvas,
|
|
30
|
+
type SurfaceLayer,
|
|
31
|
+
} from './surface-kit.ts';
|
|
32
|
+
import { drawTextBlock } from './surface-kit-extra.ts';
|
|
33
|
+
|
|
34
|
+
interface ShortcutGroup {
|
|
35
|
+
readonly title: string;
|
|
36
|
+
readonly items: ReadonlyArray<readonly [key: string, action: string]>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Keys that are real but are not KeyAction entries (context chords, literal characters). */
|
|
40
|
+
const HARDCODED_ROWS: ReadonlyArray<readonly [string, string]> = [
|
|
41
|
+
['Up / Down', 'Recall input history; Down at bottom focuses the process indicator'],
|
|
42
|
+
['PageUp / PageDn', 'Scroll by full page'],
|
|
43
|
+
['Home / End', 'Jump to start / end of line'],
|
|
44
|
+
['n / N (search)', 'Next / previous match'],
|
|
45
|
+
['Mouse wheel', 'Scroll the conversation (or the open modal)'],
|
|
46
|
+
['Enter', 'Submit message (composer empty: open the block-actions menu)'],
|
|
47
|
+
['Shift+Enter', 'Insert newline'],
|
|
48
|
+
['@', 'Open file picker'],
|
|
49
|
+
['/', 'Slash command mode'],
|
|
50
|
+
['Esc', 'Close overlay / clear prompt / leave an agent or process view / interrupt main'],
|
|
51
|
+
['Shift+Tab', 'Cycle permission mode; with session chips showing and an empty composer, the previous session'],
|
|
52
|
+
['F2', 'Open Agents'],
|
|
53
|
+
['? or /help', 'Open the command browser (search & run any command)'],
|
|
54
|
+
// Precedence: a partial path under the cursor claims Tab for completion,
|
|
55
|
+
// otherwise it collapses or expands the nearest block.
|
|
56
|
+
['Tab', 'Path-complete, else collapse/expand block; with session chips showing and an empty composer, the next session'],
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
/** Where the views live now (they open as modals over the conversation). */
|
|
60
|
+
const VIEW_ROWS: ReadonlyArray<readonly [string, string]> = [
|
|
61
|
+
['/usage', 'Usage: context, tokens and cost'],
|
|
62
|
+
['/changes', 'Changes: files, diff, review'],
|
|
63
|
+
['/notifications', 'Notification history'],
|
|
64
|
+
['Esc', 'Close the top modal (one level)'],
|
|
65
|
+
['Enter on lane', 'Open that agent full screen; typing there steers it'],
|
|
66
|
+
['Enter on ▶ bead', 'Open that background process\'s live output'],
|
|
67
|
+
['ctrl+x in view', 'Stop the agent or process shown (press twice)'],
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
/** Every shortcut group, generated from the live keybindings table where it can be. */
|
|
71
|
+
function shortcutGroups(keybindingsManager: KeybindingsManager): ShortcutGroup[] {
|
|
72
|
+
const all = keybindingsManager.getAll();
|
|
73
|
+
const combos = (entry: (typeof all)[number]): string => entry.combos.map((c) => keybindingsManager.formatCombo(c)).join(', ');
|
|
74
|
+
return [
|
|
75
|
+
{ title: 'Navigation & editing', items: HARDCODED_ROWS },
|
|
76
|
+
{ title: 'Actions', items: all.map((e) => [combos(e), e.description] as const) },
|
|
77
|
+
{ title: 'Views', items: VIEW_ROWS },
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function filterGroups(groups: readonly ShortcutGroup[], query: string): ShortcutGroup[] {
|
|
82
|
+
const q = query.trim().toLowerCase();
|
|
83
|
+
if (!q) return groups.filter((g) => g.items.length > 0);
|
|
84
|
+
return groups
|
|
85
|
+
.map((g) => ({
|
|
86
|
+
title: g.title,
|
|
87
|
+
items: g.title.toLowerCase().includes(q)
|
|
88
|
+
? g.items
|
|
89
|
+
: g.items.filter(([key, action]) => key.toLowerCase().includes(q) || action.toLowerCase().includes(q)),
|
|
90
|
+
}))
|
|
91
|
+
.filter((g) => g.items.length > 0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** One drawn row of one column. */
|
|
95
|
+
type GridCell =
|
|
96
|
+
| { readonly kind: 'blank' }
|
|
97
|
+
| { readonly kind: 'header'; readonly text: string }
|
|
98
|
+
| { readonly kind: 'pair'; readonly key: string; readonly action: string };
|
|
99
|
+
|
|
100
|
+
interface Block { readonly cells: GridCell[]; readonly group: string; readonly header: boolean }
|
|
101
|
+
|
|
102
|
+
function blocksFor(groups: readonly ShortcutGroup[], keyW: number, actionW: number): Block[] {
|
|
103
|
+
const blocks: Block[] = [];
|
|
104
|
+
groups.forEach((g, gi) => {
|
|
105
|
+
blocks.push({ cells: [...(gi > 0 ? [{ kind: 'blank' as const }] : []), { kind: 'header', text: g.title }], group: g.title, header: true });
|
|
106
|
+
for (const [key, action] of g.items) {
|
|
107
|
+
const keys = wrapLines(key, keyW);
|
|
108
|
+
const actions = wrapLines(action, actionW);
|
|
109
|
+
const cells: GridCell[] = [];
|
|
110
|
+
for (let k = 0; k < Math.max(keys.length, actions.length); k++) cells.push({ kind: 'pair', key: keys[k] ?? '', action: actions[k] ?? '' });
|
|
111
|
+
blocks.push({ cells, group: g.title, header: false });
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
return blocks;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Split blocks into `n` balanced columns; a header never ends a column, a continued group repeats its header. */
|
|
118
|
+
function columnsFor(blocks: readonly Block[], n: number): GridCell[][] {
|
|
119
|
+
const total = blocks.reduce((sum, b) => sum + b.cells.length, 0);
|
|
120
|
+
const target = Math.ceil(total / n);
|
|
121
|
+
const cols: GridCell[][] = [[]];
|
|
122
|
+
blocks.forEach((block, i) => {
|
|
123
|
+
let col = cols[cols.length - 1]!;
|
|
124
|
+
const next = blocks[i + 1];
|
|
125
|
+
const need = block.cells.length + (block.header && next && !next.header ? next.cells.length : 0);
|
|
126
|
+
if (col.length > 0 && col.length + need > target && cols.length < n) {
|
|
127
|
+
col = [];
|
|
128
|
+
cols.push(col);
|
|
129
|
+
if (!block.header) col.push({ kind: 'header', text: block.group });
|
|
130
|
+
}
|
|
131
|
+
const cells = col.length === 0 ? block.cells.filter((c) => c.kind !== 'blank') : block.cells;
|
|
132
|
+
col.push(...cells);
|
|
133
|
+
});
|
|
134
|
+
return cols;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function drawCell(canvas: SurfaceCanvas, x: number, y: number, keyW: number, cell: GridCell): void {
|
|
138
|
+
const t = activeTokens();
|
|
139
|
+
if (cell.kind === 'header') {
|
|
140
|
+
canvas.put(x - MODAL_MARK_INSET, y, '✦', { fg: t.brandEnd });
|
|
141
|
+
canvas.put(x, y, cell.text.toLowerCase(), { fg: t.accent, bold: true });
|
|
142
|
+
} else if (cell.kind === 'pair') {
|
|
143
|
+
canvas.put(x, y, cell.key, { fg: t.text, bold: true });
|
|
144
|
+
canvas.put(x + keyW + 2, y, cell.action, { fg: t.textMuted });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const HINTS: readonly KitHint[] = [['↑↓', 'scroll'], ['type', 'to filter']];
|
|
149
|
+
const COLUMN_GAP = 4;
|
|
150
|
+
/** Screen width from which the grid has three columns instead of two. */
|
|
151
|
+
const SHORTCUTS_THREE_COLUMN_SCREEN = 170;
|
|
152
|
+
/** Widest key column; longer key lists wrap inside it. */
|
|
153
|
+
const MAX_KEY_WIDTH = 18;
|
|
154
|
+
/** Below this inner width the grid is one column. */
|
|
155
|
+
const ONE_COLUMN_INNER = 60;
|
|
156
|
+
|
|
157
|
+
/** The shortcuts modal's width: the standard 86% rule without the 124-column cap. */
|
|
158
|
+
function shortcutsModalWidth(screenW: number): number {
|
|
159
|
+
if (screenW < NARROW_MODAL_BREAKPOINT) return standardModalWidth(screenW);
|
|
160
|
+
return Math.max(standardModalWidth(screenW), Math.min(screenW - 2, Math.round(screenW * 0.86)));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Columns in the grid for a screen width and the modal's inner width. */
|
|
164
|
+
function shortcutsColumnCount(screenW: number, inner: number): number {
|
|
165
|
+
if (inner < ONE_COLUMN_INNER) return 1;
|
|
166
|
+
return screenW >= SHORTCUTS_THREE_COLUMN_SCREEN ? 3 : 2;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Render the keyboard shortcuts modal as a SurfaceLayer in screen coordinates.
|
|
171
|
+
* `filter` holds the search row's query; the renderer records how far the
|
|
172
|
+
* grid can scroll in it.
|
|
173
|
+
*/
|
|
174
|
+
export function renderShortcutsOverlay(
|
|
175
|
+
screenWidth: number,
|
|
176
|
+
screenHeight: number,
|
|
177
|
+
keybindingsManager: KeybindingsManager,
|
|
178
|
+
scrollOffset = 0,
|
|
179
|
+
filter?: OverlayFilter,
|
|
180
|
+
): SurfaceLayer {
|
|
181
|
+
const t = activeTokens();
|
|
182
|
+
const query = filter?.query ?? '';
|
|
183
|
+
const f = beginModal(screenWidth, screenHeight, { title: 'Keyboard shortcuts', sub: 'customize with /keybindings', hints: HINTS, width: shortcutsModalWidth(screenWidth) });
|
|
184
|
+
const all = shortcutGroups(keybindingsManager);
|
|
185
|
+
const groups = filterGroups(all, query);
|
|
186
|
+
const totalPairs = all.reduce((n, g) => n + g.items.length, 0);
|
|
187
|
+
const shown = groups.reduce((n, g) => n + g.items.length, 0);
|
|
188
|
+
searchRow(f, f.top, query, 'Filter shortcuts', query ? `${shown} of ${totalPairs}` : `${totalPairs} shortcuts`);
|
|
189
|
+
|
|
190
|
+
const top = f.top + 2;
|
|
191
|
+
const inner = f.r - f.l + 1;
|
|
192
|
+
if (groups.length === 0) {
|
|
193
|
+
if (filter) filter.maxScroll = 0;
|
|
194
|
+
drawTextBlock(f.canvas, f.l, top, inner, [{ text: `No shortcuts match "${query}".`, style: { fg: t.textMuted } }], f.bottom);
|
|
195
|
+
return finishModal(f);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const n = shortcutsColumnCount(screenWidth, inner);
|
|
199
|
+
const colW = Math.max(8, Math.floor((inner - COLUMN_GAP * (n - 1)) / n));
|
|
200
|
+
// Measured over every shortcut, not just the filtered ones, so filtering never shifts the columns.
|
|
201
|
+
const longestKey = all.reduce((w, g) => g.items.reduce((m, [key]) => Math.max(m, getDisplayWidth(key)), w), 0);
|
|
202
|
+
const keyW = Math.max(4, Math.min(MAX_KEY_WIDTH, longestKey, Math.floor(colW * 0.45)));
|
|
203
|
+
const actionW = Math.max(4, colW - keyW - 2);
|
|
204
|
+
const cols = columnsFor(blocksFor(groups, keyW, actionW), n);
|
|
205
|
+
const gridH = Math.max(...cols.map((c) => c.length));
|
|
206
|
+
const capacity = Math.max(1, f.bottom - top + 1);
|
|
207
|
+
const maxScroll = Math.max(0, gridH - capacity);
|
|
208
|
+
if (filter) filter.maxScroll = maxScroll;
|
|
209
|
+
const offset = Math.max(0, Math.min(scrollOffset, maxScroll));
|
|
210
|
+
|
|
211
|
+
cols.forEach((col, c) => {
|
|
212
|
+
const x = f.l + c * (colW + COLUMN_GAP);
|
|
213
|
+
for (let k = 0; k < capacity && offset + k < col.length; k++) drawCell(f.canvas, x, top + k, keyW, col[offset + k]!);
|
|
214
|
+
});
|
|
215
|
+
f.hintRight = scrollCountText(offset, Math.max(0, gridH - offset - capacity));
|
|
216
|
+
return finishModal(f);
|
|
217
|
+
}
|