@pellux/goodvibes-agent 0.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/.goodvibes/GOODVIBES.md +35 -0
- package/.goodvibes/agents/reviewer.md +48 -0
- package/.goodvibes/skills/add-provider/SKILL.md +199 -0
- package/CHANGELOG.md +25 -0
- package/README.md +74 -0
- package/bin/goodvibes-agent.ts +2 -0
- package/docs/README.md +23 -0
- package/docs/deployment-and-services.md +57 -0
- package/docs/getting-started.md +53 -0
- package/docs/release-and-publishing.md +46 -0
- package/package.json +134 -0
- package/scripts/check-bun.sh +20 -0
- package/src/audio/player.ts +156 -0
- package/src/audio/spoken-turn-controller.ts +203 -0
- package/src/audio/spoken-turn-model-routing.ts +117 -0
- package/src/audio/spoken-turn-wiring.ts +44 -0
- package/src/audio/text-chunker.ts +110 -0
- package/src/cli/bundle-command.ts +227 -0
- package/src/cli/completion.ts +90 -0
- package/src/cli/config-overrides.ts +159 -0
- package/src/cli/endpoints.ts +63 -0
- package/src/cli/entrypoint.ts +172 -0
- package/src/cli/help.ts +299 -0
- package/src/cli/index.ts +11 -0
- package/src/cli/management-commands.ts +426 -0
- package/src/cli/management.ts +744 -0
- package/src/cli/network-posture.ts +46 -0
- package/src/cli/package-verification.ts +123 -0
- package/src/cli/parser.ts +369 -0
- package/src/cli/provider-auth-routes.ts +22 -0
- package/src/cli/provider-classification.ts +107 -0
- package/src/cli/redaction.ts +105 -0
- package/src/cli/service-command.ts +26 -0
- package/src/cli/service-posture.ts +482 -0
- package/src/cli/status.ts +383 -0
- package/src/cli/surface-command.ts +247 -0
- package/src/cli/tui-startup.ts +32 -0
- package/src/cli/types.ts +69 -0
- package/src/cli-flags.ts +21 -0
- package/src/config/goodvibes-home-audit.ts +465 -0
- package/src/config/index.ts +57 -0
- package/src/config/provider-model.ts +23 -0
- package/src/config/secret-config.ts +119 -0
- package/src/config/secrets.ts +71 -0
- package/src/config/surface.ts +1 -0
- package/src/core/composer-state.ts +61 -0
- package/src/core/conversation-rendering.ts +359 -0
- package/src/core/conversation.ts +551 -0
- package/src/core/history.ts +45 -0
- package/src/core/orchestrator.ts +7 -0
- package/src/core/system-message-router.ts +171 -0
- package/src/daemon/cli.ts +55 -0
- package/src/daemon/safe-serve.ts +61 -0
- package/src/input/agent-workspace.ts +428 -0
- package/src/input/autocomplete.ts +96 -0
- package/src/input/bookmark-modal.ts +115 -0
- package/src/input/command-args-hint.ts +36 -0
- package/src/input/command-registry.ts +329 -0
- package/src/input/commands/agent-externalized-tui.ts +73 -0
- package/src/input/commands/agent-workspace-runtime.ts +17 -0
- package/src/input/commands/branch-runtime.ts +72 -0
- package/src/input/commands/cloudflare-runtime.ts +370 -0
- package/src/input/commands/config.ts +18 -0
- package/src/input/commands/control-room-runtime.ts +255 -0
- package/src/input/commands/conversation-runtime.ts +207 -0
- package/src/input/commands/discovery-runtime.ts +52 -0
- package/src/input/commands/eval.ts +204 -0
- package/src/input/commands/experience-runtime.ts +278 -0
- package/src/input/commands/guidance-runtime.ts +106 -0
- package/src/input/commands/health-runtime.ts +434 -0
- package/src/input/commands/hooks-runtime.ts +148 -0
- package/src/input/commands/incident-runtime.ts +95 -0
- package/src/input/commands/integration-runtime.ts +394 -0
- package/src/input/commands/intelligence-runtime.ts +223 -0
- package/src/input/commands/knowledge.ts +531 -0
- package/src/input/commands/local-auth-runtime.ts +105 -0
- package/src/input/commands/local-provider-runtime.ts +170 -0
- package/src/input/commands/local-runtime.ts +392 -0
- package/src/input/commands/local-setup-review.ts +199 -0
- package/src/input/commands/local-setup-transfer.ts +135 -0
- package/src/input/commands/local-setup.ts +282 -0
- package/src/input/commands/managed-runtime.ts +209 -0
- package/src/input/commands/marketplace-runtime.ts +290 -0
- package/src/input/commands/mcp-runtime.ts +432 -0
- package/src/input/commands/memory-product-runtime.ts +111 -0
- package/src/input/commands/memory.ts +151 -0
- package/src/input/commands/notify-runtime.ts +83 -0
- package/src/input/commands/onboarding-runtime.ts +14 -0
- package/src/input/commands/operator-panel-runtime.ts +146 -0
- package/src/input/commands/operator-runtime.ts +392 -0
- package/src/input/commands/planning-runtime.ts +205 -0
- package/src/input/commands/platform-access-runtime.ts +422 -0
- package/src/input/commands/platform-services-runtime.ts +246 -0
- package/src/input/commands/policy-dispatch.ts +339 -0
- package/src/input/commands/policy.ts +17 -0
- package/src/input/commands/product-runtime.ts +351 -0
- package/src/input/commands/profile-sync-runtime.ts +99 -0
- package/src/input/commands/provider-accounts-runtime.ts +113 -0
- package/src/input/commands/provider.ts +363 -0
- package/src/input/commands/qrcode-runtime.ts +20 -0
- package/src/input/commands/quit-shared.ts +162 -0
- package/src/input/commands/recall-bundle.ts +132 -0
- package/src/input/commands/recall-capture.ts +152 -0
- package/src/input/commands/recall-query.ts +229 -0
- package/src/input/commands/recall-review.ts +98 -0
- package/src/input/commands/recall-shared.ts +22 -0
- package/src/input/commands/remote-runtime-pool.ts +106 -0
- package/src/input/commands/remote-runtime-setup.ts +199 -0
- package/src/input/commands/remote-runtime.ts +431 -0
- package/src/input/commands/replay-runtime.ts +18 -0
- package/src/input/commands/runtime-services.ts +291 -0
- package/src/input/commands/schedule-runtime.ts +91 -0
- package/src/input/commands/services-runtime.ts +209 -0
- package/src/input/commands/session-content.ts +408 -0
- package/src/input/commands/session-workflow.ts +464 -0
- package/src/input/commands/session.ts +375 -0
- package/src/input/commands/settings-sync-runtime.ts +174 -0
- package/src/input/commands/share-runtime.ts +119 -0
- package/src/input/commands/shell-core.ts +307 -0
- package/src/input/commands/skills-runtime.ts +221 -0
- package/src/input/commands/subscription-runtime.ts +434 -0
- package/src/input/commands/tasks-runtime.ts +230 -0
- package/src/input/commands/teamwork-runtime.ts +339 -0
- package/src/input/commands/teleport-runtime.ts +57 -0
- package/src/input/commands/tts-runtime.ts +29 -0
- package/src/input/commands/work-plan-runtime.ts +169 -0
- package/src/input/commands.ts +131 -0
- package/src/input/feed-context-factory.ts +254 -0
- package/src/input/file-picker.ts +192 -0
- package/src/input/handler-command-route.ts +180 -0
- package/src/input/handler-content-actions.ts +497 -0
- package/src/input/handler-feed-routes.ts +648 -0
- package/src/input/handler-feed.ts +452 -0
- package/src/input/handler-interactions.ts +281 -0
- package/src/input/handler-modal-routes.ts +418 -0
- package/src/input/handler-modal-stack.ts +263 -0
- package/src/input/handler-modal-token-routes.ts +329 -0
- package/src/input/handler-onboarding-cloudflare.ts +391 -0
- package/src/input/handler-onboarding.ts +620 -0
- package/src/input/handler-picker-routes.ts +472 -0
- package/src/input/handler-prompt-buffer.ts +320 -0
- package/src/input/handler-shortcuts.ts +213 -0
- package/src/input/handler-ui-state.ts +372 -0
- package/src/input/handler.ts +729 -0
- package/src/input/input-history.ts +297 -0
- package/src/input/keybindings.ts +292 -0
- package/src/input/mcp-workspace.ts +554 -0
- package/src/input/model-picker-provider-filter.ts +28 -0
- package/src/input/model-picker-types.ts +137 -0
- package/src/input/model-picker.ts +797 -0
- package/src/input/onboarding/handler-onboarding-routes.ts +125 -0
- package/src/input/onboarding/onboarding-runtime-status.ts +87 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +277 -0
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +494 -0
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +204 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +158 -0
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +130 -0
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +762 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +167 -0
- package/src/input/onboarding/onboarding-wizard-rules.ts +256 -0
- package/src/input/onboarding/onboarding-wizard-state.ts +365 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +798 -0
- package/src/input/onboarding/onboarding-wizard-types.ts +195 -0
- package/src/input/onboarding/onboarding-wizard.ts +711 -0
- package/src/input/panel-integration-actions.ts +78 -0
- package/src/input/profile-picker-modal.ts +222 -0
- package/src/input/search.ts +100 -0
- package/src/input/selection-modal.ts +163 -0
- package/src/input/selection.ts +135 -0
- package/src/input/session-picker-modal.ts +136 -0
- package/src/input/settings-modal-behavior.ts +37 -0
- package/src/input/settings-modal-secrets.ts +41 -0
- package/src/input/settings-modal-subscriptions.ts +95 -0
- package/src/input/settings-modal-types.ts +91 -0
- package/src/input/settings-modal.ts +793 -0
- package/src/input/submission-intent.ts +17 -0
- package/src/input/submission-router.ts +59 -0
- package/src/input/tts-settings-actions.ts +100 -0
- package/src/main.ts +792 -0
- package/src/mcp/runtime-reload.ts +81 -0
- package/src/panels/agent-inspector-panel.ts +521 -0
- package/src/panels/agent-inspector-shared.ts +94 -0
- package/src/panels/agent-logs-panel.ts +559 -0
- package/src/panels/agent-logs-shared.ts +129 -0
- package/src/panels/approval-panel.ts +150 -0
- package/src/panels/automation-control-panel.ts +212 -0
- package/src/panels/base-panel.ts +254 -0
- package/src/panels/builtin/agent.ts +117 -0
- package/src/panels/builtin/development.ts +31 -0
- package/src/panels/builtin/knowledge.ts +26 -0
- package/src/panels/builtin/operations.ts +349 -0
- package/src/panels/builtin/session.ts +129 -0
- package/src/panels/builtin/shared.ts +274 -0
- package/src/panels/builtin-panels.ts +23 -0
- package/src/panels/cockpit-panel.ts +183 -0
- package/src/panels/communication-panel.ts +153 -0
- package/src/panels/confirm-state.ts +61 -0
- package/src/panels/context-visualizer-panel.ts +204 -0
- package/src/panels/control-plane-panel.ts +211 -0
- package/src/panels/cost-tracker-panel.ts +444 -0
- package/src/panels/debug-panel.ts +432 -0
- package/src/panels/diff-panel.ts +520 -0
- package/src/panels/docs-panel.ts +283 -0
- package/src/panels/eval-panel.ts +399 -0
- package/src/panels/file-explorer-panel.ts +584 -0
- package/src/panels/file-preview-panel.ts +434 -0
- package/src/panels/forensics-panel.ts +364 -0
- package/src/panels/git-panel.ts +638 -0
- package/src/panels/hooks-panel.ts +239 -0
- package/src/panels/incident-review-panel.ts +197 -0
- package/src/panels/index.ts +46 -0
- package/src/panels/intelligence-panel.ts +176 -0
- package/src/panels/knowledge-panel.ts +345 -0
- package/src/panels/local-auth-panel.ts +130 -0
- package/src/panels/marketplace-panel.ts +212 -0
- package/src/panels/memory-panel.ts +225 -0
- package/src/panels/ops-control-panel.ts +150 -0
- package/src/panels/ops-strategy-panel.ts +235 -0
- package/src/panels/orchestration-panel.ts +273 -0
- package/src/panels/panel-list-panel.ts +509 -0
- package/src/panels/panel-manager.ts +570 -0
- package/src/panels/panel-picker.ts +106 -0
- package/src/panels/plan-dashboard-panel.ts +274 -0
- package/src/panels/plugins-panel.ts +178 -0
- package/src/panels/policy-panel.ts +308 -0
- package/src/panels/polish.ts +717 -0
- package/src/panels/project-planning-panel.ts +711 -0
- package/src/panels/provider-account-snapshot.ts +259 -0
- package/src/panels/provider-accounts-panel.ts +218 -0
- package/src/panels/provider-health-domains.ts +215 -0
- package/src/panels/provider-health-panel.ts +727 -0
- package/src/panels/provider-health-tracker.ts +115 -0
- package/src/panels/provider-stats-panel.ts +366 -0
- package/src/panels/qr-panel.ts +182 -0
- package/src/panels/remote-panel.ts +449 -0
- package/src/panels/routes-panel.ts +178 -0
- package/src/panels/sandbox-panel.ts +283 -0
- package/src/panels/schedule-panel.ts +329 -0
- package/src/panels/scrollable-list-panel.ts +491 -0
- package/src/panels/search-focus.ts +32 -0
- package/src/panels/security-panel.ts +295 -0
- package/src/panels/services-panel.ts +231 -0
- package/src/panels/session-browser-panel.ts +400 -0
- package/src/panels/session-maintenance.ts +125 -0
- package/src/panels/settings-sync-panel.ts +120 -0
- package/src/panels/skills-panel.ts +431 -0
- package/src/panels/subscription-panel.ts +263 -0
- package/src/panels/symbol-outline-panel.ts +486 -0
- package/src/panels/system-messages-panel.ts +230 -0
- package/src/panels/tasks-panel.ts +399 -0
- package/src/panels/thinking-panel.ts +304 -0
- package/src/panels/token-budget-panel.ts +475 -0
- package/src/panels/tool-inspector-panel.ts +429 -0
- package/src/panels/types.ts +54 -0
- package/src/panels/watchers-panel.ts +193 -0
- package/src/panels/work-plan-panel.ts +175 -0
- package/src/panels/worktree-panel.ts +182 -0
- package/src/panels/wrfc-panel.ts +609 -0
- package/src/permissions/prompt.ts +165 -0
- package/src/planning/project-planning-coordinator.ts +543 -0
- package/src/plugins/loader.ts +15 -0
- package/src/renderer/agent-detail-modal.ts +331 -0
- package/src/renderer/agent-workspace.ts +238 -0
- package/src/renderer/ansi-sanitize.ts +76 -0
- package/src/renderer/autocomplete-overlay.ts +154 -0
- package/src/renderer/block-actions.ts +76 -0
- package/src/renderer/bookmark-modal.ts +101 -0
- package/src/renderer/bottom-bar.ts +58 -0
- package/src/renderer/buffer.ts +113 -0
- package/src/renderer/code-block.ts +373 -0
- package/src/renderer/compositor.ts +283 -0
- package/src/renderer/context-inspector.ts +219 -0
- package/src/renderer/conversation-layout.ts +67 -0
- package/src/renderer/conversation-overlays.ts +140 -0
- package/src/renderer/conversation-surface.ts +260 -0
- package/src/renderer/diff-view.ts +132 -0
- package/src/renderer/diff.ts +130 -0
- package/src/renderer/file-picker-overlay.ts +101 -0
- package/src/renderer/file-tree.ts +153 -0
- package/src/renderer/fullscreen-primitives.ts +130 -0
- package/src/renderer/fullscreen-workspace.ts +199 -0
- package/src/renderer/git-status.ts +89 -0
- package/src/renderer/help-overlay.ts +267 -0
- package/src/renderer/history-search-overlay.ts +73 -0
- package/src/renderer/layout-engine.ts +97 -0
- package/src/renderer/layout.ts +32 -0
- package/src/renderer/live-tail-modal.ts +156 -0
- package/src/renderer/markdown.ts +635 -0
- package/src/renderer/mcp-workspace.ts +237 -0
- package/src/renderer/modal-factory.ts +467 -0
- package/src/renderer/modal-utils.ts +24 -0
- package/src/renderer/model-picker-overlay.ts +473 -0
- package/src/renderer/model-workspace.ts +488 -0
- package/src/renderer/onboarding/onboarding-wizard.ts +615 -0
- package/src/renderer/overlay-box.ts +146 -0
- package/src/renderer/overlay-viewport.ts +104 -0
- package/src/renderer/panel-composite.ts +158 -0
- package/src/renderer/panel-picker-overlay.ts +202 -0
- package/src/renderer/panel-tab-bar.ts +69 -0
- package/src/renderer/panel-workspace-bar.ts +42 -0
- package/src/renderer/process-indicator.ts +96 -0
- package/src/renderer/process-modal.ts +656 -0
- package/src/renderer/process-summary.ts +67 -0
- package/src/renderer/profile-picker-modal.ts +129 -0
- package/src/renderer/progress.ts +98 -0
- package/src/renderer/qr-renderer.ts +120 -0
- package/src/renderer/search-overlay.ts +54 -0
- package/src/renderer/selection-modal-overlay.ts +214 -0
- package/src/renderer/semantic-diff.ts +369 -0
- package/src/renderer/session-picker-modal.ts +127 -0
- package/src/renderer/settings-modal-helpers.ts +193 -0
- package/src/renderer/settings-modal.ts +537 -0
- package/src/renderer/shell-surface.ts +88 -0
- package/src/renderer/status-glyphs.ts +21 -0
- package/src/renderer/status-token.ts +67 -0
- package/src/renderer/surface-layout.ts +101 -0
- package/src/renderer/syntax-highlighter.ts +542 -0
- package/src/renderer/system-message.ts +83 -0
- package/src/renderer/tab-strip.ts +108 -0
- package/src/renderer/text-layout.ts +31 -0
- package/src/renderer/thinking.ts +17 -0
- package/src/renderer/tool-call.ts +234 -0
- package/src/renderer/ui-factory.ts +524 -0
- package/src/renderer/ui-primitives.ts +96 -0
- package/src/runtime/bootstrap-command-context.ts +278 -0
- package/src/runtime/bootstrap-command-parts.ts +386 -0
- package/src/runtime/bootstrap-core.ts +540 -0
- package/src/runtime/bootstrap-hook-bridge.ts +112 -0
- package/src/runtime/bootstrap-shell.ts +283 -0
- package/src/runtime/bootstrap.ts +575 -0
- package/src/runtime/cloudflare-control-plane.ts +349 -0
- package/src/runtime/context.ts +142 -0
- package/src/runtime/diagnostics/panels/index.ts +24 -0
- package/src/runtime/diagnostics/panels/ops.ts +156 -0
- package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
- package/src/runtime/diagnostics/panels/policy.ts +177 -0
- package/src/runtime/index.ts +662 -0
- package/src/runtime/onboarding/apply.ts +642 -0
- package/src/runtime/onboarding/derivation.ts +534 -0
- package/src/runtime/onboarding/index.ts +7 -0
- package/src/runtime/onboarding/markers.ts +148 -0
- package/src/runtime/onboarding/snapshot.ts +406 -0
- package/src/runtime/onboarding/state.ts +141 -0
- package/src/runtime/onboarding/types.ts +404 -0
- package/src/runtime/onboarding/verify.ts +171 -0
- package/src/runtime/operator-token-cleanup.ts +27 -0
- package/src/runtime/perf/panel-contracts.ts +32 -0
- package/src/runtime/perf/panel-health-monitor.ts +18 -0
- package/src/runtime/sandbox-public-gaps.ts +358 -0
- package/src/runtime/services.ts +670 -0
- package/src/runtime/store/domains/domain-read-matrix.ts +15 -0
- package/src/runtime/store/domains/index.ts +222 -0
- package/src/runtime/store/domains/panels.ts +117 -0
- package/src/runtime/store/domains/ui-perf.ts +103 -0
- package/src/runtime/store/index.ts +305 -0
- package/src/runtime/store/selectors/index.ts +359 -0
- package/src/runtime/store/state.ts +145 -0
- package/src/runtime/surface-feature-flags.ts +65 -0
- package/src/runtime/terminal-output-guard.ts +228 -0
- package/src/runtime/ui/index.ts +39 -0
- package/src/runtime/ui/model-picker/data-provider.ts +182 -0
- package/src/runtime/ui/model-picker/health-enrichment.ts +228 -0
- package/src/runtime/ui/model-picker/index.ts +59 -0
- package/src/runtime/ui/model-picker/types.ts +149 -0
- package/src/runtime/ui/provider-health/data-provider.ts +244 -0
- package/src/runtime/ui/provider-health/fallback-visualizer.ts +71 -0
- package/src/runtime/ui/provider-health/index.ts +46 -0
- package/src/runtime/ui/provider-health/types.ts +146 -0
- package/src/runtime/ui-events.ts +1 -0
- package/src/runtime/ui-read-model-helpers.ts +1 -0
- package/src/runtime/ui-read-models-observability-maintenance.ts +1 -0
- package/src/runtime/ui-read-models-observability-options.ts +1 -0
- package/src/runtime/ui-read-models-observability-remote.ts +1 -0
- package/src/runtime/ui-read-models-observability-security.ts +1 -0
- package/src/runtime/ui-read-models-observability-system.ts +1 -0
- package/src/runtime/ui-read-models-observability.ts +1 -0
- package/src/runtime/ui-read-models.ts +61 -0
- package/src/runtime/ui-service-queries.ts +1 -0
- package/src/runtime/ui-services.ts +190 -0
- package/src/scripts/process-messages.ts +42 -0
- package/src/shell/blocking-input.ts +98 -0
- package/src/shell/service-settings-sync.ts +273 -0
- package/src/shell/ui-openers.ts +352 -0
- package/src/tools/index.ts +1 -0
- package/src/tools/wrfc-agent-guard.ts +49 -0
- package/src/types/grid.ts +48 -0
- package/src/types/sql-js.d.ts +15 -0
- package/src/utils/clipboard.ts +22 -0
- package/src/utils/splash-lines.ts +46 -0
- package/src/utils/terminal-width.ts +185 -0
- package/src/verification/live-verifier.ts +430 -0
- package/src/verification/verification-ledger.ts +242 -0
- package/src/version.ts +17 -0
- package/src/widget/index.ts +2 -0
- package/src/widget/types.ts +9 -0
- package/src/widget/widget.ts +8 -0
- package/src/work-plans/work-plan-store.ts +374 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { BasePanel } from './base-panel.ts';
|
|
2
|
+
import { createEmptyLine } from '../types/grid.ts';
|
|
3
|
+
import type { Line } from '../types/grid.ts';
|
|
4
|
+
import {
|
|
5
|
+
buildEmptyState,
|
|
6
|
+
buildPanelLine,
|
|
7
|
+
buildSelectablePanelLine,
|
|
8
|
+
buildPanelWorkspace,
|
|
9
|
+
resolveScrollablePanelSection,
|
|
10
|
+
DEFAULT_PANEL_PALETTE,
|
|
11
|
+
} from './polish.ts';
|
|
12
|
+
import { getDisplayWidth } from '../utils/terminal-width.ts';
|
|
13
|
+
|
|
14
|
+
// ── Symbol types ────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export type SymbolKind = 'function' | 'class' | 'interface' | 'type' | 'const' | 'method' | 'namespace';
|
|
17
|
+
|
|
18
|
+
export interface SymbolEntry {
|
|
19
|
+
kind: SymbolKind;
|
|
20
|
+
name: string;
|
|
21
|
+
line: number;
|
|
22
|
+
/** If set, this symbol is a child of a parent container (class/namespace). */
|
|
23
|
+
parentName?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── Rendering constants ──────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/** ANSI 256-color fg codes per symbol kind. */
|
|
29
|
+
const KIND_COLORS: Record<SymbolKind, string> = {
|
|
30
|
+
function: '87', // cyan
|
|
31
|
+
method: '87', // cyan
|
|
32
|
+
class: '141', // purple
|
|
33
|
+
namespace: '141', // purple
|
|
34
|
+
interface: '219', // pink
|
|
35
|
+
type: '228', // yellow
|
|
36
|
+
const: '245', // grey
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Short type indicator labels. */
|
|
40
|
+
const KIND_LABELS: Record<SymbolKind, string> = {
|
|
41
|
+
function: 'fn ',
|
|
42
|
+
method: 'fn ',
|
|
43
|
+
class: 'cls',
|
|
44
|
+
namespace: 'ns ',
|
|
45
|
+
interface: 'int',
|
|
46
|
+
type: 'typ',
|
|
47
|
+
const: 'cst',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** Regex patterns to extract symbols. Each produces named groups: kind, name, line. */
|
|
51
|
+
const SYMBOL_PATTERNS: Array<{ re: RegExp; kind: SymbolKind; isContainer?: boolean }> = [
|
|
52
|
+
// export class Foo / abstract class Foo
|
|
53
|
+
{ re: /^(?:export\s+)?(?:abstract\s+)?class\s+(\w+)/, kind: 'class', isContainer: true },
|
|
54
|
+
// export namespace Foo
|
|
55
|
+
{ re: /^(?:export\s+)?namespace\s+(\w+)/, kind: 'namespace', isContainer: true },
|
|
56
|
+
// export interface Foo
|
|
57
|
+
{ re: /^(?:export\s+)?interface\s+(\w+)/, kind: 'interface' },
|
|
58
|
+
// export type Foo =
|
|
59
|
+
{ re: /^(?:export\s+)?type\s+(\w+)\s*[=<{]/, kind: 'type' },
|
|
60
|
+
// export function foo / export async function foo
|
|
61
|
+
{ re: /^(?:export\s+)?(?:async\s+)?function\s+(\w+)/, kind: 'function' },
|
|
62
|
+
// export const foo = ... (function expressions / arrow fns / values)
|
|
63
|
+
{ re: /^(?:export\s+)?const\s+(\w+)\s*(?::[^=]*)?=\s*(?:async\s+)?(?:function|\(|\w+\s*=>)/, kind: 'function' },
|
|
64
|
+
// export const foo = <non-function>
|
|
65
|
+
{ re: /^(?:export\s+)?const\s+(\w+)\s*(?::[^=]*)?=/, kind: 'const' },
|
|
66
|
+
// methods inside class: indented methodName(...)
|
|
67
|
+
{ re: /^\s{2,}(?:(?:public|private|protected|static|async|override|readonly|abstract)\s+)*(\w+)\s*\(/, kind: 'method' },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
// ── Panel ────────────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* SymbolOutlinePanel — renders a hierarchical symbol outline of the current
|
|
74
|
+
* file. Symbols are parsed from source text using lightweight regex heuristics
|
|
75
|
+
* (no tree-sitter or LSP required).
|
|
76
|
+
*/
|
|
77
|
+
export class SymbolOutlinePanel extends BasePanel {
|
|
78
|
+
/** Flat list of parsed symbols (methods nested after their parent class). */
|
|
79
|
+
private symbols: SymbolEntry[] = [];
|
|
80
|
+
|
|
81
|
+
/** Index of the currently highlighted row in the visible flat list. */
|
|
82
|
+
private selectedIndex: number = 0;
|
|
83
|
+
|
|
84
|
+
/** Set of container names (class/namespace) that are collapsed. */
|
|
85
|
+
private collapsed: Set<string> = new Set();
|
|
86
|
+
|
|
87
|
+
/** Scroll offset (top-visible row index in the flat rendered list). */
|
|
88
|
+
private scrollOffset: number = 0;
|
|
89
|
+
|
|
90
|
+
/** Path of the file currently loaded. */
|
|
91
|
+
private currentPath: string = '';
|
|
92
|
+
|
|
93
|
+
constructor() {
|
|
94
|
+
super('symbols', 'Symbols', 'S', 'development');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Load and parse symbols from the given file source text.
|
|
101
|
+
* Call this when the active file changes in the file-preview panel.
|
|
102
|
+
*/
|
|
103
|
+
loadFile(path: string, source: string): void {
|
|
104
|
+
this.currentPath = path;
|
|
105
|
+
this.symbols = parseSymbols(source);
|
|
106
|
+
this.selectedIndex = 0;
|
|
107
|
+
this.scrollOffset = 0;
|
|
108
|
+
this.collapsed.clear();
|
|
109
|
+
this.markDirty();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Returns the { path, line } for the currently selected symbol so the
|
|
114
|
+
* caller can jump to it in the file-preview panel.
|
|
115
|
+
*/
|
|
116
|
+
getSelectedLocation(): { path: string; line: number } | null {
|
|
117
|
+
const visible = this._visibleRows();
|
|
118
|
+
const row = visible[this.selectedIndex];
|
|
119
|
+
if (!row || row.kind === 'header') return null;
|
|
120
|
+
return { path: this.currentPath, line: row.symbol.line };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── Input ──────────────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
handleInput(key: string): boolean {
|
|
126
|
+
const visible = this._visibleRows();
|
|
127
|
+
|
|
128
|
+
if (key === 'up' || key === 'k') {
|
|
129
|
+
if (this.selectedIndex > 0) {
|
|
130
|
+
this.selectedIndex--;
|
|
131
|
+
this._clampScroll(visible.length);
|
|
132
|
+
this.markDirty();
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (key === 'down' || key === 'j') {
|
|
138
|
+
if (this.selectedIndex < visible.length - 1) {
|
|
139
|
+
this.selectedIndex++;
|
|
140
|
+
this._clampScroll(visible.length);
|
|
141
|
+
this.markDirty();
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (key === 'return' || key === 'enter') {
|
|
147
|
+
// Caller should call getSelectedLocation() after this returns true.
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (key === 'space' || key === 'right' || key === 'left') {
|
|
152
|
+
const row = visible[this.selectedIndex];
|
|
153
|
+
if (row?.kind === 'header') {
|
|
154
|
+
const name = row.name;
|
|
155
|
+
if (this.collapsed.has(name)) {
|
|
156
|
+
this.collapsed.delete(name);
|
|
157
|
+
} else {
|
|
158
|
+
this.collapsed.add(name);
|
|
159
|
+
}
|
|
160
|
+
// Clamp selection so it doesn't point into a now-hidden row
|
|
161
|
+
const newVisible = this._visibleRows();
|
|
162
|
+
if (this.selectedIndex >= newVisible.length) {
|
|
163
|
+
this.selectedIndex = Math.max(0, newVisible.length - 1);
|
|
164
|
+
}
|
|
165
|
+
this._clampScroll(newVisible.length);
|
|
166
|
+
this.markDirty();
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Rendering ──────────────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
render(width: number, height: number): Line[] {
|
|
177
|
+
this.needsRender = false;
|
|
178
|
+
|
|
179
|
+
if (this.symbols.length === 0) {
|
|
180
|
+
return buildPanelWorkspace(width, height, {
|
|
181
|
+
title: ' Symbols',
|
|
182
|
+
intro: 'Outline the current file into navigable symbols and lightweight parent/child structure.',
|
|
183
|
+
sections: [
|
|
184
|
+
{
|
|
185
|
+
lines: buildEmptyState(
|
|
186
|
+
width,
|
|
187
|
+
this.currentPath ? ' No symbols found' : ' No file loaded',
|
|
188
|
+
this.currentPath
|
|
189
|
+
? 'The current file did not produce outline entries with the lightweight parser heuristics.'
|
|
190
|
+
: 'Load a file in the preview panel to populate its outline here.',
|
|
191
|
+
[],
|
|
192
|
+
DEFAULT_PANEL_PALETTE,
|
|
193
|
+
),
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
palette: DEFAULT_PANEL_PALETTE,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const visible = this._visibleRows();
|
|
201
|
+
const outlineSection = resolveScrollablePanelSection(width, height, {
|
|
202
|
+
intro: this.currentPath ? this.currentPath : 'Outline the current file into navigable symbols and lightweight parent/child structure.',
|
|
203
|
+
footerLines: [
|
|
204
|
+
buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' navigate', DEFAULT_PANEL_PALETTE.dim], [' Space', DEFAULT_PANEL_PALETTE.info], [' collapse', DEFAULT_PANEL_PALETTE.dim], [' Enter', DEFAULT_PANEL_PALETTE.info], [' jump target', DEFAULT_PANEL_PALETTE.dim]]),
|
|
205
|
+
],
|
|
206
|
+
palette: DEFAULT_PANEL_PALETTE,
|
|
207
|
+
beforeSections: [
|
|
208
|
+
{
|
|
209
|
+
title: 'Summary',
|
|
210
|
+
lines: [
|
|
211
|
+
buildPanelLine(width, [
|
|
212
|
+
[' Symbols ', DEFAULT_PANEL_PALETTE.label],
|
|
213
|
+
[String(this.symbols.length), DEFAULT_PANEL_PALETTE.value],
|
|
214
|
+
[' Collapsed ', DEFAULT_PANEL_PALETTE.label],
|
|
215
|
+
[String(this.collapsed.size), this.collapsed.size > 0 ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.dim],
|
|
216
|
+
]),
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
section: {
|
|
221
|
+
title: 'Outline',
|
|
222
|
+
scrollableLines: visible.map((row, i) => {
|
|
223
|
+
const isSelected = i === this.selectedIndex;
|
|
224
|
+
const bgColor = isSelected ? '236' : '';
|
|
225
|
+
return row.kind === 'header'
|
|
226
|
+
? _renderHeader(width, row, isSelected, bgColor, this.collapsed)
|
|
227
|
+
: _renderSymbol(width, row, isSelected, bgColor);
|
|
228
|
+
}),
|
|
229
|
+
selectedIndex: this.selectedIndex,
|
|
230
|
+
scrollOffset: this.scrollOffset,
|
|
231
|
+
minRows: 8,
|
|
232
|
+
},
|
|
233
|
+
afterSections: [
|
|
234
|
+
{
|
|
235
|
+
title: 'Selected',
|
|
236
|
+
lines: (() => {
|
|
237
|
+
const selected = visible[this.selectedIndex];
|
|
238
|
+
return selected
|
|
239
|
+
? [
|
|
240
|
+
buildPanelLine(width, [
|
|
241
|
+
[' Kind ', DEFAULT_PANEL_PALETTE.label],
|
|
242
|
+
[selected.kind === 'header' ? selected.symbolKind : selected.symbol.kind, DEFAULT_PANEL_PALETTE.info],
|
|
243
|
+
[' Line ', DEFAULT_PANEL_PALETTE.label],
|
|
244
|
+
[String(selected.kind === 'header' ? selected.line : selected.symbol.line), DEFAULT_PANEL_PALETTE.value],
|
|
245
|
+
]),
|
|
246
|
+
buildPanelLine(width, [
|
|
247
|
+
[' Name ', DEFAULT_PANEL_PALETTE.label],
|
|
248
|
+
[selected.kind === 'header' ? selected.name : selected.symbol.name, DEFAULT_PANEL_PALETTE.value],
|
|
249
|
+
]),
|
|
250
|
+
]
|
|
251
|
+
: [];
|
|
252
|
+
})(),
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
});
|
|
256
|
+
this.scrollOffset = outlineSection.scrollOffset;
|
|
257
|
+
|
|
258
|
+
const selected = visible[this.selectedIndex];
|
|
259
|
+
return buildPanelWorkspace(width, height, {
|
|
260
|
+
title: ' Symbols',
|
|
261
|
+
intro: this.currentPath ? this.currentPath : 'Outline the current file into navigable symbols and lightweight parent/child structure.',
|
|
262
|
+
sections: [
|
|
263
|
+
{
|
|
264
|
+
title: 'Summary',
|
|
265
|
+
lines: [
|
|
266
|
+
buildPanelLine(width, [
|
|
267
|
+
[' Symbols ', DEFAULT_PANEL_PALETTE.label],
|
|
268
|
+
[String(this.symbols.length), DEFAULT_PANEL_PALETTE.value],
|
|
269
|
+
[' Collapsed ', DEFAULT_PANEL_PALETTE.label],
|
|
270
|
+
[String(this.collapsed.size), this.collapsed.size > 0 ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.dim],
|
|
271
|
+
]),
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
outlineSection.section,
|
|
275
|
+
{
|
|
276
|
+
title: 'Selected',
|
|
277
|
+
lines: selected
|
|
278
|
+
? [
|
|
279
|
+
buildPanelLine(width, [
|
|
280
|
+
[' Kind ', DEFAULT_PANEL_PALETTE.label],
|
|
281
|
+
[selected.kind === 'header' ? selected.symbolKind : selected.symbol.kind, DEFAULT_PANEL_PALETTE.info],
|
|
282
|
+
[' Line ', DEFAULT_PANEL_PALETTE.label],
|
|
283
|
+
[String(selected.kind === 'header' ? selected.line : selected.symbol.line), DEFAULT_PANEL_PALETTE.value],
|
|
284
|
+
]),
|
|
285
|
+
buildPanelLine(width, [
|
|
286
|
+
[' Name ', DEFAULT_PANEL_PALETTE.label],
|
|
287
|
+
[selected.kind === 'header' ? selected.name : selected.symbol.name, DEFAULT_PANEL_PALETTE.value],
|
|
288
|
+
]),
|
|
289
|
+
]
|
|
290
|
+
: [],
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
footerLines: [
|
|
294
|
+
buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' navigate', DEFAULT_PANEL_PALETTE.dim], [' Space', DEFAULT_PANEL_PALETTE.info], [' collapse', DEFAULT_PANEL_PALETTE.dim], [' Enter', DEFAULT_PANEL_PALETTE.info], [' jump target', DEFAULT_PANEL_PALETTE.dim]]),
|
|
295
|
+
],
|
|
296
|
+
palette: DEFAULT_PANEL_PALETTE,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ── Private helpers ────────────────────────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
private _clampScroll(totalRows: number): void {
|
|
303
|
+
// Ensure selected is within scroll view (assumes last known height ~ 20)
|
|
304
|
+
// We keep a conservative viewport window; render() uses this.scrollOffset.
|
|
305
|
+
const GUARD = 3;
|
|
306
|
+
if (this.selectedIndex < this.scrollOffset + GUARD) {
|
|
307
|
+
this.scrollOffset = Math.max(0, this.selectedIndex - GUARD);
|
|
308
|
+
}
|
|
309
|
+
// We don't know height here, so we defer bottom-clamp to render().
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private _visibleRows(): VisibleRow[] {
|
|
313
|
+
return buildVisibleRows(this.symbols, this.collapsed);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ── Row types for rendering ──────────────────────────────────────────────────
|
|
318
|
+
|
|
319
|
+
type VisibleRow =
|
|
320
|
+
| { kind: 'header'; name: string; symbolKind: SymbolKind; line: number; hasChildren: boolean }
|
|
321
|
+
| { kind: 'symbol'; symbol: SymbolEntry; depth: number };
|
|
322
|
+
|
|
323
|
+
// ── Pure helpers ─────────────────────────────────────────────────────────────
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Parse symbols from source text. Returns a flat list ordered by line number.
|
|
327
|
+
* Methods are tagged with their parent class name so the renderer can group them.
|
|
328
|
+
*/
|
|
329
|
+
function parseSymbols(source: string): SymbolEntry[] {
|
|
330
|
+
const lines = source.split('\n');
|
|
331
|
+
const result: SymbolEntry[] = [];
|
|
332
|
+
let currentContainer: string | undefined;
|
|
333
|
+
let containerKind: SymbolKind | undefined;
|
|
334
|
+
let containerBraceDepth = 0;
|
|
335
|
+
let braceDepth = 0;
|
|
336
|
+
|
|
337
|
+
for (let i = 0; i < lines.length; i++) {
|
|
338
|
+
const raw = lines[i];
|
|
339
|
+
const trimmed = raw.trimStart();
|
|
340
|
+
const lineNo = i + 1; // 1-based
|
|
341
|
+
|
|
342
|
+
// Strip string literals before counting braces to avoid false positives
|
|
343
|
+
const rawNoBraceStrings = raw.replace(/("|\'|\`)(?:(?!\1|\\).|\\[\s\S])*\1/g, '');
|
|
344
|
+
|
|
345
|
+
// Track brace depth for container scoping
|
|
346
|
+
for (const ch of rawNoBraceStrings) {
|
|
347
|
+
if (ch === '{') braceDepth++;
|
|
348
|
+
else if (ch === '}') {
|
|
349
|
+
braceDepth--;
|
|
350
|
+
if (currentContainer !== undefined && braceDepth <= containerBraceDepth) {
|
|
351
|
+
currentContainer = undefined;
|
|
352
|
+
containerKind = undefined;
|
|
353
|
+
containerBraceDepth = 0;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Skip comment lines and blank lines
|
|
359
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*') || trimmed === '') continue;
|
|
360
|
+
|
|
361
|
+
// Check container-level patterns first (class/namespace)
|
|
362
|
+
let matched = false;
|
|
363
|
+
for (const { re, kind, isContainer } of SYMBOL_PATTERNS) {
|
|
364
|
+
const m = trimmed.match(re);
|
|
365
|
+
if (!m) continue;
|
|
366
|
+
const name = m[1];
|
|
367
|
+
if (!name) continue;
|
|
368
|
+
|
|
369
|
+
const entry: SymbolEntry = { kind, name, line: lineNo };
|
|
370
|
+
|
|
371
|
+
if (currentContainer && kind === 'method') {
|
|
372
|
+
entry.parentName = currentContainer;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Don't add methods as top-level if they're inside a container
|
|
376
|
+
if (kind === 'method' && !currentContainer) {
|
|
377
|
+
// standalone function-like at wrong indent — skip
|
|
378
|
+
matched = true;
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
result.push(entry);
|
|
383
|
+
|
|
384
|
+
if (isContainer) {
|
|
385
|
+
currentContainer = name;
|
|
386
|
+
containerKind = kind;
|
|
387
|
+
// Opening brace may be on this line or a subsequent line — track from current depth
|
|
388
|
+
containerBraceDepth = braceDepth - (raw.includes('{') ? 1 : 0);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
matched = true;
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return result;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Build the flat list of rows to render, respecting collapse state.
|
|
401
|
+
* Container symbols become header rows; their children are indented below.
|
|
402
|
+
*/
|
|
403
|
+
function buildVisibleRows(symbols: SymbolEntry[], collapsed: Set<string>): VisibleRow[] {
|
|
404
|
+
const rows: VisibleRow[] = [];
|
|
405
|
+
|
|
406
|
+
// Pre-compute children map to avoid O(n^2) scans inside the loop
|
|
407
|
+
const childrenByParent = new Map<string, SymbolEntry[]>();
|
|
408
|
+
for (const sym of symbols) {
|
|
409
|
+
if (sym.parentName) {
|
|
410
|
+
const arr = childrenByParent.get(sym.parentName);
|
|
411
|
+
if (arr) arr.push(sym);
|
|
412
|
+
else childrenByParent.set(sym.parentName, [sym]);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
for (const sym of symbols) {
|
|
417
|
+
if (sym.kind === 'class' || sym.kind === 'namespace') {
|
|
418
|
+
const children = childrenByParent.get(sym.name) ?? [];
|
|
419
|
+
const hasChildren = children.length > 0;
|
|
420
|
+
rows.push({ kind: 'header', name: sym.name, symbolKind: sym.kind, line: sym.line, hasChildren });
|
|
421
|
+
// If collapsed, skip children
|
|
422
|
+
if (collapsed.has(sym.name)) continue;
|
|
423
|
+
// Add children immediately after header
|
|
424
|
+
for (const child of children) {
|
|
425
|
+
rows.push({ kind: 'symbol', symbol: child, depth: 1 });
|
|
426
|
+
}
|
|
427
|
+
} else if (!sym.parentName) {
|
|
428
|
+
// Top-level non-container symbol
|
|
429
|
+
rows.push({ kind: 'symbol', symbol: sym, depth: 0 });
|
|
430
|
+
}
|
|
431
|
+
// Children with parentName are rendered under their header above
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return rows;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Write a string into a Line starting at column x, applying fg/bg/style.
|
|
439
|
+
*/
|
|
440
|
+
/** Render a container header row (class / namespace). */
|
|
441
|
+
function _renderHeader(
|
|
442
|
+
width: number,
|
|
443
|
+
row: Extract<VisibleRow, { kind: 'header' }>,
|
|
444
|
+
isSelected: boolean,
|
|
445
|
+
bgColor: string,
|
|
446
|
+
collapsed: Set<string>,
|
|
447
|
+
): Line {
|
|
448
|
+
// Collapse indicator
|
|
449
|
+
const isCollapsed = collapsed.has(row.name);
|
|
450
|
+
const chevron = row.hasChildren ? (isCollapsed ? '▸ ' : '▾ ') : ' ';
|
|
451
|
+
const lineNumStr = `:${row.line}`;
|
|
452
|
+
const kindLabel = KIND_LABELS[row.symbolKind];
|
|
453
|
+
const leadingWidth = 1 + getDisplayWidth(chevron) + getDisplayWidth(kindLabel) + 1 + getDisplayWidth(row.name);
|
|
454
|
+
const gap = Math.max(1, width - leadingWidth - getDisplayWidth(lineNumStr) - 1);
|
|
455
|
+
return buildSelectablePanelLine(width, [
|
|
456
|
+
{ text: ` ${chevron}`, fg: '245' },
|
|
457
|
+
{ text: kindLabel, fg: KIND_COLORS[row.symbolKind], bold: true },
|
|
458
|
+
{ text: ' ', fg: isSelected ? '255' : '252' },
|
|
459
|
+
{ text: row.name, fg: isSelected ? '255' : '252', bold: isSelected },
|
|
460
|
+
{ text: ' '.repeat(gap), fg: DEFAULT_PANEL_PALETTE.dim },
|
|
461
|
+
{ text: lineNumStr, fg: DEFAULT_PANEL_PALETTE.dim },
|
|
462
|
+
], { selected: isSelected, selectedBg: bgColor, fillFg: isSelected ? '255' : '' });
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/** Render a regular symbol row. */
|
|
466
|
+
function _renderSymbol(
|
|
467
|
+
width: number,
|
|
468
|
+
row: Extract<VisibleRow, { kind: 'symbol' }>,
|
|
469
|
+
isSelected: boolean,
|
|
470
|
+
bgColor: string,
|
|
471
|
+
): Line {
|
|
472
|
+
const { symbol, depth } = row;
|
|
473
|
+
const indent = depth === 0 ? 1 : 3; // children indented by 3 (chevron + space)
|
|
474
|
+
const kindLabel = KIND_LABELS[symbol.kind];
|
|
475
|
+
const lineNumStr = `:${symbol.line}`;
|
|
476
|
+
const leadingWidth = indent + getDisplayWidth(kindLabel) + 1 + getDisplayWidth(symbol.name);
|
|
477
|
+
const gap = Math.max(1, width - leadingWidth - getDisplayWidth(lineNumStr) - 1);
|
|
478
|
+
return buildSelectablePanelLine(width, [
|
|
479
|
+
{ text: ' '.repeat(indent), fg: DEFAULT_PANEL_PALETTE.dim },
|
|
480
|
+
{ text: kindLabel, fg: KIND_COLORS[symbol.kind] },
|
|
481
|
+
{ text: ' ', fg: isSelected ? '255' : '251' },
|
|
482
|
+
{ text: symbol.name, fg: isSelected ? '255' : '251', bold: isSelected },
|
|
483
|
+
{ text: ' '.repeat(gap), fg: DEFAULT_PANEL_PALETTE.dim },
|
|
484
|
+
{ text: lineNumStr, fg: DEFAULT_PANEL_PALETTE.dim },
|
|
485
|
+
], { selected: isSelected, selectedBg: bgColor, fillFg: isSelected ? '255' : '' });
|
|
486
|
+
}
|