@pellux/goodvibes-tui 2.0.20 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/README.md +16 -16
- package/docs/README.md +4 -4
- package/docs/foundation-artifacts/operator-contract.json +59 -4
- package/package.json +16 -6
- package/src/cli/management.ts +5 -2
- package/src/cli/tui-startup.ts +34 -39
- package/src/core/agent-view-render.ts +157 -0
- package/src/core/approval-alert.ts +49 -11
- package/src/core/bookmark-navigation.ts +7 -1
- package/src/core/budget-breach-notifier.ts +27 -8
- package/src/core/compaction-receipt.ts +1 -1
- package/src/core/context-usage.ts +1 -1
- package/src/core/conversation-compaction-render.ts +3 -3
- package/src/core/conversation-display-only.ts +86 -0
- package/src/core/conversation-event-navigation.ts +27 -0
- package/src/core/conversation-line-cache.ts +216 -447
- package/src/core/conversation-render-context.ts +20 -27
- package/src/core/conversation-rendering.ts +157 -575
- package/src/core/conversation-splash-state.ts +6 -1
- package/src/core/conversation-types.ts +35 -0
- package/src/core/conversation-usage.ts +12 -0
- package/src/core/conversation.ts +130 -131
- package/src/core/long-task-notifier.ts +100 -31
- package/src/core/notices.ts +75 -0
- package/src/core/pairing-tailscale-gateway.ts +1 -1
- package/src/core/process-output-log.ts +143 -0
- package/src/core/rewind-receipt.ts +1 -1
- package/src/core/session-focus.ts +190 -0
- package/src/core/session-resume-core.ts +23 -81
- package/src/core/stream-event-wiring.ts +31 -5
- package/src/core/system-message-noise.ts +2 -2
- package/src/core/system-message-router.ts +16 -18
- package/src/core/turn-event-wiring.ts +272 -33
- package/src/core/work-tree-focus.ts +290 -0
- package/src/core/work-tree-fold-store.ts +143 -0
- package/src/core/work-tree-model.ts +799 -0
- package/src/core/work-tree-render.ts +384 -0
- package/src/core/work-tree-sources.ts +184 -0
- package/src/core/work-tree-wiring.ts +176 -0
- package/src/core/workstream-notification.ts +36 -6
- package/src/core/wrfc-notice-titles.ts +37 -0
- package/src/input/agents-modal-text.ts +119 -0
- package/src/input/agents-modal.ts +699 -0
- package/src/input/bookmark-modal.ts +33 -9
- package/src/input/changes-git.ts +251 -0
- package/src/input/changes-modal.ts +629 -0
- package/src/input/changes-model.ts +125 -0
- package/src/input/command-palette-types.ts +41 -0
- package/src/input/command-palette.ts +272 -0
- package/src/input/command-registry.ts +77 -35
- package/src/input/commands/acp-runtime.ts +19 -9
- package/src/input/commands/channel-runtime.ts +2 -2
- package/src/input/commands/ci-runtime.ts +1 -1
- package/src/input/commands/codebase-runtime.ts +1 -1
- package/src/input/commands/config.ts +7 -1
- package/src/input/commands/context-window.ts +25 -19
- package/src/input/commands/control-room-runtime.ts +18 -18
- package/src/input/commands/conversation-runtime.ts +0 -1
- package/src/input/commands/cost-runtime.ts +29 -20
- package/src/input/commands/diff-runtime.ts +35 -181
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/experience-runtime.ts +3 -7
- package/src/input/commands/fresh-conversation.ts +86 -0
- package/src/input/commands/git-runtime.ts +5 -22
- package/src/input/commands/graph-runtime.ts +1 -1
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +8 -9
- package/src/input/commands/hooks-runtime.ts +2 -2
- package/src/input/commands/hosted-runtime.ts +3 -3
- package/src/input/commands/incident-runtime.ts +3 -3
- package/src/input/commands/intelligence-runtime.ts +6 -9
- package/src/input/commands/knowledge.ts +2 -2
- package/src/input/commands/legacy-panel-command.ts +57 -0
- package/src/input/commands/local-auth-runtime.ts +8 -9
- package/src/input/commands/local-provider-runtime.ts +7 -4
- package/src/input/commands/local-runtime.ts +12 -15
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/local-setup.ts +13 -19
- package/src/input/commands/memory.ts +1 -1
- package/src/input/commands/notify-runtime.ts +9 -0
- package/src/input/commands/operator-runtime.ts +10 -12
- package/src/input/commands/permissions-provenance.ts +3 -3
- package/src/input/commands/planning-runtime.ts +7 -7
- package/src/input/commands/platform-sandbox-runtime.ts +2 -2
- package/src/input/commands/platform-services-runtime.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +2 -2
- package/src/input/commands/policy.ts +3 -3
- package/src/input/commands/provider.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/review-runtime.ts +39 -94
- package/src/input/commands/rewind-runtime.ts +81 -108
- package/src/input/commands/runtime-services.ts +15 -17
- package/src/input/commands/services-runtime.ts +2 -2
- package/src/input/commands/session-content.ts +21 -5
- package/src/input/commands/session-workflow.ts +13 -38
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/shell-core.ts +19 -4
- package/src/input/commands/skills-runtime.ts +2 -2
- package/src/input/commands/status-runtime.ts +13 -0
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/tasks-runtime.ts +2 -8
- package/src/input/commands/work-plan-runtime.ts +4 -4
- package/src/input/commands/workstream-attempts.ts +24 -47
- package/src/input/commands/workstream-runtime.ts +2 -2
- package/src/input/commands/worktree-runtime.ts +3 -3
- package/src/input/commands.ts +3 -1
- package/src/input/composer-editor.ts +44 -0
- package/src/input/concealed-input.ts +1 -1
- package/src/input/config-modal-types.ts +20 -8
- package/src/input/config-modal.ts +106 -105
- package/src/input/confirm-dialog-types.ts +28 -0
- package/src/input/confirm-dialog.ts +70 -0
- package/src/input/delete-key-policy.ts +3 -3
- package/src/input/feed-context-factory.ts +15 -16
- package/src/input/handler-command-route.ts +7 -42
- package/src/input/handler-content-actions.ts +16 -15
- package/src/input/handler-feed-routes.ts +26 -211
- package/src/input/handler-feed.ts +68 -95
- package/src/input/handler-interactions.ts +5 -3
- package/src/input/handler-modal-routes.ts +215 -216
- package/src/input/handler-modal-stack.ts +81 -11
- package/src/input/handler-modal-token-routes.ts +4 -0
- package/src/input/handler-onboarding.ts +0 -1
- package/src/input/handler-picker-routes.ts +65 -107
- package/src/input/handler-session-view-route.ts +149 -0
- package/src/input/handler-shortcuts.ts +16 -154
- package/src/input/handler-types.ts +4 -2
- package/src/input/handler-ui-state.ts +45 -15
- package/src/input/handler-work-tree-route.ts +123 -0
- package/src/input/handler.ts +25 -70
- package/src/input/keybindings.ts +16 -71
- package/src/input/masked-entry-modal.ts +117 -0
- package/src/input/mcp-workspace.ts +48 -17
- package/src/input/model-picker-filter.ts +8 -0
- package/src/input/model-picker-open.ts +99 -0
- package/src/input/model-picker-types.ts +1 -1
- package/src/input/model-picker.ts +92 -9
- package/src/input/notifications-modal.ts +123 -0
- package/src/input/overlay-filter.ts +26 -0
- package/src/input/profile-picker-modal.ts +37 -8
- package/src/input/search.ts +3 -3
- package/src/input/selection-modal-queue.ts +2 -0
- package/src/input/selection-modal.ts +28 -16
- package/src/input/session-picker-modal.ts +33 -8
- package/src/input/settings-modal-activation.ts +7 -2
- package/src/input/settings-modal-data.ts +11 -6
- package/src/input/settings-modal-types.ts +1 -1
- package/src/input/settings-modal.ts +20 -20
- package/src/input/submission-intent.ts +1 -1
- package/src/input/submission-router.ts +4 -9
- package/src/input/surface-modal-host.ts +110 -0
- package/src/input/theme-settings-actions.ts +82 -0
- package/src/input/usage-modal.ts +183 -0
- package/src/input/views.ts +97 -0
- package/src/main.ts +175 -165
- package/src/permissions/hunk-selection.ts +1 -1
- package/src/permissions/prompt-card.ts +511 -0
- package/src/permissions/prompt.ts +59 -622
- package/src/renderer/agents-modal.ts +310 -0
- package/src/renderer/autocomplete-overlay.ts +42 -220
- package/src/renderer/back-to-bottom.ts +80 -0
- package/src/renderer/block-actions-overlay.ts +30 -48
- package/src/renderer/bookmark-modal.ts +48 -79
- package/src/renderer/changes-modal.ts +325 -0
- package/src/renderer/code-block.ts +184 -106
- package/src/renderer/command-palette.ts +93 -0
- package/src/renderer/composer-fragments.ts +4 -4
- package/src/renderer/composer.ts +146 -0
- package/src/renderer/compositor.ts +26 -160
- package/src/renderer/config-modal.ts +185 -81
- package/src/renderer/confirm-dialog.ts +50 -0
- package/src/renderer/context-inspector.ts +116 -154
- package/src/renderer/context-status-hint.ts +4 -4
- package/src/renderer/conversation-layout.ts +20 -0
- package/src/renderer/conversation-overlays.ts +94 -84
- package/src/renderer/conversation-surface.ts +19 -38
- package/src/renderer/diff-tint.ts +98 -0
- package/src/renderer/file-picker-overlay.ts +63 -90
- package/src/renderer/footer-targets.ts +38 -0
- package/src/renderer/git-status.ts +3 -4
- package/src/renderer/header-line.ts +140 -117
- package/src/renderer/help-overlay.ts +133 -219
- package/src/renderer/history-search-overlay.ts +15 -12
- package/src/renderer/lane-graph/bead.ts +800 -0
- package/src/renderer/lane-graph/glyphs.ts +134 -0
- package/src/renderer/lane-graph/layout.ts +248 -0
- package/src/renderer/lane-graph/paint.ts +537 -0
- package/src/renderer/lane-graph/semantic-memo.ts +77 -0
- package/src/renderer/layout-engine.ts +2 -57
- package/src/renderer/layout.ts +7 -13
- package/src/renderer/markdown-inline.ts +2 -1
- package/src/renderer/markdown-table.ts +17 -12
- package/src/renderer/markdown.ts +92 -34
- package/src/renderer/masked-entry-modal.ts +78 -0
- package/src/renderer/mcp-workspace.ts +166 -203
- package/src/renderer/model-workspace.ts +381 -436
- package/src/renderer/notifications-modal.ts +116 -0
- package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
- package/src/renderer/process-view.ts +192 -0
- package/src/renderer/profile-picker-modal.ts +58 -110
- package/src/renderer/search-overlay.ts +6 -3
- package/src/renderer/selection-modal-overlay.ts +87 -327
- package/src/renderer/semantic-diff.ts +2 -2
- package/src/renderer/session-chips.ts +102 -0
- package/src/renderer/session-picker-modal.ts +177 -297
- package/src/renderer/settings-modal-connections.ts +14 -23
- package/src/renderer/settings-modal-context.ts +367 -0
- package/src/renderer/settings-modal-helpers.ts +6 -11
- package/src/renderer/settings-modal.ts +322 -726
- package/src/renderer/shell-surface.ts +277 -175
- package/src/renderer/shortcuts-overlay.ts +217 -0
- package/src/renderer/status-line.ts +475 -0
- package/src/renderer/surface-compose.ts +174 -0
- package/src/renderer/surface-kit-extra.ts +303 -0
- package/src/renderer/surface-kit-list.ts +366 -0
- package/src/renderer/surface-kit-parts.ts +360 -0
- package/src/renderer/surface-kit.ts +568 -0
- package/src/renderer/syntax-highlighter.ts +280 -197
- package/src/renderer/syntax-theme.ts +51 -0
- package/src/renderer/system-message.ts +0 -1
- package/src/renderer/term-caps.ts +42 -1
- package/src/renderer/terminal-bg-probe.ts +116 -19
- package/src/renderer/terminal-palette-probe.ts +322 -0
- package/src/renderer/terminal-palette-reader.ts +183 -0
- package/src/renderer/terminal-palette.ts +58 -0
- package/src/renderer/theme-mode-config.ts +16 -1
- package/src/renderer/theme.ts +234 -207
- package/src/renderer/thinking.ts +0 -1
- package/src/renderer/throbber.ts +188 -0
- package/src/renderer/toast-center.ts +119 -0
- package/src/renderer/ui-factory.ts +39 -602
- package/src/renderer/ui-primitives.ts +8 -25
- package/src/renderer/usage-modal.ts +353 -0
- package/src/renderer/user-message.ts +58 -0
- package/src/renderer/voice-capture-chip.ts +53 -0
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +38 -89
- package/src/runtime/bootstrap-core.ts +13 -15
- package/src/runtime/bootstrap-hook-bridge.ts +12 -21
- package/src/runtime/bootstrap-notifier-sync.ts +17 -1
- package/src/runtime/bootstrap-shell.ts +16 -50
- package/src/runtime/bootstrap.ts +14 -14
- package/src/runtime/client/fleet-union.ts +5 -5
- package/src/runtime/client/hosted-exit.ts +2 -2
- package/src/runtime/client/hosted-sessions.ts +1 -1
- package/src/runtime/client/spine-adoption.ts +1 -1
- package/src/runtime/context-accounting-source.ts +5 -4
- package/src/runtime/index.ts +1 -0
- package/src/runtime/notification-dispatch.ts +102 -29
- package/src/runtime/recovery-offer-wiring.ts +4 -1
- package/src/runtime/recovery-prompt.ts +31 -15
- package/src/runtime/runtime-services-types.ts +10 -6
- package/src/runtime/services.ts +17 -12
- package/src/runtime/session-continuity-hints.ts +2 -5
- package/src/runtime/session-inbound-inputs.ts +1 -1
- package/src/runtime/session-resume-liveness-confirm.ts +14 -2
- package/src/runtime/startup-typeahead-gate.ts +103 -0
- package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
- package/src/runtime/store/domains/index.ts +0 -8
- package/src/runtime/store/selectors/index.ts +0 -28
- package/src/runtime/store/state.ts +0 -8
- package/src/runtime/ui-services.ts +4 -6
- package/src/runtime/usage-tracker.ts +348 -0
- package/src/runtime/wrfc-persistence.ts +4 -4
- package/src/shell/blocking-input.ts +92 -2
- package/src/shell/recovery-input-helpers.ts +1 -1
- package/src/shell/session-views.ts +539 -0
- package/src/shell/status-report.ts +86 -0
- package/src/shell/transcript-scroll.ts +68 -0
- package/src/shell/ui-openers.ts +84 -172
- package/src/shell/view-openers.ts +205 -0
- package/src/tools/wrfc-agent-guard.ts +22 -2
- package/src/utils/format-duration.ts +4 -8
- package/src/utils/splash-lines.ts +9 -1
- package/src/utils/terminal-width.ts +56 -0
- package/src/verification/verification-ledger.ts +18 -16
- package/src/{panels → views}/agent-inspector-shared.ts +2 -2
- package/src/{panels → views}/builtin-modals.ts +22 -21
- package/src/views/builtin-views.ts +135 -0
- package/src/{panels → views}/confirm-state.ts +9 -9
- package/src/{panels → views}/diff-review-model.ts +10 -3
- package/src/{panels → views}/eval-registry.ts +1 -1
- package/src/{panels → views}/fleet-acts.ts +49 -85
- package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
- package/src/{panels → views}/fleet-gateway.ts +5 -5
- package/src/{panels → views}/fleet-observed-render.ts +16 -16
- package/src/{panels → views}/fleet-read-model.ts +9 -9
- package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
- package/src/{panels → views}/fleet-spawn.ts +33 -49
- package/src/{panels → views}/fleet-steer.ts +12 -12
- package/src/{panels → views}/fleet-stop.ts +4 -4
- package/src/{panels → views}/fleet-transcript.ts +26 -27
- package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
- package/src/{panels → views}/hosted-session-feed.ts +6 -6
- package/src/views/modal-surface-registry.ts +41 -0
- package/src/{panels → views}/modals/devices-modal.ts +2 -2
- package/src/{panels → views}/modals/hooks-modal.ts +13 -13
- package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
- package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
- package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
- package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
- package/src/{panels → views}/modals/memory-modal.ts +8 -8
- package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
- package/src/{panels → views}/modals/pairing-modal.ts +9 -9
- package/src/{panels → views}/modals/planning-modal.ts +15 -20
- package/src/{panels → views}/modals/plugins-modal.ts +6 -6
- package/src/{panels → views}/modals/policy-modal.ts +13 -5
- package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
- package/src/{panels → views}/modals/remote-modal.ts +3 -3
- package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
- package/src/{panels → views}/modals/security-modal.ts +22 -16
- package/src/{panels → views}/modals/services-modal.ts +3 -3
- package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
- package/src/{panels → views}/modals/skills-modal.ts +9 -9
- package/src/{panels → views}/modals/subscription-modal.ts +3 -3
- package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
- package/src/views/notifications-feed.ts +314 -0
- package/src/{panels → views}/polish-core.ts +38 -36
- package/src/{panels → views}/polish-tables.ts +35 -26
- package/src/{panels → views}/polish.ts +101 -95
- package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
- package/src/{panels → views}/provider-health-routes.ts +14 -14
- package/src/{panels → views}/search-focus.ts +8 -8
- package/src/{panels → views}/session-maintenance.ts +11 -11
- package/src/views/skills-discovery.ts +159 -0
- package/src/views/view-deps.ts +80 -0
- package/src/views/view-panel-adapter.ts +53 -0
- package/src/{panels → views}/workstream-graph-render.ts +1 -1
- package/src/core/conversation-fold.ts +0 -101
- package/src/core/conversation-turn-structure.ts +0 -464
- package/src/core/conversation-user-receipts.ts +0 -42
- package/src/input/commands/operator-panel-runtime.ts +0 -186
- package/src/input/panel-integration-actions.ts +0 -33
- package/src/input/panel-mouse-geometry.ts +0 -97
- package/src/input/panel-paste-flood-guard.ts +0 -86
- package/src/panels/base-panel.ts +0 -301
- package/src/panels/builtin/agent.ts +0 -27
- package/src/panels/builtin/development.ts +0 -92
- package/src/panels/builtin/knowledge.ts +0 -12
- package/src/panels/builtin/operations.ts +0 -177
- package/src/panels/builtin/session.ts +0 -81
- package/src/panels/builtin/shared.ts +0 -357
- package/src/panels/builtin-panels.ts +0 -28
- package/src/panels/cost-tracker-panel.ts +0 -746
- package/src/panels/diff-panel.ts +0 -797
- package/src/panels/diff-review-panel.ts +0 -395
- package/src/panels/expandable-list-panel.ts +0 -189
- package/src/panels/fleet-deep-link.ts +0 -31
- package/src/panels/fleet-panel.ts +0 -799
- package/src/panels/git-panel.ts +0 -800
- package/src/panels/hosted-session-panel.ts +0 -221
- package/src/panels/index.ts +0 -21
- package/src/panels/local-auth-panel.ts +0 -478
- package/src/panels/notifications-feed.ts +0 -127
- package/src/panels/notifications-panel.ts +0 -160
- package/src/panels/panel-confirm-overlay.ts +0 -72
- package/src/panels/panel-manager.ts +0 -791
- package/src/panels/plugins-panel.ts +0 -371
- package/src/panels/scrollable-list-panel.ts +0 -498
- package/src/panels/skills-panel.ts +0 -454
- package/src/panels/token-budget-panel.ts +0 -688
- package/src/panels/types.ts +0 -191
- package/src/panels/wrfc-panel-format.ts +0 -133
- package/src/renderer/diff-view.ts +0 -134
- package/src/renderer/fleet-tab-strip.ts +0 -56
- package/src/renderer/footer-tips.ts +0 -53
- package/src/renderer/fullscreen-primitives.ts +0 -146
- package/src/renderer/fullscreen-workspace.ts +0 -194
- package/src/renderer/hint-grammar.ts +0 -52
- package/src/renderer/modal-factory.ts +0 -480
- package/src/renderer/overlay-box.ts +0 -154
- package/src/renderer/overlay-viewport.ts +0 -104
- package/src/renderer/panel-composite.ts +0 -145
- package/src/renderer/panel-workspace-bar.ts +0 -54
- package/src/renderer/process-indicator.ts +0 -163
- package/src/renderer/tab-strip.ts +0 -222
- package/src/renderer/text-layout.ts +0 -31
- package/src/renderer/tool-call.ts +0 -276
- package/src/renderer/tool-result-expanded-lines.ts +0 -48
- package/src/renderer/tool-result-summary.ts +0 -219
- package/src/runtime/diagnostics/panels/index.ts +0 -21
- package/src/runtime/diagnostics/panels/ops.ts +0 -156
- package/src/runtime/perf/panel-contracts.ts +0 -32
- package/src/runtime/store/domains/panels.ts +0 -117
- /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
- /package/src/{panels → views}/modals/modal-theme.ts +0 -0
- /package/src/{panels → views}/provider-health-domains.ts +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { activeTokens } from '../renderer/theme.ts';
|
|
1
2
|
import { loadSkillByTrigger } from '@pellux/goodvibes-sdk/platform/tools';
|
|
2
3
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
3
4
|
import type { CommandContext, CommandRegistry } from './command-registry.ts';
|
|
4
5
|
import type { AutocompleteEngine } from './autocomplete.ts';
|
|
5
6
|
import type { InputToken } from '@pellux/goodvibes-sdk/platform/core';
|
|
6
7
|
import type { ConversationManager } from '../core/conversation';
|
|
7
|
-
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
8
8
|
import { handleClipboardPaste, type ClipboardPasteSource } from './handler-content-actions.ts';
|
|
9
9
|
|
|
10
10
|
export type CommandModeRouteState = {
|
|
@@ -15,8 +15,6 @@ export type CommandModeRouteState = {
|
|
|
15
15
|
modalStack: string[];
|
|
16
16
|
commandRegistry: CommandRegistry | null;
|
|
17
17
|
commandContext?: CommandContext;
|
|
18
|
-
panelFocused: boolean;
|
|
19
|
-
panelManager: PanelManager;
|
|
20
18
|
conversationManager: ConversationManager | null;
|
|
21
19
|
requestRender: () => void;
|
|
22
20
|
handleEscape: () => void;
|
|
@@ -93,7 +91,7 @@ export function handleCommandModeToken(state: CommandModeRouteState, token: Inpu
|
|
|
93
91
|
const parts = raw.slice(1).trim().split(/\s+/);
|
|
94
92
|
const name = parts[0];
|
|
95
93
|
const args = parts.slice(1);
|
|
96
|
-
const ctx =
|
|
94
|
+
const ctx = withComposerBindings(state.commandContext, state);
|
|
97
95
|
const commandPromise = state.commandRegistry.get(name)
|
|
98
96
|
? state.commandRegistry.execute(name, args, ctx)
|
|
99
97
|
: (ctx.executeCommand?.(name, args) ?? Promise.resolve(false));
|
|
@@ -112,7 +110,7 @@ export function handleCommandModeToken(state: CommandModeRouteState, token: Inpu
|
|
|
112
110
|
if (skillContent) {
|
|
113
111
|
state.commandContext?.submitInput?.(skillContent);
|
|
114
112
|
} else {
|
|
115
|
-
state.conversationManager?.log(`Unknown command: /${name}. Type /help for available commands.`, { fg:
|
|
113
|
+
state.conversationManager?.log(`Unknown command: /${name}. Type /help for available commands.`, { fg: activeTokens().error });
|
|
116
114
|
state.requestRender();
|
|
117
115
|
}
|
|
118
116
|
}
|
|
@@ -122,7 +120,7 @@ export function handleCommandModeToken(state: CommandModeRouteState, token: Inpu
|
|
|
122
120
|
// already guard their own internals: a handler that throws or
|
|
123
121
|
// awaits a rejected promise must never become a silent unhandled
|
|
124
122
|
// rejection, it renders the same way an unknown command does.
|
|
125
|
-
state.conversationManager?.log(`Command /${name} failed: ${summarizeError(err)}`, { fg:
|
|
123
|
+
state.conversationManager?.log(`Command /${name} failed: ${summarizeError(err)}`, { fg: activeTokens().error });
|
|
126
124
|
state.requestRender();
|
|
127
125
|
});
|
|
128
126
|
} else {
|
|
@@ -134,43 +132,10 @@ export function handleCommandModeToken(state: CommandModeRouteState, token: Inpu
|
|
|
134
132
|
return token.logicalName !== 'left' && token.logicalName !== 'right' && token.logicalName !== 'home' && token.logicalName !== 'end';
|
|
135
133
|
}
|
|
136
134
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
state.panelManager.isVisible() && state.panelManager.getAllOpen().length > 0;
|
|
140
|
-
|
|
135
|
+
/** The command context with the composer-bound actions (clipboard paste, nested commands) bound to this route's state. */
|
|
136
|
+
function withComposerBindings(context: CommandContext, state: CommandModeRouteState): CommandContext {
|
|
141
137
|
return {
|
|
142
138
|
...context,
|
|
143
|
-
openPanelPicker: context.openPanelPicker
|
|
144
|
-
? () => {
|
|
145
|
-
context.openPanelPicker?.();
|
|
146
|
-
state.panelFocused = panelIsFocusable();
|
|
147
|
-
}
|
|
148
|
-
: undefined,
|
|
149
|
-
showPanel: context.showPanel
|
|
150
|
-
? (panelId, pane, target, opts) => {
|
|
151
|
-
context.showPanel?.(panelId, pane, target, opts);
|
|
152
|
-
// showPanel no longer transfers focus by default, the command
|
|
153
|
-
// path leaves the composer focused (ui-openers.ts's showPanel only
|
|
154
|
-
// calls focusPanels() when opts.focus is set). Only flip the local
|
|
155
|
-
// panelFocused mirror when the caller explicitly asked for focus;
|
|
156
|
-
// otherwise leave it exactly as it was (the composer, since you can
|
|
157
|
-
// only type a command from there in the first place). `target` is the
|
|
158
|
-
// deep-link jump target, forwarded verbatim.
|
|
159
|
-
if (opts?.focus) state.panelFocused = true;
|
|
160
|
-
}
|
|
161
|
-
: undefined,
|
|
162
|
-
focusPanels: context.focusPanels
|
|
163
|
-
? () => {
|
|
164
|
-
context.focusPanels?.();
|
|
165
|
-
state.panelFocused = panelIsFocusable();
|
|
166
|
-
}
|
|
167
|
-
: undefined,
|
|
168
|
-
focusPrompt: context.focusPrompt
|
|
169
|
-
? () => {
|
|
170
|
-
context.focusPrompt?.();
|
|
171
|
-
state.panelFocused = false;
|
|
172
|
-
}
|
|
173
|
-
: undefined,
|
|
174
139
|
pasteFromClipboard: () => {
|
|
175
140
|
const result = handleClipboardPaste({
|
|
176
141
|
prompt: state.prompt,
|
|
@@ -190,7 +155,7 @@ function withPanelFocusSync(context: CommandContext, state: CommandModeRouteStat
|
|
|
190
155
|
return result;
|
|
191
156
|
},
|
|
192
157
|
executeCommand: async (name, args) => {
|
|
193
|
-
const wrapped =
|
|
158
|
+
const wrapped = withComposerBindings(context, state);
|
|
194
159
|
const handled = state.commandRegistry?.get(name)
|
|
195
160
|
? await state.commandRegistry.execute(name, args, wrapped)
|
|
196
161
|
: false;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { activeTokens } from '../renderer/theme.ts';
|
|
1
2
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
3
|
import { copyToClipboard, pasteFromClipboard, pasteImageFromClipboard } from '../utils/clipboard.ts';
|
|
3
4
|
import type { InfiniteBuffer } from '@pellux/goodvibes-terminal-shell';
|
|
@@ -299,7 +300,7 @@ export function handleBlockCopy(
|
|
|
299
300
|
const lineIndex = getAnchorLine();
|
|
300
301
|
const nearest = conversationManager.findNearestBlock(lineIndex);
|
|
301
302
|
if (!nearest) {
|
|
302
|
-
conversationManager.log('[Ctrl+Y: No block found nearby]', { fg:
|
|
303
|
+
conversationManager.log('[Ctrl+Y: No block found nearby]', { fg: activeTokens().textFaint });
|
|
303
304
|
requestRender();
|
|
304
305
|
return;
|
|
305
306
|
}
|
|
@@ -310,7 +311,7 @@ export function handleBlockCopy(
|
|
|
310
311
|
// copying content the user just had search reveal is a deliberate choice
|
|
311
312
|
// to keep it visible, not an accident of typing a query.
|
|
312
313
|
conversationManager.searchExpansion.noteUserTouch(nearest.collapseKey);
|
|
313
|
-
conversationManager.log(`[Copied ${describeBlockForReceipt(nearest)}]`, { fg:
|
|
314
|
+
conversationManager.log(`[Copied ${describeBlockForReceipt(nearest)}]`, { fg: activeTokens().success });
|
|
314
315
|
requestRender();
|
|
315
316
|
setTimeout(() => requestRender(), 2005);
|
|
316
317
|
}
|
|
@@ -325,7 +326,7 @@ export function handleBookmark(
|
|
|
325
326
|
const lineIndex = getAnchorLine();
|
|
326
327
|
const nearest = conversationManager.findNearestBlock(lineIndex);
|
|
327
328
|
if (!nearest) {
|
|
328
|
-
conversationManager.log('[Ctrl+B: No block found nearby]', { fg:
|
|
329
|
+
conversationManager.log('[Ctrl+B: No block found nearby]', { fg: activeTokens().textFaint });
|
|
329
330
|
requestRender();
|
|
330
331
|
return;
|
|
331
332
|
}
|
|
@@ -337,7 +338,7 @@ export function handleBookmark(
|
|
|
337
338
|
const msg = added
|
|
338
339
|
? `[Bookmarked: ${target}]`
|
|
339
340
|
: `[Bookmark removed: ${target}]`;
|
|
340
|
-
conversationManager.log(msg, { fg: added ?
|
|
341
|
+
conversationManager.log(msg, { fg: added ? activeTokens().success : activeTokens().textMuted });
|
|
341
342
|
requestRender();
|
|
342
343
|
}
|
|
343
344
|
|
|
@@ -351,7 +352,7 @@ export function handleBlockSave(
|
|
|
351
352
|
const lineIndex = getAnchorLine();
|
|
352
353
|
const nearest = conversationManager.findNearestBlock(lineIndex);
|
|
353
354
|
if (!nearest) {
|
|
354
|
-
conversationManager.log('[Ctrl+S: No block found nearby]', { fg:
|
|
355
|
+
conversationManager.log('[Ctrl+S: No block found nearby]', { fg: activeTokens().textFaint });
|
|
355
356
|
requestRender();
|
|
356
357
|
return;
|
|
357
358
|
}
|
|
@@ -362,10 +363,10 @@ export function handleBlockSave(
|
|
|
362
363
|
const filePath = bookmarkManager.saveToFile(nearest.rawContent, nearest.type);
|
|
363
364
|
const homePath = process.env.HOME || process.env.USERPROFILE || '';
|
|
364
365
|
const displayPath = homePath ? filePath.replace(homePath, '~') : filePath;
|
|
365
|
-
conversationManager.log(`[Saved ${target} to: ${displayPath}]`, { fg:
|
|
366
|
+
conversationManager.log(`[Saved ${target} to: ${displayPath}]`, { fg: activeTokens().success });
|
|
366
367
|
} catch (err) {
|
|
367
368
|
const msg = summarizeError(err);
|
|
368
|
-
conversationManager.log(`[Save failed (${target}): ${msg}]`, { fg:
|
|
369
|
+
conversationManager.log(`[Save failed (${target}): ${msg}]`, { fg: activeTokens().error });
|
|
369
370
|
}
|
|
370
371
|
requestRender();
|
|
371
372
|
}
|
|
@@ -382,7 +383,7 @@ export function handleBlockToggle(
|
|
|
382
383
|
const wasCollapsed = conversationManager.isCollapsed(nearest.blockIndex);
|
|
383
384
|
const blockIdx = conversationManager.toggleCollapseAtLine(lineIndex);
|
|
384
385
|
if (blockIdx >= 0) {
|
|
385
|
-
conversationManager.log(`[${wasCollapsed ? 'Expanded' : 'Collapsed'} ${describeBlockForReceipt(nearest)}]`, { fg:
|
|
386
|
+
conversationManager.log(`[${wasCollapsed ? 'Expanded' : 'Collapsed'} ${describeBlockForReceipt(nearest)}]`, { fg: activeTokens().textMuted });
|
|
386
387
|
requestRender();
|
|
387
388
|
}
|
|
388
389
|
}
|
|
@@ -415,14 +416,14 @@ export function handleDiffApply(
|
|
|
415
416
|
}).then(({ approved }) => {
|
|
416
417
|
if (!approved) return;
|
|
417
418
|
if (!projectRoot) {
|
|
418
|
-
conversationManager.log('[Diff apply failed: missing working directory]', { fg:
|
|
419
|
+
conversationManager.log('[Diff apply failed: missing working directory]', { fg: activeTokens().error });
|
|
419
420
|
return;
|
|
420
421
|
}
|
|
421
422
|
let resolvedPath: string;
|
|
422
423
|
try {
|
|
423
424
|
resolvedPath = resolveAndValidatePath(diff.filePath!, projectRoot);
|
|
424
425
|
} catch (err) {
|
|
425
|
-
conversationManager.log(`[Diff apply failed: ${err instanceof Error ? err.message : err}]`, { fg:
|
|
426
|
+
conversationManager.log(`[Diff apply failed: ${err instanceof Error ? err.message : err}]`, { fg: activeTokens().error });
|
|
426
427
|
return;
|
|
427
428
|
}
|
|
428
429
|
try {
|
|
@@ -430,22 +431,22 @@ export function handleDiffApply(
|
|
|
430
431
|
if (diff.original && content.includes(diff.original)) {
|
|
431
432
|
const occurrenceCount = content.split(diff.original).length - 1;
|
|
432
433
|
if (occurrenceCount > 1) {
|
|
433
|
-
conversationManager.log(`[Diff apply failed: pattern found ${occurrenceCount} times in ${diff.filePath} - ambiguous]`, { fg:
|
|
434
|
+
conversationManager.log(`[Diff apply failed: pattern found ${occurrenceCount} times in ${diff.filePath} - ambiguous]`, { fg: activeTokens().error });
|
|
434
435
|
} else {
|
|
435
436
|
const newContent = content.replace(diff.original, diff.updated);
|
|
436
437
|
writeFileSync(resolvedPath, newContent, 'utf-8');
|
|
437
|
-
conversationManager.log(`[Applied diff to ${diff.filePath}]`, { fg:
|
|
438
|
+
conversationManager.log(`[Applied diff to ${diff.filePath}]`, { fg: activeTokens().success });
|
|
438
439
|
}
|
|
439
440
|
} else {
|
|
440
|
-
conversationManager.log(`[Diff apply failed: original text not found in ${diff.filePath}]`, { fg:
|
|
441
|
+
conversationManager.log(`[Diff apply failed: original text not found in ${diff.filePath}]`, { fg: activeTokens().error });
|
|
441
442
|
}
|
|
442
443
|
} catch (err) {
|
|
443
444
|
const msg = summarizeError(err);
|
|
444
|
-
conversationManager.log(`[Diff apply error: ${msg}]`, { fg:
|
|
445
|
+
conversationManager.log(`[Diff apply error: ${msg}]`, { fg: activeTokens().error });
|
|
445
446
|
}
|
|
446
447
|
requestRender();
|
|
447
448
|
}).catch((err) => {
|
|
448
|
-
conversationManager.log(`[Diff apply error: ${summarizeError(err)}]`, { fg:
|
|
449
|
+
conversationManager.log(`[Diff apply error: ${summarizeError(err)}]`, { fg: activeTokens().error });
|
|
449
450
|
requestRender();
|
|
450
451
|
});
|
|
451
452
|
return true;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { InputToken } from '@pellux/goodvibes-sdk/platform/core';
|
|
2
|
+
import type { FooterTarget } from '../renderer/footer-targets.ts';
|
|
2
3
|
import type { BlockMeta, ConversationManager } from '../core/conversation';
|
|
3
4
|
import type { InputHistory } from './input-history.ts';
|
|
4
5
|
import type { ContentPart } from '@pellux/goodvibes-sdk/platform/providers';
|
|
@@ -12,185 +13,14 @@ import {
|
|
|
12
13
|
moveCursorVertical as computeCursorVerticalMove,
|
|
13
14
|
} from './handler-prompt-buffer.ts';
|
|
14
15
|
import { cleanupMarkerRegistry, expandPrompt, findMarkerAtPos, registerPaste } from './handler-content-actions.ts';
|
|
15
|
-
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
16
|
-
import { getPanelUnderMouse, workspaceTabAtMouse } from './panel-mouse-geometry.ts';
|
|
17
|
-
import type { PanelMouseLayout } from './panel-mouse-geometry.ts';
|
|
18
|
-
export type { PanelMouseLayout };
|
|
19
|
-
import type { KeybindingsManager } from './keybindings.ts';
|
|
20
16
|
import type { KillRing } from './kill-ring.ts';
|
|
21
17
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
22
|
-
import { trackPanelPasteFloodGuard, type PanelBurstGuardState } from './panel-paste-flood-guard.ts';
|
|
23
|
-
|
|
24
|
-
export type PanelFocusRouteState = {
|
|
25
|
-
panelManager: PanelManager;
|
|
26
|
-
keybindingsManager: KeybindingsManager;
|
|
27
|
-
panelFocused: boolean;
|
|
28
|
-
commandMode: boolean;
|
|
29
|
-
searchActive: boolean;
|
|
30
|
-
autocompleteActive: boolean;
|
|
31
|
-
requestRender: () => void;
|
|
32
|
-
handlePathCompletion: () => boolean;
|
|
33
|
-
cyclePanelTab: (direction: 'next' | 'prev') => void;
|
|
34
|
-
onPanelInputConsumed?: (activePanel: import('../panels/types.ts').Panel | null, key: string) => void;
|
|
35
|
-
/**
|
|
36
|
-
* True when THIS token is a paste: a single 'text' token whose value holds
|
|
37
|
-
* more than one character. The SDK tokenizer emits a bracketed paste
|
|
38
|
-
* (\x1b[?2004h, enabled in main.ts init) as exactly one such token; discrete
|
|
39
|
-
* keystrokes, even several batched into one feed() by render-tick latency,
|
|
40
|
-
* arrive as separate 1-char tokens. See the text-token branch below.
|
|
41
|
-
*/
|
|
42
|
-
isPasteToken: boolean;
|
|
43
|
-
/**
|
|
44
|
-
* One-shot hint emitted when a paste is dropped because the focused panel has
|
|
45
|
-
* no text capture (Invariant A: focus must not silently flip to the composer).
|
|
46
|
-
*/
|
|
47
|
-
onPasteDropped?: (panelName: string) => void;
|
|
48
|
-
/** Wall-clock time (ms) for this token; `burstGuard` is item 5's flood-guard state, mutated in place across tokens (see panel-paste-flood-guard.ts). */
|
|
49
|
-
now: number;
|
|
50
|
-
burstGuard: PanelBurstGuardState;
|
|
51
|
-
/**
|
|
52
|
-
* True while a turn is actively streaming; gates Escape's cancel-first
|
|
53
|
-
* precedence (I6.1) ahead of the panel's own two-stage escape contract
|
|
54
|
-
* below.
|
|
55
|
-
*/
|
|
56
|
-
isTurnActive: () => boolean;
|
|
57
|
-
/** Cancel the active turn. Wired to commandContext.cancelGeneration. */
|
|
58
|
-
cancelGeneration: () => void;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export function handlePanelFocusToken(state: PanelFocusRouteState, token: InputToken): {
|
|
62
|
-
handled: boolean;
|
|
63
|
-
panelFocused: boolean;
|
|
64
|
-
} {
|
|
65
|
-
let panelFocused = state.panelFocused;
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
token.type === 'key' &&
|
|
69
|
-
token.logicalName === 'tab' &&
|
|
70
|
-
!state.commandMode &&
|
|
71
|
-
!state.searchActive &&
|
|
72
|
-
!state.autocompleteActive
|
|
73
|
-
) {
|
|
74
|
-
const pm = state.panelManager;
|
|
75
|
-
if (pm.isVisible() && pm.getAllOpen().length > 0) {
|
|
76
|
-
if (panelFocused) {
|
|
77
|
-
panelFocused = false;
|
|
78
|
-
} else if (!state.handlePathCompletion()) {
|
|
79
|
-
panelFocused = true;
|
|
80
|
-
}
|
|
81
|
-
state.requestRender();
|
|
82
|
-
return { handled: true, panelFocused };
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (!panelFocused) {
|
|
87
|
-
return { handled: false, panelFocused };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (token.type === 'key') {
|
|
91
|
-
// I6: two-stage Escape, give the panel a chance to consume escape first
|
|
92
|
-
// (e.g. dismiss a confirm dialog or clear search). Only unfocus if the
|
|
93
|
-
// panel returns false (unconsumed) or there is no active panel.
|
|
94
|
-
if (token.logicalName === 'escape') {
|
|
95
|
-
// While streaming, a focused panel must not swallow the
|
|
96
|
-
// only way to cancel, Escape's first job is always cancel-turn; the
|
|
97
|
-
// panel's own two-stage consume-or-unfocus contract only runs once no
|
|
98
|
-
// turn is active, so a *second* Escape falls through to it normally
|
|
99
|
-
// (panelFocused stays unchanged here). Calls cancelGeneration()
|
|
100
|
-
// directly rather than the shared handleEscape() (handler-modal-stack.ts),
|
|
101
|
-
// whose fallthrough order doesn't guarantee reaching it first.
|
|
102
|
-
if (state.isTurnActive()) {
|
|
103
|
-
state.cancelGeneration();
|
|
104
|
-
state.requestRender();
|
|
105
|
-
return { handled: true, panelFocused };
|
|
106
|
-
}
|
|
107
|
-
const activePanel = state.panelManager.getActive();
|
|
108
|
-
const panelConsumedEscape = activePanel?.handleInput?.('escape') ?? false;
|
|
109
|
-
if (panelConsumedEscape) {
|
|
110
|
-
state.onPanelInputConsumed?.(activePanel!, 'escape');
|
|
111
|
-
state.requestRender();
|
|
112
|
-
return { handled: true, panelFocused };
|
|
113
|
-
}
|
|
114
|
-
panelFocused = false;
|
|
115
|
-
state.requestRender();
|
|
116
|
-
return { handled: true, panelFocused };
|
|
117
|
-
}
|
|
118
|
-
const kb = state.keybindingsManager;
|
|
119
|
-
// NOTE: panel-tab-next/prev, panel-close, and panel-close-all are handled
|
|
120
|
-
// globally in handleGlobalShortcutToken (runs earlier in the feed loop),
|
|
121
|
-
// their old, drifted copies here were unreachable and are gone. Only
|
|
122
|
-
// panel-focus-toggle stays, since it's meaningful only once the panel
|
|
123
|
-
// workspace already owns focus (it swaps between the two panes).
|
|
124
|
-
if (kb.matches('panel-focus-toggle', token)) {
|
|
125
|
-
// Switch keyboard focus between the top and bottom panes (no-op when
|
|
126
|
-
// there is no visible, non-empty bottom pane).
|
|
127
|
-
state.panelManager.togglePaneFocus();
|
|
128
|
-
state.requestRender();
|
|
129
|
-
return { handled: true, panelFocused };
|
|
130
|
-
}
|
|
131
|
-
// Alt+1..9 (panel-tab-1..9) is a global KeyAction consumed earlier by
|
|
132
|
-
// handleGlobalShortcutToken, so those meta+digit tokens never reach here.
|
|
133
|
-
// Any other ctrl/meta combo this route does not own falls through so the
|
|
134
|
-
// global handler (which ran first) or the prompt routes can act on it.
|
|
135
|
-
if (token.ctrl || token.meta) {
|
|
136
|
-
return { handled: false, panelFocused };
|
|
137
|
-
}
|
|
138
|
-
const activePanel = state.panelManager.getActive();
|
|
139
|
-
if (activePanel?.handleInput) {
|
|
140
|
-
const consumed = activePanel.handleInput(token.logicalName);
|
|
141
|
-
if (consumed) {
|
|
142
|
-
state.onPanelInputConsumed?.(activePanel, token.logicalName);
|
|
143
|
-
state.requestRender();
|
|
144
|
-
return { handled: true, panelFocused };
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (token.type === 'text' && token.value) {
|
|
150
|
-
const activePanel = state.panelManager.getActive();
|
|
151
|
-
// '/' is an explicit transfer verb back to the composer (a capturing panel, isCapturingTextBurst, keeps '/' for itself).
|
|
152
|
-
if (token.value === '/' && !activePanel?.isCapturingTextBurst?.()) {
|
|
153
|
-
panelFocused = false;
|
|
154
|
-
return { handled: false, panelFocused };
|
|
155
|
-
}
|
|
156
|
-
// Invariant A/B: a paste (isPasteToken, one multi-char token) into
|
|
157
|
-
// a capturing panel forwards verbatim below; elsewhere it is DROPPED with
|
|
158
|
-
// a one-shot hint (never exploded into hotkeys, never a silent focus
|
|
159
|
-
// flip, focus moves only on an explicit verb). Discrete keystrokes fall
|
|
160
|
-
// through to the per-char dispatch below.
|
|
161
|
-
if (state.isPasteToken && !activePanel?.isCapturingTextBurst?.()) {
|
|
162
|
-
state.onPasteDropped?.(activePanel?.name ?? 'the panel');
|
|
163
|
-
state.requestRender();
|
|
164
|
-
return { handled: true, panelFocused };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// item 5, paste flood guard (rate-based; see panel-paste-flood-guard.ts).
|
|
168
|
-
if (!state.isPasteToken && !activePanel?.isCapturingTextBurst?.()) {
|
|
169
|
-
const guard = trackPanelPasteFloodGuard(state.burstGuard, state.now);
|
|
170
|
-
if (!guard.dispatch) {
|
|
171
|
-
if (guard.showHintNow) state.onPasteDropped?.(activePanel?.name ?? 'the panel');
|
|
172
|
-
state.requestRender();
|
|
173
|
-
return { handled: true, panelFocused };
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (activePanel?.handleInput) {
|
|
178
|
-
for (const ch of token.value) {
|
|
179
|
-
activePanel.handleInput(ch);
|
|
180
|
-
}
|
|
181
|
-
state.requestRender();
|
|
182
|
-
}
|
|
183
|
-
return { handled: true, panelFocused };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return { handled: true, panelFocused };
|
|
187
|
-
}
|
|
188
18
|
|
|
189
19
|
export type IndicatorFocusRouteState = {
|
|
190
20
|
indicatorFocused: boolean;
|
|
191
21
|
modalOpened: (name: string) => void;
|
|
192
|
-
/** The footer process indicator's [Enter] opens the
|
|
193
|
-
|
|
22
|
+
/** The footer process indicator's [Enter] opens the Agents modal (F2 also opens it). */
|
|
23
|
+
openAgentsView: () => void;
|
|
194
24
|
requestRender: () => void;
|
|
195
25
|
};
|
|
196
26
|
|
|
@@ -211,8 +41,7 @@ export function handleIndicatorFocusToken(state: IndicatorFocusRouteState, token
|
|
|
211
41
|
}
|
|
212
42
|
if (token.logicalName === 'enter') {
|
|
213
43
|
indicatorFocused = false;
|
|
214
|
-
|
|
215
|
-
state.openFleetPanel();
|
|
44
|
+
state.openAgentsView();
|
|
216
45
|
state.requestRender();
|
|
217
46
|
return { handled: true, indicatorFocused };
|
|
218
47
|
}
|
|
@@ -349,8 +178,8 @@ export type KeyRouteState = {
|
|
|
349
178
|
* Anchors Enter-on-empty-composer's block-actions menu to that block
|
|
350
179
|
* instead of an arbitrary fixed line. */
|
|
351
180
|
getBlockAnchorLine: () => number;
|
|
352
|
-
/** F2 opens
|
|
353
|
-
|
|
181
|
+
/** F2 opens the Agents modal. */
|
|
182
|
+
openAgentsView: () => void;
|
|
354
183
|
modalOpened: (name: string) => void;
|
|
355
184
|
saveUndoState: () => void;
|
|
356
185
|
/** Break the undo coalescing group (call on cursor moves). */
|
|
@@ -641,12 +470,11 @@ export function handlePromptKeyToken(state: KeyRouteState, token: InputToken): {
|
|
|
641
470
|
return { handled: true, prompt, cursorPos, inputScrollTop, commandMode, indicatorFocused };
|
|
642
471
|
}
|
|
643
472
|
|
|
644
|
-
// F2 is
|
|
645
|
-
//
|
|
646
|
-
// pipeline but stays for panel-entry-points-reachable.test.ts's direct test.
|
|
473
|
+
// F2 is handled globally (handler-shortcuts.ts) before it reaches this
|
|
474
|
+
// route; this branch keeps the route correct when it is driven directly.
|
|
647
475
|
if (token.logicalName === 'f2') {
|
|
648
476
|
indicatorFocused = false;
|
|
649
|
-
state.
|
|
477
|
+
state.openAgentsView();
|
|
650
478
|
return { handled: true, prompt, cursorPos, inputScrollTop, commandMode, indicatorFocused };
|
|
651
479
|
}
|
|
652
480
|
|
|
@@ -656,8 +484,6 @@ export function handlePromptKeyToken(state: KeyRouteState, token: InputToken): {
|
|
|
656
484
|
export type MouseRouteState = {
|
|
657
485
|
conversationManager: ConversationManager | null;
|
|
658
486
|
selection: SelectionManager;
|
|
659
|
-
panelManager: PanelManager;
|
|
660
|
-
panelMouseLayout: PanelMouseLayout | null;
|
|
661
487
|
mouseDownRow: number;
|
|
662
488
|
mouseDownCol: number;
|
|
663
489
|
scrollTop: number;
|
|
@@ -667,20 +493,13 @@ export type MouseRouteState = {
|
|
|
667
493
|
requestRender: () => void;
|
|
668
494
|
handlePaste: () => void;
|
|
669
495
|
handleCopy: () => void;
|
|
496
|
+
/** The view a footer screen row opens on a click (the usage rows open Usage). */
|
|
497
|
+
footerTargetAt?: (row: number) => FooterTarget | undefined;
|
|
498
|
+
openFooterTarget?: (target: FooterTarget) => void;
|
|
499
|
+
/** Screen rows above the transcript (the header, the chips, the gap under them); 2 when not given. */
|
|
500
|
+
bodyTopRow?: () => number;
|
|
670
501
|
};
|
|
671
502
|
|
|
672
|
-
function scrollPanelUnderMouse(
|
|
673
|
-
state: MouseRouteState,
|
|
674
|
-
token: Extract<InputToken, { type: 'mouse' }>,
|
|
675
|
-
deltaRows: number,
|
|
676
|
-
): boolean {
|
|
677
|
-
const panel = getPanelUnderMouse(state.panelManager, state.panelMouseLayout, token.row, token.col);
|
|
678
|
-
if (!panel?.handleScroll) return false;
|
|
679
|
-
const consumed = panel.handleScroll(deltaRows);
|
|
680
|
-
if (consumed) state.requestRender();
|
|
681
|
-
return true;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
503
|
export function handleMouseToken(state: MouseRouteState, token: InputToken): {
|
|
685
504
|
handled: boolean;
|
|
686
505
|
mouseDownRow: number;
|
|
@@ -692,20 +511,14 @@ export function handleMouseToken(state: MouseRouteState, token: InputToken): {
|
|
|
692
511
|
return { handled: false, mouseDownRow, mouseDownCol };
|
|
693
512
|
}
|
|
694
513
|
|
|
695
|
-
|
|
696
|
-
const viewportRow = token.row -
|
|
514
|
+
// Mouse rows are 1-based; the transcript's first row is the one after the header block.
|
|
515
|
+
const viewportRow = token.row - 1 - (state.bodyTopRow?.() ?? 2);
|
|
697
516
|
|
|
698
517
|
if (token.button === 64) {
|
|
699
|
-
if (scrollPanelUnderMouse(state, token, -3)) {
|
|
700
|
-
return { handled: true, mouseDownRow, mouseDownCol };
|
|
701
|
-
}
|
|
702
518
|
state.scroll(-3);
|
|
703
519
|
return { handled: true, mouseDownRow, mouseDownCol };
|
|
704
520
|
}
|
|
705
521
|
if (token.button === 65) {
|
|
706
|
-
if (scrollPanelUnderMouse(state, token, 3)) {
|
|
707
|
-
return { handled: true, mouseDownRow, mouseDownCol };
|
|
708
|
-
}
|
|
709
522
|
state.scroll(3);
|
|
710
523
|
return { handled: true, mouseDownRow, mouseDownCol };
|
|
711
524
|
}
|
|
@@ -713,15 +526,17 @@ export function handleMouseToken(state: MouseRouteState, token: InputToken): {
|
|
|
713
526
|
state.handlePaste();
|
|
714
527
|
return { handled: true, mouseDownRow, mouseDownCol };
|
|
715
528
|
}
|
|
529
|
+
// A click on a clickable footer row (press and release on the same row)
|
|
530
|
+
// opens its view; it never starts a text selection.
|
|
531
|
+
const footerTarget = state.footerTargetAt?.(token.row);
|
|
532
|
+
if (footerTarget && token.button === 0 && token.action === 'press') {
|
|
533
|
+
return { handled: true, mouseDownRow: token.row, mouseDownCol: token.col };
|
|
534
|
+
}
|
|
535
|
+
if (footerTarget && token.action === 'release' && mouseDownRow === token.row) {
|
|
536
|
+
state.openFooterTarget?.(footerTarget);
|
|
537
|
+
return { handled: true, mouseDownRow: -1, mouseDownCol: -1 };
|
|
538
|
+
}
|
|
716
539
|
if (token.button === 0 && token.action === 'press') {
|
|
717
|
-
// Click on a workspace tab activates it (checked before starting a text
|
|
718
|
-
// selection so a tab click doesn't begin a drag-select).
|
|
719
|
-
const tabIndex = workspaceTabAtMouse(state.panelManager, state.panelMouseLayout, token.row, token.col);
|
|
720
|
-
if (tabIndex !== null) {
|
|
721
|
-
state.panelManager.activateWorkspaceIndex(tabIndex);
|
|
722
|
-
state.requestRender();
|
|
723
|
-
return { handled: true, mouseDownRow, mouseDownCol };
|
|
724
|
-
}
|
|
725
540
|
mouseDownRow = token.row;
|
|
726
541
|
mouseDownCol = token.col;
|
|
727
542
|
state.selection.startSelection(token.col, viewportRow, state.scrollTop, state.viewportHeight, state.lineCount);
|