@herbertgao/pi-extensions 2026.8.6 → 2026.8.8
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/README.md +8 -6
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
- package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
- package/node_modules/@narumitw/pi-btw/README.md +120 -111
- package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-btw/package.json +10 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
- package/node_modules/pi-lens/CHANGELOG.md +999 -0
- package/node_modules/pi-lens/README.md +117 -3
- package/node_modules/pi-lens/config/ruff/core.toml +42 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
- package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
- package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
- package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
- package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
- package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
- package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
- package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
- package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
- package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
- package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
- package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
- package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
- package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
- package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
- package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +61407 -48577
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/docs/agent-guide.md +6 -1
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +19 -5
- package/node_modules/pi-lens/docs/globalconfig.md +3 -0
- package/node_modules/pi-lens/docs/language-coverage.md +71 -5
- package/node_modules/pi-lens/docs/mcp.md +2 -2
- package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
- package/node_modules/pi-lens/docs/settings.md +19 -1
- package/node_modules/pi-lens/docs/usage.md +1 -0
- package/node_modules/pi-lens/package.json +27 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
- package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
- package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
- package/node_modules/pi-mcp-adapter/README.md +47 -5
- package/node_modules/pi-mcp-adapter/cli.js +163 -1
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +25 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
- package/node_modules/pi-mcp-adapter/index.ts +219 -32
- package/node_modules/pi-mcp-adapter/init.ts +36 -4
- package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
- package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +3 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
- package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
- package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +2 -0
- package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
- package/node_modules/pi-mcp-adapter/utils.ts +70 -0
- package/node_modules/pi-web-access/CHANGELOG.md +37 -0
- package/node_modules/pi-web-access/README.md +32 -13
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
- package/node_modules/pi-web-access/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +40 -5
- package/node_modules/pi-web-access/firecrawl.ts +18 -2
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/github-extract.ts +65 -36
- package/node_modules/pi-web-access/index.ts +60 -61
- package/node_modules/pi-web-access/openai-search.ts +17 -6
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/page-query.ts +4 -2
- package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
- package/node_modules/pi-web-access/parallel.ts +2 -2
- package/node_modules/pi-web-access/pdf-extract.ts +18 -5
- package/node_modules/pi-web-access/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +44 -22
- package/node_modules/pi-web-access/tavily.ts +16 -4
- package/node_modules/pi-web-access/utils.ts +87 -0
- package/node_modules/pi-web-access/valyu.ts +199 -0
- package/package.json +14 -14
|
@@ -43,6 +43,9 @@ TUI produces. Trade-offs inherent to the native primitives:
|
|
|
43
43
|
- No side-by-side preview pane. Previews are folded into the dialog title instead,
|
|
44
44
|
truncated at 600 characters each.
|
|
45
45
|
- No tab bar and no Submit review tab — one dialog per question, in order.
|
|
46
|
+
- No notes. Both note kinds — per-question `n` on a question tab and global `n` on the
|
|
47
|
+
Submit tab — are terminal-only; the host's native `select` and `input` primitives carry
|
|
48
|
+
no note field.
|
|
46
49
|
- Multi-select is a free-text input: type the option numbers, comma-separated
|
|
47
50
|
(`1,3`). Any token that is not a valid option index is treated as a typed custom answer,
|
|
48
51
|
which is how the `Type something.` escape survives. An empty input commits an empty
|
|
@@ -64,7 +67,8 @@ Some parts of the dialog exist only under the right conditions:
|
|
|
64
67
|
| `Type something.` row | Always |
|
|
65
68
|
| Side-by-side preview | An option carries a `preview`, and terminal and pane are both ≥ 100 columns |
|
|
66
69
|
| Preview pane at all | Single-select questions only |
|
|
67
|
-
| Collapse shortcut | `collapseKey` is not `"off"`
|
|
70
|
+
| Collapse shortcut | `collapseKey` is not `"off"` |
|
|
71
|
+
| Full overlay hide on collapse | The host also exposes raw terminal input (the only path that can reopen a hidden overlay); without it, collapsing shrinks the dialog to a visible one-line row instead |
|
|
68
72
|
| Localized chrome | `@juicesharp/rpiv-i18n` is installed |
|
|
69
73
|
|
|
70
74
|
## Loading and startup cost
|
|
@@ -13,7 +13,7 @@ adapts to the size of your terminal.
|
|
|
13
13
|
| `Esc` | Cancel the whole questionnaire. | Everywhere except the notes editor, where it closes notes |
|
|
14
14
|
| `Tab` / `Shift+Tab` | Next / previous tab, wrapping. `→` / `←` do the same. | Multi-question dialogs only |
|
|
15
15
|
| `Space` | Toggle the focused checkbox. | Multi-select questions |
|
|
16
|
-
| `n` | Open the notes editor for the
|
|
16
|
+
| `n` | Open the notes editor for the focused question — or, on the Submit tab, the global note for the whole questionnaire. | Every question tab; the Submit tab in multi-question dialogs |
|
|
17
17
|
| `Ctrl+G` | Open Pi's configured external editor with the current custom-answer draft. | `Type something.` input |
|
|
18
18
|
| `Ctrl+U` | Clear the current custom-answer draft. | `Type something.` input |
|
|
19
19
|
| `Ctrl+]` | Collapse or expand the dialog. Configurable via `collapseKey`. | Everywhere, including while collapsed |
|
|
@@ -62,6 +62,11 @@ side-band keyed by tab index, not inside the answer, so writing a note does not
|
|
|
62
62
|
question as answered — the Submit tab still lists it as outstanding. The note merges into
|
|
63
63
|
the answer when you confirm it, and reaches the model as `user notes: <text>`.
|
|
64
64
|
|
|
65
|
+
On the Submit tab, `n` opens the global note editor instead — one note covering the whole
|
|
66
|
+
questionnaire. It lives outside every answer, so it survives tab switches and never marks
|
|
67
|
+
a question as answered; it reaches the model as `global note: <text>`, and submitting with
|
|
68
|
+
nothing but a global note still returns an answered result rather than a decline.
|
|
69
|
+
|
|
65
70
|
Inside the editor, `Shift+Enter` inserts a newline, while `Esc` and `Enter` close it; other
|
|
66
71
|
keystrokes edit the buffer, so `n` types an `n`. Pasted line breaks are preserved.
|
|
67
72
|
|
|
@@ -98,6 +103,9 @@ content above, `↓` for content below, `↕` for both.
|
|
|
98
103
|
|
|
99
104
|
The footer hint line adapts to context — it drops the notes hint and appends the
|
|
100
105
|
`Shift+Enter` newline hint whenever a text editor has the keyboard, with `Ctrl+U` still at
|
|
101
|
-
the far right for custom answers. It adds the tab hint only in multi-question dialogs.
|
|
106
|
+
the far right for custom answers. It adds the tab hint only in multi-question dialogs. The
|
|
107
|
+
Submit tab follows the same idiom: its bottom hint row sits below the picker and carries
|
|
108
|
+
an `n to add a note` part that gives way to the `Shift+Enter` newline hint while the
|
|
109
|
+
global-note editor is open; a committed note shows as a `Note` entry in the review list.
|
|
102
110
|
`Ctrl+G` remains Pi's global external-editor shortcut and is not repeated there. On narrow
|
|
103
111
|
terminals the right edge clips with `…` so the core hints survive.
|
|
@@ -52,11 +52,13 @@ Nine locale files ship in `locales/`:
|
|
|
52
52
|
## What is translated, and what is not
|
|
53
53
|
|
|
54
54
|
Translation covers the chrome you read: the `Type something.` and `Next` sentinel rows,
|
|
55
|
-
the footer hint segments (including the collapsed-state line
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
the footer hint segments (including the collapsed-state line and the Submit tab's
|
|
56
|
+
`n to add a note` hint), the Submit picker labels, the review-tab heading with its
|
|
57
|
+
submit-readiness prompt, incomplete-answers warning, and committed-note `Note` label, the
|
|
58
|
+
preview pane's empty and notes-affordance lines, the notes header and the global-note
|
|
59
|
+
header on the Submit tab, the
|
|
60
|
+
external-editor failure notification, and the two RPC dialog prompts. Twenty-six keys in total,
|
|
61
|
+
all under the namespace `@juicesharp/rpiv-ask-user-question`.
|
|
60
62
|
|
|
61
63
|
Everything the *model* reads stays English by design: the tool description, the parameter
|
|
62
64
|
schema descriptions, error messages, and the reserved-label list. Those are prompt inputs,
|
|
@@ -71,7 +73,9 @@ No code change is required.
|
|
|
71
73
|
1. Copy `locales/en.json` to `locales/<code>.json`, where `<code>` is a locale the SDK
|
|
72
74
|
supports.
|
|
73
75
|
2. Translate the values. Keep every key, and keep the placeholders and leading symbols
|
|
74
|
-
(`↑/↓`, `⚠`) intact.
|
|
76
|
+
(`↑/↓`, `⚠`) intact. `hint.collapse` and `hint.expand_line` contain a literal `{key}`
|
|
77
|
+
placeholder that is replaced at render time with the user's configured `collapseKey` —
|
|
78
|
+
keep it verbatim, positioned wherever your language's word order wants the key name.
|
|
75
79
|
3. Restart Pi and select the language with `/languages`.
|
|
76
80
|
|
|
77
81
|
The loader iterates the SDK's supported-locale list over this directory at startup, so a
|
|
@@ -83,6 +83,7 @@ code. The `content[0].text` string is written for the model, not for a log.
|
|
|
83
83
|
preview?: string, // echoed back when the chosen option carried a preview
|
|
84
84
|
}>,
|
|
85
85
|
cancelled: boolean,
|
|
86
|
+
globalNote?: string, // Submit-tab note; present even when cancelled is true
|
|
86
87
|
error?: QuestionnaireError, // one of the codes above
|
|
87
88
|
}
|
|
88
89
|
}
|
|
@@ -92,11 +93,16 @@ code. The `content[0].text` string is written for the model, not for a log.
|
|
|
92
93
|
|
|
93
94
|
On success the text reads `User has answered your questions: "<question>"="<answer>". …
|
|
94
95
|
You can now continue with the user's answers in mind.` A chosen option's `preview` is
|
|
95
|
-
appended as `selected preview: <markdown>`,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
appended as `selected preview: <markdown>`, a per-question note as `user notes: <text>`,
|
|
97
|
+
and the Submit tab's global note as a trailing `global note: <text>` segment. A global
|
|
98
|
+
note alone still yields the answered envelope — it counts as an answer even when every
|
|
99
|
+
question is blank.
|
|
100
|
+
|
|
101
|
+
Cancelling, and any result with neither answer segments nor a global note, both collapse
|
|
102
|
+
to the single string `User declined to answer questions` so the model sees one canonical
|
|
103
|
+
signal. Partial submission is allowed: unanswered questions simply contribute no segment.
|
|
104
|
+
A cancelled result always reads as the decline in text; its note, if any, survives only
|
|
105
|
+
in `details.globalNote`.
|
|
100
106
|
|
|
101
107
|
## Event contract
|
|
102
108
|
|
|
@@ -12,17 +12,20 @@
|
|
|
12
12
|
"hint.toggle": "Space to toggle",
|
|
13
13
|
"hint.notes": "n to add notes",
|
|
14
14
|
"hint.tab": "Tab to switch questions",
|
|
15
|
-
"hint.collapse": "
|
|
16
|
-
"hint.expand_line": "
|
|
15
|
+
"hint.collapse": "{key} to collapse",
|
|
16
|
+
"hint.expand_line": "{key} to expand · Esc to cancel",
|
|
17
17
|
"hint.cancel": "Esc to cancel",
|
|
18
18
|
|
|
19
19
|
"review.heading": "Review your answers",
|
|
20
20
|
"review.ready": "Ready to submit your answers?",
|
|
21
21
|
"review.incomplete": "⚠ Answer remaining questions before submitting:",
|
|
22
|
+
"review.global_hint": "n to add a note",
|
|
23
|
+
"review.note_label": "Note",
|
|
22
24
|
|
|
23
25
|
"preview.no_preview": "No preview available",
|
|
24
26
|
"preview.notes_affordance": "Notes: press n to add notes",
|
|
25
27
|
"notes.header": "Notes:",
|
|
28
|
+
"notes.global_header": "Global note:",
|
|
26
29
|
|
|
27
30
|
"editor.failed": "External editor failed",
|
|
28
31
|
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"hint.toggle": "空格切换",
|
|
15
15
|
"hint.notes": "按 n 添加备注",
|
|
16
16
|
"hint.tab": "Tab 切换问题",
|
|
17
|
-
"hint.collapse": "
|
|
18
|
-
"hint.expand_line": "
|
|
17
|
+
"hint.collapse": "{key} 折叠",
|
|
18
|
+
"hint.expand_line": "{key} 展开 · Esc 取消",
|
|
19
19
|
"hint.cancel": "Esc 取消",
|
|
20
20
|
|
|
21
21
|
"review.heading": "检查你的答案",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juicesharp/rpiv-ask-user-question",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Pi extension. A structured questionnaire the model can put to you when it would otherwise guess, with typed options instead of free-form replies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"view/tab-content-strategy.ts"
|
|
79
79
|
],
|
|
80
80
|
"pi": {
|
|
81
|
+
"image": "https://raw.githubusercontent.com/juicesharp/rpiv-mono/main/packages/rpiv-ask-user-question/docs/cover.png",
|
|
81
82
|
"extensions": [
|
|
82
83
|
"./index.ts"
|
|
83
84
|
]
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
}
|
|
94
95
|
},
|
|
95
96
|
"dependencies": {
|
|
96
|
-
"@juicesharp/rpiv-config": "^2.
|
|
97
|
+
"@juicesharp/rpiv-config": "^2.7.0",
|
|
97
98
|
"typebox": "^1.1.24"
|
|
98
99
|
}
|
|
99
100
|
}
|
|
@@ -40,6 +40,13 @@ export interface QuestionnaireBuildConfig {
|
|
|
40
40
|
isMulti: boolean;
|
|
41
41
|
initialState: QuestionnaireState;
|
|
42
42
|
getCurrentTab: () => number;
|
|
43
|
+
/**
|
|
44
|
+
* Resolved collapse key spec (`"ctrl+]"`, `"alt+o"`, or `"off"`). Threaded to
|
|
45
|
+
* `DialogConfig` as construction-time config so the footer hint can name the
|
|
46
|
+
* real key — deliberately NOT part of `QuestionnaireState`, which stays free
|
|
47
|
+
* of runtime context.
|
|
48
|
+
*/
|
|
49
|
+
collapseKey: string;
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
export interface QuestionnaireBuilt {
|
|
@@ -109,6 +116,7 @@ class QuestionnaireBuilder {
|
|
|
109
116
|
private readonly isMulti: boolean;
|
|
110
117
|
private readonly initialState: QuestionnaireState;
|
|
111
118
|
private readonly getCurrentTab: () => number;
|
|
119
|
+
private readonly collapseKey: string;
|
|
112
120
|
|
|
113
121
|
private readonly selectTheme: WrappingSelectTheme;
|
|
114
122
|
private readonly markdownTheme = getMarkdownTheme();
|
|
@@ -125,6 +133,7 @@ class QuestionnaireBuilder {
|
|
|
125
133
|
this.isMulti = config.isMulti;
|
|
126
134
|
this.initialState = config.initialState;
|
|
127
135
|
this.getCurrentTab = config.getCurrentTab;
|
|
136
|
+
this.collapseKey = config.collapseKey;
|
|
128
137
|
|
|
129
138
|
this.selectTheme = this.makeSelectTheme();
|
|
130
139
|
const textEditorTheme = editorTheme(this.theme);
|
|
@@ -258,6 +267,7 @@ class QuestionnaireBuilder {
|
|
|
258
267
|
getBodyHeight: heights.global,
|
|
259
268
|
getCurrentBodyHeight: heights.current,
|
|
260
269
|
getTerminalRows: this.getTerminalRows,
|
|
270
|
+
collapseKey: this.collapseKey,
|
|
261
271
|
},
|
|
262
272
|
{ state: this.initialState, activePreviewPane: this.pickInitialActivePreview(tabs) },
|
|
263
273
|
);
|
|
@@ -150,6 +150,135 @@ function prevNavOnUp(state: QuestionnaireState, runtime: QuestionnaireRuntime):
|
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
// Collapsed-mode lockout: while collapsed, swallow every keystroke except cancel so
|
|
154
|
+
// the user can read the now-uncovered transcript without accidentally mutating
|
|
155
|
+
// answers or notes. The collapse toggle itself is already handled above.
|
|
156
|
+
function routeCollapsed(kb: QuestionnaireKeybindings, data: string): QuestionnaireAction {
|
|
157
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
158
|
+
return { kind: "ignore" };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function routeNotesMode(kb: QuestionnaireKeybindings, data: string): QuestionnaireAction {
|
|
162
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "notes_exit" };
|
|
163
|
+
if (kb.matches(data, KEYBIND_NEW_LINE)) return { kind: "notes_forward", data };
|
|
164
|
+
if (isConfirm(kb, data)) return { kind: "notes_exit" };
|
|
165
|
+
return { kind: "notes_forward", data };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function routeInputMode(
|
|
169
|
+
kb: QuestionnaireKeybindings,
|
|
170
|
+
data: string,
|
|
171
|
+
state: QuestionnaireState,
|
|
172
|
+
runtime: QuestionnaireRuntime,
|
|
173
|
+
): QuestionnaireAction {
|
|
174
|
+
// Newline takes precedence over confirmation if a user configuration binds
|
|
175
|
+
// the same physical key to both semantic actions.
|
|
176
|
+
if (kb.matches(data, KEYBIND_NEW_LINE)) return { kind: "ignore" };
|
|
177
|
+
if (isConfirm(kb, data)) {
|
|
178
|
+
const answer = buildSingleSelectAnswer(state, runtime);
|
|
179
|
+
if (!answer) return { kind: "ignore" };
|
|
180
|
+
return { kind: "confirm", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };
|
|
181
|
+
}
|
|
182
|
+
// Treat Pi's Ctrl+U line-kill binding as an explicit whole-draft clear,
|
|
183
|
+
// independent of the current cursor position.
|
|
184
|
+
if (kb.matches(data, KEYBIND_CLEAR)) return { kind: "input_clear" };
|
|
185
|
+
if (kb.matches(data, KEYBIND_EXTERNAL_EDITOR)) return { kind: "input_edit", value: runtime.inputBuffer };
|
|
186
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
187
|
+
if (kb.matches(data, KEYBIND_EDITOR_UP) && runtime.canMoveInputUp) return { kind: "ignore" };
|
|
188
|
+
if (kb.matches(data, KEYBIND_EDITOR_DOWN) && runtime.canMoveInputDown) return { kind: "ignore" };
|
|
189
|
+
if (kb.matches(data, KEYBIND_UP)) return prevNavOnUp(state, runtime);
|
|
190
|
+
if (kb.matches(data, KEYBIND_DOWN)) return nextNavOnDown(state, runtime);
|
|
191
|
+
return { kind: "ignore" };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function routeSubmitTab(
|
|
195
|
+
kb: QuestionnaireKeybindings,
|
|
196
|
+
data: string,
|
|
197
|
+
state: QuestionnaireState,
|
|
198
|
+
runtime: QuestionnaireRuntime,
|
|
199
|
+
): QuestionnaireAction {
|
|
200
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
201
|
+
const tab = tabSwitchAction(data, state, runtime);
|
|
202
|
+
if (tab) return tab;
|
|
203
|
+
if (kb.matches(data, KEYBIND_UP) || kb.matches(data, KEYBIND_DOWN)) {
|
|
204
|
+
const delta = kb.matches(data, KEYBIND_DOWN) ? 1 : -1;
|
|
205
|
+
const next = wrapTab(state.submitChoiceIndex + delta, 2);
|
|
206
|
+
return { kind: "submit_nav", nextIndex: (next === 1 ? 1 : 0) as 0 | 1 };
|
|
207
|
+
}
|
|
208
|
+
if (isConfirm(kb, data)) {
|
|
209
|
+
// D1 (revised): Submit always submits; Cancel always cancels. The warning header
|
|
210
|
+
// is informational only — `allAnswered(state)` no longer gates submission. Partial
|
|
211
|
+
// answers flow through `orderedAnswers()` in the host.
|
|
212
|
+
return state.submitChoiceIndex === 1 ? { kind: "cancel" } : { kind: "submit" };
|
|
213
|
+
}
|
|
214
|
+
// Global note (#182): `n` on the Submit tab opens the notes editor scoped to the whole
|
|
215
|
+
// questionnaire at the pseudo-index (`questions.length` in notesByTab). Reachable only
|
|
216
|
+
// with the editor closed — `routeKey` dispatches notesVisible traffic to
|
|
217
|
+
// `routeNotesMode` before the submit-tab block runs. The branch shadows nothing above
|
|
218
|
+
// it (cancel, tab switch, submit-nav, confirm): a literal `n` byte matches none of them
|
|
219
|
+
// under default bindings, and a user who deliberately binds one of those actions to
|
|
220
|
+
// `n` keeps that mapping because the earlier branch still wins (Enter still submits).
|
|
221
|
+
if (data === NOTES_ACTIVATE_KEY) {
|
|
222
|
+
return { kind: "notes_enter" };
|
|
223
|
+
}
|
|
224
|
+
return { kind: "ignore" };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function routeMultiSelectTab(
|
|
228
|
+
kb: QuestionnaireKeybindings,
|
|
229
|
+
data: string,
|
|
230
|
+
state: QuestionnaireState,
|
|
231
|
+
runtime: QuestionnaireRuntime,
|
|
232
|
+
): QuestionnaireAction {
|
|
233
|
+
const focusedKind = runtime.currentItem?.kind;
|
|
234
|
+
const focusedMeta = focusedKind ? ROW_INTENT_META[focusedKind] : undefined;
|
|
235
|
+
// Space toggles the focused row's checkbox. Suppressed on rows whose META declares
|
|
236
|
+
// `blocksMultiToggle` (the Next sentinel) or `activatesInputMode` (the "Type
|
|
237
|
+
// something." row — it is an inline input, not a checkable option).
|
|
238
|
+
if (data === SPACE_KEY) {
|
|
239
|
+
if (focusedMeta?.blocksMultiToggle) return { kind: "ignore" };
|
|
240
|
+
if (focusedMeta?.activatesInputMode) return { kind: "ignore" };
|
|
241
|
+
return { kind: "toggle", index: state.optionIndex };
|
|
242
|
+
}
|
|
243
|
+
if (isConfirm(kb, data)) {
|
|
244
|
+
// Enter on the "Type something." row is handled by the inputMode block above
|
|
245
|
+
// (→ confirm kind:"custom"). Defensive: never enter the toggle/multi_confirm
|
|
246
|
+
// path for an inputMode-activating row.
|
|
247
|
+
if (focusedMeta?.activatesInputMode) return { kind: "ignore" };
|
|
248
|
+
// Enter on a regular row toggles (matching Space) — committing the question is now
|
|
249
|
+
// gated behind explicit focus on a row whose META declares `autoSubmitsInMulti`
|
|
250
|
+
// (the Next sentinel), so Enter on options is a no-cost way to flip checkboxes
|
|
251
|
+
// without leaving the keyboard home row.
|
|
252
|
+
if (!focusedMeta?.autoSubmitsInMulti) return { kind: "toggle", index: state.optionIndex };
|
|
253
|
+
// Enter on Next: carry autoAdvanceTab so the host can advance to the next tab in
|
|
254
|
+
// multi-question mode, OR submit the dialog in single-question mode
|
|
255
|
+
// (autoAdvanceTab === undefined when !isMulti). Without this, a single multi-select
|
|
256
|
+
// question would have no way to commit at all.
|
|
257
|
+
return {
|
|
258
|
+
kind: "multi_confirm",
|
|
259
|
+
selected: buildMultiSelected(state, runtime),
|
|
260
|
+
autoAdvanceTab: computeAutoAdvanceTab(state, runtime),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
264
|
+
return { kind: "ignore" };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function routeSingleSelectTab(
|
|
268
|
+
kb: QuestionnaireKeybindings,
|
|
269
|
+
data: string,
|
|
270
|
+
state: QuestionnaireState,
|
|
271
|
+
runtime: QuestionnaireRuntime,
|
|
272
|
+
): QuestionnaireAction {
|
|
273
|
+
if (isConfirm(kb, data)) {
|
|
274
|
+
const answer = buildSingleSelectAnswer(state, runtime);
|
|
275
|
+
if (!answer) return { kind: "ignore" };
|
|
276
|
+
return { kind: "confirm", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };
|
|
277
|
+
}
|
|
278
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
279
|
+
return { kind: "ignore" };
|
|
280
|
+
}
|
|
281
|
+
|
|
153
282
|
export function routeKey(data: string, state: QuestionnaireState, runtime: QuestionnaireRuntime): QuestionnaireAction {
|
|
154
283
|
const kb = runtime.keybindings;
|
|
155
284
|
|
|
@@ -174,7 +303,9 @@ export function routeKey(data: string, state: QuestionnaireState, runtime: Quest
|
|
|
174
303
|
// replaces the lazily imported QuestionnaireSession graph on disk. Passing
|
|
175
304
|
// undefined into matchesKey reaches parseKeyId().toLowerCase() and crashes the
|
|
176
305
|
// entire host process, so keep the runtime boundary defensive even though the
|
|
177
|
-
// TypeScript contract requires a string.
|
|
306
|
+
// TypeScript contract requires a string. The "off" literal is deliberate:
|
|
307
|
+
// importing COLLAPSE_KEY_OFF would pull ../config.js (and its rpiv-config
|
|
308
|
+
// loader graph) into this pure module for a string that cannot change.
|
|
178
309
|
if (
|
|
179
310
|
typeof runtime.collapseKey === "string" &&
|
|
180
311
|
runtime.collapseKey !== "off" &&
|
|
@@ -183,60 +314,11 @@ export function routeKey(data: string, state: QuestionnaireState, runtime: Quest
|
|
|
183
314
|
return { kind: "toggle_collapsed" };
|
|
184
315
|
}
|
|
185
316
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (state.collapsed) {
|
|
190
|
-
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
191
|
-
return { kind: "ignore" };
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (state.notesVisible) {
|
|
195
|
-
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "notes_exit" };
|
|
196
|
-
if (kb.matches(data, KEYBIND_NEW_LINE)) return { kind: "notes_forward", data };
|
|
197
|
-
if (isConfirm(kb, data)) return { kind: "notes_exit" };
|
|
198
|
-
return { kind: "notes_forward", data };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (state.inputMode) {
|
|
202
|
-
// Newline takes precedence over confirmation if a user configuration binds
|
|
203
|
-
// the same physical key to both semantic actions.
|
|
204
|
-
if (kb.matches(data, KEYBIND_NEW_LINE)) return { kind: "ignore" };
|
|
205
|
-
if (isConfirm(kb, data)) {
|
|
206
|
-
const answer = buildSingleSelectAnswer(state, runtime);
|
|
207
|
-
if (!answer) return { kind: "ignore" };
|
|
208
|
-
return { kind: "confirm", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };
|
|
209
|
-
}
|
|
210
|
-
// Treat Pi's Ctrl+U line-kill binding as an explicit whole-draft clear,
|
|
211
|
-
// independent of the current cursor position.
|
|
212
|
-
if (kb.matches(data, KEYBIND_CLEAR)) return { kind: "input_clear" };
|
|
213
|
-
if (kb.matches(data, KEYBIND_EXTERNAL_EDITOR)) return { kind: "input_edit", value: runtime.inputBuffer };
|
|
214
|
-
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
215
|
-
if (kb.matches(data, KEYBIND_EDITOR_UP) && runtime.canMoveInputUp) return { kind: "ignore" };
|
|
216
|
-
if (kb.matches(data, KEYBIND_EDITOR_DOWN) && runtime.canMoveInputDown) {
|
|
217
|
-
return { kind: "ignore" };
|
|
218
|
-
}
|
|
219
|
-
if (kb.matches(data, KEYBIND_UP)) return prevNavOnUp(state, runtime);
|
|
220
|
-
if (kb.matches(data, KEYBIND_DOWN)) return nextNavOnDown(state, runtime);
|
|
221
|
-
return { kind: "ignore" };
|
|
222
|
-
}
|
|
223
|
-
|
|
317
|
+
if (state.collapsed) return routeCollapsed(kb, data);
|
|
318
|
+
if (state.notesVisible) return routeNotesMode(kb, data);
|
|
319
|
+
if (state.inputMode) return routeInputMode(kb, data, state, runtime);
|
|
224
320
|
if (runtime.isMulti && state.currentTab === runtime.questions.length) {
|
|
225
|
-
|
|
226
|
-
const tab = tabSwitchAction(data, state, runtime);
|
|
227
|
-
if (tab) return tab;
|
|
228
|
-
if (kb.matches(data, KEYBIND_UP) || kb.matches(data, KEYBIND_DOWN)) {
|
|
229
|
-
const delta = kb.matches(data, KEYBIND_DOWN) ? 1 : -1;
|
|
230
|
-
const next = wrapTab(state.submitChoiceIndex + delta, 2);
|
|
231
|
-
return { kind: "submit_nav", nextIndex: (next === 1 ? 1 : 0) as 0 | 1 };
|
|
232
|
-
}
|
|
233
|
-
if (isConfirm(kb, data)) {
|
|
234
|
-
// D1 (revised): Submit always submits; Cancel always cancels. The warning header
|
|
235
|
-
// is informational only — `allAnswered(state)` no longer gates submission. Partial
|
|
236
|
-
// answers flow through `orderedAnswers()` in the host.
|
|
237
|
-
return state.submitChoiceIndex === 1 ? { kind: "cancel" } : { kind: "submit" };
|
|
238
|
-
}
|
|
239
|
-
return { kind: "ignore" };
|
|
321
|
+
return routeSubmitTab(kb, data, state, runtime);
|
|
240
322
|
}
|
|
241
323
|
|
|
242
324
|
const tab = tabSwitchAction(data, state, runtime);
|
|
@@ -249,7 +331,8 @@ export function routeKey(data: string, state: QuestionnaireState, runtime: Quest
|
|
|
249
331
|
// (single- or multi-select, preview or no-preview). The blocks above already
|
|
250
332
|
// swallow `n` when it should NOT reach here — notesVisible forwards to the
|
|
251
333
|
// notes Input, inputMode forwards to the inline Input, the submit-tab block
|
|
252
|
-
//
|
|
334
|
+
// activates it via its own branch (the global note, ahead of its ignore
|
|
335
|
+
// fall-through), and tabSwitchAction ignores it — so by the time we reach this
|
|
253
336
|
// gate, `n` is unambiguously a notes-enter request. Row intent (Next sentinel,
|
|
254
337
|
// "Type something.") is irrelevant: the gate sits ABOVE the multi-select
|
|
255
338
|
// toggle block and the Next sentinel never activates inputMode, so `n` is
|
|
@@ -265,46 +348,6 @@ export function routeKey(data: string, state: QuestionnaireState, runtime: Quest
|
|
|
265
348
|
return nextNavOnDown(state, runtime);
|
|
266
349
|
}
|
|
267
350
|
|
|
268
|
-
if (q.multiSelect)
|
|
269
|
-
|
|
270
|
-
const focusedMeta = focusedKind ? ROW_INTENT_META[focusedKind] : undefined;
|
|
271
|
-
// Space toggles the focused row's checkbox. Suppressed on rows whose META declares
|
|
272
|
-
// `blocksMultiToggle` (the Next sentinel) or `activatesInputMode` (the "Type
|
|
273
|
-
// something." row — it is an inline input, not a checkable option).
|
|
274
|
-
if (data === SPACE_KEY) {
|
|
275
|
-
if (focusedMeta?.blocksMultiToggle) return { kind: "ignore" };
|
|
276
|
-
if (focusedMeta?.activatesInputMode) return { kind: "ignore" };
|
|
277
|
-
return { kind: "toggle", index: state.optionIndex };
|
|
278
|
-
}
|
|
279
|
-
if (isConfirm(kb, data)) {
|
|
280
|
-
// Enter on the "Type something." row is handled by the inputMode block above
|
|
281
|
-
// (→ confirm kind:"custom"). Defensive: never enter the toggle/multi_confirm
|
|
282
|
-
// path for an inputMode-activating row.
|
|
283
|
-
if (focusedMeta?.activatesInputMode) return { kind: "ignore" };
|
|
284
|
-
// Enter on a regular row toggles (matching Space) — committing the question is now
|
|
285
|
-
// gated behind explicit focus on a row whose META declares `autoSubmitsInMulti`
|
|
286
|
-
// (the Next sentinel), so Enter on options is a no-cost way to flip checkboxes
|
|
287
|
-
// without leaving the keyboard home row.
|
|
288
|
-
if (!focusedMeta?.autoSubmitsInMulti) return { kind: "toggle", index: state.optionIndex };
|
|
289
|
-
// Enter on Next: carry autoAdvanceTab so the host can advance to the next tab in
|
|
290
|
-
// multi-question mode, OR submit the dialog in single-question mode
|
|
291
|
-
// (autoAdvanceTab === undefined when !isMulti). Without this, a single multi-select
|
|
292
|
-
// question would have no way to commit at all.
|
|
293
|
-
return {
|
|
294
|
-
kind: "multi_confirm",
|
|
295
|
-
selected: buildMultiSelected(state, runtime),
|
|
296
|
-
autoAdvanceTab: computeAutoAdvanceTab(state, runtime),
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
300
|
-
return { kind: "ignore" };
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (isConfirm(kb, data)) {
|
|
304
|
-
const answer = buildSingleSelectAnswer(state, runtime);
|
|
305
|
-
if (!answer) return { kind: "ignore" };
|
|
306
|
-
return { kind: "confirm", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };
|
|
307
|
-
}
|
|
308
|
-
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
309
|
-
return { kind: "ignore" };
|
|
351
|
+
if (q.multiSelect) return routeMultiSelectTab(kb, data, state, runtime);
|
|
352
|
+
return routeSingleSelectTab(kb, data, state, runtime);
|
|
310
353
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { Editor, OverlayHandle, TUI } from "@earendil-works/pi-tui";
|
|
3
|
+
import { COLLAPSE_KEY_OFF, formatKeySpecForDisplay } from "../config.js";
|
|
3
4
|
import type { QuestionData, QuestionnaireResult, QuestionParams } from "../tool/types.js";
|
|
4
5
|
import type { WrappingSelectItem } from "../view/components/wrapping-select.js";
|
|
5
|
-
import {
|
|
6
|
+
import { COLLAPSED_HINT_TEMPLATE, HINT_PART_CANCEL, KEY_PLACEHOLDER } from "../view/dialog-builder.js";
|
|
6
7
|
import type { QuestionnairePropsAdapter } from "../view/props-adapter.js";
|
|
7
|
-
import { buildQuestionnaire } from "./build-questionnaire.js";
|
|
8
|
+
import { buildQuestionnaire, type QuestionnaireBuilt } from "./build-questionnaire.js";
|
|
8
9
|
import { t } from "./i18n-bridge.js";
|
|
9
10
|
import { type QuestionnaireAction, routeKey } from "./key-router.js";
|
|
10
11
|
import type { QuestionnaireRuntime, QuestionnaireState } from "./state.js";
|
|
@@ -21,6 +22,14 @@ export interface QuestionnaireSessionConfig {
|
|
|
21
22
|
editInput: (value: string) => Promise<string | undefined>;
|
|
22
23
|
/** Key spec for the collapse/expand shortcut, e.g. `"ctrl+]"` or `"alt+o"`. */
|
|
23
24
|
collapseKey: string;
|
|
25
|
+
/**
|
|
26
|
+
* True iff `execute()` registered the raw `ctx.ui.onTerminalInput` listener — the only
|
|
27
|
+
* path that can reach a hidden overlay. Gates the `set_overlay_hidden` effect: a host
|
|
28
|
+
* that delivers an `OverlayHandle` but no raw terminal input must fall back to the
|
|
29
|
+
* visible one-line collapsed row (which keeps focus and input routing), or collapsing
|
|
30
|
+
* would hide the overlay into a state nothing can reopen.
|
|
31
|
+
*/
|
|
32
|
+
canReopenWhileHidden: boolean;
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
export interface QuestionnaireSessionComponent {
|
|
@@ -64,6 +73,7 @@ export class QuestionnaireSession {
|
|
|
64
73
|
private readonly keybindings: QuestionnaireRuntime["keybindings"];
|
|
65
74
|
private readonly editInput: QuestionnaireSessionConfig["editInput"];
|
|
66
75
|
private readonly collapseKey: string;
|
|
76
|
+
private readonly canReopenWhileHidden: boolean;
|
|
67
77
|
private inputEditorOpen = false;
|
|
68
78
|
|
|
69
79
|
/**
|
|
@@ -86,6 +96,7 @@ export class QuestionnaireSession {
|
|
|
86
96
|
this.keybindings = config.keybindings;
|
|
87
97
|
this.editInput = config.editInput;
|
|
88
98
|
this.collapseKey = config.collapseKey;
|
|
99
|
+
this.canReopenWhileHidden = config.canReopenWhileHidden;
|
|
89
100
|
|
|
90
101
|
const built = buildQuestionnaire({
|
|
91
102
|
tui: this.tui,
|
|
@@ -95,29 +106,45 @@ export class QuestionnaireSession {
|
|
|
95
106
|
isMulti: this.isMulti,
|
|
96
107
|
initialState: this.state,
|
|
97
108
|
getCurrentTab: () => this.state.currentTab,
|
|
109
|
+
collapseKey: this.collapseKey,
|
|
98
110
|
});
|
|
99
111
|
|
|
100
112
|
this.notesInput = built.notesInput;
|
|
101
113
|
this.inlineInput = built.inlineInput;
|
|
102
114
|
this.viewAdapter = built.adapter;
|
|
103
115
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
theme.fg("dim", ` ${t("hint.expand_line", COLLAPSED_HINT)} `),
|
|
112
|
-
];
|
|
113
|
-
|
|
114
|
-
this.component = {
|
|
116
|
+
this.component = this.assembleComponent(built, config.theme);
|
|
117
|
+
this.viewAdapter.apply(this.state);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private assembleComponent(built: QuestionnaireBuilt, theme: Theme): QuestionnaireSessionComponent {
|
|
121
|
+
const collapsedRender = this.buildCollapsedRender(theme);
|
|
122
|
+
return {
|
|
115
123
|
render: (width) => (this.state.collapsed ? collapsedRender(width) : built.render(width)),
|
|
116
124
|
invalidate: built.invalidate,
|
|
117
125
|
handleInput: (data) => this.dispatch(data),
|
|
118
126
|
};
|
|
127
|
+
}
|
|
119
128
|
|
|
120
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Collapsed render: a single dim row at the bottom of the overlay. pi-tui sizes
|
|
131
|
+
* the overlay to `min(lines.length, maxHeight)`, so returning one line shrinks
|
|
132
|
+
* the bottom-anchored overlay from full-height to one row and the transcript
|
|
133
|
+
* behind it becomes readable (#47). The overlay stays focused and in the
|
|
134
|
+
* stack, so the collapse key still routes here to expand. `t` stays inside the
|
|
135
|
+
* closure (live locale updates); the key display is static per session.
|
|
136
|
+
*
|
|
137
|
+
* With collapseKey "off" the router and raw listener never toggle `collapsed`,
|
|
138
|
+
* but `toggleCollapsedExternal()` is a public ungated entry — fall back to the
|
|
139
|
+
* cancel-only line rather than rendering a literal "Off to expand".
|
|
140
|
+
*/
|
|
141
|
+
private buildCollapsedRender(theme: Theme): (width: number) => string[] {
|
|
142
|
+
const collapseKeyDisplay = formatKeySpecForDisplay(this.collapseKey);
|
|
143
|
+
const collapsedHintLine = (): string =>
|
|
144
|
+
this.collapseKey === COLLAPSE_KEY_OFF
|
|
145
|
+
? t("hint.cancel", HINT_PART_CANCEL)
|
|
146
|
+
: t("hint.expand_line", COLLAPSED_HINT_TEMPLATE).replace(KEY_PLACEHOLDER, collapseKeyDisplay);
|
|
147
|
+
return (_width: number): string[] => [theme.fg("dim", ` ${collapsedHintLine()} `)];
|
|
121
148
|
}
|
|
122
149
|
|
|
123
150
|
dispatch(data: string): void {
|
|
@@ -154,18 +181,7 @@ export class QuestionnaireSession {
|
|
|
154
181
|
this.inlineInput.setText("");
|
|
155
182
|
return;
|
|
156
183
|
case "open_input_editor":
|
|
157
|
-
|
|
158
|
-
this.inputEditorOpen = true;
|
|
159
|
-
void this.editInput(effect.value).then(
|
|
160
|
-
(value) => {
|
|
161
|
-
this.inputEditorOpen = false;
|
|
162
|
-
if (value !== undefined) this.commit({ kind: "input_replace", value });
|
|
163
|
-
},
|
|
164
|
-
() => {
|
|
165
|
-
// The host callback reports launch errors; retain the draft and restore input handling.
|
|
166
|
-
this.inputEditorOpen = false;
|
|
167
|
-
},
|
|
168
|
-
);
|
|
184
|
+
this.openInputEditorAsync(effect.value);
|
|
169
185
|
return;
|
|
170
186
|
case "set_notes_value":
|
|
171
187
|
this.notesInput.setText(effect.value);
|
|
@@ -178,8 +194,11 @@ export class QuestionnaireSession {
|
|
|
178
194
|
return;
|
|
179
195
|
case "set_overlay_hidden":
|
|
180
196
|
// No-op until `setOverlayHandle` has been called (the handle arrives via
|
|
181
|
-
// `ctx.ui.custom`'s `onHandle` right after the overlay is shown)
|
|
182
|
-
|
|
197
|
+
// `ctx.ui.custom`'s `onHandle` right after the overlay is shown), and
|
|
198
|
+
// suppressed entirely when no raw terminal listener exists — hiding would
|
|
199
|
+
// then be irreversible (pi-tui routes no input to a hidden overlay), so the
|
|
200
|
+
// visible one-line collapsed row serves as the fallback rendering instead.
|
|
201
|
+
if (this.canReopenWhileHidden) this.overlayHandle?.setHidden(effect.hidden);
|
|
183
202
|
return;
|
|
184
203
|
case "done":
|
|
185
204
|
this.done(effect.result);
|
|
@@ -187,6 +206,22 @@ export class QuestionnaireSession {
|
|
|
187
206
|
}
|
|
188
207
|
}
|
|
189
208
|
|
|
209
|
+
/** Opens Pi's configured external editor; on success commits the replacement buffer, on reported failure retains the draft. */
|
|
210
|
+
private openInputEditorAsync(value: string): void {
|
|
211
|
+
if (this.inputEditorOpen) return;
|
|
212
|
+
this.inputEditorOpen = true;
|
|
213
|
+
void this.editInput(value).then(
|
|
214
|
+
(edited) => {
|
|
215
|
+
this.inputEditorOpen = false;
|
|
216
|
+
if (edited !== undefined) this.commit({ kind: "input_replace", value: edited });
|
|
217
|
+
},
|
|
218
|
+
() => {
|
|
219
|
+
// The host callback reports launch errors; retain the draft and restore input handling.
|
|
220
|
+
this.inputEditorOpen = false;
|
|
221
|
+
},
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
190
225
|
/**
|
|
191
226
|
* Per-keystroke `ignore` fast path: delegates text editing to Pi's headless
|
|
192
227
|
* multiline `Editor`, including paste, undo, cursor movement, and configured
|