@bastani/atomic 0.8.11 → 0.8.12
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 +43 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +3 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/agents/agent-serializer.ts +3 -2
- package/dist/builtin/subagents/src/agents/agents.ts +1 -1
- package/dist/builtin/subagents/src/extension/index.ts +597 -471
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +16 -8
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +9 -13
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +7 -3
- package/dist/builtin/subagents/src/shared/formatters.ts +8 -3
- package/dist/builtin/subagents/src/slash/slash-commands.ts +625 -468
- package/dist/builtin/subagents/src/tui/render.ts +342 -158
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +15 -4
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +75 -33
- package/dist/builtin/workflows/src/shared/store-types.ts +3 -4
- package/dist/builtin/workflows/src/shared/store.ts +2 -3
- package/dist/builtin/workflows/src/tui/graph-view.ts +12 -1
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +1 -2
- package/dist/builtin/workflows/src/tui/status-helpers.ts +1 -1
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js.map +1 -1
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts +2 -2
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/initial-message.d.ts +1 -1
- package/dist/cli/initial-message.d.ts.map +1 -1
- package/dist/cli/initial-message.js.map +1 -1
- package/dist/cli/list-models.d.ts +1 -1
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/session-picker.d.ts +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -6
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +45 -22
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +9 -9
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -3
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +7 -7
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +10 -10
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/auth-guidance.d.ts.map +1 -1
- package/dist/core/auth-guidance.js.map +1 -1
- package/dist/core/auth-storage.d.ts +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +1 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/builtin-packages.d.ts.map +1 -1
- package/dist/core/builtin-packages.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +3 -3
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +2 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/index.d.ts +3 -3
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/export-html/index.d.ts +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts +2 -2
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +8 -8
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +3 -3
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +6 -6
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +20 -20
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts +2 -2
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts +32 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -0
- package/dist/core/http-dispatcher.js +43 -0
- package/dist/core/http-dispatcher.js.map +1 -0
- package/dist/core/index.d.ts +8 -8
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts +4 -4
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +10 -11
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +1 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts +9 -9
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +13 -13
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +21 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +2 -2
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/source-info.d.ts +1 -1
- package/dist/core/source-info.d.ts.map +1 -1
- package/dist/core/source-info.js.map +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +7 -8
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/telemetry.d.ts +1 -1
- package/dist/core/telemetry.d.ts.map +1 -1
- package/dist/core/telemetry.js.map +1 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.d.ts +4 -4
- package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
- package/dist/core/tools/ask-user-question/index.d.ts +1 -1
- package/dist/core/tools/ask-user-question/index.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/index.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts +5 -5
- package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/build-questionnaire.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/key-router.d.ts +2 -2
- package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/key-router.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +3 -3
- package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/row-intent.d.ts +2 -2
- package/dist/core/tools/ask-user-question/state/row-intent.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/row-intent.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +6 -6
- package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/derivations.d.ts +2 -2
- package/dist/core/tools/ask-user-question/state/selectors/derivations.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/derivations.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/focus.d.ts +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/focus.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/focus.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts +8 -8
- package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +4 -4
- package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/state.d.ts +2 -2
- package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/state.js.map +1 -1
- package/dist/core/tools/ask-user-question/tool/format-answer.d.ts +1 -1
- package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/tool/format-answer.js.map +1 -1
- package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts +1 -1
- package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/tool/response-envelope.js.map +1 -1
- package/dist/core/tools/ask-user-question/tool/types.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/tool/types.js.map +1 -1
- package/dist/core/tools/ask-user-question/tool/validate-questionnaire.d.ts +1 -1
- package/dist/core/tools/ask-user-question/tool/validate-questionnaire.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/tool/validate-questionnaire.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/body-residual-spacer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/body-residual-spacer.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/component-binding.d.ts +4 -4
- package/dist/core/tools/ask-user-question/view/component-binding.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/component-binding.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts +2 -2
- package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/chat-row-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts +4 -4
- package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/multi-select-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts +2 -2
- package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts +2 -2
- package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts +3 -3
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts +2 -2
- package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.d.ts +8 -8
- package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts +3 -3
- package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/submit-picker.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts +3 -3
- package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/tab-bar.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts +8 -8
- package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/dialog-builder.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.d.ts +5 -5
- package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/tab-components.d.ts +4 -4
- package/dist/core/tools/ask-user-question/view/tab-components.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/tab-components.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts +9 -9
- package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/tab-content-strategy.js +2 -2
- package/dist/core/tools/ask-user-question/view/tab-content-strategy.js.map +1 -1
- package/dist/core/tools/bash.d.ts +2 -2
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +9 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +2 -2
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +2 -2
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +2 -2
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +19 -19
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +2 -2
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/read.d.ts +2 -2
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/todos.d.ts +1 -1
- package/dist/core/tools/todos.d.ts.map +1 -1
- package/dist/core/tools/todos.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/write.d.ts +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +30 -29
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -0
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/index.d.ts +5 -5
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/atomic-banner.d.ts +1 -1
- package/dist/modes/interactive/components/atomic-banner.d.ts.map +1 -1
- package/dist/modes/interactive/components/atomic-banner.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +3 -3
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts +2 -2
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts +2 -2
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +2 -2
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
- package/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/dist/modes/interactive/components/diff.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +3 -3
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +34 -34
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +1 -1
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +2 -2
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +3 -3
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +13 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/working-status.d.ts.map +1 -1
- package/dist/modes/interactive/components/working-status.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +5 -5
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +1 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +4 -4
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +22 -6
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/changelog.d.ts +1 -1
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/child-process.d.ts +5 -2
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +9 -7
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/windows-self-update.d.ts +3 -0
- package/dist/utils/windows-self-update.d.ts.map +1 -0
- package/dist/utils/windows-self-update.js +78 -0
- package/dist/utils/windows-self-update.js.map +1 -0
- package/docs/compaction.md +5 -5
- package/docs/custom-provider.md +2 -2
- package/docs/development.md +22 -16
- package/docs/docs.json +6 -2
- package/docs/extensions.md +25 -25
- package/docs/index.md +11 -14
- package/docs/keybindings.md +5 -5
- package/docs/models.md +6 -6
- package/docs/packages.md +55 -48
- package/docs/prompt-templates.md +5 -5
- package/docs/providers.md +10 -10
- package/docs/quickstart.md +32 -30
- package/docs/rpc.md +9 -9
- package/docs/sdk.md +1 -1
- package/docs/session-format.md +3 -3
- package/docs/sessions.md +11 -11
- package/docs/settings.md +18 -15
- package/docs/shell-aliases.md +2 -2
- package/docs/skills.md +11 -11
- package/docs/terminal-setup.md +8 -8
- package/docs/termux.md +6 -6
- package/docs/themes.md +10 -10
- package/docs/tui.md +5 -4
- package/docs/usage.md +50 -50
- package/docs/windows.md +2 -2
- package/docs/workflows.md +695 -0
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +1 -1
- package/examples/extensions/doom-overlay/doom-component.ts +2 -2
- package/examples/extensions/doom-overlay/index.ts +3 -3
- package/examples/extensions/overlay-qa-tests.ts +116 -33
- package/examples/extensions/overlay-test.ts +9 -3
- package/examples/extensions/plan-mode/index.ts +1 -1
- package/examples/extensions/subagent/index.ts +1159 -903
- package/package.json +6 -4
- package/dist/builtin/workflows/skills/workflow/SKILL.md +0 -322
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/advanced-evaluation.md +0 -404
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/bdi-mental-states.md +0 -313
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/context-compression.md +0 -274
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/context-degradation.md +0 -208
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/context-fundamentals.md +0 -203
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/context-optimization.md +0 -197
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/evaluation.md +0 -253
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/filesystem-context.md +0 -289
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/hosted-agents.md +0 -262
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/memory-systems.md +0 -221
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/multi-agent-patterns.md +0 -259
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/project-development.md +0 -293
- package/dist/builtin/workflows/skills/workflow/references/context-engineering/tool-design.md +0 -273
- package/dist/builtin/workflows/skills/workflow/references/context-engineering.md +0 -23
- package/dist/builtin/workflows/skills/workflow/references/design-checklist.md +0 -83
- package/dist/builtin/workflows/skills/workflow/references/running-workflows.md +0 -159
- package/dist/builtin/workflows/skills/workflow/references/sdk-authoring.md +0 -260
package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-content-cache.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/markdown-content-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,EAAY,KAAK,aAAa,EAAgB,MAAM,wBAAwB,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,wCAAwC;AACxC,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAClD,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAC7C,eAAO,MAAM,eAAe,yBAAyB,CAAC;AACtD,qJAAqJ;AACrJ,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwB;IACtD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAE9C,YAAY,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAS7E;IAED,aAAa,IAAI,OAAO,CAEvB;IAED,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEhC;IAED;;;OAGG;IACH,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBzD;IAED,UAAU,IAAI,IAAI,CAGjB;CACD","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.
|
|
1
|
+
{"version":3,"file":"markdown-content-cache.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/markdown-content-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,EAAY,KAAK,aAAa,EAAgB,MAAM,wBAAwB,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,wCAAwC;AACxC,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAClD,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAC7C,eAAO,MAAM,eAAe,yBAAyB,CAAC;AACtD,qJAAqJ;AACrJ,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwB;IACtD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAE9C,YAAY,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAS7E;IAED,aAAa,IAAI,OAAO,CAEvB;IAED,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEhC;IAED;;;OAGG;IACH,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBzD;IAED,UAAU,IAAI,IAAI,CAGjB;CACD","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.ts\";\nimport { Markdown, type MarkdownTheme, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\nimport { stripFenceMarkers } from \"./preview-box-renderer.ts\";\n\n/** CC parity in side-by-side layout. */\nexport const MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE = 20;\n/** Preserves narrow-terminal protection in stacked layout. */\nexport const MAX_PREVIEW_HEIGHT_STACKED = 15;\nexport const NO_PREVIEW_TEXT = \"No preview available\";\n/** 1 blank separator + 1 affordance text row reserved when `hasAnyPreview` (height stability of the affordance row's offset relative to the box). */\nexport const NOTES_AFFORDANCE_OVERHEAD = 2;\n\n/**\n * Per-question cache for rendered markdown previews. Width-keyed: switching the\n * inner width invalidates every cached `Markdown`'s render output (pi-tui's\n * `Markdown.render(width)` re-wraps when width changes).\n *\n * Replaces the inline `previewTexts`, `markdownCache`, `cachedWidth` triple from\n * the previous monolithic `preview-pane.ts`. One Markdown per option, lazy on\n * first request, never re-constructed — count semantics frozen by tests.\n */\nexport class MarkdownContentCache {\n\tprivate readonly previewTexts: Map<number, string>;\n\tprivate readonly markdownCache: Map<number, Markdown>;\n\tprivate cachedWidth: number | undefined;\n\tprivate readonly theme: Theme;\n\tprivate readonly markdownTheme: MarkdownTheme;\n\n\tconstructor(question: QuestionData, theme: Theme, markdownTheme: MarkdownTheme) {\n\t\tthis.theme = theme;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.previewTexts = new Map();\n\t\tfor (let i = 0; i < question.options.length; i++) {\n\t\t\tconst raw = question.options[i]?.preview;\n\t\t\tif (raw && raw.length > 0) this.previewTexts.set(i, raw);\n\t\t}\n\t\tthis.markdownCache = new Map();\n\t}\n\n\thasAnyPreview(): boolean {\n\t\treturn this.previewTexts.size > 0;\n\t}\n\n\thas(optionIndex: number): boolean {\n\t\treturn this.previewTexts.has(optionIndex);\n\t}\n\n\t/**\n\t * Compute the body lines for a given option at a given inner width. Width changes\n\t * invalidate the per-Markdown render cache.\n\t */\n\tbodyFor(optionIndex: number, innerWidth: number): string[] {\n\t\tif (this.cachedWidth !== innerWidth) {\n\t\t\tfor (const md of this.markdownCache.values()) md.invalidate();\n\t\t\tthis.cachedWidth = innerWidth;\n\t\t}\n\t\tconst text = this.previewTexts.get(optionIndex);\n\t\tif (!text) {\n\t\t\tconst placeholder = this.theme.fg(\"dim\", NO_PREVIEW_TEXT);\n\t\t\tconst pad = Math.max(0, innerWidth - visibleWidth(placeholder));\n\t\t\treturn [placeholder + \" \".repeat(pad)];\n\t\t}\n\t\tlet md = this.markdownCache.get(optionIndex);\n\t\tif (!md) {\n\t\t\tmd = new Markdown(text, 0, 0, this.markdownTheme);\n\t\t\tthis.markdownCache.set(optionIndex, md);\n\t\t}\n\t\treturn stripFenceMarkers(md.render(innerWidth));\n\t}\n\n\tinvalidate(): void {\n\t\tfor (const md of this.markdownCache.values()) md.invalidate();\n\t\tthis.cachedWidth = undefined;\n\t}\n}\n"]}
|
package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-content-cache.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/markdown-content-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.
|
|
1
|
+
{"version":3,"file":"markdown-content-cache.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/markdown-content-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.ts\";\nimport { Markdown, type MarkdownTheme, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\nimport { stripFenceMarkers } from \"./preview-box-renderer.ts\";\n\n/** CC parity in side-by-side layout. */\nexport const MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE = 20;\n/** Preserves narrow-terminal protection in stacked layout. */\nexport const MAX_PREVIEW_HEIGHT_STACKED = 15;\nexport const NO_PREVIEW_TEXT = \"No preview available\";\n/** 1 blank separator + 1 affordance text row reserved when `hasAnyPreview` (height stability of the affordance row's offset relative to the box). */\nexport const NOTES_AFFORDANCE_OVERHEAD = 2;\n\n/**\n * Per-question cache for rendered markdown previews. Width-keyed: switching the\n * inner width invalidates every cached `Markdown`'s render output (pi-tui's\n * `Markdown.render(width)` re-wraps when width changes).\n *\n * Replaces the inline `previewTexts`, `markdownCache`, `cachedWidth` triple from\n * the previous monolithic `preview-pane.ts`. One Markdown per option, lazy on\n * first request, never re-constructed — count semantics frozen by tests.\n */\nexport class MarkdownContentCache {\n\tprivate readonly previewTexts: Map<number, string>;\n\tprivate readonly markdownCache: Map<number, Markdown>;\n\tprivate cachedWidth: number | undefined;\n\tprivate readonly theme: Theme;\n\tprivate readonly markdownTheme: MarkdownTheme;\n\n\tconstructor(question: QuestionData, theme: Theme, markdownTheme: MarkdownTheme) {\n\t\tthis.theme = theme;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.previewTexts = new Map();\n\t\tfor (let i = 0; i < question.options.length; i++) {\n\t\t\tconst raw = question.options[i]?.preview;\n\t\t\tif (raw && raw.length > 0) this.previewTexts.set(i, raw);\n\t\t}\n\t\tthis.markdownCache = new Map();\n\t}\n\n\thasAnyPreview(): boolean {\n\t\treturn this.previewTexts.size > 0;\n\t}\n\n\thas(optionIndex: number): boolean {\n\t\treturn this.previewTexts.has(optionIndex);\n\t}\n\n\t/**\n\t * Compute the body lines for a given option at a given inner width. Width changes\n\t * invalidate the per-Markdown render cache.\n\t */\n\tbodyFor(optionIndex: number, innerWidth: number): string[] {\n\t\tif (this.cachedWidth !== innerWidth) {\n\t\t\tfor (const md of this.markdownCache.values()) md.invalidate();\n\t\t\tthis.cachedWidth = innerWidth;\n\t\t}\n\t\tconst text = this.previewTexts.get(optionIndex);\n\t\tif (!text) {\n\t\t\tconst placeholder = this.theme.fg(\"dim\", NO_PREVIEW_TEXT);\n\t\t\tconst pad = Math.max(0, innerWidth - visibleWidth(placeholder));\n\t\t\treturn [placeholder + \" \".repeat(pad)];\n\t\t}\n\t\tlet md = this.markdownCache.get(optionIndex);\n\t\tif (!md) {\n\t\t\tmd = new Markdown(text, 0, 0, this.markdownTheme);\n\t\t\tthis.markdownCache.set(optionIndex, md);\n\t\t}\n\t\treturn stripFenceMarkers(md.render(innerWidth));\n\t}\n\n\tinvalidate(): void {\n\t\tfor (const md of this.markdownCache.values()) md.invalidate();\n\t\tthis.cachedWidth = undefined;\n\t}\n}\n"]}
|
package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Theme } from "../../../../../../modes/interactive/theme/theme.
|
|
1
|
+
import type { Theme } from "../../../../../../modes/interactive/theme/theme.ts";
|
|
2
2
|
import type { MarkdownTheme } from "@earendil-works/pi-tui";
|
|
3
|
-
import type { QuestionData } from "../../../tool/types.
|
|
4
|
-
import type { PreviewLayoutMode } from "./preview-layout-decider.
|
|
3
|
+
import type { QuestionData } from "../../../tool/types.ts";
|
|
4
|
+
import type { PreviewLayoutMode } from "./preview-layout-decider.ts";
|
|
5
5
|
/**
|
|
6
6
|
* Affordance text shown below the bordered preview when focused on a preview-bearing option.
|
|
7
7
|
* Re-exported by `preview-pane.ts` for the existing test surface.
|
package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-block-renderer.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-block-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAc3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,uBAAuB,CAAC;AAE1D,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,aAAa,EAAE,aAAa,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAE7C,YAAY,MAAM,EAAE,0BAA0B,EAG7C;IAED,aAAa,IAAI,OAAO,CAEvB;IAED,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEhC;IAED,UAAU,IAAI,IAAI,CAEjB;IAED;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAO/E;IAED;;;;;OAKG;IACH,WAAW,CACV,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,OAAO,GACnB,MAAM,EAAE,CAmBV;CACD","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.
|
|
1
|
+
{"version":3,"file":"preview-block-renderer.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-block-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAc3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,uBAAuB,CAAC;AAE1D,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,aAAa,EAAE,aAAa,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAE7C,YAAY,MAAM,EAAE,0BAA0B,EAG7C;IAED,aAAa,IAAI,OAAO,CAEvB;IAED,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEhC;IAED,UAAU,IAAI,IAAI,CAEjB;IAED;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAO/E;IAED;;;;;OAKG;IACH,WAAW,CACV,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,OAAO,GACnB,MAAM,EAAE,CAmBV;CACD","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.ts\";\nimport type { MarkdownTheme } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\nimport {\n\tMAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,\n\tMAX_PREVIEW_HEIGHT_STACKED,\n\tMarkdownContentCache,\n\tNOTES_AFFORDANCE_OVERHEAD,\n} from \"./markdown-content-cache.ts\";\nimport {\n\tBORDER_HORIZONTAL_OVERHEAD,\n\tBORDER_INNER_PADDING_HORIZONTAL,\n\tBORDER_VERTICAL_OVERHEAD,\n\tcomputeBoxDimensions,\n\trenderBorderedBox,\n} from \"./preview-box-renderer.ts\";\nimport type { PreviewLayoutMode } from \"./preview-layout-decider.ts\";\n\n/**\n * Affordance text shown below the bordered preview when focused on a preview-bearing option.\n * Re-exported by `preview-pane.ts` for the existing test surface.\n */\nexport const NOTES_AFFORDANCE_TEXT = \"Notes: n add notes\";\n\nexport interface PreviewBlockRendererConfig {\n\tquestion: QuestionData;\n\ttheme: Theme;\n\tmarkdownTheme: MarkdownTheme;\n}\n\n/**\n * Renders the bordered markdown preview block for a single question (one block per render call,\n * for the option at `optionIndex`). Owns a per-question `MarkdownContentCache`.\n *\n * NOT a `Component` — pure render-and-measure helper consumed by `PreviewPane`. The layout mode\n * is threaded as an explicit param (never re-derived from column width post-split).\n *\n * The affordance row is always emitted (visually empty when gated) so the preview block's row\n * count is height-stable across affordance-state transitions.\n */\nexport class PreviewBlockRenderer {\n\tprivate readonly theme: Theme;\n\tprivate readonly cache: MarkdownContentCache;\n\n\tconstructor(config: PreviewBlockRendererConfig) {\n\t\tthis.theme = config.theme;\n\t\tthis.cache = new MarkdownContentCache(config.question, config.theme, config.markdownTheme);\n\t}\n\n\thasAnyPreview(): boolean {\n\t\treturn this.cache.hasAnyPreview();\n\t}\n\n\thas(optionIndex: number): boolean {\n\t\treturn this.cache.has(optionIndex);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cache.invalidate();\n\t}\n\n\t/**\n\t * Height contribution of the preview block: `BORDER_VERTICAL_OVERHEAD + contentRows +\n\t * NOTES_AFFORDANCE_OVERHEAD`. Always returns the same value as `renderBlock(...).length`\n\t * — the affordance overhead is constant, not gated by `focused`/`notesVisible`.\n\t */\n\tblockHeight(width: number, optionIndex: number, mode: PreviewLayoutMode): number {\n\t\tconst cap = mode === \"side-by-side\" ? MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE : MAX_PREVIEW_HEIGHT_STACKED;\n\t\tconst contentBudget = Math.max(1, cap - BORDER_VERTICAL_OVERHEAD - NOTES_AFFORDANCE_OVERHEAD);\n\t\tconst innerWidth = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL);\n\t\tconst rawRows = this.cache.bodyFor(optionIndex, innerWidth).length;\n\t\tconst contentRows = Math.min(rawRows, contentBudget);\n\t\treturn BORDER_VERTICAL_OVERHEAD + contentRows + NOTES_AFFORDANCE_OVERHEAD;\n\t}\n\n\t/**\n\t * Render the full preview block at `width`: bordered box + blank separator + affordance row.\n\t * `focused` and `notesVisible` together gate the affordance text (visible only when the\n\t * focused option carries a preview AND notes mode is inactive). The affordance row is ALWAYS\n\t * emitted (as an empty string when gated) so the row count is invariant.\n\t */\n\trenderBlock(\n\t\twidth: number,\n\t\toptionIndex: number,\n\t\tmode: PreviewLayoutMode,\n\t\tfocused: boolean,\n\t\tnotesVisible: boolean,\n\t): string[] {\n\t\tconst cap = mode === \"side-by-side\" ? MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE : MAX_PREVIEW_HEIGHT_STACKED;\n\t\tconst contentBudget = Math.max(1, cap - BORDER_VERTICAL_OVERHEAD - NOTES_AFFORDANCE_OVERHEAD);\n\t\tconst maxInnerWidth = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL);\n\n\t\tconst raw = this.cache.bodyFor(optionIndex, maxInnerWidth);\n\t\tconst truncated = raw.length > contentBudget;\n\t\tconst hidden = truncated ? raw.length - contentBudget : 0;\n\t\tconst contentLines = truncated ? raw.slice(0, contentBudget) : raw;\n\n\t\tconst { boxWidth } = computeBoxDimensions(contentLines, maxInnerWidth);\n\t\tconst colorFn = (s: string) => this.theme.fg(\"accent\", s);\n\t\tconst boxedLines = renderBorderedBox(contentLines, boxWidth, colorFn, hidden);\n\n\t\tconst showAffordance = focused && !notesVisible && this.cache.has(optionIndex);\n\t\tconst affordance = showAffordance\n\t\t\t? this.theme.fg(\"muted\", NOTES_AFFORDANCE_TEXT)\n\t\t\t: \"\";\n\t\treturn [...boxedLines, \"\", affordance];\n\t}\n}\n"]}
|
package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-block-renderer.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-block-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.
|
|
1
|
+
{"version":3,"file":"preview-block-renderer.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-block-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.ts\";\nimport type { MarkdownTheme } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\nimport {\n\tMAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,\n\tMAX_PREVIEW_HEIGHT_STACKED,\n\tMarkdownContentCache,\n\tNOTES_AFFORDANCE_OVERHEAD,\n} from \"./markdown-content-cache.ts\";\nimport {\n\tBORDER_HORIZONTAL_OVERHEAD,\n\tBORDER_INNER_PADDING_HORIZONTAL,\n\tBORDER_VERTICAL_OVERHEAD,\n\tcomputeBoxDimensions,\n\trenderBorderedBox,\n} from \"./preview-box-renderer.ts\";\nimport type { PreviewLayoutMode } from \"./preview-layout-decider.ts\";\n\n/**\n * Affordance text shown below the bordered preview when focused on a preview-bearing option.\n * Re-exported by `preview-pane.ts` for the existing test surface.\n */\nexport const NOTES_AFFORDANCE_TEXT = \"Notes: n add notes\";\n\nexport interface PreviewBlockRendererConfig {\n\tquestion: QuestionData;\n\ttheme: Theme;\n\tmarkdownTheme: MarkdownTheme;\n}\n\n/**\n * Renders the bordered markdown preview block for a single question (one block per render call,\n * for the option at `optionIndex`). Owns a per-question `MarkdownContentCache`.\n *\n * NOT a `Component` — pure render-and-measure helper consumed by `PreviewPane`. The layout mode\n * is threaded as an explicit param (never re-derived from column width post-split).\n *\n * The affordance row is always emitted (visually empty when gated) so the preview block's row\n * count is height-stable across affordance-state transitions.\n */\nexport class PreviewBlockRenderer {\n\tprivate readonly theme: Theme;\n\tprivate readonly cache: MarkdownContentCache;\n\n\tconstructor(config: PreviewBlockRendererConfig) {\n\t\tthis.theme = config.theme;\n\t\tthis.cache = new MarkdownContentCache(config.question, config.theme, config.markdownTheme);\n\t}\n\n\thasAnyPreview(): boolean {\n\t\treturn this.cache.hasAnyPreview();\n\t}\n\n\thas(optionIndex: number): boolean {\n\t\treturn this.cache.has(optionIndex);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cache.invalidate();\n\t}\n\n\t/**\n\t * Height contribution of the preview block: `BORDER_VERTICAL_OVERHEAD + contentRows +\n\t * NOTES_AFFORDANCE_OVERHEAD`. Always returns the same value as `renderBlock(...).length`\n\t * — the affordance overhead is constant, not gated by `focused`/`notesVisible`.\n\t */\n\tblockHeight(width: number, optionIndex: number, mode: PreviewLayoutMode): number {\n\t\tconst cap = mode === \"side-by-side\" ? MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE : MAX_PREVIEW_HEIGHT_STACKED;\n\t\tconst contentBudget = Math.max(1, cap - BORDER_VERTICAL_OVERHEAD - NOTES_AFFORDANCE_OVERHEAD);\n\t\tconst innerWidth = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL);\n\t\tconst rawRows = this.cache.bodyFor(optionIndex, innerWidth).length;\n\t\tconst contentRows = Math.min(rawRows, contentBudget);\n\t\treturn BORDER_VERTICAL_OVERHEAD + contentRows + NOTES_AFFORDANCE_OVERHEAD;\n\t}\n\n\t/**\n\t * Render the full preview block at `width`: bordered box + blank separator + affordance row.\n\t * `focused` and `notesVisible` together gate the affordance text (visible only when the\n\t * focused option carries a preview AND notes mode is inactive). The affordance row is ALWAYS\n\t * emitted (as an empty string when gated) so the row count is invariant.\n\t */\n\trenderBlock(\n\t\twidth: number,\n\t\toptionIndex: number,\n\t\tmode: PreviewLayoutMode,\n\t\tfocused: boolean,\n\t\tnotesVisible: boolean,\n\t): string[] {\n\t\tconst cap = mode === \"side-by-side\" ? MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE : MAX_PREVIEW_HEIGHT_STACKED;\n\t\tconst contentBudget = Math.max(1, cap - BORDER_VERTICAL_OVERHEAD - NOTES_AFFORDANCE_OVERHEAD);\n\t\tconst maxInnerWidth = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL);\n\n\t\tconst raw = this.cache.bodyFor(optionIndex, maxInnerWidth);\n\t\tconst truncated = raw.length > contentBudget;\n\t\tconst hidden = truncated ? raw.length - contentBudget : 0;\n\t\tconst contentLines = truncated ? raw.slice(0, contentBudget) : raw;\n\n\t\tconst { boxWidth } = computeBoxDimensions(contentLines, maxInnerWidth);\n\t\tconst colorFn = (s: string) => this.theme.fg(\"accent\", s);\n\t\tconst boxedLines = renderBorderedBox(contentLines, boxWidth, colorFn, hidden);\n\n\t\tconst showAffordance = focused && !notesVisible && this.cache.has(optionIndex);\n\t\tconst affordance = showAffordance\n\t\t\t? this.theme.fg(\"muted\", NOTES_AFFORDANCE_TEXT)\n\t\t\t: \"\";\n\t\treturn [...boxedLines, \"\", affordance];\n\t}\n}\n"]}
|
package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { QuestionData } from "../../../tool/types.
|
|
2
|
-
import type { WrappingSelectItem } from "../wrapping-select.
|
|
1
|
+
import type { QuestionData } from "../../../tool/types.ts";
|
|
2
|
+
import type { WrappingSelectItem } from "../wrapping-select.ts";
|
|
3
3
|
/** Min terminal/pane width for the side-by-side layout to engage. */
|
|
4
4
|
export declare const PREVIEW_MIN_WIDTH = 100;
|
|
5
5
|
/** Visual gap between options column and preview column in side-by-side. */
|
package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-layout-decider.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-layout-decider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGhE,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,oFAAoF;AACpF,eAAO,MAAM,QAAQ,KAAK,CAAC;AAC3B,4EAA4E;AAC5E,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC;+EAC+E;AAC/E,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,SAAS,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAExF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,MAAM,GACf,MAAM,CAYR;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,aAAa,CAAC;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EAC9C,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,SAAS,EAAE,MAAM,GACf,MAAM,CASR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAWjE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CASnG;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,6BAA6B,CAC5C,IAAI,EAAE,aAAa,CAAC;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EAC9C,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,SAAS,EAAE,MAAM,GACf,MAAM,CAUR;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GAClB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAKxD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACzB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,iBAAiB,EACvB,YAAY,EAAE,MAAM,GAClB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAIhD","sourcesContent":["import { visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.js\";\nimport type { WrappingSelectItem } from \"../wrapping-select.js\";\nimport { BORDER_HORIZONTAL_OVERHEAD, BORDER_INNER_PADDING_HORIZONTAL } from \"./preview-box-renderer.js\";\n\n/** Min terminal/pane width for the side-by-side layout to engage. */\nexport const PREVIEW_MIN_WIDTH = 100;\n/** Visual gap between options column and preview column in side-by-side. */\nexport const PREVIEW_COLUMN_GAP = 2;\n/** 1 col padding inside the preview column (between gap and `│`). */\nexport const PREVIEW_PADDING_LEFT = 1;\n/** Empty rows between options and preview blocks in stacked (narrow) layout. */\nexport const STACKED_GAP_ROWS = 1;\n\n/** Floor for the adaptive left column width — prevents collapse on short labels. */\nexport const MIN_LEFT = 30;\n/** Ceiling ratio: left column never exceeds this fraction of pane width. */\nexport const MAX_LEFT_RATIO = 0.5;\n/** Floor for the preview column width — prevents right-side collapse on narrow terminals. */\nexport const MIN_PREVIEW_WIDTH = 45;\n/** visibleWidth(\" ✔\") = 2 (space + ✔ codepoint). Reserved on the longest-label measurement\n * so a confirmed row never gets truncated when MIN_LEFT clamps the column. */\nexport const CONFIRMED_OVERHEAD = 2;\n\nexport type PreviewLayoutMode = \"side-by-side\" | \"stacked\";\n\n/**\n * Decide layout mode from terminal + pane widths. Pure of inputs.\n *\n * The terminal-width gate is the AND check from the previous `preview-pane.ts` —\n * lifted here so the decision is computed ONCE per render and threaded explicitly\n * through `previewBlockHeight`. Removes the bug class where `previewBlockHeight`\n * re-derived `sideBySide` from a column width (already < pane width post-split),\n * capping height too short.\n */\nexport function decideLayout(terminalWidth: number, paneWidth: number): PreviewLayoutMode {\n\treturn terminalWidth >= PREVIEW_MIN_WIDTH && paneWidth >= PREVIEW_MIN_WIDTH ? \"side-by-side\" : \"stacked\";\n}\n\n/**\n * Compute the adaptive left column width from option labels.\n * Pure function — deterministic for a given (items, totalForNumbering, paneWidth).\n *\n * Pipeline:\n * 1. Measure: longest visible label width + prefix overhead + confirmed-mark overhead\n * 2. Clamp: floor MIN_LEFT, ceiling paneWidth * MAX_LEFT_RATIO\n * 3. Safety net: never exceed available = paneWidth - GAP - MIN_PREVIEW_WIDTH\n */\nexport function adaptiveLeftWidth(\n\titems: readonly WrappingSelectItem[],\n\ttotalForNumbering: number,\n\tpaneWidth: number,\n): number {\n\tconst prefixW = String(Math.max(1, totalForNumbering)).length + 4; // digits + \"❯ \" + \". \"\n\tconst confirmedOverhead = CONFIRMED_OVERHEAD; // visibleWidth(\" ✔\") = 2 (space + ✔ codepoint)\n\tlet maxLabel = 0;\n\tfor (const item of items) {\n\t\tconst w = visibleWidth(item.label);\n\t\tif (w > maxLabel) maxLabel = w;\n\t}\n\tconst desired = maxLabel + prefixW + confirmedOverhead;\n\tconst ratioCapped = Math.min(desired, Math.floor(paneWidth * MAX_LEFT_RATIO));\n\tconst available = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.max(MIN_LEFT, Math.min(ratioCapped, Math.max(1, available)));\n}\n\n/**\n * Cross-tab maximum left-column width. Aggregates `adaptiveLeftWidth` over every tab\n * and returns the widest result, so the options column stays stable on tab switch.\n *\n * Pure function — `tabs.length` MUST equal `itemsByTab.length`. Multi-select tabs\n * use `items.length` for numbering; single-select tabs add 1 for the chat row slot.\n * Floor is `MIN_LEFT` so an all-empty input still produces a usable column.\n */\nexport function crossTabMaxLeftWidth(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tpaneWidth: number,\n): number {\n\tlet max = MIN_LEFT;\n\tfor (let i = 0; i < tabs.length; i++) {\n\t\tconst items = itemsByTab[i] ?? [];\n\t\tconst totalForNumbering = tabs[i]?.multiSelect ? items.length : items.length + 1;\n\t\tconst tabWidth = adaptiveLeftWidth(items, totalForNumbering, paneWidth);\n\t\tif (tabWidth > max) max = tabWidth;\n\t}\n\treturn max;\n}\n\n/**\n * Source-line probe: measures the widest source line across all options' previews.\n * Returns 0 when no option carries a preview.\n *\n * V1 heuristic — works well for tree/table/heading/list content where source-line\n * width ≈ rendered width. Paragraphs overstate (source width \"wants\" the full pane);\n * the worst case is \"donation does nothing useful\" — fallback to the label-driven path.\n * Upgrade path: replace with a render-width probe that invokes Markdown at a probe width.\n *\n * Pure function — O(total chars) per question; no Markdown invocation, no cache interaction.\n */\nexport function previewSourceWidth(question: QuestionData): number {\n\tlet max = 0;\n\tfor (const option of question.options) {\n\t\tconst text = option.preview;\n\t\tif (!text) continue;\n\t\tfor (const line of text.split(\"\\n\")) {\n\t\t\tconst w = visibleWidth(line);\n\t\t\tif (w > max) max = w;\n\t\t}\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab/cross-option preview budget. Iterates all questions, computes each question's\n * preview appetite via `previewSourceWidth`, adds box + padding overhead (5 cols), and\n * returns the widest result. Floor is `MIN_PREVIEW_WIDTH` so a previewless question still\n * reserves a usable column. Ceiling ensures the left column retains its `MIN_LEFT` floor.\n *\n * Pure function — same cross-tab max pattern as `crossTabMaxLeftWidth`.\n */\nexport function crossTabPreviewBudget(questions: readonly QuestionData[], paneWidth: number): number {\n\tlet max = MIN_PREVIEW_WIDTH;\n\tfor (const question of questions) {\n\t\tconst rawWidth = previewSourceWidth(question);\n\t\tconst capped = Math.min(rawWidth, paneWidth - PREVIEW_COLUMN_GAP - MIN_LEFT);\n\t\tconst budget = capped + BORDER_HORIZONTAL_OVERHEAD + 2 * BORDER_INNER_PADDING_HORIZONTAL + PREVIEW_PADDING_LEFT;\n\t\tif (budget > max) max = budget;\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab left-column width with slack donation. Combines the label-driven width\n * (from `crossTabMaxLeftWidth`) with the slack donated by narrow previews.\n *\n * Pipeline:\n * 1. `labelDriven` = `crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth)`\n * 2. `previewBudget` = `crossTabPreviewBudget(questions, paneWidth)`\n * 3. `slackDonation` = `paneWidth − GAP − previewBudget`\n * 4. Return `min(max(labelDriven, slackDonation), ceiling)` where `ceiling = paneWidth − GAP − MIN_PREVIEW_WIDTH`\n *\n * Invariants:\n * - Floor: result ≥ MIN_LEFT (labelDriven ≥ MIN_LEFT)\n * - Preview floor: right column ≥ MIN_PREVIEW_WIDTH (ceiling enforces this)\n * - Cross-tab stability: both reductions are tab-independent\n * - Determinism: pure of (questions, itemsByTab, paneWidth)\n *\n * `crossTabMaxLeftWidth` is NOT replaced — it continues to exist as the primitive.\n * `MAX_LEFT_RATIO` caps the label-driven path inside `adaptiveLeftWidth`; donation\n * operates above the cap when preview slack is available.\n */\nexport function crossTabLeftWidthWithDonation(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tquestions: readonly QuestionData[],\n\tpaneWidth: number,\n): number {\n\tconst labelDriven = crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth);\n\t// Compact-content guard: labels at MIN_LEFT fit below the floor, so widening\n\t// the column would only inject dead space between the option list and the\n\t// preview box. Skip donation and preserve the compact intent.\n\tif (labelDriven <= MIN_LEFT) return labelDriven;\n\tconst previewBudget = crossTabPreviewBudget(questions, paneWidth);\n\tconst slackDonation = paneWidth - PREVIEW_COLUMN_GAP - previewBudget;\n\tconst ceiling = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.min(Math.max(labelDriven, slackDonation), Math.max(1, ceiling));\n}\n\n/**\n * Width allocation for side-by-side mode.\n * `adaptiveLeft` is the pre-computed left column width (from `adaptiveLeftWidth`,\n * cross-tab aggregated). The Math.max(1, ...) calls keep both columns >= 1 col on\n * extreme inputs.\n */\nexport function columnWidths(\n\tpaneWidth: number,\n\tadaptiveLeft: number,\n): { leftWidth: number; rightWidth: number; gap: number } {\n\tconst gap = PREVIEW_COLUMN_GAP;\n\tconst leftWidth = Math.min(adaptiveLeft, Math.max(1, paneWidth - gap - 1));\n\tconst rightWidth = Math.max(1, paneWidth - leftWidth - gap);\n\treturn { leftWidth, rightWidth, gap };\n}\n\n/**\n * Returns the widths actually passed to `options.render` and `previewLines` inside\n * `render()`. Stacked uses the full pane width for both; side-by-side splits via\n * `columnWidths`, with the preview column offset by `PREVIEW_PADDING_LEFT`.\n */\nexport function bodyWidths(\n\tpaneWidth: number,\n\tmode: PreviewLayoutMode,\n\tadaptiveLeft: number,\n): { optionsWidth: number; previewWidth: number } {\n\tif (mode === \"stacked\") return { optionsWidth: paneWidth, previewWidth: paneWidth };\n\tconst { leftWidth, rightWidth } = columnWidths(paneWidth, adaptiveLeft);\n\treturn { optionsWidth: leftWidth, previewWidth: Math.max(1, rightWidth - PREVIEW_PADDING_LEFT) };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"preview-layout-decider.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-layout-decider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGhE,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,oFAAoF;AACpF,eAAO,MAAM,QAAQ,KAAK,CAAC;AAC3B,4EAA4E;AAC5E,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC;+EAC+E;AAC/E,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,SAAS,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAExF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,MAAM,GACf,MAAM,CAYR;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,aAAa,CAAC;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EAC9C,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,SAAS,EAAE,MAAM,GACf,MAAM,CASR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAWjE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CASnG;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,6BAA6B,CAC5C,IAAI,EAAE,aAAa,CAAC;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EAC9C,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,SAAS,EAAE,MAAM,GACf,MAAM,CAUR;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GAClB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAKxD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACzB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,iBAAiB,EACvB,YAAY,EAAE,MAAM,GAClB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAIhD","sourcesContent":["import { visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../wrapping-select.ts\";\nimport { BORDER_HORIZONTAL_OVERHEAD, BORDER_INNER_PADDING_HORIZONTAL } from \"./preview-box-renderer.ts\";\n\n/** Min terminal/pane width for the side-by-side layout to engage. */\nexport const PREVIEW_MIN_WIDTH = 100;\n/** Visual gap between options column and preview column in side-by-side. */\nexport const PREVIEW_COLUMN_GAP = 2;\n/** 1 col padding inside the preview column (between gap and `│`). */\nexport const PREVIEW_PADDING_LEFT = 1;\n/** Empty rows between options and preview blocks in stacked (narrow) layout. */\nexport const STACKED_GAP_ROWS = 1;\n\n/** Floor for the adaptive left column width — prevents collapse on short labels. */\nexport const MIN_LEFT = 30;\n/** Ceiling ratio: left column never exceeds this fraction of pane width. */\nexport const MAX_LEFT_RATIO = 0.5;\n/** Floor for the preview column width — prevents right-side collapse on narrow terminals. */\nexport const MIN_PREVIEW_WIDTH = 45;\n/** visibleWidth(\" ✔\") = 2 (space + ✔ codepoint). Reserved on the longest-label measurement\n * so a confirmed row never gets truncated when MIN_LEFT clamps the column. */\nexport const CONFIRMED_OVERHEAD = 2;\n\nexport type PreviewLayoutMode = \"side-by-side\" | \"stacked\";\n\n/**\n * Decide layout mode from terminal + pane widths. Pure of inputs.\n *\n * The terminal-width gate is the AND check from the previous `preview-pane.ts` —\n * lifted here so the decision is computed ONCE per render and threaded explicitly\n * through `previewBlockHeight`. Removes the bug class where `previewBlockHeight`\n * re-derived `sideBySide` from a column width (already < pane width post-split),\n * capping height too short.\n */\nexport function decideLayout(terminalWidth: number, paneWidth: number): PreviewLayoutMode {\n\treturn terminalWidth >= PREVIEW_MIN_WIDTH && paneWidth >= PREVIEW_MIN_WIDTH ? \"side-by-side\" : \"stacked\";\n}\n\n/**\n * Compute the adaptive left column width from option labels.\n * Pure function — deterministic for a given (items, totalForNumbering, paneWidth).\n *\n * Pipeline:\n * 1. Measure: longest visible label width + prefix overhead + confirmed-mark overhead\n * 2. Clamp: floor MIN_LEFT, ceiling paneWidth * MAX_LEFT_RATIO\n * 3. Safety net: never exceed available = paneWidth - GAP - MIN_PREVIEW_WIDTH\n */\nexport function adaptiveLeftWidth(\n\titems: readonly WrappingSelectItem[],\n\ttotalForNumbering: number,\n\tpaneWidth: number,\n): number {\n\tconst prefixW = String(Math.max(1, totalForNumbering)).length + 4; // digits + \"❯ \" + \". \"\n\tconst confirmedOverhead = CONFIRMED_OVERHEAD; // visibleWidth(\" ✔\") = 2 (space + ✔ codepoint)\n\tlet maxLabel = 0;\n\tfor (const item of items) {\n\t\tconst w = visibleWidth(item.label);\n\t\tif (w > maxLabel) maxLabel = w;\n\t}\n\tconst desired = maxLabel + prefixW + confirmedOverhead;\n\tconst ratioCapped = Math.min(desired, Math.floor(paneWidth * MAX_LEFT_RATIO));\n\tconst available = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.max(MIN_LEFT, Math.min(ratioCapped, Math.max(1, available)));\n}\n\n/**\n * Cross-tab maximum left-column width. Aggregates `adaptiveLeftWidth` over every tab\n * and returns the widest result, so the options column stays stable on tab switch.\n *\n * Pure function — `tabs.length` MUST equal `itemsByTab.length`. Multi-select tabs\n * use `items.length` for numbering; single-select tabs add 1 for the chat row slot.\n * Floor is `MIN_LEFT` so an all-empty input still produces a usable column.\n */\nexport function crossTabMaxLeftWidth(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tpaneWidth: number,\n): number {\n\tlet max = MIN_LEFT;\n\tfor (let i = 0; i < tabs.length; i++) {\n\t\tconst items = itemsByTab[i] ?? [];\n\t\tconst totalForNumbering = tabs[i]?.multiSelect ? items.length : items.length + 1;\n\t\tconst tabWidth = adaptiveLeftWidth(items, totalForNumbering, paneWidth);\n\t\tif (tabWidth > max) max = tabWidth;\n\t}\n\treturn max;\n}\n\n/**\n * Source-line probe: measures the widest source line across all options' previews.\n * Returns 0 when no option carries a preview.\n *\n * V1 heuristic — works well for tree/table/heading/list content where source-line\n * width ≈ rendered width. Paragraphs overstate (source width \"wants\" the full pane);\n * the worst case is \"donation does nothing useful\" — fallback to the label-driven path.\n * Upgrade path: replace with a render-width probe that invokes Markdown at a probe width.\n *\n * Pure function — O(total chars) per question; no Markdown invocation, no cache interaction.\n */\nexport function previewSourceWidth(question: QuestionData): number {\n\tlet max = 0;\n\tfor (const option of question.options) {\n\t\tconst text = option.preview;\n\t\tif (!text) continue;\n\t\tfor (const line of text.split(\"\\n\")) {\n\t\t\tconst w = visibleWidth(line);\n\t\t\tif (w > max) max = w;\n\t\t}\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab/cross-option preview budget. Iterates all questions, computes each question's\n * preview appetite via `previewSourceWidth`, adds box + padding overhead (5 cols), and\n * returns the widest result. Floor is `MIN_PREVIEW_WIDTH` so a previewless question still\n * reserves a usable column. Ceiling ensures the left column retains its `MIN_LEFT` floor.\n *\n * Pure function — same cross-tab max pattern as `crossTabMaxLeftWidth`.\n */\nexport function crossTabPreviewBudget(questions: readonly QuestionData[], paneWidth: number): number {\n\tlet max = MIN_PREVIEW_WIDTH;\n\tfor (const question of questions) {\n\t\tconst rawWidth = previewSourceWidth(question);\n\t\tconst capped = Math.min(rawWidth, paneWidth - PREVIEW_COLUMN_GAP - MIN_LEFT);\n\t\tconst budget = capped + BORDER_HORIZONTAL_OVERHEAD + 2 * BORDER_INNER_PADDING_HORIZONTAL + PREVIEW_PADDING_LEFT;\n\t\tif (budget > max) max = budget;\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab left-column width with slack donation. Combines the label-driven width\n * (from `crossTabMaxLeftWidth`) with the slack donated by narrow previews.\n *\n * Pipeline:\n * 1. `labelDriven` = `crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth)`\n * 2. `previewBudget` = `crossTabPreviewBudget(questions, paneWidth)`\n * 3. `slackDonation` = `paneWidth − GAP − previewBudget`\n * 4. Return `min(max(labelDriven, slackDonation), ceiling)` where `ceiling = paneWidth − GAP − MIN_PREVIEW_WIDTH`\n *\n * Invariants:\n * - Floor: result ≥ MIN_LEFT (labelDriven ≥ MIN_LEFT)\n * - Preview floor: right column ≥ MIN_PREVIEW_WIDTH (ceiling enforces this)\n * - Cross-tab stability: both reductions are tab-independent\n * - Determinism: pure of (questions, itemsByTab, paneWidth)\n *\n * `crossTabMaxLeftWidth` is NOT replaced — it continues to exist as the primitive.\n * `MAX_LEFT_RATIO` caps the label-driven path inside `adaptiveLeftWidth`; donation\n * operates above the cap when preview slack is available.\n */\nexport function crossTabLeftWidthWithDonation(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tquestions: readonly QuestionData[],\n\tpaneWidth: number,\n): number {\n\tconst labelDriven = crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth);\n\t// Compact-content guard: labels at MIN_LEFT fit below the floor, so widening\n\t// the column would only inject dead space between the option list and the\n\t// preview box. Skip donation and preserve the compact intent.\n\tif (labelDriven <= MIN_LEFT) return labelDriven;\n\tconst previewBudget = crossTabPreviewBudget(questions, paneWidth);\n\tconst slackDonation = paneWidth - PREVIEW_COLUMN_GAP - previewBudget;\n\tconst ceiling = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.min(Math.max(labelDriven, slackDonation), Math.max(1, ceiling));\n}\n\n/**\n * Width allocation for side-by-side mode.\n * `adaptiveLeft` is the pre-computed left column width (from `adaptiveLeftWidth`,\n * cross-tab aggregated). The Math.max(1, ...) calls keep both columns >= 1 col on\n * extreme inputs.\n */\nexport function columnWidths(\n\tpaneWidth: number,\n\tadaptiveLeft: number,\n): { leftWidth: number; rightWidth: number; gap: number } {\n\tconst gap = PREVIEW_COLUMN_GAP;\n\tconst leftWidth = Math.min(adaptiveLeft, Math.max(1, paneWidth - gap - 1));\n\tconst rightWidth = Math.max(1, paneWidth - leftWidth - gap);\n\treturn { leftWidth, rightWidth, gap };\n}\n\n/**\n * Returns the widths actually passed to `options.render` and `previewLines` inside\n * `render()`. Stacked uses the full pane width for both; side-by-side splits via\n * `columnWidths`, with the preview column offset by `PREVIEW_PADDING_LEFT`.\n */\nexport function bodyWidths(\n\tpaneWidth: number,\n\tmode: PreviewLayoutMode,\n\tadaptiveLeft: number,\n): { optionsWidth: number; previewWidth: number } {\n\tif (mode === \"stacked\") return { optionsWidth: paneWidth, previewWidth: paneWidth };\n\tconst { leftWidth, rightWidth } = columnWidths(paneWidth, adaptiveLeft);\n\treturn { optionsWidth: leftWidth, previewWidth: Math.max(1, rightWidth - PREVIEW_PADDING_LEFT) };\n}\n"]}
|
package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-layout-decider.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-layout-decider.ts"],"names":[],"mappings":"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","sourcesContent":["import { visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.js\";\nimport type { WrappingSelectItem } from \"../wrapping-select.js\";\nimport { BORDER_HORIZONTAL_OVERHEAD, BORDER_INNER_PADDING_HORIZONTAL } from \"./preview-box-renderer.js\";\n\n/** Min terminal/pane width for the side-by-side layout to engage. */\nexport const PREVIEW_MIN_WIDTH = 100;\n/** Visual gap between options column and preview column in side-by-side. */\nexport const PREVIEW_COLUMN_GAP = 2;\n/** 1 col padding inside the preview column (between gap and `│`). */\nexport const PREVIEW_PADDING_LEFT = 1;\n/** Empty rows between options and preview blocks in stacked (narrow) layout. */\nexport const STACKED_GAP_ROWS = 1;\n\n/** Floor for the adaptive left column width — prevents collapse on short labels. */\nexport const MIN_LEFT = 30;\n/** Ceiling ratio: left column never exceeds this fraction of pane width. */\nexport const MAX_LEFT_RATIO = 0.5;\n/** Floor for the preview column width — prevents right-side collapse on narrow terminals. */\nexport const MIN_PREVIEW_WIDTH = 45;\n/** visibleWidth(\" ✔\") = 2 (space + ✔ codepoint). Reserved on the longest-label measurement\n * so a confirmed row never gets truncated when MIN_LEFT clamps the column. */\nexport const CONFIRMED_OVERHEAD = 2;\n\nexport type PreviewLayoutMode = \"side-by-side\" | \"stacked\";\n\n/**\n * Decide layout mode from terminal + pane widths. Pure of inputs.\n *\n * The terminal-width gate is the AND check from the previous `preview-pane.ts` —\n * lifted here so the decision is computed ONCE per render and threaded explicitly\n * through `previewBlockHeight`. Removes the bug class where `previewBlockHeight`\n * re-derived `sideBySide` from a column width (already < pane width post-split),\n * capping height too short.\n */\nexport function decideLayout(terminalWidth: number, paneWidth: number): PreviewLayoutMode {\n\treturn terminalWidth >= PREVIEW_MIN_WIDTH && paneWidth >= PREVIEW_MIN_WIDTH ? \"side-by-side\" : \"stacked\";\n}\n\n/**\n * Compute the adaptive left column width from option labels.\n * Pure function — deterministic for a given (items, totalForNumbering, paneWidth).\n *\n * Pipeline:\n * 1. Measure: longest visible label width + prefix overhead + confirmed-mark overhead\n * 2. Clamp: floor MIN_LEFT, ceiling paneWidth * MAX_LEFT_RATIO\n * 3. Safety net: never exceed available = paneWidth - GAP - MIN_PREVIEW_WIDTH\n */\nexport function adaptiveLeftWidth(\n\titems: readonly WrappingSelectItem[],\n\ttotalForNumbering: number,\n\tpaneWidth: number,\n): number {\n\tconst prefixW = String(Math.max(1, totalForNumbering)).length + 4; // digits + \"❯ \" + \". \"\n\tconst confirmedOverhead = CONFIRMED_OVERHEAD; // visibleWidth(\" ✔\") = 2 (space + ✔ codepoint)\n\tlet maxLabel = 0;\n\tfor (const item of items) {\n\t\tconst w = visibleWidth(item.label);\n\t\tif (w > maxLabel) maxLabel = w;\n\t}\n\tconst desired = maxLabel + prefixW + confirmedOverhead;\n\tconst ratioCapped = Math.min(desired, Math.floor(paneWidth * MAX_LEFT_RATIO));\n\tconst available = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.max(MIN_LEFT, Math.min(ratioCapped, Math.max(1, available)));\n}\n\n/**\n * Cross-tab maximum left-column width. Aggregates `adaptiveLeftWidth` over every tab\n * and returns the widest result, so the options column stays stable on tab switch.\n *\n * Pure function — `tabs.length` MUST equal `itemsByTab.length`. Multi-select tabs\n * use `items.length` for numbering; single-select tabs add 1 for the chat row slot.\n * Floor is `MIN_LEFT` so an all-empty input still produces a usable column.\n */\nexport function crossTabMaxLeftWidth(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tpaneWidth: number,\n): number {\n\tlet max = MIN_LEFT;\n\tfor (let i = 0; i < tabs.length; i++) {\n\t\tconst items = itemsByTab[i] ?? [];\n\t\tconst totalForNumbering = tabs[i]?.multiSelect ? items.length : items.length + 1;\n\t\tconst tabWidth = adaptiveLeftWidth(items, totalForNumbering, paneWidth);\n\t\tif (tabWidth > max) max = tabWidth;\n\t}\n\treturn max;\n}\n\n/**\n * Source-line probe: measures the widest source line across all options' previews.\n * Returns 0 when no option carries a preview.\n *\n * V1 heuristic — works well for tree/table/heading/list content where source-line\n * width ≈ rendered width. Paragraphs overstate (source width \"wants\" the full pane);\n * the worst case is \"donation does nothing useful\" — fallback to the label-driven path.\n * Upgrade path: replace with a render-width probe that invokes Markdown at a probe width.\n *\n * Pure function — O(total chars) per question; no Markdown invocation, no cache interaction.\n */\nexport function previewSourceWidth(question: QuestionData): number {\n\tlet max = 0;\n\tfor (const option of question.options) {\n\t\tconst text = option.preview;\n\t\tif (!text) continue;\n\t\tfor (const line of text.split(\"\\n\")) {\n\t\t\tconst w = visibleWidth(line);\n\t\t\tif (w > max) max = w;\n\t\t}\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab/cross-option preview budget. Iterates all questions, computes each question's\n * preview appetite via `previewSourceWidth`, adds box + padding overhead (5 cols), and\n * returns the widest result. Floor is `MIN_PREVIEW_WIDTH` so a previewless question still\n * reserves a usable column. Ceiling ensures the left column retains its `MIN_LEFT` floor.\n *\n * Pure function — same cross-tab max pattern as `crossTabMaxLeftWidth`.\n */\nexport function crossTabPreviewBudget(questions: readonly QuestionData[], paneWidth: number): number {\n\tlet max = MIN_PREVIEW_WIDTH;\n\tfor (const question of questions) {\n\t\tconst rawWidth = previewSourceWidth(question);\n\t\tconst capped = Math.min(rawWidth, paneWidth - PREVIEW_COLUMN_GAP - MIN_LEFT);\n\t\tconst budget = capped + BORDER_HORIZONTAL_OVERHEAD + 2 * BORDER_INNER_PADDING_HORIZONTAL + PREVIEW_PADDING_LEFT;\n\t\tif (budget > max) max = budget;\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab left-column width with slack donation. Combines the label-driven width\n * (from `crossTabMaxLeftWidth`) with the slack donated by narrow previews.\n *\n * Pipeline:\n * 1. `labelDriven` = `crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth)`\n * 2. `previewBudget` = `crossTabPreviewBudget(questions, paneWidth)`\n * 3. `slackDonation` = `paneWidth − GAP − previewBudget`\n * 4. Return `min(max(labelDriven, slackDonation), ceiling)` where `ceiling = paneWidth − GAP − MIN_PREVIEW_WIDTH`\n *\n * Invariants:\n * - Floor: result ≥ MIN_LEFT (labelDriven ≥ MIN_LEFT)\n * - Preview floor: right column ≥ MIN_PREVIEW_WIDTH (ceiling enforces this)\n * - Cross-tab stability: both reductions are tab-independent\n * - Determinism: pure of (questions, itemsByTab, paneWidth)\n *\n * `crossTabMaxLeftWidth` is NOT replaced — it continues to exist as the primitive.\n * `MAX_LEFT_RATIO` caps the label-driven path inside `adaptiveLeftWidth`; donation\n * operates above the cap when preview slack is available.\n */\nexport function crossTabLeftWidthWithDonation(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tquestions: readonly QuestionData[],\n\tpaneWidth: number,\n): number {\n\tconst labelDriven = crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth);\n\t// Compact-content guard: labels at MIN_LEFT fit below the floor, so widening\n\t// the column would only inject dead space between the option list and the\n\t// preview box. Skip donation and preserve the compact intent.\n\tif (labelDriven <= MIN_LEFT) return labelDriven;\n\tconst previewBudget = crossTabPreviewBudget(questions, paneWidth);\n\tconst slackDonation = paneWidth - PREVIEW_COLUMN_GAP - previewBudget;\n\tconst ceiling = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.min(Math.max(labelDriven, slackDonation), Math.max(1, ceiling));\n}\n\n/**\n * Width allocation for side-by-side mode.\n * `adaptiveLeft` is the pre-computed left column width (from `adaptiveLeftWidth`,\n * cross-tab aggregated). The Math.max(1, ...) calls keep both columns >= 1 col on\n * extreme inputs.\n */\nexport function columnWidths(\n\tpaneWidth: number,\n\tadaptiveLeft: number,\n): { leftWidth: number; rightWidth: number; gap: number } {\n\tconst gap = PREVIEW_COLUMN_GAP;\n\tconst leftWidth = Math.min(adaptiveLeft, Math.max(1, paneWidth - gap - 1));\n\tconst rightWidth = Math.max(1, paneWidth - leftWidth - gap);\n\treturn { leftWidth, rightWidth, gap };\n}\n\n/**\n * Returns the widths actually passed to `options.render` and `previewLines` inside\n * `render()`. Stacked uses the full pane width for both; side-by-side splits via\n * `columnWidths`, with the preview column offset by `PREVIEW_PADDING_LEFT`.\n */\nexport function bodyWidths(\n\tpaneWidth: number,\n\tmode: PreviewLayoutMode,\n\tadaptiveLeft: number,\n): { optionsWidth: number; previewWidth: number } {\n\tif (mode === \"stacked\") return { optionsWidth: paneWidth, previewWidth: paneWidth };\n\tconst { leftWidth, rightWidth } = columnWidths(paneWidth, adaptiveLeft);\n\treturn { optionsWidth: leftWidth, previewWidth: Math.max(1, rightWidth - PREVIEW_PADDING_LEFT) };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"preview-layout-decider.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-layout-decider.ts"],"names":[],"mappings":"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","sourcesContent":["import { visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../wrapping-select.ts\";\nimport { BORDER_HORIZONTAL_OVERHEAD, BORDER_INNER_PADDING_HORIZONTAL } from \"./preview-box-renderer.ts\";\n\n/** Min terminal/pane width for the side-by-side layout to engage. */\nexport const PREVIEW_MIN_WIDTH = 100;\n/** Visual gap between options column and preview column in side-by-side. */\nexport const PREVIEW_COLUMN_GAP = 2;\n/** 1 col padding inside the preview column (between gap and `│`). */\nexport const PREVIEW_PADDING_LEFT = 1;\n/** Empty rows between options and preview blocks in stacked (narrow) layout. */\nexport const STACKED_GAP_ROWS = 1;\n\n/** Floor for the adaptive left column width — prevents collapse on short labels. */\nexport const MIN_LEFT = 30;\n/** Ceiling ratio: left column never exceeds this fraction of pane width. */\nexport const MAX_LEFT_RATIO = 0.5;\n/** Floor for the preview column width — prevents right-side collapse on narrow terminals. */\nexport const MIN_PREVIEW_WIDTH = 45;\n/** visibleWidth(\" ✔\") = 2 (space + ✔ codepoint). Reserved on the longest-label measurement\n * so a confirmed row never gets truncated when MIN_LEFT clamps the column. */\nexport const CONFIRMED_OVERHEAD = 2;\n\nexport type PreviewLayoutMode = \"side-by-side\" | \"stacked\";\n\n/**\n * Decide layout mode from terminal + pane widths. Pure of inputs.\n *\n * The terminal-width gate is the AND check from the previous `preview-pane.ts` —\n * lifted here so the decision is computed ONCE per render and threaded explicitly\n * through `previewBlockHeight`. Removes the bug class where `previewBlockHeight`\n * re-derived `sideBySide` from a column width (already < pane width post-split),\n * capping height too short.\n */\nexport function decideLayout(terminalWidth: number, paneWidth: number): PreviewLayoutMode {\n\treturn terminalWidth >= PREVIEW_MIN_WIDTH && paneWidth >= PREVIEW_MIN_WIDTH ? \"side-by-side\" : \"stacked\";\n}\n\n/**\n * Compute the adaptive left column width from option labels.\n * Pure function — deterministic for a given (items, totalForNumbering, paneWidth).\n *\n * Pipeline:\n * 1. Measure: longest visible label width + prefix overhead + confirmed-mark overhead\n * 2. Clamp: floor MIN_LEFT, ceiling paneWidth * MAX_LEFT_RATIO\n * 3. Safety net: never exceed available = paneWidth - GAP - MIN_PREVIEW_WIDTH\n */\nexport function adaptiveLeftWidth(\n\titems: readonly WrappingSelectItem[],\n\ttotalForNumbering: number,\n\tpaneWidth: number,\n): number {\n\tconst prefixW = String(Math.max(1, totalForNumbering)).length + 4; // digits + \"❯ \" + \". \"\n\tconst confirmedOverhead = CONFIRMED_OVERHEAD; // visibleWidth(\" ✔\") = 2 (space + ✔ codepoint)\n\tlet maxLabel = 0;\n\tfor (const item of items) {\n\t\tconst w = visibleWidth(item.label);\n\t\tif (w > maxLabel) maxLabel = w;\n\t}\n\tconst desired = maxLabel + prefixW + confirmedOverhead;\n\tconst ratioCapped = Math.min(desired, Math.floor(paneWidth * MAX_LEFT_RATIO));\n\tconst available = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.max(MIN_LEFT, Math.min(ratioCapped, Math.max(1, available)));\n}\n\n/**\n * Cross-tab maximum left-column width. Aggregates `adaptiveLeftWidth` over every tab\n * and returns the widest result, so the options column stays stable on tab switch.\n *\n * Pure function — `tabs.length` MUST equal `itemsByTab.length`. Multi-select tabs\n * use `items.length` for numbering; single-select tabs add 1 for the chat row slot.\n * Floor is `MIN_LEFT` so an all-empty input still produces a usable column.\n */\nexport function crossTabMaxLeftWidth(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tpaneWidth: number,\n): number {\n\tlet max = MIN_LEFT;\n\tfor (let i = 0; i < tabs.length; i++) {\n\t\tconst items = itemsByTab[i] ?? [];\n\t\tconst totalForNumbering = tabs[i]?.multiSelect ? items.length : items.length + 1;\n\t\tconst tabWidth = adaptiveLeftWidth(items, totalForNumbering, paneWidth);\n\t\tif (tabWidth > max) max = tabWidth;\n\t}\n\treturn max;\n}\n\n/**\n * Source-line probe: measures the widest source line across all options' previews.\n * Returns 0 when no option carries a preview.\n *\n * V1 heuristic — works well for tree/table/heading/list content where source-line\n * width ≈ rendered width. Paragraphs overstate (source width \"wants\" the full pane);\n * the worst case is \"donation does nothing useful\" — fallback to the label-driven path.\n * Upgrade path: replace with a render-width probe that invokes Markdown at a probe width.\n *\n * Pure function — O(total chars) per question; no Markdown invocation, no cache interaction.\n */\nexport function previewSourceWidth(question: QuestionData): number {\n\tlet max = 0;\n\tfor (const option of question.options) {\n\t\tconst text = option.preview;\n\t\tif (!text) continue;\n\t\tfor (const line of text.split(\"\\n\")) {\n\t\t\tconst w = visibleWidth(line);\n\t\t\tif (w > max) max = w;\n\t\t}\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab/cross-option preview budget. Iterates all questions, computes each question's\n * preview appetite via `previewSourceWidth`, adds box + padding overhead (5 cols), and\n * returns the widest result. Floor is `MIN_PREVIEW_WIDTH` so a previewless question still\n * reserves a usable column. Ceiling ensures the left column retains its `MIN_LEFT` floor.\n *\n * Pure function — same cross-tab max pattern as `crossTabMaxLeftWidth`.\n */\nexport function crossTabPreviewBudget(questions: readonly QuestionData[], paneWidth: number): number {\n\tlet max = MIN_PREVIEW_WIDTH;\n\tfor (const question of questions) {\n\t\tconst rawWidth = previewSourceWidth(question);\n\t\tconst capped = Math.min(rawWidth, paneWidth - PREVIEW_COLUMN_GAP - MIN_LEFT);\n\t\tconst budget = capped + BORDER_HORIZONTAL_OVERHEAD + 2 * BORDER_INNER_PADDING_HORIZONTAL + PREVIEW_PADDING_LEFT;\n\t\tif (budget > max) max = budget;\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab left-column width with slack donation. Combines the label-driven width\n * (from `crossTabMaxLeftWidth`) with the slack donated by narrow previews.\n *\n * Pipeline:\n * 1. `labelDriven` = `crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth)`\n * 2. `previewBudget` = `crossTabPreviewBudget(questions, paneWidth)`\n * 3. `slackDonation` = `paneWidth − GAP − previewBudget`\n * 4. Return `min(max(labelDriven, slackDonation), ceiling)` where `ceiling = paneWidth − GAP − MIN_PREVIEW_WIDTH`\n *\n * Invariants:\n * - Floor: result ≥ MIN_LEFT (labelDriven ≥ MIN_LEFT)\n * - Preview floor: right column ≥ MIN_PREVIEW_WIDTH (ceiling enforces this)\n * - Cross-tab stability: both reductions are tab-independent\n * - Determinism: pure of (questions, itemsByTab, paneWidth)\n *\n * `crossTabMaxLeftWidth` is NOT replaced — it continues to exist as the primitive.\n * `MAX_LEFT_RATIO` caps the label-driven path inside `adaptiveLeftWidth`; donation\n * operates above the cap when preview slack is available.\n */\nexport function crossTabLeftWidthWithDonation(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tquestions: readonly QuestionData[],\n\tpaneWidth: number,\n): number {\n\tconst labelDriven = crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth);\n\t// Compact-content guard: labels at MIN_LEFT fit below the floor, so widening\n\t// the column would only inject dead space between the option list and the\n\t// preview box. Skip donation and preserve the compact intent.\n\tif (labelDriven <= MIN_LEFT) return labelDriven;\n\tconst previewBudget = crossTabPreviewBudget(questions, paneWidth);\n\tconst slackDonation = paneWidth - PREVIEW_COLUMN_GAP - previewBudget;\n\tconst ceiling = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.min(Math.max(labelDriven, slackDonation), Math.max(1, ceiling));\n}\n\n/**\n * Width allocation for side-by-side mode.\n * `adaptiveLeft` is the pre-computed left column width (from `adaptiveLeftWidth`,\n * cross-tab aggregated). The Math.max(1, ...) calls keep both columns >= 1 col on\n * extreme inputs.\n */\nexport function columnWidths(\n\tpaneWidth: number,\n\tadaptiveLeft: number,\n): { leftWidth: number; rightWidth: number; gap: number } {\n\tconst gap = PREVIEW_COLUMN_GAP;\n\tconst leftWidth = Math.min(adaptiveLeft, Math.max(1, paneWidth - gap - 1));\n\tconst rightWidth = Math.max(1, paneWidth - leftWidth - gap);\n\treturn { leftWidth, rightWidth, gap };\n}\n\n/**\n * Returns the widths actually passed to `options.render` and `previewLines` inside\n * `render()`. Stacked uses the full pane width for both; side-by-side splits via\n * `columnWidths`, with the preview column offset by `PREVIEW_PADDING_LEFT`.\n */\nexport function bodyWidths(\n\tpaneWidth: number,\n\tmode: PreviewLayoutMode,\n\tadaptiveLeft: number,\n): { optionsWidth: number; previewWidth: number } {\n\tif (mode === \"stacked\") return { optionsWidth: paneWidth, previewWidth: paneWidth };\n\tconst { leftWidth, rightWidth } = columnWidths(paneWidth, adaptiveLeft);\n\treturn { optionsWidth: leftWidth, previewWidth: Math.max(1, rightWidth - PREVIEW_PADDING_LEFT) };\n}\n"]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type Component } from "@earendil-works/pi-tui";
|
|
2
|
-
import type { QuestionData } from "../../../tool/types.
|
|
3
|
-
import type { StatefulView } from "../../stateful-view.
|
|
4
|
-
import type { OptionListView } from "../option-list-view.
|
|
5
|
-
import type { PreviewBlockRenderer } from "./preview-block-renderer.
|
|
6
|
-
export { MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE, MAX_PREVIEW_HEIGHT_STACKED, NO_PREVIEW_TEXT, NOTES_AFFORDANCE_OVERHEAD, } from "./markdown-content-cache.
|
|
7
|
-
export { NOTES_AFFORDANCE_TEXT, PreviewBlockRenderer } from "./preview-block-renderer.
|
|
8
|
-
export { BORDER_HORIZONTAL_OVERHEAD, BORDER_INNER_PADDING_HORIZONTAL, BORDER_VERTICAL_OVERHEAD, BOX_MIN_CONTENT_WIDTH, renderBorderedBox, stripFenceMarkers, } from "./preview-box-renderer.
|
|
9
|
-
export { PREVIEW_COLUMN_GAP, PREVIEW_MIN_WIDTH, PREVIEW_PADDING_LEFT, STACKED_GAP_ROWS, } from "./preview-layout-decider.
|
|
2
|
+
import type { QuestionData } from "../../../tool/types.ts";
|
|
3
|
+
import type { StatefulView } from "../../stateful-view.ts";
|
|
4
|
+
import type { OptionListView } from "../option-list-view.ts";
|
|
5
|
+
import type { PreviewBlockRenderer } from "./preview-block-renderer.ts";
|
|
6
|
+
export { MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE, MAX_PREVIEW_HEIGHT_STACKED, NO_PREVIEW_TEXT, NOTES_AFFORDANCE_OVERHEAD, } from "./markdown-content-cache.ts";
|
|
7
|
+
export { NOTES_AFFORDANCE_TEXT, PreviewBlockRenderer } from "./preview-block-renderer.ts";
|
|
8
|
+
export { BORDER_HORIZONTAL_OVERHEAD, BORDER_INNER_PADDING_HORIZONTAL, BORDER_VERTICAL_OVERHEAD, BOX_MIN_CONTENT_WIDTH, renderBorderedBox, stripFenceMarkers, } from "./preview-box-renderer.ts";
|
|
9
|
+
export { PREVIEW_COLUMN_GAP, PREVIEW_MIN_WIDTH, PREVIEW_PADDING_LEFT, STACKED_GAP_ROWS, } from "./preview-layout-decider.ts";
|
|
10
10
|
/**
|
|
11
11
|
* Per-tick projection of PreviewPane state. Replaces the prior
|
|
12
12
|
* `setNotesVisible(boolean)` sliver-setter and the sibling reads of
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-pane.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-pane.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAWxE,OAAO,EACN,+BAA+B,EAC/B,0BAA0B,EAC1B,eAAe,EACf,yBAAyB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EACN,0BAA0B,EAC1B,+BAA+B,EAC/B,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,GACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,6BAA6B,CAAC;AAErC;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,YAAY,CAAC;IACvB,gBAAgB,EAAE,MAAM,MAAM,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,oBAAoB,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,WAAY,YAAW,YAAY,CAAC,gBAAgB,CAAC,EAAE,SAAS;IAC5E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuB;IACpD,OAAO,CAAC,KAAK,CAAmB;IAChC;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAErB;IAEF,YAAY,MAAM,EAAE,iBAAiB,EAMpC;IAED,kBAAkB,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAE9D;IAED,OAAO,CAAC,eAAe;IAIvB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAEtC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAGjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAoB9B;IAED,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUnC;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CActC;IAED,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,wBAAwB;CAYhC","sourcesContent":["import { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.
|
|
1
|
+
{"version":3,"file":"preview-pane.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-pane.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAWxE,OAAO,EACN,+BAA+B,EAC/B,0BAA0B,EAC1B,eAAe,EACf,yBAAyB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EACN,0BAA0B,EAC1B,+BAA+B,EAC/B,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,GACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,6BAA6B,CAAC;AAErC;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,YAAY,CAAC;IACvB,gBAAgB,EAAE,MAAM,MAAM,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,oBAAoB,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,WAAY,YAAW,YAAY,CAAC,gBAAgB,CAAC,EAAE,SAAS;IAC5E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuB;IACpD,OAAO,CAAC,KAAK,CAAmB;IAChC;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAErB;IAEF,YAAY,MAAM,EAAE,iBAAiB,EAMpC;IAED,kBAAkB,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAE9D;IAED,OAAO,CAAC,eAAe;IAIvB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAEtC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAGjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAoB9B;IAED,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUnC;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CActC;IAED,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,wBAAwB;CAYhC","sourcesContent":["import { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\nimport type { StatefulView } from \"../../stateful-view.ts\";\nimport type { OptionListView } from \"../option-list-view.ts\";\nimport type { PreviewBlockRenderer } from \"./preview-block-renderer.ts\";\nimport {\n\tbodyWidths,\n\tcolumnWidths,\n\tdecideLayout,\n\tPREVIEW_PADDING_LEFT,\n\ttype PreviewLayoutMode,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.ts\";\n\n// ----- Re-exports for test imports — keep `./preview-pane.js` as the public surface -----\nexport {\n\tMAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,\n\tMAX_PREVIEW_HEIGHT_STACKED,\n\tNO_PREVIEW_TEXT,\n\tNOTES_AFFORDANCE_OVERHEAD,\n} from \"./markdown-content-cache.ts\";\nexport { NOTES_AFFORDANCE_TEXT, PreviewBlockRenderer } from \"./preview-block-renderer.ts\";\nexport {\n\tBORDER_HORIZONTAL_OVERHEAD,\n\tBORDER_INNER_PADDING_HORIZONTAL,\n\tBORDER_VERTICAL_OVERHEAD,\n\tBOX_MIN_CONTENT_WIDTH,\n\trenderBorderedBox,\n\tstripFenceMarkers,\n} from \"./preview-box-renderer.ts\";\nexport {\n\tPREVIEW_COLUMN_GAP,\n\tPREVIEW_MIN_WIDTH,\n\tPREVIEW_PADDING_LEFT,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.ts\";\n\n/**\n * Per-tick projection of PreviewPane state. Replaces the prior\n * `setNotesVisible(boolean)` sliver-setter and the sibling reads of\n * `optionListView.getSelectedIndex()` / `isFocused()`. The pane now reads\n * `selectedIndex` and `focused` from its own props — both derived from\n * canonical state via `selectPreviewPaneProps`. `OptionListView` and\n * `PreviewPane` see the same source of truth without the cross-component\n * live read.\n */\nexport interface PreviewPaneProps {\n\tnotesVisible: boolean;\n\tselectedIndex: number;\n\tfocused: boolean;\n}\n\nexport interface PreviewPaneConfig {\n\tquestion: QuestionData;\n\tgetTerminalWidth: () => number;\n\toptionListView: OptionListView;\n\tpreviewBlock: PreviewBlockRenderer;\n}\n\n/**\n * Thin layout composer. Receives `selectedIndex` / `focused` / `notesVisible`\n * via `setProps` per tick (computed by `selectPreviewPaneProps` from canonical\n * state). Delegates option-side rendering to `OptionListView` (which still\n * owns its render-time state — input buffer, confirmedIndex) and preview-side\n * rendering to `PreviewBlockRenderer` (which owns the markdown cache and\n * bordered-box composition).\n *\n * `naturalHeight` and `maxNaturalHeight` query both children's heights; `render`\n * combines them via `decideLayout` (mode threaded into both calls — never\n * re-derived).\n */\nexport class PreviewPane implements StatefulView<PreviewPaneProps>, Component {\n\tprivate readonly question: QuestionData;\n\tprivate readonly getTerminalWidth: () => number;\n\tprivate readonly optionListView: OptionListView;\n\tprivate readonly previewBlock: PreviewBlockRenderer;\n\tprivate props: PreviewPaneProps;\n\t/**\n\t * Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`\n\t * before any render. Initialized to a throwing sentinel so missing injection is a hard fail\n\t * rather than a silent fallback to a magic constant — render is illegal until injected.\n\t */\n\tprivate globalLeftWidth: (paneWidth: number) => number = () => {\n\t\tthrow new Error(\"PreviewPane.setGlobalLeftWidth must be called before render()\");\n\t};\n\n\tconstructor(config: PreviewPaneConfig) {\n\t\tthis.question = config.question;\n\t\tthis.getTerminalWidth = config.getTerminalWidth;\n\t\tthis.optionListView = config.optionListView;\n\t\tthis.previewBlock = config.previewBlock;\n\t\tthis.props = { notesVisible: false, selectedIndex: 0, focused: false };\n\t}\n\n\tsetGlobalLeftWidth(getter: (paneWidth: number) => number): void {\n\t\tthis.globalLeftWidth = getter;\n\t}\n\n\tprivate getAdaptiveLeft(paneWidth: number): number {\n\t\treturn this.globalLeftWidth(paneWidth);\n\t}\n\n\tsetProps(props: PreviewPaneProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {\n\t\tthis.previewBlock.invalidate();\n\t\tthis.optionListView.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width);\n\t\t// Spec: hide the preview pane entirely when no option carries a `preview`.\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width);\n\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tif (mode === \"side-by-side\") return this.renderSideBySide(width, mode);\n\n\t\t// Stacked: options + blank gap + preview block.\n\t\treturn [\n\t\t\t...this.optionListView.render(width),\n\t\t\t...Array(STACKED_GAP_ROWS).fill(\"\"),\n\t\t\t...this.previewBlock.renderBlock(\n\t\t\t\twidth,\n\t\t\t\tthis.props.selectedIndex,\n\t\t\t\tmode,\n\t\t\t\tthis.props.focused,\n\t\t\t\tthis.props.notesVisible,\n\t\t\t),\n\t\t];\n\t}\n\n\tnaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tconst previewBlockHeight = this.previewBlock.blockHeight(previewWidth, this.props.selectedIndex, mode);\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, previewBlockHeight);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + previewBlockHeight;\n\t}\n\n\tmaxNaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tlet maxPreviewBlock = 0;\n\t\tfor (let i = 0; i < this.question.options.length; i++) {\n\t\t\tconst h = this.previewBlock.blockHeight(previewWidth, i, mode);\n\t\t\tif (h > maxPreviewBlock) maxPreviewBlock = h;\n\t\t}\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, maxPreviewBlock);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + maxPreviewBlock;\n\t}\n\n\tprivate renderSideBySide(width: number, mode: PreviewLayoutMode): string[] {\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { leftWidth, rightWidth, gap } = columnWidths(width, adaptiveLeft);\n\t\tconst leftLines = this.optionListView.render(leftWidth);\n\t\tconst rightLines = this.renderPaddedPreviewLines(rightWidth, mode);\n\t\tconst rows = Math.max(leftLines.length, rightLines.length);\n\t\tconst gapStr = \" \".repeat(gap);\n\t\tconst out: string[] = [];\n\t\tfor (let i = 0; i < rows; i++) {\n\t\t\tconst leftRaw = leftLines[i] ?? \"\";\n\t\t\tconst rightRaw = rightLines[i] ?? \"\";\n\t\t\tconst leftClamped = truncateToWidth(leftRaw, leftWidth, \"\");\n\t\t\tconst leftPad = \" \".repeat(Math.max(0, leftWidth - visibleWidth(leftClamped)));\n\t\t\tconst joined = `${leftClamped}${leftPad}${gapStr}${rightRaw}`;\n\t\t\tout.push(truncateToWidth(joined, width, \"\"));\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate renderPaddedPreviewLines(colWidth: number, mode: PreviewLayoutMode): string[] {\n\t\tconst inner = Math.max(1, colWidth - PREVIEW_PADDING_LEFT);\n\t\tconst contentLines = this.previewBlock.renderBlock(\n\t\t\tinner,\n\t\t\tthis.props.selectedIndex,\n\t\t\tmode,\n\t\t\tthis.props.focused,\n\t\t\tthis.props.notesVisible,\n\t\t);\n\t\tconst pad = \" \".repeat(PREVIEW_PADDING_LEFT);\n\t\treturn contentLines.map((l) => (l === \"\" ? \"\" : `${pad}${l}`));\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-pane.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-pane.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.js\";\nimport type { StatefulView } from \"../../stateful-view.js\";\nimport type { OptionListView } from \"../option-list-view.js\";\nimport type { PreviewBlockRenderer } from \"./preview-block-renderer.js\";\nimport {\n\tbodyWidths,\n\tcolumnWidths,\n\tdecideLayout,\n\tPREVIEW_PADDING_LEFT,\n\ttype PreviewLayoutMode,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.js\";\n\n// ----- Re-exports for test imports — keep `./preview-pane.js` as the public surface -----\nexport {\n\tMAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,\n\tMAX_PREVIEW_HEIGHT_STACKED,\n\tNO_PREVIEW_TEXT,\n\tNOTES_AFFORDANCE_OVERHEAD,\n} from \"./markdown-content-cache.js\";\nexport { NOTES_AFFORDANCE_TEXT, PreviewBlockRenderer } from \"./preview-block-renderer.js\";\nexport {\n\tBORDER_HORIZONTAL_OVERHEAD,\n\tBORDER_INNER_PADDING_HORIZONTAL,\n\tBORDER_VERTICAL_OVERHEAD,\n\tBOX_MIN_CONTENT_WIDTH,\n\trenderBorderedBox,\n\tstripFenceMarkers,\n} from \"./preview-box-renderer.js\";\nexport {\n\tPREVIEW_COLUMN_GAP,\n\tPREVIEW_MIN_WIDTH,\n\tPREVIEW_PADDING_LEFT,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.js\";\n\n/**\n * Per-tick projection of PreviewPane state. Replaces the prior\n * `setNotesVisible(boolean)` sliver-setter and the sibling reads of\n * `optionListView.getSelectedIndex()` / `isFocused()`. The pane now reads\n * `selectedIndex` and `focused` from its own props — both derived from\n * canonical state via `selectPreviewPaneProps`. `OptionListView` and\n * `PreviewPane` see the same source of truth without the cross-component\n * live read.\n */\nexport interface PreviewPaneProps {\n\tnotesVisible: boolean;\n\tselectedIndex: number;\n\tfocused: boolean;\n}\n\nexport interface PreviewPaneConfig {\n\tquestion: QuestionData;\n\tgetTerminalWidth: () => number;\n\toptionListView: OptionListView;\n\tpreviewBlock: PreviewBlockRenderer;\n}\n\n/**\n * Thin layout composer. Receives `selectedIndex` / `focused` / `notesVisible`\n * via `setProps` per tick (computed by `selectPreviewPaneProps` from canonical\n * state). Delegates option-side rendering to `OptionListView` (which still\n * owns its render-time state — input buffer, confirmedIndex) and preview-side\n * rendering to `PreviewBlockRenderer` (which owns the markdown cache and\n * bordered-box composition).\n *\n * `naturalHeight` and `maxNaturalHeight` query both children's heights; `render`\n * combines them via `decideLayout` (mode threaded into both calls — never\n * re-derived).\n */\nexport class PreviewPane implements StatefulView<PreviewPaneProps>, Component {\n\tprivate readonly question: QuestionData;\n\tprivate readonly getTerminalWidth: () => number;\n\tprivate readonly optionListView: OptionListView;\n\tprivate readonly previewBlock: PreviewBlockRenderer;\n\tprivate props: PreviewPaneProps;\n\t/**\n\t * Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`\n\t * before any render. Initialized to a throwing sentinel so missing injection is a hard fail\n\t * rather than a silent fallback to a magic constant — render is illegal until injected.\n\t */\n\tprivate globalLeftWidth: (paneWidth: number) => number = () => {\n\t\tthrow new Error(\"PreviewPane.setGlobalLeftWidth must be called before render()\");\n\t};\n\n\tconstructor(config: PreviewPaneConfig) {\n\t\tthis.question = config.question;\n\t\tthis.getTerminalWidth = config.getTerminalWidth;\n\t\tthis.optionListView = config.optionListView;\n\t\tthis.previewBlock = config.previewBlock;\n\t\tthis.props = { notesVisible: false, selectedIndex: 0, focused: false };\n\t}\n\n\tsetGlobalLeftWidth(getter: (paneWidth: number) => number): void {\n\t\tthis.globalLeftWidth = getter;\n\t}\n\n\tprivate getAdaptiveLeft(paneWidth: number): number {\n\t\treturn this.globalLeftWidth(paneWidth);\n\t}\n\n\tsetProps(props: PreviewPaneProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {\n\t\tthis.previewBlock.invalidate();\n\t\tthis.optionListView.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width);\n\t\t// Spec: hide the preview pane entirely when no option carries a `preview`.\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width);\n\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tif (mode === \"side-by-side\") return this.renderSideBySide(width, mode);\n\n\t\t// Stacked: options + blank gap + preview block.\n\t\treturn [\n\t\t\t...this.optionListView.render(width),\n\t\t\t...Array(STACKED_GAP_ROWS).fill(\"\"),\n\t\t\t...this.previewBlock.renderBlock(\n\t\t\t\twidth,\n\t\t\t\tthis.props.selectedIndex,\n\t\t\t\tmode,\n\t\t\t\tthis.props.focused,\n\t\t\t\tthis.props.notesVisible,\n\t\t\t),\n\t\t];\n\t}\n\n\tnaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tconst previewBlockHeight = this.previewBlock.blockHeight(previewWidth, this.props.selectedIndex, mode);\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, previewBlockHeight);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + previewBlockHeight;\n\t}\n\n\tmaxNaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tlet maxPreviewBlock = 0;\n\t\tfor (let i = 0; i < this.question.options.length; i++) {\n\t\t\tconst h = this.previewBlock.blockHeight(previewWidth, i, mode);\n\t\t\tif (h > maxPreviewBlock) maxPreviewBlock = h;\n\t\t}\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, maxPreviewBlock);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + maxPreviewBlock;\n\t}\n\n\tprivate renderSideBySide(width: number, mode: PreviewLayoutMode): string[] {\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { leftWidth, rightWidth, gap } = columnWidths(width, adaptiveLeft);\n\t\tconst leftLines = this.optionListView.render(leftWidth);\n\t\tconst rightLines = this.renderPaddedPreviewLines(rightWidth, mode);\n\t\tconst rows = Math.max(leftLines.length, rightLines.length);\n\t\tconst gapStr = \" \".repeat(gap);\n\t\tconst out: string[] = [];\n\t\tfor (let i = 0; i < rows; i++) {\n\t\t\tconst leftRaw = leftLines[i] ?? \"\";\n\t\t\tconst rightRaw = rightLines[i] ?? \"\";\n\t\t\tconst leftClamped = truncateToWidth(leftRaw, leftWidth, \"\");\n\t\t\tconst leftPad = \" \".repeat(Math.max(0, leftWidth - visibleWidth(leftClamped)));\n\t\t\tconst joined = `${leftClamped}${leftPad}${gapStr}${rightRaw}`;\n\t\t\tout.push(truncateToWidth(joined, width, \"\"));\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate renderPaddedPreviewLines(colWidth: number, mode: PreviewLayoutMode): string[] {\n\t\tconst inner = Math.max(1, colWidth - PREVIEW_PADDING_LEFT);\n\t\tconst contentLines = this.previewBlock.renderBlock(\n\t\t\tinner,\n\t\t\tthis.props.selectedIndex,\n\t\t\tmode,\n\t\t\tthis.props.focused,\n\t\t\tthis.props.notesVisible,\n\t\t);\n\t\tconst pad = \" \".repeat(PREVIEW_PADDING_LEFT);\n\t\treturn contentLines.map((l) => (l === \"\" ? \"\" : `${pad}${l}`));\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"preview-pane.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-pane.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\nimport type { StatefulView } from \"../../stateful-view.ts\";\nimport type { OptionListView } from \"../option-list-view.ts\";\nimport type { PreviewBlockRenderer } from \"./preview-block-renderer.ts\";\nimport {\n\tbodyWidths,\n\tcolumnWidths,\n\tdecideLayout,\n\tPREVIEW_PADDING_LEFT,\n\ttype PreviewLayoutMode,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.ts\";\n\n// ----- Re-exports for test imports — keep `./preview-pane.js` as the public surface -----\nexport {\n\tMAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,\n\tMAX_PREVIEW_HEIGHT_STACKED,\n\tNO_PREVIEW_TEXT,\n\tNOTES_AFFORDANCE_OVERHEAD,\n} from \"./markdown-content-cache.ts\";\nexport { NOTES_AFFORDANCE_TEXT, PreviewBlockRenderer } from \"./preview-block-renderer.ts\";\nexport {\n\tBORDER_HORIZONTAL_OVERHEAD,\n\tBORDER_INNER_PADDING_HORIZONTAL,\n\tBORDER_VERTICAL_OVERHEAD,\n\tBOX_MIN_CONTENT_WIDTH,\n\trenderBorderedBox,\n\tstripFenceMarkers,\n} from \"./preview-box-renderer.ts\";\nexport {\n\tPREVIEW_COLUMN_GAP,\n\tPREVIEW_MIN_WIDTH,\n\tPREVIEW_PADDING_LEFT,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.ts\";\n\n/**\n * Per-tick projection of PreviewPane state. Replaces the prior\n * `setNotesVisible(boolean)` sliver-setter and the sibling reads of\n * `optionListView.getSelectedIndex()` / `isFocused()`. The pane now reads\n * `selectedIndex` and `focused` from its own props — both derived from\n * canonical state via `selectPreviewPaneProps`. `OptionListView` and\n * `PreviewPane` see the same source of truth without the cross-component\n * live read.\n */\nexport interface PreviewPaneProps {\n\tnotesVisible: boolean;\n\tselectedIndex: number;\n\tfocused: boolean;\n}\n\nexport interface PreviewPaneConfig {\n\tquestion: QuestionData;\n\tgetTerminalWidth: () => number;\n\toptionListView: OptionListView;\n\tpreviewBlock: PreviewBlockRenderer;\n}\n\n/**\n * Thin layout composer. Receives `selectedIndex` / `focused` / `notesVisible`\n * via `setProps` per tick (computed by `selectPreviewPaneProps` from canonical\n * state). Delegates option-side rendering to `OptionListView` (which still\n * owns its render-time state — input buffer, confirmedIndex) and preview-side\n * rendering to `PreviewBlockRenderer` (which owns the markdown cache and\n * bordered-box composition).\n *\n * `naturalHeight` and `maxNaturalHeight` query both children's heights; `render`\n * combines them via `decideLayout` (mode threaded into both calls — never\n * re-derived).\n */\nexport class PreviewPane implements StatefulView<PreviewPaneProps>, Component {\n\tprivate readonly question: QuestionData;\n\tprivate readonly getTerminalWidth: () => number;\n\tprivate readonly optionListView: OptionListView;\n\tprivate readonly previewBlock: PreviewBlockRenderer;\n\tprivate props: PreviewPaneProps;\n\t/**\n\t * Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`\n\t * before any render. Initialized to a throwing sentinel so missing injection is a hard fail\n\t * rather than a silent fallback to a magic constant — render is illegal until injected.\n\t */\n\tprivate globalLeftWidth: (paneWidth: number) => number = () => {\n\t\tthrow new Error(\"PreviewPane.setGlobalLeftWidth must be called before render()\");\n\t};\n\n\tconstructor(config: PreviewPaneConfig) {\n\t\tthis.question = config.question;\n\t\tthis.getTerminalWidth = config.getTerminalWidth;\n\t\tthis.optionListView = config.optionListView;\n\t\tthis.previewBlock = config.previewBlock;\n\t\tthis.props = { notesVisible: false, selectedIndex: 0, focused: false };\n\t}\n\n\tsetGlobalLeftWidth(getter: (paneWidth: number) => number): void {\n\t\tthis.globalLeftWidth = getter;\n\t}\n\n\tprivate getAdaptiveLeft(paneWidth: number): number {\n\t\treturn this.globalLeftWidth(paneWidth);\n\t}\n\n\tsetProps(props: PreviewPaneProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {\n\t\tthis.previewBlock.invalidate();\n\t\tthis.optionListView.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width);\n\t\t// Spec: hide the preview pane entirely when no option carries a `preview`.\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width);\n\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tif (mode === \"side-by-side\") return this.renderSideBySide(width, mode);\n\n\t\t// Stacked: options + blank gap + preview block.\n\t\treturn [\n\t\t\t...this.optionListView.render(width),\n\t\t\t...Array(STACKED_GAP_ROWS).fill(\"\"),\n\t\t\t...this.previewBlock.renderBlock(\n\t\t\t\twidth,\n\t\t\t\tthis.props.selectedIndex,\n\t\t\t\tmode,\n\t\t\t\tthis.props.focused,\n\t\t\t\tthis.props.notesVisible,\n\t\t\t),\n\t\t];\n\t}\n\n\tnaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tconst previewBlockHeight = this.previewBlock.blockHeight(previewWidth, this.props.selectedIndex, mode);\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, previewBlockHeight);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + previewBlockHeight;\n\t}\n\n\tmaxNaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tlet maxPreviewBlock = 0;\n\t\tfor (let i = 0; i < this.question.options.length; i++) {\n\t\t\tconst h = this.previewBlock.blockHeight(previewWidth, i, mode);\n\t\t\tif (h > maxPreviewBlock) maxPreviewBlock = h;\n\t\t}\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, maxPreviewBlock);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + maxPreviewBlock;\n\t}\n\n\tprivate renderSideBySide(width: number, mode: PreviewLayoutMode): string[] {\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { leftWidth, rightWidth, gap } = columnWidths(width, adaptiveLeft);\n\t\tconst leftLines = this.optionListView.render(leftWidth);\n\t\tconst rightLines = this.renderPaddedPreviewLines(rightWidth, mode);\n\t\tconst rows = Math.max(leftLines.length, rightLines.length);\n\t\tconst gapStr = \" \".repeat(gap);\n\t\tconst out: string[] = [];\n\t\tfor (let i = 0; i < rows; i++) {\n\t\t\tconst leftRaw = leftLines[i] ?? \"\";\n\t\t\tconst rightRaw = rightLines[i] ?? \"\";\n\t\t\tconst leftClamped = truncateToWidth(leftRaw, leftWidth, \"\");\n\t\t\tconst leftPad = \" \".repeat(Math.max(0, leftWidth - visibleWidth(leftClamped)));\n\t\t\tconst joined = `${leftClamped}${leftPad}${gapStr}${rightRaw}`;\n\t\t\tout.push(truncateToWidth(joined, width, \"\"));\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate renderPaddedPreviewLines(colWidth: number, mode: PreviewLayoutMode): string[] {\n\t\tconst inner = Math.max(1, colWidth - PREVIEW_PADDING_LEFT);\n\t\tconst contentLines = this.previewBlock.renderBlock(\n\t\t\tinner,\n\t\t\tthis.props.selectedIndex,\n\t\t\tmode,\n\t\t\tthis.props.focused,\n\t\t\tthis.props.notesVisible,\n\t\t);\n\t\tconst pad = \" \".repeat(PREVIEW_PADDING_LEFT);\n\t\treturn contentLines.map((l) => (l === \"\" ? \"\" : `${pad}${l}`));\n\t}\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Theme } from "../../../../../modes/interactive/theme/theme.
|
|
2
|
-
import type { StatefulView } from "../stateful-view.
|
|
1
|
+
import type { Theme } from "../../../../../modes/interactive/theme/theme.ts";
|
|
2
|
+
import type { StatefulView } from "../stateful-view.ts";
|
|
3
3
|
export declare const SUBMIT_LABEL = "Submit answers";
|
|
4
4
|
export declare const CANCEL_LABEL = "Cancel";
|
|
5
5
|
/**
|
|
@@ -25,8 +25,8 @@ export interface SubmitPickerProps {
|
|
|
25
25
|
* re-rendering.
|
|
26
26
|
*/
|
|
27
27
|
export declare class SubmitPicker implements StatefulView<SubmitPickerProps> {
|
|
28
|
-
private readonly theme;
|
|
29
28
|
private props;
|
|
29
|
+
private readonly theme;
|
|
30
30
|
constructor(theme: Theme);
|
|
31
31
|
setProps(props: SubmitPickerProps): void;
|
|
32
32
|
handleInput(_data: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"submit-picker.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/submit-picker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMxD,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAC7C,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,4EAA4E;IAC5E,IAAI,EAAE,aAAa,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACzC;AAED;;;;;;;;;;GAUG;AACH,qBAAa,YAAa,YAAW,YAAY,CAAC,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"submit-picker.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/submit-picker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMxD,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAC7C,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,4EAA4E;IAC5E,IAAI,EAAE,aAAa,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACzC;AAED;;;;;;;;;;GAUG;AACH,qBAAa,YAAa,YAAW,YAAY,CAAC,iBAAiB,CAAC;IACnE,OAAO,CAAC,KAAK,CAAoB;IAEjC,iBAAyB,KAAK,CAAQ;IAEtC,YAAY,KAAK,EAAE,KAAK,EAGvB;IAED,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAEvC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAW9B;CACD","sourcesContent":["import type { Theme } from \"../../../../../modes/interactive/theme/theme.ts\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\nconst ACTIVE_POINTER = \"❯ \";\nconst INACTIVE_POINTER = \" \";\nconst NUMBER_SEPARATOR = \". \";\n\nexport const SUBMIT_LABEL = \"Submit answers\";\nexport const CANCEL_LABEL = \"Cancel\";\n\n/**\n * Per-tick projection of SubmitPicker state. The picker is a fixed 2-row\n * structure (Submit / Cancel) — labels are static, only the active marker\n * varies per tick. `selectSubmitPickerProps` precomputes `active` per row.\n */\nexport interface SubmitPickerProps {\n\t/** Per-row active flag. Length always 2: row 0 = Submit, row 1 = Cancel. */\n\trows: ReadonlyArray<{ active: boolean }>;\n}\n\n/**\n * Static 2-row picker rendered on the Submit Tab. Row 0 = \"Submit answers\", Row 1 = \"Cancel\".\n *\n * - Active pointer (❯) follows `props.rows[i].active` per tick.\n * - Both rows render in normal style at all times — D1 (revised) allows partial submission,\n * so Submit is never dimmed or visually marked as unselectable. The warning header in\n * `buildSubmitContainer` is the sole signal of incompleteness.\n * - `naturalHeight(width)` is state-INDEPENDENT and returns a constant 2, so the\n * chrome-mirror layout in `buildSubmitContainer` can subtract a fixed 2 lines without\n * re-rendering.\n */\nexport class SubmitPicker implements StatefulView<SubmitPickerProps> {\n\tprivate props: SubmitPickerProps;\n\n\tdeclare private readonly theme: Theme;\n\n\tconstructor(theme: Theme) {\n\t\tthis.theme = theme;\n\t\tthis.props = { rows: [{ active: false }, { active: false }] };\n\t}\n\n\tsetProps(props: SubmitPickerProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\tnaturalHeight(_width: number): number {\n\t\treturn 2;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tfor (let i = 0; i < 2; i++) {\n\t\t\tconst text = i === 0 ? SUBMIT_LABEL : CANCEL_LABEL;\n\t\t\tconst active = this.props.rows[i]?.active ?? false;\n\t\t\tconst pointer = active ? ACTIVE_POINTER : INACTIVE_POINTER;\n\t\t\tconst number = `${i + 1}${NUMBER_SEPARATOR}`;\n\t\t\tconst label = active ? this.theme.fg(\"accent\", this.theme.bold(text)) : this.theme.fg(\"text\", text);\n\t\t\tlines.push(truncateToWidth(`${pointer}${number}${label}`, width, \"\"));\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"submit-picker.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/submit-picker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAC7C,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAYrC;;;;;;;;;;GAUG;AACH,MAAM,OAAO,YAAY;
|
|
1
|
+
{"version":3,"file":"submit-picker.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/submit-picker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAC7C,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAYrC;;;;;;;;;;GAUG;AACH,MAAM,OAAO,YAAY;IAKxB,YAAY,KAAY;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAC/D,CAAC;IAED,QAAQ,CAAC,KAAwB;QAChC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU,KAAU,CAAC;IAErB,aAAa,CAAC,MAAc;QAC3B,OAAO,CAAC,CAAC;IACV,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,KAAK,CAAC;YACnD,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,gBAAgB,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACpG,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC;CACD","sourcesContent":["import type { Theme } from \"../../../../../modes/interactive/theme/theme.ts\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\nconst ACTIVE_POINTER = \"❯ \";\nconst INACTIVE_POINTER = \" \";\nconst NUMBER_SEPARATOR = \". \";\n\nexport const SUBMIT_LABEL = \"Submit answers\";\nexport const CANCEL_LABEL = \"Cancel\";\n\n/**\n * Per-tick projection of SubmitPicker state. The picker is a fixed 2-row\n * structure (Submit / Cancel) — labels are static, only the active marker\n * varies per tick. `selectSubmitPickerProps` precomputes `active` per row.\n */\nexport interface SubmitPickerProps {\n\t/** Per-row active flag. Length always 2: row 0 = Submit, row 1 = Cancel. */\n\trows: ReadonlyArray<{ active: boolean }>;\n}\n\n/**\n * Static 2-row picker rendered on the Submit Tab. Row 0 = \"Submit answers\", Row 1 = \"Cancel\".\n *\n * - Active pointer (❯) follows `props.rows[i].active` per tick.\n * - Both rows render in normal style at all times — D1 (revised) allows partial submission,\n * so Submit is never dimmed or visually marked as unselectable. The warning header in\n * `buildSubmitContainer` is the sole signal of incompleteness.\n * - `naturalHeight(width)` is state-INDEPENDENT and returns a constant 2, so the\n * chrome-mirror layout in `buildSubmitContainer` can subtract a fixed 2 lines without\n * re-rendering.\n */\nexport class SubmitPicker implements StatefulView<SubmitPickerProps> {\n\tprivate props: SubmitPickerProps;\n\n\tdeclare private readonly theme: Theme;\n\n\tconstructor(theme: Theme) {\n\t\tthis.theme = theme;\n\t\tthis.props = { rows: [{ active: false }, { active: false }] };\n\t}\n\n\tsetProps(props: SubmitPickerProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\tnaturalHeight(_width: number): number {\n\t\treturn 2;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tfor (let i = 0; i < 2; i++) {\n\t\t\tconst text = i === 0 ? SUBMIT_LABEL : CANCEL_LABEL;\n\t\t\tconst active = this.props.rows[i]?.active ?? false;\n\t\t\tconst pointer = active ? ACTIVE_POINTER : INACTIVE_POINTER;\n\t\t\tconst number = `${i + 1}${NUMBER_SEPARATOR}`;\n\t\t\tconst label = active ? this.theme.fg(\"accent\", this.theme.bold(text)) : this.theme.fg(\"text\", text);\n\t\t\tlines.push(truncateToWidth(`${pointer}${number}${label}`, width, \"\"));\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Theme } from "../../../../../modes/interactive/theme/theme.
|
|
2
|
-
import type { StatefulView } from "../stateful-view.
|
|
1
|
+
import type { Theme } from "../../../../../modes/interactive/theme/theme.ts";
|
|
2
|
+
import type { StatefulView } from "../stateful-view.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Per-tick projection of TabBar state. The selector
|
|
5
5
|
* (`selectTabBarProps`) hoists every render-time derivation
|
|
@@ -21,8 +21,8 @@ export interface TabBarProps {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
export declare class TabBar implements StatefulView<TabBarProps> {
|
|
24
|
-
private readonly theme;
|
|
25
24
|
private props;
|
|
25
|
+
private readonly theme;
|
|
26
26
|
constructor(theme: Theme);
|
|
27
27
|
setProps(props: TabBarProps): void;
|
|
28
28
|
handleInput(_data: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-bar.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/tab-bar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,iDAAiD;IACjD,IAAI,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC3E,2EAA2E;IAC3E,MAAM,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;CAClD;AAED,qBAAa,MAAO,YAAW,YAAY,CAAC,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"tab-bar.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/tab-bar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,iDAAiD;IACjD,IAAI,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC3E,2EAA2E;IAC3E,MAAM,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;CAClD;AAED,qBAAa,MAAO,YAAW,YAAY,CAAC,WAAW,CAAC;IACvD,OAAO,CAAC,KAAK,CAAc;IAE3B,iBAAyB,KAAK,CAAQ;IAEtC,YAAY,KAAK,EAAE,KAAK,EAGvB;IAED,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEjC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAsB9B;CACD","sourcesContent":["import type { Theme } from \"../../../../../modes/interactive/theme/theme.ts\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\n/**\n * Per-tick projection of TabBar state. The selector\n * (`selectTabBarProps`) hoists every render-time derivation\n * (`allAnswered`, `answered`, `isActive`, `submitActive`) into props so\n * `render()` is pure styling. Replaces the prior `setConfig(TabBarConfig)`\n * snowflake and the inline `+ 1` magic at `props-adapter.ts:127`.\n */\nexport interface TabBarProps {\n\t/** One per author-defined question, in order. */\n\ttabs: ReadonlyArray<{ label: string; answered: boolean; active: boolean }>;\n\t/** Submit-tab state. `allAnswered` drives the success/dim color picker. */\n\tsubmit: { active: boolean; allAnswered: boolean };\n}\n\nexport class TabBar implements StatefulView<TabBarProps> {\n\tprivate props: TabBarProps;\n\n\tdeclare private readonly theme: Theme;\n\n\tconstructor(theme: Theme) {\n\t\tthis.theme = theme;\n\t\tthis.props = { tabs: [], submit: { active: false, allAnswered: false } };\n\t}\n\n\tsetProps(props: TabBarProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst pieces: string[] = [\" ← \"];\n\n\t\tfor (const tab of this.props.tabs) {\n\t\t\tconst box = tab.answered ? \"■\" : \"□\";\n\t\t\tconst rawSeg = ` ${box} ${tab.label} `;\n\t\t\tconst styled = tab.active\n\t\t\t\t? this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", rawSeg))\n\t\t\t\t: this.theme.fg(tab.answered ? \"success\" : \"muted\", rawSeg);\n\t\t\tpieces.push(styled);\n\t\t\tpieces.push(\" \");\n\t\t}\n\n\t\tconst submitText = \" ✓ Submit \";\n\t\tconst submitStyled = this.props.submit.active\n\t\t\t? this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", submitText))\n\t\t\t: this.theme.fg(this.props.submit.allAnswered ? \"success\" : \"dim\", submitText);\n\t\tpieces.push(submitStyled);\n\t\tpieces.push(\" →\");\n\n\t\tconst tabLine = truncateToWidth(pieces.join(\"\"), width, \"\");\n\t\treturn [tabLine, \"\"];\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-bar.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/tab-bar.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAiBzD,MAAM,OAAO,MAAM;
|
|
1
|
+
{"version":3,"file":"tab-bar.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/tab-bar.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAiBzD,MAAM,OAAO,MAAM;IAKlB,YAAY,KAAY;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,CAAC;IAC1E,CAAC;IAED,QAAQ,CAAC,KAAkB;QAC1B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU,KAAU,CAAC;IAErB,MAAM,CAAC,KAAa;QACnB,MAAM,MAAM,GAAa,CAAC,KAAK,CAAC,CAAC;QAEjC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC;YACvC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM;gBACxB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAC5D,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,UAAU,GAAG,YAAY,CAAC;QAChC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM;YAC5C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YAChE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAChF,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAElB,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5D,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACtB,CAAC;CACD","sourcesContent":["import type { Theme } from \"../../../../../modes/interactive/theme/theme.ts\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\n/**\n * Per-tick projection of TabBar state. The selector\n * (`selectTabBarProps`) hoists every render-time derivation\n * (`allAnswered`, `answered`, `isActive`, `submitActive`) into props so\n * `render()` is pure styling. Replaces the prior `setConfig(TabBarConfig)`\n * snowflake and the inline `+ 1` magic at `props-adapter.ts:127`.\n */\nexport interface TabBarProps {\n\t/** One per author-defined question, in order. */\n\ttabs: ReadonlyArray<{ label: string; answered: boolean; active: boolean }>;\n\t/** Submit-tab state. `allAnswered` drives the success/dim color picker. */\n\tsubmit: { active: boolean; allAnswered: boolean };\n}\n\nexport class TabBar implements StatefulView<TabBarProps> {\n\tprivate props: TabBarProps;\n\n\tdeclare private readonly theme: Theme;\n\n\tconstructor(theme: Theme) {\n\t\tthis.theme = theme;\n\t\tthis.props = { tabs: [], submit: { active: false, allAnswered: false } };\n\t}\n\n\tsetProps(props: TabBarProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst pieces: string[] = [\" ← \"];\n\n\t\tfor (const tab of this.props.tabs) {\n\t\t\tconst box = tab.answered ? \"■\" : \"□\";\n\t\t\tconst rawSeg = ` ${box} ${tab.label} `;\n\t\t\tconst styled = tab.active\n\t\t\t\t? this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", rawSeg))\n\t\t\t\t: this.theme.fg(tab.answered ? \"success\" : \"muted\", rawSeg);\n\t\t\tpieces.push(styled);\n\t\t\tpieces.push(\" \");\n\t\t}\n\n\t\tconst submitText = \" ✓ Submit \";\n\t\tconst submitStyled = this.props.submit.active\n\t\t\t? this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", submitText))\n\t\t\t: this.theme.fg(this.props.submit.allAnswered ? \"success\" : \"dim\", submitText);\n\t\tpieces.push(submitStyled);\n\t\tpieces.push(\" →\");\n\n\t\tconst tabLine = truncateToWidth(pieces.join(\"\"), width, \"\");\n\t\treturn [tabLine, \"\"];\n\t}\n}\n"]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { Theme } from "../../../../modes/interactive/theme/theme.
|
|
1
|
+
import type { Theme } from "../../../../modes/interactive/theme/theme.ts";
|
|
2
2
|
import { type Component, type Input } from "@earendil-works/pi-tui";
|
|
3
|
-
import type { QuestionnaireState } from "../state/state.
|
|
4
|
-
import type { QuestionData } from "../tool/types.
|
|
5
|
-
import type { ChatRowView } from "./components/chat-row-view.
|
|
6
|
-
import type { PreviewPaneProps } from "./components/preview/preview-pane.
|
|
7
|
-
import type { TabBar } from "./components/tab-bar.
|
|
8
|
-
import type { StatefulView } from "./stateful-view.
|
|
9
|
-
import type { TabComponents } from "./tab-components.
|
|
3
|
+
import type { QuestionnaireState } from "../state/state.ts";
|
|
4
|
+
import type { QuestionData } from "../tool/types.ts";
|
|
5
|
+
import type { ChatRowView } from "./components/chat-row-view.ts";
|
|
6
|
+
import type { PreviewPaneProps } from "./components/preview/preview-pane.ts";
|
|
7
|
+
import type { TabBar } from "./components/tab-bar.ts";
|
|
8
|
+
import type { StatefulView } from "./stateful-view.ts";
|
|
9
|
+
import type { TabComponents } from "./tab-components.ts";
|
|
10
10
|
export declare const HINT_PART_ENTER = "enter select";
|
|
11
11
|
export declare const HINT_PART_NAV = "\u2191/\u2193 navigate";
|
|
12
12
|
export declare const HINT_PART_TOGGLE = "space toggle";
|