@pellux/goodvibes-tui 2.0.21 → 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 +58 -0
- package/README.md +16 -16
- package/docs/README.md +4 -4
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- 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
|
@@ -1,46 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* renderHelpOverlay,
|
|
2
|
+
* renderHelpOverlay, the /commands reference: keyboard shortcuts plus the
|
|
3
|
+
* full slash-command list, drawn with the modal surface kit as one grouped,
|
|
4
|
+
* filterable kit list (✦ group headers, each entry's description on the left
|
|
5
|
+
* and its key or command right-aligned, muted). The search row is always
|
|
6
|
+
* live; ↑↓ scroll.
|
|
3
7
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
8
|
+
* `?` and `/help` open the searchable command browser (a selection modal that
|
|
9
|
+
* runs the picked command) instead; that is a different, complementary
|
|
10
|
+
* surface, not this one.
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
|
-
import { type Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
10
|
-
import { ModalFactory } from './modal-factory.ts';
|
|
11
13
|
import type { SlashCommand } from '../input/command-registry.ts';
|
|
12
14
|
import type { KeybindingsManager } from '../input/keybindings.ts';
|
|
13
|
-
import {
|
|
14
|
-
import { getVisibleWindow } from './surface-layout.ts';
|
|
15
|
-
import { formatHints } from './hint-grammar.ts';
|
|
15
|
+
import type { OverlayFilter } from '../input/overlay-filter.ts';
|
|
16
16
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
17
|
+
import { activeTokens } from './theme.ts';
|
|
18
|
+
import {
|
|
19
|
+
beginModal,
|
|
20
|
+
finishModal,
|
|
21
|
+
searchRow,
|
|
22
|
+
scrollCountText,
|
|
23
|
+
type KitHint,
|
|
24
|
+
type SurfaceLayer,
|
|
25
|
+
} from './surface-kit.ts';
|
|
26
|
+
import { drawList, listScrollEnd, type KitRow } from './surface-kit-list.ts';
|
|
27
|
+
import { drawTextBlock } from './surface-kit-extra.ts';
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
if (row.startsWith(' \u2500')) return { type: 'separator' as const };
|
|
25
|
-
return { type: 'text' as const, content: row };
|
|
26
|
-
});
|
|
29
|
+
export { renderShortcutsOverlay } from './shortcuts-overlay.ts';
|
|
30
|
+
|
|
31
|
+
interface HelpGroup {
|
|
32
|
+
readonly title: string;
|
|
33
|
+
readonly entries: Array<{ readonly label: string; readonly right: string }>;
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* @param width Terminal width.
|
|
34
|
-
* @param commands List of registered slash commands.
|
|
35
|
-
* @param scrollOffset Number of lines scrolled (for navigation).
|
|
37
|
+
* The row strings (" Title", " ────", " key\tdescription", "") become
|
|
38
|
+
* groups: a title row opens a group, rule rows are dropped, entry rows split
|
|
39
|
+
* at the tab into key (right-aligned) and description (the row's label).
|
|
36
40
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
41
|
+
function groupsFromRows(rows: readonly string[]): HelpGroup[] {
|
|
42
|
+
const groups: HelpGroup[] = [];
|
|
43
|
+
for (const row of rows) {
|
|
44
|
+
const text = row.trim();
|
|
45
|
+
if (!text || text.startsWith('\u2500')) continue;
|
|
46
|
+
const tab = text.indexOf('\t');
|
|
47
|
+
if (tab < 0) {
|
|
48
|
+
groups.push({ title: text, entries: [] });
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (groups.length === 0) groups.push({ title: 'Commands', entries: [] });
|
|
52
|
+
groups[groups.length - 1]!.entries.push({ right: text.slice(0, tab).trim(), label: text.slice(tab + 1).trim() });
|
|
53
|
+
}
|
|
54
|
+
return groups.filter((g) => g.entries.length > 0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Every help group for the live keybindings and registry. */
|
|
58
|
+
function helpGroups(keybindingsManager: KeybindingsManager, commands?: SlashCommand[]): HelpGroup[] {
|
|
44
59
|
const kb = (action: Parameters<typeof keybindingsManager.getComboLabel>[0]) => keybindingsManager.getComboLabel(action);
|
|
45
60
|
|
|
46
61
|
const hasCommand = (name: string): boolean => {
|
|
@@ -55,53 +70,38 @@ export function renderHelpOverlay(
|
|
|
55
70
|
return false;
|
|
56
71
|
};
|
|
57
72
|
|
|
58
|
-
const shortcutLine = (label: string, desc: string): string => ` ${label
|
|
59
|
-
|
|
60
|
-
// Enumerate EVERY workspace/panel binding straight from the live keybindings
|
|
61
|
-
// table so each rebindable action (including user overrides) is discoverable
|
|
62
|
-
// here and stays in lockstep with /keybindings. Any action id prefixed
|
|
63
|
-
// `panel-` is a workspace affordance.
|
|
64
|
-
const panelBindingRows: string[] = keybindingsManager
|
|
65
|
-
.getAll()
|
|
66
|
-
.filter((entry) => entry.action.startsWith('panel-'))
|
|
67
|
-
.map((entry) => shortcutLine(keybindingsManager.getComboLabel(entry.action), entry.description));
|
|
73
|
+
const shortcutLine = (label: string, desc: string): string => ` ${label}\t${desc}`;
|
|
68
74
|
|
|
69
75
|
// Keyboard shortcut sections
|
|
70
76
|
const shortcutRows: string[] = [
|
|
71
77
|
' Core Navigation',
|
|
72
78
|
' ' + '\u2500'.repeat(40),
|
|
73
|
-
` ${'Up / Down'
|
|
74
|
-
` ${'PageUp / PageDn'
|
|
75
|
-
` ${kb('search')
|
|
79
|
+
` ${'Up / Down'}\tRecall input history; Down at bottom focuses the process indicator`,
|
|
80
|
+
` ${'PageUp / PageDn'}\tScroll by full page`,
|
|
81
|
+
` ${kb('search')}\tSearch conversation (Ctrl+F)`,
|
|
76
82
|
'',
|
|
77
83
|
' Prompt And Editing',
|
|
78
84
|
' ' + '\u2500'.repeat(40),
|
|
79
|
-
` ${'Enter'
|
|
80
|
-
` ${'Shift+Enter'
|
|
81
|
-
` ${kb('paste')
|
|
82
|
-
` ${(kb('undo') + ' / ' + kb('redo'))
|
|
85
|
+
` ${'Enter'}\tSubmit message (composer empty: open the block-actions menu)`,
|
|
86
|
+
` ${'Shift+Enter'}\tInsert newline`,
|
|
87
|
+
` ${kb('paste')}\tPaste (image priority)`,
|
|
88
|
+
` ${(kb('undo') + ' / ' + kb('redo'))}\tUndo / redo`,
|
|
83
89
|
shortcutLine('(paste >8 lines)', 'Folds to [TEXT: pN, M lines]; /pastes previews it before you submit'),
|
|
84
90
|
'',
|
|
85
|
-
' Overlays And
|
|
91
|
+
' Overlays And Views',
|
|
86
92
|
' ' + '\u2500'.repeat(40),
|
|
87
93
|
shortcutLine('? or /help', 'Open the command browser (search & run any command)'),
|
|
88
94
|
shortcutLine('/commands', 'This reference: keyboard shortcuts + full command list'),
|
|
89
95
|
shortcutLine('/shortcuts', 'Keyboard-shortcuts-only reference (a separate, generated overlay)'),
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
shortcutLine('
|
|
94
|
-
|
|
95
|
-
'',
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
' In-Panel Controls',
|
|
100
|
-
' ' + '\u2500'.repeat(40),
|
|
101
|
-
shortcutLine('j / k', 'Move selection down / up'),
|
|
102
|
-
shortcutLine('g / G', 'Jump to top / bottom'),
|
|
103
|
-
shortcutLine('Enter / Esc', 'Activate / dismiss or leave panel'),
|
|
104
|
-
shortcutLine('/', 'Filter the list'),
|
|
96
|
+
shortcutLine(kb('command-palette'), 'Command palette: every command and view'),
|
|
97
|
+
shortcutLine(`F2 / ${kb('open-agents')}`, 'Agents: running agents, chains and hosted sessions'),
|
|
98
|
+
shortcutLine('/usage', 'Usage: context, tokens and cost'),
|
|
99
|
+
shortcutLine('/changes', 'Changes: files, diff, review'),
|
|
100
|
+
shortcutLine('/notifications', 'Notification history'),
|
|
101
|
+
shortcutLine('Esc', 'Close the top modal (one level)'),
|
|
102
|
+
// A partial path under the cursor claims Tab for completion; otherwise it
|
|
103
|
+
// collapses or expands the nearest block.
|
|
104
|
+
shortcutLine('Tab', 'Path-complete, else collapse/expand block'),
|
|
105
105
|
'',
|
|
106
106
|
];
|
|
107
107
|
|
|
@@ -110,7 +110,10 @@ export function renderHelpOverlay(
|
|
|
110
110
|
// Commands not registered in the live registry are omitted at render time.
|
|
111
111
|
const FEATURED_COMMANDS: Array<[name: string, argHint: string, desc: string]> = [
|
|
112
112
|
['onboarding', '', 'Open the onboarding wizard with current settings preloaded'],
|
|
113
|
-
['
|
|
113
|
+
['agents', '', 'Agents: running agents, chains and hosted sessions'],
|
|
114
|
+
['usage', '', 'Usage: context, tokens and cost'],
|
|
115
|
+
['changes', '', 'Changes: files, tinted diff, review and staging'],
|
|
116
|
+
['notifications','', 'Notification history'],
|
|
114
117
|
['settings', '', 'Settings and config browser'],
|
|
115
118
|
['provider', '', 'Choose provider or model family'],
|
|
116
119
|
['subscription', '', 'Review provider logins and subscriptions'],
|
|
@@ -122,15 +125,13 @@ export function renderHelpOverlay(
|
|
|
122
125
|
['incident', '', 'Incident workspace and export flows'],
|
|
123
126
|
['knowledge', '', 'Durable knowledge and review queue'],
|
|
124
127
|
['hooks', '', 'Hook workbench and runtime activity'],
|
|
125
|
-
['orchestration','', 'Graph and recursive-agent control room'],
|
|
126
|
-
['communication','', 'Structured agent communication workspace'],
|
|
127
128
|
['tasks', '', 'Task surface for list/show/pause/resume/output'],
|
|
128
129
|
];
|
|
129
130
|
|
|
130
131
|
// Build command rows from featured list, filtering out unregistered commands.
|
|
131
132
|
function featuredRow(name: string, argHint: string, desc: string): string {
|
|
132
133
|
const invocation = argHint ? `/${name} ${argHint}` : `/${name}`;
|
|
133
|
-
return ` ${invocation
|
|
134
|
+
return ` ${invocation}\t${desc}`;
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
const quickStartRows: string[] = [];
|
|
@@ -156,9 +157,9 @@ export function renderHelpOverlay(
|
|
|
156
157
|
['sessions', 'Browse and resume saved sessions'],
|
|
157
158
|
['save', 'Save the current session'],
|
|
158
159
|
['compact', 'Compact the conversation history'],
|
|
159
|
-
['clear', '
|
|
160
|
+
['clear', 'Start a fresh conversation (current one saved)'],
|
|
160
161
|
['keybindings', 'List and customize key bindings'],
|
|
161
|
-
['panel', 'Open
|
|
162
|
+
['panel', 'Open a view by its old pane name'],
|
|
162
163
|
];
|
|
163
164
|
|
|
164
165
|
const commandRows: string[] = [];
|
|
@@ -170,7 +171,7 @@ export function renderHelpOverlay(
|
|
|
170
171
|
for (const [name, desc] of ESSENTIAL_COMMANDS) {
|
|
171
172
|
if (!hasCommand(name)) continue;
|
|
172
173
|
seen.add(name);
|
|
173
|
-
essentialRows.push(` ${`/${name}
|
|
174
|
+
essentialRows.push(` ${`/${name}`}\t${desc}`);
|
|
174
175
|
}
|
|
175
176
|
if (essentialRows.length > 0) {
|
|
176
177
|
commandRows.push(' Essentials', ' ' + '\u2500'.repeat(40), ...essentialRows, '');
|
|
@@ -187,10 +188,10 @@ export function renderHelpOverlay(
|
|
|
187
188
|
const cmd = commands.find((entry) => entry.name === name);
|
|
188
189
|
if (!cmd || seen.has(cmd.name)) continue;
|
|
189
190
|
seen.add(cmd.name);
|
|
190
|
-
const nameCol = `/${cmd.name}
|
|
191
|
-
commandRows.push(` ${nameCol}
|
|
191
|
+
const nameCol = `/${cmd.name}`;
|
|
192
|
+
commandRows.push(` ${nameCol}\t${cmd.description}`);
|
|
192
193
|
}
|
|
193
|
-
// No command cap: the overlay
|
|
194
|
+
// No command cap: the overlay list scrolls,
|
|
194
195
|
// so the full remaining registry is listed rather than truncated at 24.
|
|
195
196
|
const remainder = [...commands]
|
|
196
197
|
.filter((cmd) => !seen.has(cmd.name))
|
|
@@ -198,164 +199,77 @@ export function renderHelpOverlay(
|
|
|
198
199
|
if (remainder.length > 0) {
|
|
199
200
|
commandRows.push('', ' More Commands', ' ' + '\u2500'.repeat(40));
|
|
200
201
|
for (const cmd of remainder) {
|
|
201
|
-
const nameCol = `/${cmd.name}
|
|
202
|
-
commandRows.push(` ${nameCol}
|
|
202
|
+
const nameCol = `/${cmd.name}`;
|
|
203
|
+
commandRows.push(` ${nameCol}\t${cmd.description}`);
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
} else if (!hasCommand('help')) {
|
|
206
207
|
commandRows.push('', ' Essentials', ' ' + '\u2500'.repeat(40));
|
|
207
|
-
commandRows.push(' /commands
|
|
208
|
-
commandRows.push(' /help
|
|
209
|
-
commandRows.push(' /shortcuts
|
|
210
|
-
commandRows.push(' /model
|
|
211
|
-
commandRows.push(' /clear
|
|
208
|
+
commandRows.push(' /commands\tShow this help overlay');
|
|
209
|
+
commandRows.push(' /help\tBrowse & run any command');
|
|
210
|
+
commandRows.push(' /shortcuts\tKeyboard shortcut reference');
|
|
211
|
+
commandRows.push(' /model\tSelect LLM model');
|
|
212
|
+
commandRows.push(' /clear\tStart a fresh conversation (current one saved)');
|
|
212
213
|
}
|
|
213
214
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// Apply scroll offset, show a window of rows
|
|
217
|
-
const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
|
|
218
|
-
chromeRows: 4,
|
|
219
|
-
minContentRows: 8,
|
|
220
|
-
maxContentRows: 12,
|
|
221
|
-
});
|
|
222
|
-
const maxVisible = metrics.contentRows;
|
|
223
|
-
const clampedOffset = Math.max(0, Math.min(scrollOffset, Math.max(0, allRows.length - maxVisible)));
|
|
224
|
-
const visibleRows = allRows.slice(clampedOffset, clampedOffset + maxVisible);
|
|
225
|
-
const window = getVisibleWindow(allRows.length, clampedOffset, maxVisible);
|
|
215
|
+
return groupsFromRows([...shortcutRows, ...commandRows]);
|
|
216
|
+
}
|
|
226
217
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
helpers: allRows.length > maxVisible
|
|
239
|
-
? [{ content: `[${window.start + 1}-${Math.min(allRows.length, clampedOffset + visibleRows.length)} of ${allRows.length}]` }]
|
|
240
|
-
: undefined,
|
|
241
|
-
hints: [formatHints([
|
|
242
|
-
{ key: 'Up/Down', verb: 'Scroll' },
|
|
243
|
-
{ key: '?', verb: 'Help' },
|
|
244
|
-
{ key: 'Esc', verb: 'Close' },
|
|
245
|
-
])],
|
|
246
|
-
},
|
|
247
|
-
width,
|
|
248
|
-
);
|
|
218
|
+
function filterGroups(groups: readonly HelpGroup[], query: string): HelpGroup[] {
|
|
219
|
+
const q = query.trim().toLowerCase();
|
|
220
|
+
if (!q) return [...groups];
|
|
221
|
+
return groups
|
|
222
|
+
.map((g) => ({
|
|
223
|
+
title: g.title,
|
|
224
|
+
entries: g.title.toLowerCase().includes(q)
|
|
225
|
+
? g.entries
|
|
226
|
+
: g.entries.filter((e) => e.label.toLowerCase().includes(q) || e.right.toLowerCase().includes(q)),
|
|
227
|
+
}))
|
|
228
|
+
.filter((g) => g.entries.length > 0);
|
|
249
229
|
}
|
|
250
230
|
|
|
231
|
+
const HINTS: readonly KitHint[] = [['↑↓', 'scroll'], ['?', 'close']];
|
|
232
|
+
|
|
251
233
|
/**
|
|
252
|
-
*
|
|
253
|
-
*
|
|
234
|
+
* Render the help overlay as a SurfaceLayer in screen coordinates.
|
|
235
|
+
*
|
|
236
|
+
* @param scrollOffset Rows scrolled past the top of the list.
|
|
237
|
+
* @param filter The search row's query; the renderer records how far the list can scroll in it.
|
|
254
238
|
*/
|
|
255
|
-
export function
|
|
256
|
-
|
|
239
|
+
export function renderHelpOverlay(
|
|
240
|
+
screenWidth: number,
|
|
241
|
+
screenHeight: number,
|
|
257
242
|
keybindingsManager: KeybindingsManager,
|
|
243
|
+
commands?: SlashCommand[],
|
|
258
244
|
scrollOffset = 0,
|
|
259
|
-
|
|
260
|
-
):
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
// adding a new KeyAction automatically shows up below without anyone
|
|
270
|
-
// remembering to also add a row for it here.
|
|
271
|
-
const HARDCODED_ROWS: ReadonlyArray<[key: string, desc: string]> = [
|
|
272
|
-
['Up / Down', 'Recall input history; Down at bottom focuses the process indicator'],
|
|
273
|
-
['PageUp / PageDn', 'Scroll by full page'],
|
|
274
|
-
['Home / End', 'Jump to start / end of line'],
|
|
275
|
-
['n / N (search)', 'Next / previous match'],
|
|
276
|
-
['Mouse wheel', 'Scroll conversation or hovered panel'],
|
|
277
|
-
['Enter', 'Submit message (composer empty: open the block-actions menu)'],
|
|
278
|
-
['Shift+Enter', 'Insert newline'],
|
|
279
|
-
['@', 'Open file picker'],
|
|
280
|
-
['/', 'Slash command mode'],
|
|
281
|
-
['Esc', 'Close overlay / cancel generation / clear prompt (context-dependent)'],
|
|
282
|
-
['Shift+Tab', 'Cycle permission mode (auto-approve / prompt / manual)'],
|
|
283
|
-
['F2', 'Open the Fleet panel'],
|
|
284
|
-
['? or /help', 'Open the command browser (search & run any command)'],
|
|
285
|
-
// Precedence (highest first): panel focus-swap, else path-completion,
|
|
286
|
-
// else collapse/expand \u2014 see the Panels section's Tab row below for the
|
|
287
|
-
// full chain; this row is the "what does it do here" short form.
|
|
288
|
-
['Tab', 'Collapse/expand block (composer empty \u2014 see Panels: Tab for full precedence)'],
|
|
289
|
-
];
|
|
290
|
-
|
|
291
|
-
const IN_PANEL_ROWS: ReadonlyArray<[key: string, desc: string]> = [
|
|
292
|
-
['j / k', 'Move selection down / up'],
|
|
293
|
-
['g / G', 'Jump to top / bottom'],
|
|
294
|
-
['/', 'Filter the list'],
|
|
295
|
-
];
|
|
296
|
-
|
|
297
|
-
// Generated from the live keybindings table (default bindings AND any user
|
|
298
|
-
// overrides) so this listing can never drift from what a key actually
|
|
299
|
-
// does \u2014 no action is hand-picked, none can be silently missed.
|
|
300
|
-
const allBindings = keybindingsManager.getAll();
|
|
301
|
-
const panelActionRows = allBindings
|
|
302
|
-
.filter((entry) => entry.action.startsWith('panel-'))
|
|
303
|
-
.map((entry) => row(entry.combos.map((c) => keybindingsManager.formatCombo(c)).join(', '), entry.description));
|
|
304
|
-
const otherActionRows = allBindings
|
|
305
|
-
.filter((entry) => !entry.action.startsWith('panel-'))
|
|
306
|
-
.map((entry) => row(entry.combos.map((c) => keybindingsManager.formatCombo(c)).join(', '), entry.description));
|
|
245
|
+
filter?: OverlayFilter,
|
|
246
|
+
): SurfaceLayer {
|
|
247
|
+
const t = activeTokens();
|
|
248
|
+
const query = filter?.query ?? '';
|
|
249
|
+
const all = helpGroups(keybindingsManager, commands);
|
|
250
|
+
const groups = filterGroups(all, query);
|
|
251
|
+
const f = beginModal(screenWidth, screenHeight, { title: 'Help', hints: HINTS });
|
|
252
|
+
const total = all.reduce((n, g) => n + g.entries.length, 0);
|
|
253
|
+
const shown = groups.reduce((n, g) => n + g.entries.length, 0);
|
|
254
|
+
searchRow(f, f.top, query, 'Filter commands and shortcuts', query ? `${shown} of ${total}` : `${total} entries`);
|
|
307
255
|
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
' ' + '\u2500'.repeat(40),
|
|
315
|
-
...otherActionRows,
|
|
316
|
-
'',
|
|
317
|
-
' Panels',
|
|
318
|
-
' ' + '\u2500'.repeat(40),
|
|
319
|
-
// Full Tab precedence, stated once here as the authoritative chain: a
|
|
320
|
-
// visible panel workspace claims Tab for focus-swap first; failing that,
|
|
321
|
-
// a partial path under the cursor claims it for completion; only then
|
|
322
|
-
// does it fall through to collapse/expand (the Actions section's row).
|
|
323
|
-
row('Tab', 'Panel focus-swap (1st) > path-complete (2nd) > collapse/expand block (3rd)'),
|
|
324
|
-
...panelActionRows,
|
|
325
|
-
'',
|
|
326
|
-
' In-Panel Controls',
|
|
327
|
-
' ' + '\u2500'.repeat(40),
|
|
328
|
-
...IN_PANEL_ROWS.map(([key, desc]) => row(key, desc)),
|
|
329
|
-
'',
|
|
330
|
-
` Config: /keybindings to list and customize`,
|
|
331
|
-
];
|
|
332
|
-
|
|
333
|
-
const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
|
|
334
|
-
chromeRows: 4,
|
|
335
|
-
minContentRows: 8,
|
|
336
|
-
maxContentRows: 12,
|
|
337
|
-
});
|
|
338
|
-
const maxVisible = metrics.contentRows;
|
|
339
|
-
const clampedOffset = Math.max(0, Math.min(scrollOffset, Math.max(0, allRows.length - maxVisible)));
|
|
340
|
-
const visibleRows = allRows.slice(clampedOffset, clampedOffset + maxVisible);
|
|
341
|
-
const window = getVisibleWindow(allRows.length, clampedOffset, maxVisible);
|
|
256
|
+
const top = f.top + 2;
|
|
257
|
+
if (groups.length === 0) {
|
|
258
|
+
if (filter) filter.maxScroll = 0;
|
|
259
|
+
drawTextBlock(f.canvas, f.l, top, f.r - f.l + 1, [{ text: `Nothing matches "${query}".`, style: { fg: t.textMuted } }], f.bottom);
|
|
260
|
+
return finishModal(f);
|
|
261
|
+
}
|
|
342
262
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
{ key: 'Up/Down', verb: 'Scroll' },
|
|
356
|
-
{ key: 'Esc', verb: 'Close' },
|
|
357
|
-
])],
|
|
358
|
-
},
|
|
359
|
-
width,
|
|
360
|
-
);
|
|
263
|
+
const rows: KitRow[] = [];
|
|
264
|
+
for (const g of groups) {
|
|
265
|
+
rows.push({ header: g.title });
|
|
266
|
+
for (const e of g.entries) rows.push({ label: e.label, right: e.right });
|
|
267
|
+
}
|
|
268
|
+
// The furthest start that still fills the list, by drawList's own spacing rules.
|
|
269
|
+
const listOptions = { rows, top, bottom: f.bottom, x0: f.l, x1: f.r };
|
|
270
|
+
const maxStart = listScrollEnd(f.canvas, listOptions);
|
|
271
|
+
if (filter) filter.maxScroll = maxStart;
|
|
272
|
+
const res = drawList(f.canvas, { ...listOptions, scrollStart: Math.min(scrollOffset, maxStart) });
|
|
273
|
+
f.hintRight = scrollCountText(res.above, res.below);
|
|
274
|
+
return finishModal(f);
|
|
361
275
|
}
|
|
@@ -2,7 +2,9 @@ import type { Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
|
2
2
|
import { fitDisplay, getDisplayWidth } from '../utils/terminal-width.ts';
|
|
3
3
|
import type { HistorySearch } from '../input/input-history.ts';
|
|
4
4
|
import { createBottomBarLine, writeBottomBarText } from '@pellux/goodvibes-terminal-shell';
|
|
5
|
-
import {
|
|
5
|
+
import { keycapHintsWidth, paintKeycapHints } from './surface-kit-parts.ts';
|
|
6
|
+
import type { KitHint } from './surface-kit.ts';
|
|
7
|
+
import { activeTokens } from './theme.ts';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Truncate `text` to at most `maxWidth` display columns, then pad with spaces
|
|
@@ -37,16 +39,17 @@ export function renderHistorySearchOverlay(
|
|
|
37
39
|
|
|
38
40
|
// Build the display string
|
|
39
41
|
const label = prefix + queryPart;
|
|
40
|
-
const hints =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const full = truncateToWidth(`${label}${matchText} ${hints}`, width);
|
|
42
|
+
const hints: KitHint[] = [['ctrl+r ↑', 'older'], ['ctrl+s ↓', 'newer'], ['⏎', 'accept'], ['esc', 'cancel']];
|
|
43
|
+
// Keycap hints sit at the right end of the bar when they fit beside the search.
|
|
44
|
+
const hintsW = keycapHintsWidth(hints);
|
|
45
|
+
const showHints = width - hintsW - 2 >= getDisplayWidth(label) + 8;
|
|
46
|
+
const textW = showHints ? width - hintsW - 4 : width;
|
|
47
|
+
const full = truncateToWidth(`${label}${matchText}`, textW);
|
|
47
48
|
|
|
48
|
-
const
|
|
49
|
-
|
|
49
|
+
const p = activeTokens();
|
|
50
|
+
const line = createBottomBarLine(width, { fg: p.selectedListItemText, bg: p.accent });
|
|
51
|
+
writeBottomBarText(line, 0, textW, full, { fg: p.selectedListItemText, bg: p.accent });
|
|
52
|
+
if (showHints) paintKeycapHints(line, width - hintsW - 2, width - 2, hints, { fg: p.selectedListItemText, bg: p.accent });
|
|
50
53
|
|
|
51
54
|
// Highlight the matched region in the match text with dim styling
|
|
52
55
|
if (hasMatch && match) {
|
|
@@ -56,8 +59,8 @@ export function renderHistorySearchOverlay(
|
|
|
56
59
|
const highlightWidth = Math.max(0, matchEndCol - matchStartCol);
|
|
57
60
|
const matchedSlice = truncateToWidth(match.entry.slice(match.matchStart, match.matchStart + match.matchLength), highlightWidth);
|
|
58
61
|
writeBottomBarText(line, matchStartCol, highlightWidth, matchedSlice, {
|
|
59
|
-
fg:
|
|
60
|
-
bg:
|
|
62
|
+
fg: p.selectedListItemText,
|
|
63
|
+
bg: p.accent,
|
|
61
64
|
bold: true,
|
|
62
65
|
underline: true,
|
|
63
66
|
});
|