@pellux/goodvibes-tui 2.0.21 → 2.1.1
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 +65 -1
- package/README.md +30 -29
- package/docs/README.md +8 -5
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +28 -19
- 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-reference.ts +2 -2
- 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/live-verifier.ts +0 -10
- package/src/version.ts +1 -1
- 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/verification/verification-ledger.ts +0 -707
- /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
- /package/src/{panels → views}/modals/modal-theme.ts +0 -0
- /package/src/{panels → views}/provider-health-domains.ts +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* composer.ts, the prompt box, built exactly like a user message. It holds
|
|
3
|
+
* input and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* col 2 ┃ in the mode color, on every row of the block; ╻ and ╹ on
|
|
6
|
+
* the cap rows, so the bar spans exactly the visible fill
|
|
7
|
+
* cols 3..w-3 the element fill
|
|
8
|
+
* rows ▄ cap, padding, text (one or more), padding, ▀ cap
|
|
9
|
+
* text from column 5, wrapped to width-9 (see prompt-content-width.ts)
|
|
10
|
+
*
|
|
11
|
+
* The cap rows are half rows: a ▄ row is empty on top and fill below, a ▀ row
|
|
12
|
+
* fill on top and empty below (the modal caps' technique, surface-kit.ts).
|
|
13
|
+
* They keep half a row of empty space between the input area and whatever
|
|
14
|
+
* sits above it (the throbber, or the transcript at rest) and below it (the
|
|
15
|
+
* status line), so text never sits directly on the input area.
|
|
16
|
+
*
|
|
17
|
+
* Multi-line input grows the text area; nothing else moves. The approval mode,
|
|
18
|
+
* the auto-approve warning and the safety chips are on the status line; the
|
|
19
|
+
* model and the failover marker are in the header.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { type Line, createEmptyLine } from '@pellux/goodvibes-sdk/platform/types';
|
|
23
|
+
import { getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
|
|
24
|
+
import { GLYPHS } from './ui-primitives.ts';
|
|
25
|
+
import { activeTokens } from './theme.ts';
|
|
26
|
+
|
|
27
|
+
/** Column of the mode bar. */
|
|
28
|
+
const COMPOSER_BAR_X = 2;
|
|
29
|
+
/** First fill column. */
|
|
30
|
+
const COMPOSER_FILL_X = 3;
|
|
31
|
+
/** First text column. */
|
|
32
|
+
const COMPOSER_TEXT_X = 5;
|
|
33
|
+
/** Rows the composer adds around its text rows (a cap row and a padding row above and below). */
|
|
34
|
+
export const COMPOSER_FIXED_ROWS = 4;
|
|
35
|
+
|
|
36
|
+
const COMPOSER_PLACEHOLDER = 'Ask anything, or type / for commands and @ for files';
|
|
37
|
+
|
|
38
|
+
export interface ComposerOptions {
|
|
39
|
+
readonly width: number;
|
|
40
|
+
/** Visible prompt rows joined with '\n'. */
|
|
41
|
+
readonly promptText: string;
|
|
42
|
+
/** Cursor offset into promptText (rows joined with one separator), when on screen. */
|
|
43
|
+
readonly cursorPos?: number;
|
|
44
|
+
readonly focused: boolean;
|
|
45
|
+
/** Faint hint after the cursor on the last row (a command's arguments). */
|
|
46
|
+
readonly argsHint?: string;
|
|
47
|
+
/** The bar's color: the session mode (brand, plan, auto-approving modes, shell), or the agent or process shown. */
|
|
48
|
+
readonly modeColor: string;
|
|
49
|
+
/** The empty composer's placeholder (an agent view: "Message engineer. This steers it; main keeps running."). */
|
|
50
|
+
readonly placeholder?: string;
|
|
51
|
+
/**
|
|
52
|
+
* The composer takes no input here; the reason is drawn in its place (a
|
|
53
|
+
* process whose stdin is not reachable). No cursor is drawn.
|
|
54
|
+
*/
|
|
55
|
+
readonly disabledReason?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface Style { readonly fg: string; readonly bg: string; readonly bold?: boolean }
|
|
59
|
+
|
|
60
|
+
function put(line: Line, x: number, endX: number, text: string, style: Style): number {
|
|
61
|
+
let cx = x;
|
|
62
|
+
for (const ch of text) {
|
|
63
|
+
const w = getDisplayWidth(ch);
|
|
64
|
+
if (w <= 0) continue;
|
|
65
|
+
if (cx + w > endX) break;
|
|
66
|
+
line[cx] = { char: ch, fg: style.fg, bg: style.bg, bold: style.bold ?? false, dim: false, underline: false, italic: false, strikethrough: false };
|
|
67
|
+
if (w === 2 && cx + 1 < line.length) line[cx + 1] = { ...line[cx]!, char: '' };
|
|
68
|
+
cx += w;
|
|
69
|
+
}
|
|
70
|
+
return cx;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function blockRow(width: number, bar: string, bg: string): Line {
|
|
74
|
+
const line = createEmptyLine(width);
|
|
75
|
+
for (const cell of line) cell.bg = '';
|
|
76
|
+
if (width > COMPOSER_BAR_X) line[COMPOSER_BAR_X] = { char: '┃', fg: bar, bg: '', bold: false, dim: false, underline: false, italic: false, strikethrough: false };
|
|
77
|
+
for (let x = COMPOSER_FILL_X; x <= width - 3; x++) line[x] = { char: ' ', fg: '', bg, bold: false, dim: false, underline: false, italic: false, strikethrough: false };
|
|
78
|
+
return line;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* A half row: ▄ (the fill's lower half, above the block) or ▀ (its upper
|
|
83
|
+
* half, below the block) across the fill columns, in the fill color on the
|
|
84
|
+
* terminal background, with the bar's matching half (╻ or ╹) in column 2.
|
|
85
|
+
*/
|
|
86
|
+
function capRow(width: number, bar: string, bg: string, edge: 'top' | 'bottom'): Line {
|
|
87
|
+
const line = createEmptyLine(width);
|
|
88
|
+
for (const cell of line) cell.bg = '';
|
|
89
|
+
const base = { bg: '', bold: false, dim: false, underline: false, italic: false, strikethrough: false };
|
|
90
|
+
if (width > COMPOSER_BAR_X) line[COMPOSER_BAR_X] = { ...base, char: edge === 'top' ? '╻' : '╹', fg: bar };
|
|
91
|
+
for (let x = COMPOSER_FILL_X; x <= width - 3; x++) line[x] = { ...base, char: edge === 'top' ? '▄' : '▀', fg: bg };
|
|
92
|
+
return line;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Render the composer block (at least 5 rows: cap, padding, text, padding, cap). */
|
|
96
|
+
export function renderComposer(options: ComposerOptions): Line[] {
|
|
97
|
+
const t = activeTokens();
|
|
98
|
+
const { width } = options;
|
|
99
|
+
const bg = t.backgroundElement;
|
|
100
|
+
const bar = options.modeColor;
|
|
101
|
+
const textEnd = width - 4; // exclusive: text keeps 2 columns from the fill's right edge
|
|
102
|
+
if (options.disabledReason !== undefined) {
|
|
103
|
+
const line = blockRow(width, bar, bg);
|
|
104
|
+
put(line, COMPOSER_TEXT_X, textEnd, truncateDisplay(options.disabledReason, Math.max(0, textEnd - COMPOSER_TEXT_X)), { fg: t.textFaint, bg });
|
|
105
|
+
return [capRow(width, bar, bg, 'top'), blockRow(width, bar, bg), line, blockRow(width, bar, bg), capRow(width, bar, bg, 'bottom')];
|
|
106
|
+
}
|
|
107
|
+
const rows = options.promptText.split('\n');
|
|
108
|
+
const lines: Line[] = [capRow(width, bar, bg, 'top'), blockRow(width, bar, bg)];
|
|
109
|
+
const textFg = options.focused ? t.text : t.textFaint;
|
|
110
|
+
const placeholder = options.placeholder ?? COMPOSER_PLACEHOLDER;
|
|
111
|
+
|
|
112
|
+
let offset = 0;
|
|
113
|
+
rows.forEach((text, i) => {
|
|
114
|
+
const line = blockRow(width, bar, bg);
|
|
115
|
+
const last = i === rows.length - 1;
|
|
116
|
+
const empty = rows.length === 1 && text === '';
|
|
117
|
+
if (empty) {
|
|
118
|
+
// The input area holds only input: its placeholder, focused or not (the status line says where the keys are).
|
|
119
|
+
put(line, COMPOSER_TEXT_X, textEnd, truncateDisplay(placeholder, Math.max(0, textEnd - COMPOSER_TEXT_X)), { fg: t.textFaint, bg });
|
|
120
|
+
} else {
|
|
121
|
+
put(line, COMPOSER_TEXT_X, textEnd + 1, text, { fg: textFg, bg });
|
|
122
|
+
}
|
|
123
|
+
if (options.focused) {
|
|
124
|
+
const posInRow = options.cursorPos !== undefined ? options.cursorPos - offset : (last ? text.length : -1);
|
|
125
|
+
if (posInRow >= 0 && posInRow <= text.length && (options.cursorPos !== undefined || last)) {
|
|
126
|
+
const cx = COMPOSER_TEXT_X + getDisplayWidth(text.slice(0, posInRow));
|
|
127
|
+
if (cx <= width - 4) {
|
|
128
|
+
const cell = line[cx]!;
|
|
129
|
+
const blank = empty || cell.char === ' ' || cell.char === '';
|
|
130
|
+
line[cx] = blank && !empty
|
|
131
|
+
? { ...cell, char: GLYPHS.surface.cursor, fg: t.text, bg }
|
|
132
|
+
: { ...cell, char: cell.char === '' ? ' ' : cell.char, fg: bg, bg: t.text };
|
|
133
|
+
// A command's argument hint trails the cursor while it sits at the end.
|
|
134
|
+
if (last && options.argsHint && posInRow >= text.length && !empty) {
|
|
135
|
+
put(line, cx + 2, textEnd, truncateDisplay(options.argsHint, Math.max(0, textEnd - cx - 2)), { fg: t.textFaint, bg });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
offset += text.length + 1;
|
|
141
|
+
lines.push(line);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
lines.push(blockRow(width, bar, bg), capRow(width, bar, bg, 'bottom'));
|
|
145
|
+
return lines;
|
|
146
|
+
}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { TerminalBuffer } from './buffer.ts';
|
|
2
2
|
import { DiffEngine } from './diff.ts';
|
|
3
|
-
import { type Line,
|
|
4
|
-
import { getDisplayWidth } from '../utils/terminal-width.ts';
|
|
3
|
+
import { type Line, createEmptyLine } from '@pellux/goodvibes-sdk/platform/types';
|
|
5
4
|
import type { SearchManager } from '../input/search.ts';
|
|
6
5
|
import { allowTerminalWrite } from '@pellux/goodvibes-terminal-shell/terminal-output-guard';
|
|
7
6
|
import { probeTermCaps, type TermColorCaps } from './term-caps.ts';
|
|
8
|
-
import { activeTheme } from './theme.ts';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
// Accent / dim colors for the panel focus border. The focused pane's left
|
|
12
|
-
// border column is drawn in the accent tone; the unfocused pane stays dim.
|
|
13
|
-
const PANEL_FOCUS_ACCENT = UI_TONES.state.active; // bright blue
|
|
14
|
-
const PANEL_BORDER_DIM = '238';
|
|
7
|
+
import { activeTheme, activeTokens } from './theme.ts';
|
|
8
|
+
import type { SurfaceLayer } from './surface-kit.ts';
|
|
9
|
+
import { composeLayers } from './surface-compose.ts';
|
|
15
10
|
|
|
16
11
|
export interface SelectionInfo {
|
|
17
12
|
isCellSelected: (col: number, absoluteRow: number) => boolean;
|
|
@@ -25,29 +20,6 @@ export interface SearchInfo {
|
|
|
25
20
|
viewportStartY: number;
|
|
26
21
|
}
|
|
27
22
|
|
|
28
|
-
export interface PanelCompositeData {
|
|
29
|
-
/**
|
|
30
|
-
* The single consolidated workspace tab bar spanning all open panels across
|
|
31
|
-
* both panes. There are no per-pane tab bars, pane focus is shown by the
|
|
32
|
-
* accent border (see `topFocused`/`bottomFocused`).
|
|
33
|
-
*/
|
|
34
|
-
workspaceBar: Line;
|
|
35
|
-
/** Top pane: panel content lines */
|
|
36
|
-
topContent: Line[];
|
|
37
|
-
/** Whether the top pane is focused (drives the accent border) */
|
|
38
|
-
topFocused: boolean;
|
|
39
|
-
/** Whether a bottom pane is present (splits the panel area). */
|
|
40
|
-
hasBottomPane: boolean;
|
|
41
|
-
/** Bottom pane content lines. Undefined = no bottom pane. */
|
|
42
|
-
bottomContent?: Line[];
|
|
43
|
-
/** Whether the bottom pane is focused */
|
|
44
|
-
bottomFocused?: boolean;
|
|
45
|
-
/** Separator between left and right panel area */
|
|
46
|
-
separator: boolean;
|
|
47
|
-
/** Ratio of panel height for the top pane (0–1). Only used when bottom pane is present. */
|
|
48
|
-
verticalSplitRatio: number;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
23
|
export interface CompositeRequest {
|
|
52
24
|
width: number;
|
|
53
25
|
height: number;
|
|
@@ -56,8 +28,11 @@ export interface CompositeRequest {
|
|
|
56
28
|
footer: Line[];
|
|
57
29
|
selection?: SelectionInfo;
|
|
58
30
|
search?: SearchInfo;
|
|
59
|
-
|
|
60
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Surfaces stamped over the finished screen, in order: modals (which dim
|
|
33
|
+
* everything underneath first), toasts. Screen coordinates.
|
|
34
|
+
*/
|
|
35
|
+
layers?: readonly SurfaceLayer[];
|
|
61
36
|
}
|
|
62
37
|
|
|
63
38
|
/**
|
|
@@ -124,7 +99,7 @@ export class Compositor {
|
|
|
124
99
|
}
|
|
125
100
|
|
|
126
101
|
public composite(params: CompositeRequest): void {
|
|
127
|
-
const { width, height, header, viewport, footer, selection, search,
|
|
102
|
+
const { width, height, header, viewport, footer, selection, search, layers } = params;
|
|
128
103
|
// A size change reallocates the buffer, which drops every record of what
|
|
129
104
|
// the terminal is currently showing, repaint in full rather than diff
|
|
130
105
|
// against a model that no longer describes the screen.
|
|
@@ -144,10 +119,6 @@ export class Compositor {
|
|
|
144
119
|
}
|
|
145
120
|
const newBuffer = this.backBuffer;
|
|
146
121
|
|
|
147
|
-
const hasPanel = panel !== undefined && panelWidth !== undefined && panelWidth > 0;
|
|
148
|
-
const leftWidth = hasPanel ? Math.max(1, width - panelWidth - 1) : width;
|
|
149
|
-
const sepX = hasPanel ? leftWidth : -1;
|
|
150
|
-
|
|
151
122
|
// 1. Draw Header, always full width
|
|
152
123
|
header.forEach((line, i) => newBuffer.blitLine(i, line));
|
|
153
124
|
|
|
@@ -159,33 +130,6 @@ export class Compositor {
|
|
|
159
130
|
const lineCount = selection?.lineCount ?? 0;
|
|
160
131
|
const offset = Math.max(0, vHeight - lineCount);
|
|
161
132
|
|
|
162
|
-
// --- Pre-compute panel row layout when split pane is active ---
|
|
163
|
-
// A single consolidated workspace bar heads the panel area; there are no
|
|
164
|
-
// per-pane tab bars. When both panes are visible the layout is:
|
|
165
|
-
// row 0: workspace tab bar
|
|
166
|
-
// rows 1..topH: top content
|
|
167
|
-
// row topH+1: horizontal separator (───)
|
|
168
|
-
// rows topH+2..end: bottom content
|
|
169
|
-
const hasBottomPane = hasPanel && panel!.hasBottomPane;
|
|
170
|
-
let topPaneHeight = 0; // number of content rows in top pane
|
|
171
|
-
let hSepRow = -1; // viewport row of the horizontal separator
|
|
172
|
-
if (hasPanel && hasBottomPane) {
|
|
173
|
-
const panelAreaRows = Math.max(0, vHeight - 1); // subtract workspace bar
|
|
174
|
-
const contentRows = Math.max(0, panelAreaRows - 1); // subtract h-separator
|
|
175
|
-
topPaneHeight = Math.max(1, Math.floor(contentRows * panel!.verticalSplitRatio));
|
|
176
|
-
hSepRow = 1 + topPaneHeight; // workspace bar + top content rows
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const panelFocused = hasPanel && (panel!.topFocused || panel!.bottomFocused);
|
|
180
|
-
// Per-row left-border color: the focused pane's rows get the accent tone.
|
|
181
|
-
const borderFgForRow = (i: number): string => {
|
|
182
|
-
if (!hasPanel || !panel!.separator || !panelFocused) return PANEL_BORDER_DIM;
|
|
183
|
-
if (!hasBottomPane) return panel!.topFocused ? PANEL_FOCUS_ACCENT : PANEL_BORDER_DIM;
|
|
184
|
-
if (i === 0) return PANEL_FOCUS_ACCENT; // workspace bar, panel is focused
|
|
185
|
-
if (i <= topPaneHeight) return panel!.topFocused ? PANEL_FOCUS_ACCENT : PANEL_BORDER_DIM;
|
|
186
|
-
return panel!.bottomFocused ? PANEL_FOCUS_ACCENT : PANEL_BORDER_DIM;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
133
|
// Every body row is written every frame, including rows the caller did not
|
|
190
134
|
// supply a line for. A viewport array shorter than the body (a docked
|
|
191
135
|
// overlay reserves a bottom inset, an overlay-row reservation overshoots
|
|
@@ -201,113 +145,30 @@ export class Compositor {
|
|
|
201
145
|
const screenY = viewportStartY + i;
|
|
202
146
|
if (screenY >= height) break;
|
|
203
147
|
|
|
204
|
-
|
|
205
|
-
// No panel: existing fast path
|
|
206
|
-
newBuffer.blitLine(screenY, line);
|
|
207
|
-
} else {
|
|
208
|
-
// Panel active: write cells individually to support split layout
|
|
209
|
-
// Left side: viewport cells 0..leftWidth-1
|
|
210
|
-
for (let x = 0; x < leftWidth; x++) {
|
|
211
|
-
const cell = line[x];
|
|
212
|
-
if (cell !== undefined) {
|
|
213
|
-
// If this is a wide char (2-cell) at the last left-side column,
|
|
214
|
-
// it would bleed into the separator column visually.
|
|
215
|
-
// Replace with a space to keep the separator aligned.
|
|
216
|
-
if (x === leftWidth - 1 && cell.char && cell.char.length > 0 && getDisplayWidth(cell.char) > 1) {
|
|
217
|
-
newBuffer.setCell(x, screenY, { ...cell, char: ' ' });
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
newBuffer.setCell(x, screenY, cell);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const p = panel!;
|
|
225
|
-
|
|
226
|
-
// Separator column (vertical bar between left and panel area).
|
|
227
|
-
// Colored per-row so the focused pane shows a bright accent border.
|
|
228
|
-
if (p.separator) {
|
|
229
|
-
newBuffer.setCell(sepX, screenY, createStyledCell('│', { fg: borderFgForRow(i) }));
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const panelStartX = sepX + 1;
|
|
233
|
-
const clearPanelRemainder = (fromX = 0) => {
|
|
234
|
-
for (let x = Math.max(0, fromX); x < panelWidth; x++) {
|
|
235
|
-
newBuffer.setCell(panelStartX + x, screenY, createEmptyCell());
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
const drawPanelLine = (panelLine: Line | undefined) => {
|
|
239
|
-
if (panelLine === undefined) {
|
|
240
|
-
clearPanelRemainder();
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
const limit = Math.min(panelLine.length, panelWidth);
|
|
244
|
-
for (let x = 0; x < limit; x++) {
|
|
245
|
-
const cell = panelLine[x];
|
|
246
|
-
if (cell !== undefined) {
|
|
247
|
-
newBuffer.setCell(panelStartX + x, screenY, cell);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
clearPanelRemainder(limit);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
if (!hasBottomPane) {
|
|
254
|
-
// --- Single pane mode ---
|
|
255
|
-
// viewport row 0 → workspace bar, viewport rows 1+ → panel content
|
|
256
|
-
const panelLine = i === 0 ? p.workspaceBar : p.topContent[i - 1];
|
|
257
|
-
drawPanelLine(panelLine);
|
|
258
|
-
} else {
|
|
259
|
-
// --- Two pane mode (single consolidated workspace bar) ---
|
|
260
|
-
// Row layout (by viewport row i):
|
|
261
|
-
// i = 0: workspace tab bar
|
|
262
|
-
// 1 <= i <= topPaneHeight: top content[i-1]
|
|
263
|
-
// i = hSepRow: horizontal separator
|
|
264
|
-
// i >= hSepRow+1: bottom content[i - (hSepRow+1)]
|
|
265
|
-
let panelLine: Line | undefined;
|
|
266
|
-
|
|
267
|
-
if (i === 0) {
|
|
268
|
-
panelLine = p.workspaceBar;
|
|
269
|
-
} else if (i <= topPaneHeight) {
|
|
270
|
-
panelLine = p.topContent[i - 1];
|
|
271
|
-
} else if (i === hSepRow) {
|
|
272
|
-
// Horizontal separator between the two panes. Accent when the bottom
|
|
273
|
-
// pane has focus so the divider reinforces the focus border.
|
|
274
|
-
const focusFg = p.bottomFocused ? PANEL_FOCUS_ACCENT : PANEL_BORDER_DIM;
|
|
275
|
-
for (let x = 0; x < panelWidth; x++) {
|
|
276
|
-
newBuffer.setCell(panelStartX + x, screenY, createStyledCell('─', { fg: focusFg }));
|
|
277
|
-
}
|
|
278
|
-
// T-junction (├) joins the vertical left-border with the pane divider.
|
|
279
|
-
if (p.separator) {
|
|
280
|
-
newBuffer.setCell(sepX, screenY, createStyledCell('├', { fg: focusFg }));
|
|
281
|
-
}
|
|
282
|
-
} else {
|
|
283
|
-
panelLine = p.bottomContent?.[i - (hSepRow + 1)];
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (i !== hSepRow) {
|
|
287
|
-
drawPanelLine(panelLine);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
148
|
+
newBuffer.blitLine(screenY, line);
|
|
291
149
|
|
|
292
|
-
// Apply Selection Highlighting Overlay
|
|
150
|
+
// Apply Selection Highlighting Overlay
|
|
293
151
|
// Only highlight rows that actually contain history (past the bottom-anchor offset)
|
|
294
152
|
if (selection && i >= offset) {
|
|
295
153
|
const absoluteRow = selection.scrollTop + (i - offset);
|
|
296
|
-
for (let x = 0; x <
|
|
154
|
+
for (let x = 0; x < width; x++) {
|
|
297
155
|
if (selection.isCellSelected(x, absoluteRow)) {
|
|
298
|
-
|
|
156
|
+
// Mouse selection: the theme's selection fill with body text (the
|
|
157
|
+
// inverse selectedListItemText is unreadable on this fill).
|
|
158
|
+
const sel = activeTokens();
|
|
159
|
+
newBuffer.setCell(x, screenY, { bg: sel.backgroundSelected, fg: sel.text, bold: false, dim: false });
|
|
299
160
|
}
|
|
300
161
|
}
|
|
301
162
|
}
|
|
302
163
|
|
|
303
|
-
// Apply Search Match Highlighting Overlay
|
|
164
|
+
// Apply Search Match Highlighting Overlay
|
|
304
165
|
if (search && search.manager.active && search.manager.query.length > 0 && i >= offset) {
|
|
305
166
|
const T = activeTheme();
|
|
306
167
|
const absoluteRow = search.scrollTop + (i - offset);
|
|
307
168
|
const lineMatches = search.manager.getMatchesOnLine(absoluteRow);
|
|
308
169
|
for (const match of lineMatches) {
|
|
309
170
|
const isCurrent = search.manager.isCurrentMatch(absoluteRow, match.col);
|
|
310
|
-
for (let x = match.col; x < match.col + match.length && x <
|
|
171
|
+
for (let x = match.col; x < match.col + match.length && x < width; x++) {
|
|
311
172
|
if (isCurrent) {
|
|
312
173
|
newBuffer.setCell(x, screenY, { bg: T.searchCurrentBg, fg: T.searchCurrentFg, bold: true, dim: false });
|
|
313
174
|
} else {
|
|
@@ -318,7 +179,7 @@ export class Compositor {
|
|
|
318
179
|
}
|
|
319
180
|
}
|
|
320
181
|
// (rows past the supplied viewport lines are covered by the loop above,
|
|
321
|
-
//
|
|
182
|
+
// so they can no longer keep a stale frame.)
|
|
322
183
|
|
|
323
184
|
// 3. Draw Footer (Pinned to Bottom), always full width
|
|
324
185
|
const footerStart = height - footer.length;
|
|
@@ -328,7 +189,12 @@ export class Compositor {
|
|
|
328
189
|
newBuffer.blitLine(screenY, line);
|
|
329
190
|
});
|
|
330
191
|
|
|
331
|
-
// 4.
|
|
192
|
+
// 4. Modal passes: dim the composed screen, then stamp each surface over
|
|
193
|
+
// it (cells outside a surface keep their dimmed content). Runs after the
|
|
194
|
+
// selection and search passes so those dim along with everything else.
|
|
195
|
+
if (layers && layers.length > 0) composeLayers(newBuffer, layers);
|
|
196
|
+
|
|
197
|
+
// 5. Diff and Render
|
|
332
198
|
// Diff against front-buffer (last-rendered), then swap front/back, no clone() needed.
|
|
333
199
|
// On a full repaint the SGR run-state is reset (the erase below leaves the
|
|
334
200
|
// terminal's attributes unknown) and the diff runs against no previous
|