@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
package/src/input/handler.ts
CHANGED
|
@@ -22,13 +22,13 @@ import { InputHistory, HistorySearch } from './input-history.ts';
|
|
|
22
22
|
import type { BlockMeta, ConversationManager } from '../core/conversation';
|
|
23
23
|
import { BlockActionsMenu } from '../renderer/block-actions.ts';
|
|
24
24
|
import { ContextInspectorModal } from '../renderer/context-inspector.ts';
|
|
25
|
+
import { OverlayFilters } from './overlay-filter.ts';
|
|
25
26
|
import { BookmarkModal } from './bookmark-modal.ts';
|
|
26
27
|
import { SettingsModal } from './settings-modal.ts';
|
|
27
28
|
import { McpWorkspace } from './mcp-workspace.ts';
|
|
28
29
|
import { SessionPickerModal, getSharedHostedSessionRoster } from './session-picker-modal.ts';
|
|
29
30
|
import { ConfigModal } from './config-modal.ts';
|
|
30
31
|
import { ProfilePickerModal } from './profile-picker-modal.ts';
|
|
31
|
-
import type { PanelBurstGuardState } from './panel-paste-flood-guard.ts';
|
|
32
32
|
import { OnboardingWizardController, type OnboardingWizardAction, type OnboardingWizardMode } from './onboarding/onboarding-wizard.ts';
|
|
33
33
|
import {
|
|
34
34
|
applyOnboardingRequest,
|
|
@@ -64,10 +64,8 @@ import { pasteFromClipboard, pasteImageFromClipboard } from '../utils/clipboard.
|
|
|
64
64
|
import {
|
|
65
65
|
handleIndicatorFocusToken,
|
|
66
66
|
handleMouseToken,
|
|
67
|
-
handlePanelFocusToken,
|
|
68
67
|
handlePromptKeyToken,
|
|
69
68
|
handlePromptTextToken,
|
|
70
|
-
type PanelMouseLayout,
|
|
71
69
|
} from './handler-feed-routes.ts';
|
|
72
70
|
import {
|
|
73
71
|
ensureInputCursorVisible,
|
|
@@ -115,11 +113,9 @@ import { handleGlobalShortcutToken } from './handler-shortcuts.ts';
|
|
|
115
113
|
import { feedInputTokens } from './handler-feed.ts';
|
|
116
114
|
import type { InputHandlerLike } from './handler-types.ts';
|
|
117
115
|
import { buildInitialFeedContext, syncFeedContextMutableFields } from './feed-context-factory.ts';
|
|
118
|
-
import { handlePanelIntegrationAction as runPanelIntegrationAction } from './panel-integration-actions.ts';
|
|
119
|
-
import type { Panel } from '../panels/types.ts';
|
|
120
116
|
import type { UiRuntimeServices } from '../runtime/ui-services.ts';
|
|
121
|
-
export { handlePanelIntegrationAction } from './panel-integration-actions.ts';
|
|
122
117
|
import type { ModelPickerTarget } from './model-picker.ts';
|
|
118
|
+
import { SurfaceModalHost } from './surface-modal-host.ts';
|
|
123
119
|
|
|
124
120
|
type SelectionModalCallback = (result: SelectionResult | null) => void;
|
|
125
121
|
|
|
@@ -148,34 +144,6 @@ export class InputHandler implements InputHandlerLike {
|
|
|
148
144
|
public commandMode = false;
|
|
149
145
|
/** True when the process indicator bar has keyboard focus. */
|
|
150
146
|
public indicatorFocused = false;
|
|
151
|
-
/**
|
|
152
|
-
* Fallback focus store used only when the panel manager does not implement
|
|
153
|
-
* focus ownership (lightweight test stubs). Production always delegates to
|
|
154
|
-
* PanelManager, which is the single source of truth.
|
|
155
|
-
*/
|
|
156
|
-
private _panelFocusedFallback = false;
|
|
157
|
-
/**
|
|
158
|
-
* True when keyboard focus is on the active panel (arrow/enter go to panel,
|
|
159
|
-
* not prompt). Ownership lives in PanelManager (focusTarget); this reads and
|
|
160
|
-
* writes through to it so handler focus can never disagree with what the
|
|
161
|
-
* panel workspace actually shows.
|
|
162
|
-
*/
|
|
163
|
-
public get panelFocused(): boolean {
|
|
164
|
-
const pm = this.uiServices.shell.panelManager;
|
|
165
|
-
return typeof pm.getFocusTarget === 'function'
|
|
166
|
-
? pm.getFocusTarget() === 'panel'
|
|
167
|
-
: this._panelFocusedFallback;
|
|
168
|
-
}
|
|
169
|
-
public set panelFocused(value: boolean) {
|
|
170
|
-
const pm = this.uiServices.shell.panelManager;
|
|
171
|
-
if (typeof pm.focusPanels === 'function' && typeof pm.focusPrompt === 'function') {
|
|
172
|
-
if (value) pm.focusPanels();
|
|
173
|
-
else pm.focusPrompt();
|
|
174
|
-
} else {
|
|
175
|
-
this._panelFocusedFallback = value;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
147
|
public tokenizer = new InputTokenizer();
|
|
180
148
|
public pasteRegistry = new Map<string, string>();
|
|
181
149
|
public nextPasteId = 1;
|
|
@@ -193,12 +161,18 @@ export class InputHandler implements InputHandlerLike {
|
|
|
193
161
|
public selectionModal = new SelectionModal();
|
|
194
162
|
public searchManager = new SearchManager();
|
|
195
163
|
public contextInspectorModal = new ContextInspectorModal();
|
|
164
|
+
public readonly overlayFilters = new OverlayFilters();
|
|
196
165
|
public bookmarkModal: BookmarkModal;
|
|
197
166
|
public blockActionsMenu = new BlockActionsMenu();
|
|
167
|
+
public surfaceModals = new SurfaceModalHost();
|
|
168
|
+
/** Agent and process views (shell/session-views.ts); null until the shell wires them. */
|
|
169
|
+
public sessionView: import('./handler-session-view-route.ts').SessionViewControls | null = null;
|
|
170
|
+
/** The main transcript's scroll, for Esc while scrolled back (handler-modal-stack.ts); null until the shell wires it. */
|
|
171
|
+
public transcriptScroll: import('./handler-modal-stack.ts').TranscriptScrollControls | null = null;
|
|
172
|
+
/** Screen rows above the transcript, set each frame by the shell: the header, the chips, the gap under them. */
|
|
173
|
+
public bodyTopRow = 2;
|
|
198
174
|
public settingsModal = new SettingsModal();
|
|
199
175
|
public configModal = new ConfigModal();
|
|
200
|
-
/** item 5, paste-flood guard state, mutated in place across tokens (never reallocated). */
|
|
201
|
-
private panelBurstGuard: PanelBurstGuardState = { timestamps: [], suspended: false, hintShown: false };
|
|
202
176
|
public mcpWorkspace = new McpWorkspace();
|
|
203
177
|
public onboardingWizard = new OnboardingWizardController();
|
|
204
178
|
public onboardingModelPickerCancelSnapshot: OnboardingWizardSnapshot | null = null;
|
|
@@ -211,7 +185,7 @@ export class InputHandler implements InputHandlerLike {
|
|
|
211
185
|
* Used to support back-navigation via Escape.
|
|
212
186
|
*/
|
|
213
187
|
public modalStack: string[] = [];
|
|
214
|
-
public modalReturnFocus: 'prompt' | '
|
|
188
|
+
public modalReturnFocus: 'prompt' | 'indicator' = 'prompt';
|
|
215
189
|
public sessionPickerModal: SessionPickerModal;
|
|
216
190
|
public profilePickerModal: ProfilePickerModal;
|
|
217
191
|
/** True when the help overlay is visible. */
|
|
@@ -231,6 +205,9 @@ export class InputHandler implements InputHandlerLike {
|
|
|
231
205
|
public lastBlockCopyTime = 0;
|
|
232
206
|
public mouseDownRow = -1;
|
|
233
207
|
public mouseDownCol = -1;
|
|
208
|
+
/** Clickable footer screen rows (set by the shell each frame) and what opens them. */
|
|
209
|
+
public footerTargets: ReadonlyMap<number, import('../renderer/footer-targets.ts').FooterTarget> = new Map();
|
|
210
|
+
public onFooterTarget: ((target: import('../renderer/footer-targets.ts').FooterTarget) => void) | null = null;
|
|
234
211
|
|
|
235
212
|
/** Pasted images: maps marker IDs to base64 image data. */
|
|
236
213
|
public imageRegistry = new Map<string, { data: string; mediaType: string }>();
|
|
@@ -282,8 +259,6 @@ export class InputHandler implements InputHandlerLike {
|
|
|
282
259
|
uiServices.providers.benchmarkStore,
|
|
283
260
|
uiServices.providers.providerRegistry,
|
|
284
261
|
);
|
|
285
|
-
// retirement: ProcessModal/LiveTailModal/AgentDetailModal were removed
|
|
286
|
-
//, F2 now opens the Fleet panel, which subsumes the live process tree.
|
|
287
262
|
this.bookmarkModal = new BookmarkModal(uiServices.shell.bookmarkManager);
|
|
288
263
|
this.sessionPickerModal = new SessionPickerModal(uiServices.sessions.sessionManager, uiServices.sessions.sessionBroker, getSharedHostedSessionRoster(), () => this.requestRender()); // third source: the daemon-hosted roster
|
|
289
264
|
this.profilePickerModal = new ProfilePickerModal(uiServices.shell.profileManager);
|
|
@@ -299,12 +274,12 @@ export class InputHandler implements InputHandlerLike {
|
|
|
299
274
|
this.feedContext = buildInitialFeedContext(
|
|
300
275
|
{
|
|
301
276
|
prompt: this.prompt, cursorPos: this.cursorPos, inputScrollTop: this.inputScrollTop, commandMode: this.commandMode,
|
|
302
|
-
|
|
277
|
+
indicatorFocused: this.indicatorFocused,
|
|
303
278
|
helpOverlayActive: this.helpOverlayActive, helpScrollOffset: this.helpScrollOffset,
|
|
304
279
|
shortcutsOverlayActive: this.shortcutsOverlayActive, shortcutsScrollOffset: this.shortcutsScrollOffset,
|
|
305
280
|
nextPasteId: this.nextPasteId, nextImageId: this.nextImageId,
|
|
306
281
|
mouseDownRow: this.mouseDownRow, mouseDownCol: this.mouseDownCol,
|
|
307
|
-
contentWidth: this.contentWidth,
|
|
282
|
+
contentWidth: this.contentWidth,
|
|
308
283
|
selectionCallback: this.selectionCallback,
|
|
309
284
|
},
|
|
310
285
|
{
|
|
@@ -327,16 +302,16 @@ export class InputHandler implements InputHandlerLike {
|
|
|
327
302
|
modelPicker: this.modelPicker,
|
|
328
303
|
onboardingWizard: this.onboardingWizard,
|
|
329
304
|
contextInspectorModal: this.contextInspectorModal,
|
|
305
|
+
overlayFilters: this.overlayFilters,
|
|
330
306
|
blockActionsMenu: this.blockActionsMenu,
|
|
307
|
+
surfaceModals: this.surfaceModals,
|
|
331
308
|
searchManager: this.searchManager,
|
|
332
309
|
modalStack: this.modalStack,
|
|
333
310
|
inputHistory: this.inputHistory,
|
|
334
311
|
conversationManager: this.conversationManager,
|
|
335
|
-
panelManager: this.uiServices.shell.panelManager,
|
|
336
312
|
keybindingsManager: this.uiServices.shell.keybindingsManager,
|
|
337
313
|
killRing: this.killRing,
|
|
338
314
|
focusTracker: this.uiServices.platform.focusTracker,
|
|
339
|
-
panelBurstGuard: this.panelBurstGuard,
|
|
340
315
|
getHistory: this.getHistory,
|
|
341
316
|
getViewportHeight: this.getViewportHeight,
|
|
342
317
|
getScrollTop: this.getScrollTop,
|
|
@@ -345,6 +320,7 @@ export class InputHandler implements InputHandlerLike {
|
|
|
345
320
|
},
|
|
346
321
|
{
|
|
347
322
|
modalOpened: (name: string) => this.modalOpened(name),
|
|
323
|
+
getSessionView: () => this.sessionView,
|
|
348
324
|
handleEscape: () => { if (!this.cancelConcealedInput()) this.handleEscape(); this.syncFeedContextMutableFields(); },
|
|
349
325
|
submitConcealedInput: (value: string) => this.submitConcealedInput(value),
|
|
350
326
|
handleCopy: () => this.handleCopy(),
|
|
@@ -362,11 +338,12 @@ export class InputHandler implements InputHandlerLike {
|
|
|
362
338
|
ensureInputCursorVisible: (contentWidth?: number) => this.ensureInputCursorVisible(contentWidth),
|
|
363
339
|
registerPaste: (content: string) => this.registerPaste(content),
|
|
364
340
|
executeBlockAction: (id: string) => this.executeBlockAction(id),
|
|
365
|
-
cyclePanelTab: (direction: 'next' | 'prev') => this.cyclePanelTab(direction),
|
|
366
|
-
onPanelInputConsumed: (activePanel: Panel | null, key: string) => this.handlePanelIntegrationAction(activePanel, key),
|
|
367
341
|
getWrappedPromptInfo: (contentWidth: number) => this.getWrappedPromptInfo(contentWidth),
|
|
368
342
|
moveCursorVertical: (direction: -1 | 1) => this.moveCursorVertical(direction),
|
|
369
343
|
handlePathCompletion: () => this.handlePathCompletion(),
|
|
344
|
+
footerTargetAt: (row: number) => this.footerTargets.get(row),
|
|
345
|
+
openFooterTarget: (target) => { this.onFooterTarget?.(target); },
|
|
346
|
+
bodyTopRow: () => this.bodyTopRow,
|
|
370
347
|
handleBlockToggle: () => this.handleBlockToggle(),
|
|
371
348
|
findMarkerAtPos: (pos: number) => this.findMarkerAtPos(pos),
|
|
372
349
|
cleanupMarkerRegistry: (text: string) => this.cleanupMarkerRegistry(text),
|
|
@@ -386,12 +363,11 @@ export class InputHandler implements InputHandlerLike {
|
|
|
386
363
|
public syncFeedContextMutableFields(): void {
|
|
387
364
|
const h = this;
|
|
388
365
|
syncFeedContextMutableFields({ prompt: h.prompt, cursorPos: h.cursorPos, inputScrollTop: h.inputScrollTop, commandMode: h.commandMode,
|
|
389
|
-
|
|
366
|
+
indicatorFocused: h.indicatorFocused, helpOverlayActive: h.helpOverlayActive,
|
|
390
367
|
helpScrollOffset: h.helpScrollOffset, shortcutsOverlayActive: h.shortcutsOverlayActive,
|
|
391
368
|
shortcutsScrollOffset: h.shortcutsScrollOffset, selectionCallback: h.selectionCallback,
|
|
392
369
|
nextPasteId: h.nextPasteId, nextImageId: h.nextImageId, mouseDownRow: h.mouseDownRow,
|
|
393
|
-
mouseDownCol: h.mouseDownCol, contentWidth: h.contentWidth,
|
|
394
|
-
panelMouseLayout: h.panelMouseLayout }, this.feedContext);
|
|
370
|
+
mouseDownCol: h.mouseDownCol, contentWidth: h.contentWidth }, this.feedContext);
|
|
395
371
|
}
|
|
396
372
|
|
|
397
373
|
/** Wire in the InputHistory instance. Optional; disables history navigation if unset. */
|
|
@@ -421,6 +397,7 @@ export class InputHandler implements InputHandlerLike {
|
|
|
421
397
|
allowSearch?: boolean;
|
|
422
398
|
customActions?: Map<string, SelectionAction>;
|
|
423
399
|
primaryVerbLabel?: string;
|
|
400
|
+
onHighlight?: (item: import('./selection-modal.ts').SelectionItem | null) => void;
|
|
424
401
|
} | undefined,
|
|
425
402
|
callback: SelectionModalCallback,
|
|
426
403
|
): void {
|
|
@@ -465,7 +442,6 @@ export class InputHandler implements InputHandlerLike {
|
|
|
465
442
|
public openModelPickerWithTarget(target: ModelPickerTarget, source: 'settings' | 'onboarding' = 'settings'): boolean { return openModelPickerWithTargetForHandler(this, target, source); }
|
|
466
443
|
public openProviderModelPickerWithTarget(target: ModelPickerTarget, source: 'settings' | 'onboarding' = 'settings'): boolean { return openProviderModelPickerWithTargetForHandler(this, target, source); }
|
|
467
444
|
public openMcpWorkspace(context: CommandContext): void {
|
|
468
|
-
this.panelFocused = false;
|
|
469
445
|
this.indicatorFocused = false;
|
|
470
446
|
this.modalOpened('mcpWorkspace');
|
|
471
447
|
this.mcpWorkspace.open(context);
|
|
@@ -517,7 +493,6 @@ export class InputHandler implements InputHandlerLike {
|
|
|
517
493
|
context.cursorPos = this.cursorPos;
|
|
518
494
|
context.inputScrollTop = this.inputScrollTop;
|
|
519
495
|
context.commandMode = this.commandMode;
|
|
520
|
-
context.panelFocused = this.panelFocused;
|
|
521
496
|
context.indicatorFocused = this.indicatorFocused;
|
|
522
497
|
context.helpOverlayActive = this.helpOverlayActive;
|
|
523
498
|
context.helpScrollOffset = this.helpScrollOffset;
|
|
@@ -529,7 +504,6 @@ export class InputHandler implements InputHandlerLike {
|
|
|
529
504
|
context.mouseDownRow = this.mouseDownRow;
|
|
530
505
|
context.mouseDownCol = this.mouseDownCol;
|
|
531
506
|
context.contentWidth = this.contentWidth;
|
|
532
|
-
context.panelMouseLayout = this.panelMouseLayout;
|
|
533
507
|
// Sync semi-stable refs that may be wired after construction.
|
|
534
508
|
context.commandRegistry = this.commandRegistry;
|
|
535
509
|
context.commandContext = this.commandContext;
|
|
@@ -546,7 +520,6 @@ export class InputHandler implements InputHandlerLike {
|
|
|
546
520
|
this.cursorPos = context.cursorPos;
|
|
547
521
|
this.inputScrollTop = context.inputScrollTop;
|
|
548
522
|
this.commandMode = context.commandMode;
|
|
549
|
-
this.panelFocused = context.panelFocused;
|
|
550
523
|
this.indicatorFocused = context.indicatorFocused;
|
|
551
524
|
// Overlay flags: only apply the pipeline's value when the pipeline
|
|
552
525
|
// itself changed it (e.g. Escape closing the overlay). Otherwise keep
|
|
@@ -587,17 +560,12 @@ export class InputHandler implements InputHandlerLike {
|
|
|
587
560
|
|
|
588
561
|
/** Content width for wrapping, set by main.ts via setContentWidth(). */
|
|
589
562
|
public contentWidth = 76;
|
|
590
|
-
public panelMouseLayout: PanelMouseLayout | null = null;
|
|
591
563
|
|
|
592
564
|
/** Set the content width used for wrapping calculations. Call from main.ts. */
|
|
593
565
|
public setContentWidth(w: number): void {
|
|
594
566
|
this.contentWidth = w;
|
|
595
567
|
}
|
|
596
568
|
|
|
597
|
-
public setPanelMouseLayout(layout: PanelMouseLayout | null): void {
|
|
598
|
-
this.panelMouseLayout = layout;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
569
|
/**
|
|
602
570
|
* Move cursor up or down by one WRAPPED line.
|
|
603
571
|
* Uses the segment table to navigate visual lines, not raw \n lines.
|
|
@@ -770,19 +738,6 @@ export class InputHandler implements InputHandlerLike {
|
|
|
770
738
|
* Word-wrap a single line to fit within maxW columns.
|
|
771
739
|
* Breaks at spaces; words wider than maxW are force-broken.
|
|
772
740
|
*/
|
|
773
|
-
public cyclePanelTab(direction: 'next' | 'prev'): void {
|
|
774
|
-
const pm = this.uiServices.shell.panelManager;
|
|
775
|
-
if (pm.isVisible()) {
|
|
776
|
-
if (direction === 'next') pm.nextWorkspaceTab();
|
|
777
|
-
else pm.prevWorkspaceTab();
|
|
778
|
-
this.requestRender();
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
public handlePanelIntegrationAction(activePanel: Panel | null, key: string): void {
|
|
783
|
-
runPanelIntegrationAction(this.uiServices.shell.panelManager, activePanel, key, this.commandContext);
|
|
784
|
-
}
|
|
785
|
-
|
|
786
741
|
public wordWrapLine(line: string, maxW: number): string[] {
|
|
787
742
|
return wordWrapLine(line, maxW);
|
|
788
743
|
}
|
package/src/input/keybindings.ts
CHANGED
|
@@ -34,22 +34,7 @@ export type KeyAction =
|
|
|
34
34
|
| 'clear-cancel'
|
|
35
35
|
| 'screen-clear'
|
|
36
36
|
| 'command-palette'
|
|
37
|
-
| '
|
|
38
|
-
| 'panel-close'
|
|
39
|
-
| 'panel-close-all'
|
|
40
|
-
| 'panel-tab-next'
|
|
41
|
-
| 'panel-tab-prev'
|
|
42
|
-
| 'panel-tab-1'
|
|
43
|
-
| 'panel-tab-2'
|
|
44
|
-
| 'panel-tab-3'
|
|
45
|
-
| 'panel-tab-4'
|
|
46
|
-
| 'panel-tab-5'
|
|
47
|
-
| 'panel-tab-6'
|
|
48
|
-
| 'panel-tab-7'
|
|
49
|
-
| 'panel-tab-8'
|
|
50
|
-
| 'panel-tab-9'
|
|
51
|
-
| 'panel-ops'
|
|
52
|
-
| 'panel-focus-toggle'
|
|
37
|
+
| 'open-agents'
|
|
53
38
|
| 'history-search'
|
|
54
39
|
| 'search'
|
|
55
40
|
| 'block-copy'
|
|
@@ -72,7 +57,8 @@ export type KeyAction =
|
|
|
72
57
|
| 'cancel-tool-call'
|
|
73
58
|
| 'toggle-keep-awake'
|
|
74
59
|
| 'toggle-memory-provenance'
|
|
75
|
-
| 'voice-input'
|
|
60
|
+
| 'voice-input'
|
|
61
|
+
| 'focus-work-tree';
|
|
76
62
|
|
|
77
63
|
/** Human-readable description for each action (used in /keybindings display). */
|
|
78
64
|
export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
|
|
@@ -80,22 +66,7 @@ export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
|
|
|
80
66
|
'clear-cancel': 'Clear input / cancel generation / exit (double)',
|
|
81
67
|
'screen-clear': 'Repaint the screen',
|
|
82
68
|
'command-palette': 'Open the command palette (search & run any command)',
|
|
83
|
-
'
|
|
84
|
-
'panel-close': 'Close the currently active panel',
|
|
85
|
-
'panel-close-all': 'Close all open panels',
|
|
86
|
-
'panel-tab-next': 'Next workspace panel tab',
|
|
87
|
-
'panel-tab-prev': 'Previous workspace panel tab',
|
|
88
|
-
'panel-tab-1': 'Jump to workspace panel tab 1',
|
|
89
|
-
'panel-tab-2': 'Jump to workspace panel tab 2',
|
|
90
|
-
'panel-tab-3': 'Jump to workspace panel tab 3',
|
|
91
|
-
'panel-tab-4': 'Jump to workspace panel tab 4',
|
|
92
|
-
'panel-tab-5': 'Jump to workspace panel tab 5',
|
|
93
|
-
'panel-tab-6': 'Jump to workspace panel tab 6',
|
|
94
|
-
'panel-tab-7': 'Jump to workspace panel tab 7',
|
|
95
|
-
'panel-tab-8': 'Jump to workspace panel tab 8',
|
|
96
|
-
'panel-tab-9': 'Jump to workspace panel tab 9',
|
|
97
|
-
'panel-ops': 'Open, focus, or close the Fleet panel (toggle)',
|
|
98
|
-
'panel-focus-toggle': 'Switch keyboard focus between top and bottom pane',
|
|
69
|
+
'open-agents': 'Open the Agents view (same as F2)',
|
|
99
70
|
'history-search': 'Reverse input history search',
|
|
100
71
|
'search': 'Toggle conversation search',
|
|
101
72
|
'block-copy': 'Copy nearest block to clipboard',
|
|
@@ -122,6 +93,7 @@ export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
|
|
|
122
93
|
// state, there is no release event to watch, so this is two discrete presses,
|
|
123
94
|
// and the description says so instead of implying a hold that cannot exist.
|
|
124
95
|
'voice-input': 'Voice input: press to start recording, press again to stop and transcribe into the composer',
|
|
96
|
+
'focus-work-tree': 'Move the keyboard onto the conversation work tree (arrows move and fold, Enter opens, y copies, Esc returns)',
|
|
125
97
|
};
|
|
126
98
|
|
|
127
99
|
/** Default key bindings for all actions. */
|
|
@@ -129,43 +101,14 @@ export const DEFAULT_KEYBINDINGS: Record<KeyAction, KeyCombo[]> = {
|
|
|
129
101
|
'copy-selection': [{ key: 'c', ctrl: true, shift: true }],
|
|
130
102
|
'clear-cancel': [{ key: 'c', ctrl: true }],
|
|
131
103
|
'screen-clear': [{ key: 'l', ctrl: true }],
|
|
132
|
-
// Ctrl+
|
|
133
|
-
//
|
|
134
|
-
// to
|
|
135
|
-
|
|
136
|
-
'
|
|
137
|
-
|
|
138
|
-
'
|
|
139
|
-
|
|
140
|
-
// binding. Ctrl+[ was REMOVED as prev-tab: it is byte 0x1B (ESC), so the
|
|
141
|
-
// legacy tokenizer path emits it as 'escape', the binding never matched on
|
|
142
|
-
// most terminals AND the key fired Escape (a split-brain chord). Ctrl+PageUp
|
|
143
|
-
// replaces it: Ctrl+PageUp/PageDown tokenize consistently (\x1b[5;5~ /
|
|
144
|
-
// \x1b[6;5~ -> pageup/pagedown ctrl:true) in both the legacy and CSI-u paths.
|
|
145
|
-
'panel-tab-next': [{ key: ']', ctrl: true }, { key: 'pagedown', ctrl: true }],
|
|
146
|
-
'panel-tab-prev': [{ key: 'pageup', ctrl: true }],
|
|
147
|
-
// Alt+1..9: jump directly to the Nth workspace panel tab (across both panes).
|
|
148
|
-
// The tokenizer delivers Alt as the token's `meta` modifier; comboMatches /
|
|
149
|
-
// lookup treat `meta` as an alias for `alt`, so these alt-combos route through
|
|
150
|
-
// the same rebindable path as every other action.
|
|
151
|
-
'panel-tab-1': [{ key: '1', alt: true }],
|
|
152
|
-
'panel-tab-2': [{ key: '2', alt: true }],
|
|
153
|
-
'panel-tab-3': [{ key: '3', alt: true }],
|
|
154
|
-
'panel-tab-4': [{ key: '4', alt: true }],
|
|
155
|
-
'panel-tab-5': [{ key: '5', alt: true }],
|
|
156
|
-
'panel-tab-6': [{ key: '6', alt: true }],
|
|
157
|
-
'panel-tab-7': [{ key: '7', alt: true }],
|
|
158
|
-
'panel-tab-8': [{ key: '8', alt: true }],
|
|
159
|
-
'panel-tab-9': [{ key: '9', alt: true }],
|
|
160
|
-
// Ctrl+O: TOGGLE the Fleet panel (open+focus / bring-to-front+focus / close
|
|
161
|
-
//, see toggleFleetPanel in handler-shortcuts.ts). The former Ops
|
|
162
|
-
// Control panel was retired to an 'ops-control' -> 'fleet' alias; the
|
|
163
|
-
// binding is KEPT (repointed, not removed) so the Ctrl+O muscle memory still
|
|
164
|
-
// lands somewhere useful. Routed globally in handleGlobalShortcutToken.
|
|
165
|
-
'panel-ops': [{ key: 'o', ctrl: true }],
|
|
166
|
-
// Ctrl+G: toggle keyboard focus between the top and bottom panes. Ctrl+G is
|
|
167
|
-
// otherwise unbound in the default table.
|
|
168
|
-
'panel-focus-toggle': [{ key: 'g', ctrl: true }],
|
|
104
|
+
// Ctrl+P opens the command palette, the one entry point for every command,
|
|
105
|
+
// view and setting; Ctrl+K (the other common palette chord) opens it too. The
|
|
106
|
+
// readline kill-to-end-of-line that historically owned Ctrl+K is repointed to
|
|
107
|
+
// Alt+K below so the capability is kept, not lost.
|
|
108
|
+
'command-palette': [{ key: 'p', ctrl: true }, { key: 'k', ctrl: true }],
|
|
109
|
+
// Ctrl+O: the Agents modal (same as F2), where the old Fleet and Ops
|
|
110
|
+
// consoles' content lives now. Routed globally in handleGlobalShortcutToken.
|
|
111
|
+
'open-agents': [{ key: 'o', ctrl: true }],
|
|
169
112
|
'history-search': [{ key: 'r', ctrl: true }],
|
|
170
113
|
'search': [{ key: 'f', ctrl: true }],
|
|
171
114
|
'block-copy': [{ key: 'y', ctrl: true }],
|
|
@@ -216,6 +159,8 @@ export const DEFAULT_KEYBINDINGS: Record<KeyAction, KeyCombo[]> = {
|
|
|
216
159
|
// is paste and stays paste). Two presses rather than a hold: see the action's
|
|
217
160
|
// description, a terminal has no key-release event to hold against.
|
|
218
161
|
'voice-input': [{ key: 'v', alt: true }],
|
|
162
|
+
// Alt+Up and Ctrl+Up: into the work tree from the composer (plain Up keeps input history).
|
|
163
|
+
'focus-work-tree': [{ key: 'up', alt: true }, { key: 'up', ctrl: true }],
|
|
219
164
|
};
|
|
220
165
|
|
|
221
166
|
/** Resolved overrides type: each key can be a single combo or array. */
|
|
@@ -327,7 +272,7 @@ export class KeybindingsManager {
|
|
|
327
272
|
if (!token.logicalName) return null;
|
|
328
273
|
// The tokenizer delivers the Alt modifier as `meta`; the binding table stores
|
|
329
274
|
// it as `alt`. Treat the two as one modifier at the matching boundary so
|
|
330
|
-
// Alt-based combos (word-nav, kill-ring
|
|
275
|
+
// Alt-based combos (word-nav, kill-ring) resolve at runtime.
|
|
331
276
|
const alt = token.alt ?? token.meta;
|
|
332
277
|
const key = `${token.logicalName}:${token.ctrl ? 1 : 0}:${token.shift ? 1 : 0}:${alt ? 1 : 0}`;
|
|
333
278
|
return this.lookupMap.get(key) ?? null;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* masked-entry-modal.ts, the local-auth password prompt as a kit modal.
|
|
3
|
+
*
|
|
4
|
+
* `/local-auth add-user <name>` and `/local-auth rotate-password <name>` with
|
|
5
|
+
* no password argument, and the Local Auth modal's a / p actions, open this.
|
|
6
|
+
* It is the only way to set a local password that keeps the plaintext out of
|
|
7
|
+
* argv, input history, the transcript and scrollback: keystrokes land in a
|
|
8
|
+
* private buffer that is never rendered (the row shows dots), and the buffer
|
|
9
|
+
* is dropped before the auth call runs, so it never outlives the attempt.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { InputToken } from '@pellux/goodvibes-sdk/platform/core';
|
|
13
|
+
import type { UserAuthManager } from '@pellux/goodvibes-sdk/platform/security';
|
|
14
|
+
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
15
|
+
import type { SurfaceModal, SurfaceModalHost } from './surface-modal-host.ts';
|
|
16
|
+
import type { SurfaceLayer } from '../renderer/surface-kit.ts';
|
|
17
|
+
import { renderMaskedEntryModal, type MaskedEntryView } from '../renderer/masked-entry-modal.ts';
|
|
18
|
+
import { isTextBackspace } from './delete-key-policy.ts';
|
|
19
|
+
|
|
20
|
+
export type MaskedEntryKind = 'add-user' | 'rotate-password';
|
|
21
|
+
|
|
22
|
+
export interface MaskedEntryOptions {
|
|
23
|
+
readonly kind: MaskedEntryKind;
|
|
24
|
+
/** The user; for add-user it may be empty, and the modal asks for it first. */
|
|
25
|
+
readonly username?: string;
|
|
26
|
+
readonly auth: Pick<UserAuthManager, 'addUser' | 'rotatePassword'>;
|
|
27
|
+
/** Report the outcome (never includes the password). */
|
|
28
|
+
readonly onDone?: (message: string) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Printable characters of a text token (control characters are dropped). */
|
|
32
|
+
function printable(text: string): string {
|
|
33
|
+
return [...text].filter((ch) => ch >= ' ' && ch !== '\x7f').join('');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class MaskedEntryModal implements SurfaceModal, MaskedEntryView {
|
|
37
|
+
readonly name = 'masked-entry';
|
|
38
|
+
readonly kind: MaskedEntryKind;
|
|
39
|
+
username: string;
|
|
40
|
+
step: 'username' | 'password';
|
|
41
|
+
error: string | null = null;
|
|
42
|
+
/** The password being typed. Private, never rendered, cleared on submit, cancel and close. */
|
|
43
|
+
private buffer = '';
|
|
44
|
+
|
|
45
|
+
constructor(private readonly options: MaskedEntryOptions) {
|
|
46
|
+
this.kind = options.kind;
|
|
47
|
+
this.username = (options.username ?? '').trim();
|
|
48
|
+
this.step = this.kind === 'add-user' && this.username.length === 0 ? 'username' : 'password';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get passwordLength(): number {
|
|
52
|
+
return [...this.buffer].length;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
onClose(): void {
|
|
56
|
+
this.buffer = '';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
escape(): boolean {
|
|
60
|
+
// Esc from the password step of a new user goes back to the username.
|
|
61
|
+
if (this.step === 'password' && this.kind === 'add-user' && !this.options.username) {
|
|
62
|
+
this.buffer = '';
|
|
63
|
+
this.error = null;
|
|
64
|
+
this.step = 'username';
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private submit(host: SurfaceModalHost): void {
|
|
71
|
+
if (this.step === 'username') {
|
|
72
|
+
if (this.username.length === 0) return;
|
|
73
|
+
this.step = 'password';
|
|
74
|
+
this.error = null;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (this.buffer.length === 0) return;
|
|
78
|
+
const password = this.buffer;
|
|
79
|
+
// Drop the secret before the call so it never lingers after an exception.
|
|
80
|
+
this.buffer = '';
|
|
81
|
+
try {
|
|
82
|
+
if (this.kind === 'add-user') {
|
|
83
|
+
const added = this.options.auth.addUser(this.username, password, ['admin']);
|
|
84
|
+
host.close(this, 'done');
|
|
85
|
+
this.options.onDone?.(`Added local auth user ${added.username}.`);
|
|
86
|
+
} else {
|
|
87
|
+
this.options.auth.rotatePassword(this.username, password);
|
|
88
|
+
host.close(this, 'done');
|
|
89
|
+
this.options.onDone?.(`Rotated the password for ${this.username}. Existing sessions were revoked.`);
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
this.error = summarizeError(error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
handleToken(token: InputToken, host: SurfaceModalHost): void {
|
|
97
|
+
if (token.type === 'text') {
|
|
98
|
+
const text = printable(token.value);
|
|
99
|
+
if (!text) return;
|
|
100
|
+
if (this.step === 'username') this.username += text.replace(/\s+/g, '');
|
|
101
|
+
else this.buffer += text;
|
|
102
|
+
this.error = null;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (token.type !== 'key') return;
|
|
106
|
+
const key = token.logicalName ?? '';
|
|
107
|
+
if (key === 'enter') this.submit(host);
|
|
108
|
+
else if (isTextBackspace(key)) {
|
|
109
|
+
if (this.step === 'username') this.username = this.username.slice(0, -1);
|
|
110
|
+
else this.buffer = [...this.buffer].slice(0, -1).join('');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
render(screenWidth: number, screenHeight: number): SurfaceLayer {
|
|
115
|
+
return renderMaskedEntryModal(this, screenWidth, screenHeight);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -218,6 +218,11 @@ export class McpWorkspace {
|
|
|
218
218
|
public tools: readonly RegisteredTool[] = [];
|
|
219
219
|
public loadingTools = false;
|
|
220
220
|
public lastError: string | null = null;
|
|
221
|
+
/**
|
|
222
|
+
* The browser's always-live search row. Narrows the server and action rows;
|
|
223
|
+
* selectedIndex indexes the narrowed list (visibleRows).
|
|
224
|
+
*/
|
|
225
|
+
public query = '';
|
|
221
226
|
private context: CommandContext | null = null;
|
|
222
227
|
private snapshot: McpWorkspaceSnapshot = {
|
|
223
228
|
projectPath: '',
|
|
@@ -233,6 +238,7 @@ export class McpWorkspace {
|
|
|
233
238
|
this.selectedIndex = 0;
|
|
234
239
|
this.formIndex = 0;
|
|
235
240
|
this.lastError = null;
|
|
241
|
+
this.query = '';
|
|
236
242
|
this.refreshSnapshot();
|
|
237
243
|
void this.refreshTools();
|
|
238
244
|
}
|
|
@@ -264,8 +270,23 @@ export class McpWorkspace {
|
|
|
264
270
|
];
|
|
265
271
|
}
|
|
266
272
|
|
|
273
|
+
/** The rows the search row lets through (servers by name, role or source; actions by label). */
|
|
274
|
+
get visibleRows(): readonly McpWorkspaceRow[] {
|
|
275
|
+
const q = this.query.trim().toLowerCase();
|
|
276
|
+
if (!q) return this.rows;
|
|
277
|
+
return this.rows.filter((row) => row.type === 'server'
|
|
278
|
+
? [row.server.name, row.server.role, row.server.source].some((v) => v.toLowerCase().includes(q))
|
|
279
|
+
: row.label.toLowerCase().includes(q));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Replace the browser query; the selection moves to the first match. */
|
|
283
|
+
setQuery(query: string): void {
|
|
284
|
+
this.query = query;
|
|
285
|
+
this.selectedIndex = 0;
|
|
286
|
+
}
|
|
287
|
+
|
|
267
288
|
get selectedRow(): McpWorkspaceRow | null {
|
|
268
|
-
return this.
|
|
289
|
+
return this.visibleRows[this.selectedIndex] ?? null;
|
|
269
290
|
}
|
|
270
291
|
|
|
271
292
|
get selectedServer(): McpWorkspaceServerRow | null {
|
|
@@ -303,7 +324,7 @@ export class McpWorkspace {
|
|
|
303
324
|
effectiveConfig,
|
|
304
325
|
servers: mergeServers(effectiveConfig, runtimeServers),
|
|
305
326
|
};
|
|
306
|
-
this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, this.
|
|
327
|
+
this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, this.visibleRows.length - 1));
|
|
307
328
|
}
|
|
308
329
|
|
|
309
330
|
async refreshTools(): Promise<void> {
|
|
@@ -417,7 +438,7 @@ export class McpWorkspace {
|
|
|
417
438
|
}
|
|
418
439
|
|
|
419
440
|
moveSelection(delta: number): void {
|
|
420
|
-
const total = this.mode === 'form' ? this.formFields.length : this.
|
|
441
|
+
const total = this.mode === 'form' ? this.formFields.length : this.visibleRows.length;
|
|
421
442
|
if (total <= 0) return;
|
|
422
443
|
if (this.mode === 'form') {
|
|
423
444
|
this.formIndex = Math.max(0, Math.min(total - 1, this.formIndex + delta));
|
|
@@ -496,6 +517,16 @@ export function handleMcpWorkspaceToken(
|
|
|
496
517
|
return true;
|
|
497
518
|
}
|
|
498
519
|
|
|
520
|
+
const browseAction = (key: string): boolean => {
|
|
521
|
+
if (key === 'a') workspace.openAddForm();
|
|
522
|
+
else if (key === 'e' && workspace.selectedServer) workspace.openEditForm(workspace.selectedServer.name);
|
|
523
|
+
else if (key === 'd' && workspace.selectedServer) workspace.requestDelete(workspace.selectedServer.name);
|
|
524
|
+
else if (key === 'r') void workspace.reloadRuntime();
|
|
525
|
+
else if (key === 't') void workspace.refreshTools();
|
|
526
|
+
else return false;
|
|
527
|
+
return true;
|
|
528
|
+
};
|
|
529
|
+
|
|
499
530
|
if (token.type === 'text') {
|
|
500
531
|
if (workspace.mode === 'form') {
|
|
501
532
|
workspace.appendFormText(token.value);
|
|
@@ -503,12 +534,11 @@ export function handleMcpWorkspaceToken(
|
|
|
503
534
|
if (token.value.toLowerCase() === 'y') void workspace.confirmDelete();
|
|
504
535
|
else if (token.value.toLowerCase() === 'n') workspace.cancelForm();
|
|
505
536
|
} else {
|
|
537
|
+
// Browse: the search row is always live. An action letter fires while
|
|
538
|
+
// the query is empty; everything else is typed into the query.
|
|
506
539
|
const value = token.value.toLowerCase();
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
else if (value === 'd' && workspace.selectedServer) workspace.requestDelete(workspace.selectedServer.name);
|
|
510
|
-
else if (value === 'r') void workspace.reloadRuntime();
|
|
511
|
-
else if (value === 't') void workspace.refreshTools();
|
|
540
|
+
const claimed = workspace.query.length === 0 && [...token.value].length === 1 && 'aedrt'.includes(value);
|
|
541
|
+
if (!claimed || !browseAction(value)) workspace.setQuery(workspace.query + token.value);
|
|
512
542
|
}
|
|
513
543
|
requestRender();
|
|
514
544
|
return true;
|
|
@@ -516,6 +546,8 @@ export function handleMcpWorkspaceToken(
|
|
|
516
546
|
|
|
517
547
|
if (token.type !== 'key') return true;
|
|
518
548
|
if (token.logicalName === 'escape') {
|
|
549
|
+
// One level per Esc: the form or the remove confirmation returns to the
|
|
550
|
+
// browser; the browser closes the workspace.
|
|
519
551
|
if (workspace.mode === 'form' || workspace.mode === 'delete-confirm') {
|
|
520
552
|
workspace.cancelForm();
|
|
521
553
|
requestRender();
|
|
@@ -530,7 +562,10 @@ export function handleMcpWorkspaceToken(
|
|
|
530
562
|
else if (token.logicalName === 'pageup') workspace.moveSelection(-10);
|
|
531
563
|
else if (token.logicalName === 'pagedown') workspace.moveSelection(10);
|
|
532
564
|
else if (token.logicalName === 'enter') void workspace.activateSelected();
|
|
533
|
-
else if (token.logicalName === 'backspace'
|
|
565
|
+
else if (token.logicalName === 'backspace') {
|
|
566
|
+
if (workspace.mode === 'form') workspace.backspaceFormText();
|
|
567
|
+
else if (workspace.mode === 'browse' && workspace.query.length > 0) workspace.setQuery(workspace.query.slice(0, -1));
|
|
568
|
+
} else if (token.logicalName === 'delete') {
|
|
534
569
|
if (workspace.mode === 'form') workspace.backspaceFormText();
|
|
535
570
|
else if (workspace.mode === 'browse' && workspace.selectedServer) workspace.requestDelete(workspace.selectedServer.name);
|
|
536
571
|
} else if (token.logicalName === 'left') {
|
|
@@ -539,14 +574,10 @@ export function handleMcpWorkspaceToken(
|
|
|
539
574
|
if (workspace.mode === 'form') workspace.adjustFormEnum(1);
|
|
540
575
|
} else if (token.logicalName === 'space') {
|
|
541
576
|
if (workspace.mode === 'form') workspace.appendFormText(' ');
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
} else if (token.logicalName === 'r' && workspace.mode === 'browse') {
|
|
547
|
-
void workspace.reloadRuntime();
|
|
548
|
-
} else if (token.logicalName === 't' && workspace.mode === 'browse') {
|
|
549
|
-
void workspace.refreshTools();
|
|
577
|
+
else if (workspace.mode === 'browse') workspace.setQuery(workspace.query + ' ');
|
|
578
|
+
} else if (workspace.mode === 'browse' && token.logicalName) {
|
|
579
|
+
// Modified letters (CSI-u chords) fire their action whatever the query holds.
|
|
580
|
+
browseAction(token.logicalName);
|
|
550
581
|
}
|
|
551
582
|
|
|
552
583
|
requestRender();
|