@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
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
import type { Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
2
|
-
import { createEmptyLine, createStyledCell } from '@pellux/goodvibes-sdk/platform/types';
|
|
3
|
-
import { BasePanel } from './base-panel.ts';
|
|
4
|
-
import type { PanelCategory } from './types.ts';
|
|
5
|
-
import type { ComponentHealthMonitor } from '@pellux/goodvibes-sdk/platform/runtime/observability';
|
|
6
|
-
import {
|
|
7
|
-
buildEmptyState,
|
|
8
|
-
buildKeyboardHints,
|
|
9
|
-
buildPanelWorkspace,
|
|
10
|
-
buildSearchInputLine,
|
|
11
|
-
DEFAULT_PANEL_PALETTE,
|
|
12
|
-
resolveScrollablePanelSection,
|
|
13
|
-
type PanelPalette,
|
|
14
|
-
} from './polish.ts';
|
|
15
|
-
import { GLYPHS } from '../renderer/ui-primitives.ts';
|
|
16
|
-
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
// ScrollableListPanel<T>
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Base class for all list-based panels that require scroll/cursor navigation.
|
|
23
|
-
*
|
|
24
|
-
* Subclasses implement:
|
|
25
|
-
* - `getItems()`, the ordered list of items to display
|
|
26
|
-
* - `renderItem(item, index, selected, width)`, one `Line` per item
|
|
27
|
-
*
|
|
28
|
-
* Optionally override:
|
|
29
|
-
* - `getEmptyStateMessage()` / `getEmptyStateActions()`, empty-state copy
|
|
30
|
-
* - `onSelect(item)`, called when the user presses Enter
|
|
31
|
-
* - `onAction(item, action)`, for secondary key bindings
|
|
32
|
-
* - `getPalette()`, colour palette (defaults to `DEFAULT_PANEL_PALETTE`)
|
|
33
|
-
* - `getPageSize()`, rows per page-up/page-down (default 10)
|
|
34
|
-
*
|
|
35
|
-
* `renderList()` produces the full `Line[]` output that a trivial panel's
|
|
36
|
-
* `render()` can return directly:
|
|
37
|
-
*
|
|
38
|
-
* ```ts
|
|
39
|
-
* render(width: number, height: number): Line[] {
|
|
40
|
-
* return this.renderList(width, height, { header: this.buildHeader(width) });
|
|
41
|
-
* }
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
export abstract class ScrollableListPanel<T> extends BasePanel {
|
|
45
|
-
protected selectedIndex = 0;
|
|
46
|
-
/** Tracks the first visible row index; kept in sync with resolveScrollablePanelSection. */
|
|
47
|
-
protected scrollStart = 0;
|
|
48
|
-
/**
|
|
49
|
-
* When true, prepends a 2-column `▸ ` gutter on the selected row.
|
|
50
|
-
* Unselected rows get ` ` (two spaces) to maintain alignment.
|
|
51
|
-
* Opt-in; default false to avoid breaking existing panel layouts.
|
|
52
|
-
*/
|
|
53
|
-
protected showSelectionGutter = false;
|
|
54
|
-
|
|
55
|
-
// -------------------------------------------------------------------------
|
|
56
|
-
// Opt-in inline filter ( / to filter, Esc to clear )
|
|
57
|
-
// -------------------------------------------------------------------------
|
|
58
|
-
// Set `filterEnabled = true` and override `filterMatches()` to give a list
|
|
59
|
-
// panel a `/`-to-filter affordance that coexists with single-letter action
|
|
60
|
-
// keys (filtering is modal: action keys work until you press `/`). A filter
|
|
61
|
-
// input line is auto-rendered at the top of the list.
|
|
62
|
-
protected filterEnabled = false;
|
|
63
|
-
protected filterQuery = '';
|
|
64
|
-
protected filterActive = false;
|
|
65
|
-
/** Short noun shown in the filter hint, e.g. "Filter sessions". */
|
|
66
|
-
protected filterLabel = 'Filter';
|
|
67
|
-
|
|
68
|
-
/** Override to define what the lower-cased filter query matches for an item. */
|
|
69
|
-
protected filterMatches(_item: T, _query: string): boolean {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* The list after applying the active filter, used for both display and
|
|
75
|
-
* navigation. Identical to `getItems()` unless a filter is enabled and a
|
|
76
|
-
* non-empty query is set, so existing panels are unaffected.
|
|
77
|
-
*/
|
|
78
|
-
protected getVisibleItems(): readonly T[] {
|
|
79
|
-
const all = this.getItems();
|
|
80
|
-
const q = this.filterQuery.trim().toLowerCase();
|
|
81
|
-
if (!this.filterEnabled || !q) return all;
|
|
82
|
-
return all.filter((item) => this.filterMatches(item, q));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* The item currently under the cursor, resolved against the FILTERED list
|
|
87
|
-
* that navigation actually moves over (`getVisibleItems()`), never the raw
|
|
88
|
-
* `getItems()` source.
|
|
89
|
-
*
|
|
90
|
-
* Subclasses MUST read the selected row through this method (or, when a
|
|
91
|
-
* function also needs the list for counts/windows, through a function-scope
|
|
92
|
-
* `const visible = this.getVisibleItems()` local, indexing that local, never
|
|
93
|
-
* the raw source). Indexing a raw item array (`this.rows`, `this.entries`,
|
|
94
|
-
* `getItems()`, …) with `this.selectedIndex` silently returns the wrong row
|
|
95
|
-
* whenever a filter is active, because `selectedIndex` is an offset into the
|
|
96
|
-
* visible list, not the raw one. The `no-raw-selectedindex-read` architecture
|
|
97
|
-
* rule enforces this by banning the `[this.selectedIndex]` token outside the
|
|
98
|
-
* base classes.
|
|
99
|
-
*/
|
|
100
|
-
protected getSelectedItem(): T | undefined {
|
|
101
|
-
return this.getVisibleItems()[this.selectedIndex];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Filter-mode key handling. Returns `true`/`false` when consumed/ignored in
|
|
106
|
-
* filter context, or `null` to fall through to normal navigation.
|
|
107
|
-
*/
|
|
108
|
-
private _handleFilterKey(key: string): boolean | null {
|
|
109
|
-
if (!this.filterEnabled) return null;
|
|
110
|
-
if (this.filterActive) {
|
|
111
|
-
if (key === 'escape') {
|
|
112
|
-
this.filterActive = false;
|
|
113
|
-
this.filterQuery = '';
|
|
114
|
-
this.selectedIndex = 0;
|
|
115
|
-
this.needsRender = true;
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
if (key === 'return' || key === 'enter') {
|
|
119
|
-
this.filterActive = false; // commit; keep the query applied
|
|
120
|
-
this.needsRender = true;
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
if (key === 'backspace' || key === 'delete') {
|
|
124
|
-
this.filterQuery = this.filterQuery.slice(0, -1);
|
|
125
|
-
this.selectedIndex = 0;
|
|
126
|
-
this.needsRender = true;
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
// Arrow/paging keys navigate the filtered list, fall through.
|
|
130
|
-
if (key === 'up' || key === 'down' || key === 'pageup' || key === 'pagedown' || key === 'home' || key === 'end') {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
// Any printable character (including j/k/g/G) extends the query.
|
|
134
|
-
if (key.length === 1 && key >= ' ') {
|
|
135
|
-
this.filterQuery += key;
|
|
136
|
-
this.selectedIndex = 0;
|
|
137
|
-
this.needsRender = true;
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
if (key === '/') {
|
|
143
|
-
this.filterActive = true;
|
|
144
|
-
this.needsRender = true;
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/** The filter input line shown at the top of the list when filtering is enabled. */
|
|
151
|
-
protected buildFilterLine(width: number): Line {
|
|
152
|
-
const palette = this.getPalette();
|
|
153
|
-
const label = this.filterActive ? `[${this.filterLabel}] ` : `${this.filterLabel}: `;
|
|
154
|
-
const value = this.filterActive
|
|
155
|
-
? `${this.filterQuery}_`
|
|
156
|
-
: this.filterQuery || '(/ to filter)';
|
|
157
|
-
return buildSearchInputLine(width, label, value, palette, {
|
|
158
|
-
active: false,
|
|
159
|
-
bg: this.filterActive ? palette.inputBg : palette.sectionBg,
|
|
160
|
-
valueColor: this.filterActive ? palette.info : (this.filterQuery ? palette.value : palette.dim),
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
constructor(
|
|
165
|
-
id: string,
|
|
166
|
-
name: string,
|
|
167
|
-
icon: string,
|
|
168
|
-
category: PanelCategory,
|
|
169
|
-
componentHealthMonitor?: ComponentHealthMonitor,
|
|
170
|
-
) {
|
|
171
|
-
super(id, name, icon, category, componentHealthMonitor);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// -------------------------------------------------------------------------
|
|
175
|
-
// Abstract, subclasses must implement
|
|
176
|
-
// -------------------------------------------------------------------------
|
|
177
|
-
|
|
178
|
-
/** Return the full ordered list of items to display. */
|
|
179
|
-
protected abstract getItems(): readonly T[];
|
|
180
|
-
|
|
181
|
-
/** Render a single item as one terminal `Line`. */
|
|
182
|
-
protected abstract renderItem(
|
|
183
|
-
item: T,
|
|
184
|
-
index: number,
|
|
185
|
-
selected: boolean,
|
|
186
|
-
width: number,
|
|
187
|
-
): Line;
|
|
188
|
-
|
|
189
|
-
// -------------------------------------------------------------------------
|
|
190
|
-
// Optional overrides
|
|
191
|
-
// -------------------------------------------------------------------------
|
|
192
|
-
|
|
193
|
-
/** Short label shown in the empty-state title. */
|
|
194
|
-
protected getEmptyStateMessage(): string {
|
|
195
|
-
return 'No items';
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** Suggested actions shown in the empty state. */
|
|
199
|
-
protected getEmptyStateActions(): Array<{ command: string; summary: string }> {
|
|
200
|
-
return [];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/** Called when the user presses Enter on the selected item. */
|
|
204
|
-
protected onSelect(_item: T): void {}
|
|
205
|
-
|
|
206
|
-
/** Called for secondary key bindings (e.g. 'd' for delete). */
|
|
207
|
-
protected onAction(_item: T, _action: string): void {}
|
|
208
|
-
|
|
209
|
-
/** Colour palette used by `renderList()`. */
|
|
210
|
-
protected getPalette(): PanelPalette {
|
|
211
|
-
return DEFAULT_PANEL_PALETTE;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Rows to jump on pageup / pagedown.
|
|
216
|
-
* Override in `render()` to pass the actual visible row count:
|
|
217
|
-
*
|
|
218
|
-
* ```ts
|
|
219
|
-
* this._pageSize = Math.max(1, visibleRows - 2);
|
|
220
|
-
* ```
|
|
221
|
-
*/
|
|
222
|
-
protected getPageSize(): number {
|
|
223
|
-
return 10;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// -------------------------------------------------------------------------
|
|
227
|
-
// Navigation, consistent across ALL panels
|
|
228
|
-
// -------------------------------------------------------------------------
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Handle keyboard input for list navigation.
|
|
232
|
-
*
|
|
233
|
-
* **Auto-clearError contract**: At the top of this method, `lastError` is cleared if
|
|
234
|
-
* non-null. This means any transient error set via `setError()` is dismissed on the
|
|
235
|
-
* very next keystroke the user presses. Subclasses that override `handleInput()` should
|
|
236
|
-
* either:
|
|
237
|
-
* 1. Call `super.handleInput(key)` as a fallback (preferred), which will clear the
|
|
238
|
-
* error when navigation keys are pressed, or
|
|
239
|
-
* 2. Manually call `this.clearError()` at the top of their override to maintain
|
|
240
|
-
* the same contract for their handled keys.
|
|
241
|
-
*
|
|
242
|
-
* Returns `true` if the key was consumed, `false` to let the panel manager try another
|
|
243
|
-
* handler.
|
|
244
|
-
*/
|
|
245
|
-
/**
|
|
246
|
-
* The `/`-to-filter buffer wants every character of a burst (paste, or a
|
|
247
|
-
* fast-typed query landing in one input.feed() call), same as it always
|
|
248
|
-
* has, see the interface doc on `Panel.isCapturingTextBurst`.
|
|
249
|
-
*/
|
|
250
|
-
isCapturingTextBurst(): boolean {
|
|
251
|
-
return this.filterEnabled && this.filterActive;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
handleInput(key: string): boolean {
|
|
255
|
-
// I2: auto-clear transient errors on the next keystroke so stale errors don't linger.
|
|
256
|
-
// Subclasses that override handleInput should call super.handleInput(key) OR manually
|
|
257
|
-
// call this.clearError() at the start of their handler.
|
|
258
|
-
if (this.lastError !== null) this.clearError();
|
|
259
|
-
|
|
260
|
-
// Opt-in inline filter consumes input before navigation when active.
|
|
261
|
-
const filterResult = this._handleFilterKey(key);
|
|
262
|
-
if (filterResult !== null) return filterResult;
|
|
263
|
-
|
|
264
|
-
const items = this.getVisibleItems();
|
|
265
|
-
const total = items.length;
|
|
266
|
-
|
|
267
|
-
switch (key) {
|
|
268
|
-
case 'up':
|
|
269
|
-
case 'k':
|
|
270
|
-
if (total === 0) return false;
|
|
271
|
-
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
272
|
-
this.needsRender = true;
|
|
273
|
-
return true;
|
|
274
|
-
|
|
275
|
-
case 'down':
|
|
276
|
-
case 'j':
|
|
277
|
-
if (total === 0) return false;
|
|
278
|
-
this.selectedIndex = Math.min(total - 1, this.selectedIndex + 1);
|
|
279
|
-
this.needsRender = true;
|
|
280
|
-
return true;
|
|
281
|
-
|
|
282
|
-
case 'pageup':
|
|
283
|
-
if (total === 0) return false;
|
|
284
|
-
this.selectedIndex = Math.max(0, this.selectedIndex - this.getPageSize());
|
|
285
|
-
this.needsRender = true;
|
|
286
|
-
return true;
|
|
287
|
-
|
|
288
|
-
case 'pagedown':
|
|
289
|
-
if (total === 0) return false;
|
|
290
|
-
this.selectedIndex = Math.min(total - 1, this.selectedIndex + this.getPageSize());
|
|
291
|
-
this.needsRender = true;
|
|
292
|
-
return true;
|
|
293
|
-
|
|
294
|
-
case 'home':
|
|
295
|
-
case 'g':
|
|
296
|
-
if (total === 0) return false;
|
|
297
|
-
this.selectedIndex = 0;
|
|
298
|
-
this.needsRender = true;
|
|
299
|
-
return true;
|
|
300
|
-
|
|
301
|
-
case 'end':
|
|
302
|
-
case 'G':
|
|
303
|
-
if (total === 0) return false;
|
|
304
|
-
this.selectedIndex = total - 1;
|
|
305
|
-
this.needsRender = true;
|
|
306
|
-
return true;
|
|
307
|
-
|
|
308
|
-
case 'return':
|
|
309
|
-
case 'enter': {
|
|
310
|
-
if (total === 0) return false;
|
|
311
|
-
const item = items[this.selectedIndex];
|
|
312
|
-
if (item !== undefined) this.onSelect(item);
|
|
313
|
-
return true;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
default:
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
handleScroll(deltaRows: number): boolean {
|
|
322
|
-
if (this.lastError !== null) this.clearError();
|
|
323
|
-
const total = this.getVisibleItems().length;
|
|
324
|
-
const rows = Math.trunc(deltaRows);
|
|
325
|
-
if (total === 0 || rows === 0) return false;
|
|
326
|
-
const next = Math.max(0, Math.min(total - 1, this.selectedIndex + rows));
|
|
327
|
-
if (next === this.selectedIndex) return false;
|
|
328
|
-
this.selectedIndex = next;
|
|
329
|
-
this.needsRender = true;
|
|
330
|
-
return true;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// -------------------------------------------------------------------------
|
|
334
|
-
// Scroll state helpers
|
|
335
|
-
// -------------------------------------------------------------------------
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Clamp `selectedIndex` to the current item count.
|
|
339
|
-
* Must be called after any data refresh that may shrink the list.
|
|
340
|
-
*/
|
|
341
|
-
protected clampSelection(): void {
|
|
342
|
-
const total = this.getVisibleItems().length;
|
|
343
|
-
if (total === 0) {
|
|
344
|
-
this.selectedIndex = 0;
|
|
345
|
-
} else {
|
|
346
|
-
this.selectedIndex = Math.min(this.selectedIndex, total - 1);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// -------------------------------------------------------------------------
|
|
351
|
-
// Render helper, the main convenience entry point
|
|
352
|
-
// -------------------------------------------------------------------------
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Render the full panel including optional header/footer and an empty state.
|
|
356
|
-
*
|
|
357
|
-
* Uses `resolveScrollablePanelSection` + `buildPanelWorkspace` internally,
|
|
358
|
-
* keeping `scrollStart` in sync after each call.
|
|
359
|
-
*
|
|
360
|
-
* @param width Panel width in columns.
|
|
361
|
-
* @param height Panel height in rows.
|
|
362
|
-
* @param options.header Lines prepended as the first workspace section.
|
|
363
|
-
* @param options.footer Lines appended as the last workspace section.
|
|
364
|
-
* @param options.emptyMessage Override for the empty-state title text.
|
|
365
|
-
* @param options.title Workspace title (defaults to `this.name`).
|
|
366
|
-
* @param options.spinnerFrame Animation frame for the loading spinner.
|
|
367
|
-
*/
|
|
368
|
-
protected renderList(
|
|
369
|
-
width: number,
|
|
370
|
-
height: number,
|
|
371
|
-
options: {
|
|
372
|
-
readonly header?: readonly Line[];
|
|
373
|
-
readonly footer?: readonly Line[];
|
|
374
|
-
readonly hints?: ReadonlyArray<{ keys: string; label: string }>;
|
|
375
|
-
readonly emptyMessage?: string;
|
|
376
|
-
readonly title?: string;
|
|
377
|
-
readonly spinnerFrame?: number;
|
|
378
|
-
} = {},
|
|
379
|
-
): Line[] {
|
|
380
|
-
this.needsRender = false;
|
|
381
|
-
const palette = this.getPalette();
|
|
382
|
-
const items = this.getVisibleItems();
|
|
383
|
-
const title = options.title ?? this.name;
|
|
384
|
-
|
|
385
|
-
// Auto-inject the filter input line at the top of the header when filtering
|
|
386
|
-
// is enabled, so every filterable panel gets the same affordance for free.
|
|
387
|
-
const baseHeader = options.header ? [...options.header as Line[]] : [];
|
|
388
|
-
const header: Line[] | undefined = this.filterEnabled
|
|
389
|
-
? [this.buildFilterLine(width), ...baseHeader]
|
|
390
|
-
: (options.header ? baseHeader : undefined);
|
|
391
|
-
|
|
392
|
-
// Standardized keyboard-hints footer row (rendered below any explicit footer).
|
|
393
|
-
const hintsLine = options.hints && options.hints.length > 0
|
|
394
|
-
? buildKeyboardHints(width, options.hints, palette)
|
|
395
|
-
: null;
|
|
396
|
-
|
|
397
|
-
// I2: inject error line into footer when present
|
|
398
|
-
const errorLine = this.renderErrorLine(width);
|
|
399
|
-
const baseFooter = options.footer ? [...options.footer as Line[]] : [];
|
|
400
|
-
if (hintsLine) baseFooter.push(hintsLine);
|
|
401
|
-
const effectiveFooter: Line[] = errorLine ? [errorLine, ...baseFooter] : baseFooter;
|
|
402
|
-
|
|
403
|
-
// I3: if loading, show spinner in place of normal content
|
|
404
|
-
const spinnerLine = this.renderLoadingLine(width, options.spinnerFrame ?? 0);
|
|
405
|
-
if (spinnerLine) {
|
|
406
|
-
const loadingSection = { lines: [spinnerLine] };
|
|
407
|
-
const headerSection = header ? [{ lines: header }] : [];
|
|
408
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
409
|
-
title,
|
|
410
|
-
sections: [...headerSection, loadingSection],
|
|
411
|
-
palette,
|
|
412
|
-
});
|
|
413
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
414
|
-
return lines.slice(0, height);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// Build all item lines (pre-render for resolveScrollablePanelSection)
|
|
418
|
-
const scrollableLines: Line[] = items.map((item, index) =>
|
|
419
|
-
this.renderItem(item, index, index === this.selectedIndex, width),
|
|
420
|
-
);
|
|
421
|
-
|
|
422
|
-
// I5: prepend selection gutter when opted in
|
|
423
|
-
if (this.showSelectionGutter) {
|
|
424
|
-
const infoColor = this.getPalette().info ?? DEFAULT_PANEL_PALETTE.info;
|
|
425
|
-
const dimColor = this.getPalette().dim;
|
|
426
|
-
for (let i = 0; i < scrollableLines.length; i++) {
|
|
427
|
-
const line = scrollableLines[i]!;
|
|
428
|
-
const isSelected = i === this.selectedIndex;
|
|
429
|
-
// Shift all cells right by 2, drop the last 2 to preserve width
|
|
430
|
-
const shifted = line.slice(0, width - 2);
|
|
431
|
-
const gutterChar = isSelected ? GLYPHS.navigation.selected : ' ';
|
|
432
|
-
const gutterFg = isSelected ? infoColor : dimColor;
|
|
433
|
-
const g0 = createStyledCell(gutterChar, { fg: gutterFg, bold: isSelected });
|
|
434
|
-
const g1 = createStyledCell(' ', { fg: gutterFg });
|
|
435
|
-
scrollableLines[i] = [g0, g1, ...shifted] as Line;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Empty state
|
|
440
|
-
if (scrollableLines.length === 0) {
|
|
441
|
-
// Distinguish "filter hides everything" from "genuinely empty".
|
|
442
|
-
const filteredToEmpty = this.filterEnabled && this.filterQuery.trim() !== '' && this.getItems().length > 0;
|
|
443
|
-
const emptyMessage = filteredToEmpty
|
|
444
|
-
? ` No matches for "${this.filterQuery.trim()}"`
|
|
445
|
-
: (options.emptyMessage ?? this.getEmptyStateMessage());
|
|
446
|
-
const emptyLines = buildEmptyState(
|
|
447
|
-
width,
|
|
448
|
-
emptyMessage,
|
|
449
|
-
filteredToEmpty ? 'Press Esc to clear the filter.' : '',
|
|
450
|
-
filteredToEmpty ? [] : this.getEmptyStateActions(),
|
|
451
|
-
palette,
|
|
452
|
-
);
|
|
453
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
454
|
-
title,
|
|
455
|
-
sections: [
|
|
456
|
-
...(header ? [{ lines: header }] : []),
|
|
457
|
-
{ lines: emptyLines },
|
|
458
|
-
...(effectiveFooter.length > 0 ? [{ lines: effectiveFooter }] : []),
|
|
459
|
-
],
|
|
460
|
-
palette,
|
|
461
|
-
});
|
|
462
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
463
|
-
return lines.slice(0, height);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Resolve scrollable section (updates scrollStart)
|
|
467
|
-
const beforeSections = header ? [{ lines: header }] : [];
|
|
468
|
-
const afterSections = effectiveFooter.length > 0 ? [{ lines: effectiveFooter }] : [];
|
|
469
|
-
|
|
470
|
-
const resolved = resolveScrollablePanelSection(width, height, {
|
|
471
|
-
palette,
|
|
472
|
-
beforeSections,
|
|
473
|
-
afterSections,
|
|
474
|
-
section: {
|
|
475
|
-
scrollableLines,
|
|
476
|
-
selectedIndex: this.selectedIndex,
|
|
477
|
-
scrollOffset: this.scrollStart,
|
|
478
|
-
guardRows: 1,
|
|
479
|
-
appendWindowSummary: scrollableLines.length > 5 ? { dimColor: palette.dim } : undefined,
|
|
480
|
-
},
|
|
481
|
-
});
|
|
482
|
-
this.scrollStart = resolved.scrollOffset;
|
|
483
|
-
|
|
484
|
-
const sections = [
|
|
485
|
-
...beforeSections,
|
|
486
|
-
resolved.section,
|
|
487
|
-
...afterSections,
|
|
488
|
-
];
|
|
489
|
-
|
|
490
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
491
|
-
title,
|
|
492
|
-
sections,
|
|
493
|
-
palette,
|
|
494
|
-
});
|
|
495
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
496
|
-
return lines.slice(0, height);
|
|
497
|
-
}
|
|
498
|
-
}
|