@herbertgao/pi-extensions 2026.8.14 → 2026.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -13
- package/THIRD_PARTY_NOTICES.md +26 -1
- package/node_modules/@czottmann/pi-automode/CHANGELOG.md +8 -1
- package/node_modules/@czottmann/pi-automode/README.md +4 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +71 -9
- package/node_modules/@czottmann/pi-automode/package.json +1 -1
- package/node_modules/@herbertgao/pi-bark/LICENSE +21 -0
- package/node_modules/@herbertgao/pi-bark/README.md +44 -0
- package/node_modules/@herbertgao/pi-bark/assets/pi-icon.png +0 -0
- package/node_modules/@herbertgao/pi-bark/package.json +53 -0
- package/node_modules/@herbertgao/pi-bark/src/index.ts +178 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +20 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +46 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +6 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +5 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +34 -30
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/README.md +129 -88
- package/node_modules/@narumitw/pi-btw/dist/index.ts +283 -59
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
- package/node_modules/@narumitw/pi-btw/package.json +5 -8
- package/node_modules/@narumitw/pi-btw/src/btw.ts +31 -26
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +264 -24
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -2
- package/node_modules/@narumitw/pi-btw/src/settings.ts +16 -0
- package/node_modules/@narumitw/pi-btw/src/text.ts +18 -0
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +5 -16
- package/node_modules/@narumitw/pi-caffeinate/LICENSE +21 -0
- package/node_modules/@narumitw/pi-caffeinate/README.md +224 -0
- package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +1112 -0
- package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-caffeinate/package.json +53 -0
- package/node_modules/@narumitw/pi-caffeinate/src/caffeinate.ts +811 -0
- package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +153 -0
- package/node_modules/@narumitw/pi-caffeinate/src/index.ts +1 -0
- package/node_modules/@narumitw/pi-caffeinate/src/inhibitor-process.ts +43 -0
- package/node_modules/@narumitw/pi-caffeinate/src/inhibitors.ts +167 -0
- package/node_modules/@narumitw/pi-caffeinate/src/settings.ts +202 -0
- package/node_modules/@tifan/pi-handoff/README.md +39 -13
- package/node_modules/@tifan/pi-handoff/package.json +2 -2
- package/node_modules/@tifan/pi-handoff/src/index.ts +166 -5
- package/node_modules/@tifan/pi-recap/README.md +1 -1
- package/node_modules/@tifan/pi-recap/package.json +2 -2
- package/node_modules/@tifan/pi-recap/src/index.ts +15 -0
- package/node_modules/@tifan/pi-rename/README.md +42 -21
- package/node_modules/@tifan/pi-rename/package.json +1 -1
- package/node_modules/@tifan/pi-rename/src/index.ts +62 -11
- package/node_modules/@tifan/pi-rename/src/language.ts +18 -0
- package/node_modules/@tifan/pi-rename/src/models.ts +69 -13
- package/node_modules/@tifan/pi-rename/src/naming.ts +36 -7
- package/node_modules/@tifan/pi-rename/src/sanitize.ts +22 -14
- package/node_modules/pi-lens/CHANGELOG.md +320 -0
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +79 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +16 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +13 -6
- package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +12 -0
- package/node_modules/pi-lens/dist/clients/biome-client.js +7 -0
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +20 -0
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +56 -19
- package/node_modules/pi-lens/dist/clients/build-identity.js +95 -0
- package/node_modules/pi-lens/dist/clients/bus-publish.js +3 -0
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +2 -1
- package/node_modules/pi-lens/dist/clients/cache-observability.js +2 -1
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +15 -1
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +14 -11
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +56 -4
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +14 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +20 -3
- package/node_modules/pi-lens/dist/clients/deps/minimatch.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +39 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +150 -9
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +231 -9
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +205 -100
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +87 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +21 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +174 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +54 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +37 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +11 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +8 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +76 -25
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +7 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +66 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +157 -7
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +5 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +28 -3
- package/node_modules/pi-lens/dist/clients/git-guard.js +197 -47
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +32 -1
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +5 -1
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +7 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +131 -34
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +3 -1
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +4 -13
- package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +145 -0
- package/node_modules/pi-lens/dist/clients/instance-registry.js +432 -122
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
- package/node_modules/pi-lens/dist/clients/language-profile.js +4 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +19 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +3 -1
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +4 -0
- package/node_modules/pi-lens/dist/clients/lsp/client.js +516 -31
- package/node_modules/pi-lens/dist/clients/lsp/config.js +25 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +9 -4
- package/node_modules/pi-lens/dist/clients/lsp/index.js +509 -182
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +5 -0
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +36 -8
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +116 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +142 -13
- package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +99 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +16 -6
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +32 -23
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +16 -2
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +12 -6
- package/node_modules/pi-lens/dist/clients/mcp/session.js +14 -1
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +23 -1
- package/node_modules/pi-lens/dist/clients/message-end-attribution.js +30 -0
- package/node_modules/pi-lens/dist/clients/metrics-history.js +21 -4
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +232 -18
- package/node_modules/pi-lens/dist/clients/package-manager.js +6 -18
- package/node_modules/pi-lens/dist/clients/path-keyed-map.js +14 -1
- package/node_modules/pi-lens/dist/clients/path-utils.js +32 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +0 -8
- package/node_modules/pi-lens/dist/clients/process-singletons.js +210 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +10 -4
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +33 -11
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +24 -2
- package/node_modules/pi-lens/dist/clients/read-guard.js +74 -7
- package/node_modules/pi-lens/dist/clients/recent-touches.js +17 -5
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +639 -62
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +5 -1
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +21 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +68 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +30 -12
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +15 -0
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +106 -7
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +242 -27
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +158 -18
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +48 -17
- package/node_modules/pi-lens/dist/clients/session-lifecycle.js +238 -42
- package/node_modules/pi-lens/dist/clients/sg-runner.js +27 -15
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +338 -0
- package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +37 -0
- package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +15 -1
- package/node_modules/pi-lens/dist/clients/startup-scan.js +6 -7
- package/node_modules/pi-lens/dist/clients/startup-timing.js +69 -4
- package/node_modules/pi-lens/dist/clients/string-utils.js +20 -0
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +419 -88
- package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +11 -5
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +17 -1
- package/node_modules/pi-lens/dist/clients/warm-attach.js +8 -10
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +13 -1
- package/node_modules/pi-lens/dist/clients/word-index-store.js +448 -0
- package/node_modules/pi-lens/dist/clients/word-index.js +582 -104
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +17 -0
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +26 -6
- package/node_modules/pi-lens/dist/index.js +11243 -6651
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +8 -7
- package/node_modules/pi-lens/dist/mcp/cli.js +3 -1
- package/node_modules/pi-lens/dist/mcp/server.js +6 -1
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +4 -0
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +14 -5
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
- package/node_modules/pi-lens/docs/agent-guide.md +4 -2
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +41 -10
- package/node_modules/pi-lens/docs/astplayground.md +26 -17
- package/node_modules/pi-lens/package.json +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-important.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +42 -0
- package/node_modules/pi-mcp-adapter/README.md +15 -15
- package/node_modules/pi-mcp-adapter/commands.ts +59 -22
- package/node_modules/pi-mcp-adapter/config.ts +18 -4
- package/node_modules/pi-mcp-adapter/direct-tools.ts +26 -6
- package/node_modules/pi-mcp-adapter/dist/config.d.ts +4 -0
- package/node_modules/pi-mcp-adapter/dist/config.js +13 -4
- package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/types.d.ts +12 -4
- package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
- package/node_modules/pi-mcp-adapter/error-signal.ts +15 -4
- package/node_modules/pi-mcp-adapter/errors.ts +141 -0
- package/node_modules/pi-mcp-adapter/host-html-template.ts +58 -5
- package/node_modules/pi-mcp-adapter/index.ts +2 -3
- package/node_modules/pi-mcp-adapter/init.ts +5 -0
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +105 -32
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +30 -9
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +66 -28
- package/node_modules/pi-mcp-adapter/mcp-status.ts +2 -0
- package/node_modules/pi-mcp-adapter/package.json +2 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +88 -25
- package/node_modules/pi-mcp-adapter/sandbox-proxy-template.ts +217 -0
- package/node_modules/pi-mcp-adapter/server-manager.ts +343 -6
- package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +1 -0
- package/node_modules/pi-mcp-adapter/types.ts +19 -4
- package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +18 -2
- package/node_modules/pi-mcp-adapter/ui-server.ts +179 -54
- package/node_modules/pi-mcp-adapter/ui-session.ts +28 -8
- package/node_modules/pi-web-access/CHANGELOG.md +20 -0
- package/node_modules/pi-web-access/README.md +15 -2
- package/node_modules/pi-web-access/extract.ts +57 -12
- package/node_modules/pi-web-access/github-extract.ts +47 -3
- package/node_modules/pi-web-access/index.ts +1 -1
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/page-query.ts +61 -8
- package/package.json +21 -17
- package/node_modules/@tifan/pi-titlebar-spinner/README.md +0 -24
- package/node_modules/@tifan/pi-titlebar-spinner/package.json +0 -39
- package/node_modules/@tifan/pi-titlebar-spinner/src/index.ts +0 -49
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-btw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.57.0",
|
|
4
4
|
"description": "Pi extension that adds a /btw side-question command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,9 +23,6 @@
|
|
|
23
23
|
"./dist/index.ts"
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
|
-
"piExtension": {
|
|
27
|
-
"lifecycle": "stable"
|
|
28
|
-
},
|
|
29
26
|
"scripts": {
|
|
30
27
|
"build": "node scripts/build-runtime.mjs",
|
|
31
28
|
"check": "npm run build && biome check . && npm run typecheck",
|
|
@@ -34,10 +31,10 @@
|
|
|
34
31
|
"prepack": "npm run build"
|
|
35
32
|
},
|
|
36
33
|
"devDependencies": {
|
|
37
|
-
"@biomejs/biome": "2.5.
|
|
38
|
-
"@earendil-works/pi-ai": "0.
|
|
39
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
40
|
-
"@earendil-works/pi-tui": "0.
|
|
34
|
+
"@biomejs/biome": "2.5.11",
|
|
35
|
+
"@earendil-works/pi-ai": "0.85.0",
|
|
36
|
+
"@earendil-works/pi-coding-agent": "0.85.0",
|
|
37
|
+
"@earendil-works/pi-tui": "0.85.0",
|
|
41
38
|
"esbuild": "0.28.2",
|
|
42
39
|
"typescript": "7.0.2"
|
|
43
40
|
},
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
} from "./menu.js";
|
|
34
34
|
import {
|
|
35
35
|
type BtwSettings,
|
|
36
|
+
effectiveFullscreenCopyOnSelect,
|
|
36
37
|
effectiveRememberThinkingLevelChanges,
|
|
37
38
|
parseBtwModelReference,
|
|
38
39
|
readBtwSettings,
|
|
@@ -193,7 +194,7 @@ export async function loadBtwThinkingLevel(
|
|
|
193
194
|
|
|
194
195
|
options.warn?.(
|
|
195
196
|
sanitizeSingleLine(
|
|
196
|
-
`pi-btw settings ignored: ${settings.reason}; expected optional model "provider/model-id", omitted thinkingLevel for Same as main thread or thinkingLevel "${BTW_THINKING_LEVELS.join('" | "')}", and boolean
|
|
197
|
+
`pi-btw settings ignored: ${settings.reason}; expected optional model "provider/model-id", omitted thinkingLevel for Same as main thread or thinkingLevel "${BTW_THINKING_LEVELS.join('" | "')}", boolean rememberThinkingLevelChanges, and boolean fullscreenCopyOnSelect. Using current Pi thinking level.`,
|
|
197
198
|
),
|
|
198
199
|
);
|
|
199
200
|
return currentThinkingLevel;
|
|
@@ -314,31 +315,35 @@ export default function btw(pi: ExtensionAPI, dependencies: BtwExtensionDependen
|
|
|
314
315
|
const startingTurnCount = state?.thread.turns.length ?? 0;
|
|
315
316
|
|
|
316
317
|
try {
|
|
317
|
-
await runFullscreen(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
state
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
318
|
+
await runFullscreen(
|
|
319
|
+
ctx,
|
|
320
|
+
(fullscreenCtx) => {
|
|
321
|
+
if (!state) {
|
|
322
|
+
const createdAt = Date.now();
|
|
323
|
+
state = {
|
|
324
|
+
id: `btw-${nextThreadNumber}`,
|
|
325
|
+
thread: createSideThread(
|
|
326
|
+
selectedConversationContext ??
|
|
327
|
+
buildConversationContext(fullscreenCtx.sessionManager.getBranch()),
|
|
328
|
+
),
|
|
329
|
+
thinkingLevel: settings.thinkingLevel ?? pi.getThinkingLevel(),
|
|
330
|
+
createdAt,
|
|
331
|
+
updatedAt: createdAt,
|
|
332
|
+
};
|
|
333
|
+
nextThreadNumber += 1;
|
|
334
|
+
}
|
|
335
|
+
return runThread({
|
|
336
|
+
initialQuestion: question || undefined,
|
|
337
|
+
selected: resolution.selected,
|
|
338
|
+
thinkingLevel: state.thinkingLevel,
|
|
339
|
+
rememberThinkingLevelChanges:
|
|
340
|
+
!sameAsMainThinkingLevel && effectiveRememberThinkingLevelChanges(settings),
|
|
341
|
+
state,
|
|
342
|
+
ctx: fullscreenCtx,
|
|
343
|
+
});
|
|
344
|
+
},
|
|
345
|
+
{ copyOnSelect: effectiveFullscreenCopyOnSelect(settings) },
|
|
346
|
+
);
|
|
342
347
|
} finally {
|
|
343
348
|
if (state?.title && state.thread.turns.length > 0) {
|
|
344
349
|
if (state.thread.turns.length > startingTurnCount) {
|
|
@@ -8,16 +8,18 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
type Component,
|
|
10
10
|
isKeyRelease,
|
|
11
|
+
isKittyProtocolActive,
|
|
11
12
|
Key,
|
|
12
13
|
matchesKey,
|
|
13
14
|
type OverlayHandle,
|
|
15
|
+
parseKey,
|
|
14
16
|
type TUI,
|
|
15
17
|
TuiAltScreen,
|
|
16
18
|
type TuiInputListener,
|
|
17
19
|
type TuiInputListenerResult,
|
|
18
20
|
truncateToWidth,
|
|
19
21
|
} from "@earendil-works/pi-tui";
|
|
20
|
-
import { sanitizeSingleLine } from "./text.js";
|
|
22
|
+
import { formatKeyLabel, sanitizeSingleLine } from "./text.js";
|
|
21
23
|
|
|
22
24
|
type BtwCustomOptions = Parameters<ExtensionCommandContext["ui"]["custom"]>[1];
|
|
23
25
|
type BtwCustomFactory<T> = (
|
|
@@ -30,6 +32,7 @@ type BtwCustomFactory<T> = (
|
|
|
30
32
|
type BtwFullscreenTui = TUI & {
|
|
31
33
|
flash?: (message: string, durationMs?: number) => void;
|
|
32
34
|
setLayoutRoot(component: Component | undefined): void;
|
|
35
|
+
addInputListenerBeforeAll?(listener: TuiInputListener): () => void;
|
|
33
36
|
addInputListenerBeforeViewport?(listener: TuiInputListener): () => void;
|
|
34
37
|
};
|
|
35
38
|
|
|
@@ -37,17 +40,28 @@ export interface BtwFullscreenLayoutComponent extends Component {
|
|
|
37
40
|
getFullscreenLayout(): Component;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
export
|
|
43
|
+
export interface BtwFullscreenOptions {
|
|
44
|
+
copyOnSelect?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type BtwFullscreenTuiFactory = (
|
|
48
|
+
parent: TUI,
|
|
49
|
+
theme: Theme,
|
|
50
|
+
keybindings: KeybindingsManager,
|
|
51
|
+
options: BtwFullscreenOptions,
|
|
52
|
+
) => BtwFullscreenTui;
|
|
41
53
|
|
|
42
54
|
export interface BtwFullscreenDependencies {
|
|
43
55
|
createTui?: BtwFullscreenTuiFactory;
|
|
44
56
|
openUrl?: (url: string) => void;
|
|
45
57
|
copyToClipboard?: (text: string) => Promise<void>;
|
|
58
|
+
manualSelectionCopySupported?: boolean;
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
export type RunBtwFullscreen = <T>(
|
|
49
62
|
ctx: ExtensionCommandContext,
|
|
50
63
|
run: (ctx: ExtensionCommandContext) => Promise<T>,
|
|
64
|
+
options?: BtwFullscreenOptions,
|
|
51
65
|
) => Promise<T>;
|
|
52
66
|
|
|
53
67
|
type FullscreenOutcome<T> = { kind: "completed"; value: T } | { kind: "failed"; error: unknown };
|
|
@@ -62,14 +76,23 @@ class FullscreenUiDisposedError extends Error {
|
|
|
62
76
|
export async function runBtwFullscreen<T>(
|
|
63
77
|
ctx: ExtensionCommandContext,
|
|
64
78
|
run: (ctx: ExtensionCommandContext) => Promise<T>,
|
|
79
|
+
options: BtwFullscreenOptions = {},
|
|
65
80
|
dependencies: BtwFullscreenDependencies = {},
|
|
66
81
|
): Promise<T> {
|
|
67
82
|
const createTui =
|
|
68
83
|
dependencies.createTui ??
|
|
69
|
-
((
|
|
84
|
+
((
|
|
85
|
+
parent: TUI,
|
|
86
|
+
theme: Theme,
|
|
87
|
+
keybindings: KeybindingsManager,
|
|
88
|
+
fullscreenOptions: BtwFullscreenOptions,
|
|
89
|
+
) =>
|
|
70
90
|
createBtwFullscreenTui(
|
|
71
91
|
parent,
|
|
72
92
|
theme,
|
|
93
|
+
keybindings,
|
|
94
|
+
fullscreenOptions.copyOnSelect ?? true,
|
|
95
|
+
dependencies.manualSelectionCopySupported ?? hasManualSelectionCopyApi(),
|
|
73
96
|
dependencies.openUrl ?? openUrlInBrowser,
|
|
74
97
|
dependencies.copyToClipboard ?? copyToHostClipboard,
|
|
75
98
|
));
|
|
@@ -94,6 +117,7 @@ export async function runBtwFullscreen<T>(
|
|
|
94
117
|
done(value);
|
|
95
118
|
},
|
|
96
119
|
createTui,
|
|
120
|
+
options,
|
|
97
121
|
);
|
|
98
122
|
return host;
|
|
99
123
|
},
|
|
@@ -114,6 +138,7 @@ export async function runBtwFullscreen<T>(
|
|
|
114
138
|
}
|
|
115
139
|
|
|
116
140
|
type BtwInputListeners = {
|
|
141
|
+
beforeAll: Set<TuiInputListener>;
|
|
117
142
|
beforeViewport: Set<TuiInputListener>;
|
|
118
143
|
regular: Set<TuiInputListener>;
|
|
119
144
|
};
|
|
@@ -122,7 +147,7 @@ const btwInputListeners = new WeakMap<BtwTuiAltScreen, BtwInputListeners>();
|
|
|
122
147
|
|
|
123
148
|
function dispatchBtwInput(listeners: BtwInputListeners, data: string): TuiInputListenerResult {
|
|
124
149
|
let current = data;
|
|
125
|
-
for (const group of [listeners.beforeViewport, listeners.regular]) {
|
|
150
|
+
for (const group of [listeners.beforeAll, listeners.beforeViewport, listeners.regular]) {
|
|
126
151
|
for (const listener of group) {
|
|
127
152
|
const result = listener(current);
|
|
128
153
|
if (result?.consume) return result;
|
|
@@ -133,10 +158,15 @@ function dispatchBtwInput(listeners: BtwInputListeners, data: string): TuiInputL
|
|
|
133
158
|
}
|
|
134
159
|
|
|
135
160
|
class BtwTuiAltScreen extends TuiAltScreen {
|
|
161
|
+
hasFocusedOverlay(): boolean {
|
|
162
|
+
return this.isOverlayFocused();
|
|
163
|
+
}
|
|
164
|
+
|
|
136
165
|
override addInputListener(listener: TuiInputListener): () => void {
|
|
137
166
|
let listeners = btwInputListeners.get(this);
|
|
138
167
|
if (!listeners) {
|
|
139
168
|
const registeredListeners: BtwInputListeners = {
|
|
169
|
+
beforeAll: new Set(),
|
|
140
170
|
beforeViewport: new Set(),
|
|
141
171
|
regular: new Set(),
|
|
142
172
|
};
|
|
@@ -148,6 +178,13 @@ class BtwTuiAltScreen extends TuiAltScreen {
|
|
|
148
178
|
return () => listeners.regular.delete(listener);
|
|
149
179
|
}
|
|
150
180
|
|
|
181
|
+
addInputListenerBeforeAll(listener: TuiInputListener): () => void {
|
|
182
|
+
const listeners = btwInputListeners.get(this);
|
|
183
|
+
if (!listeners) return super.addInputListener(listener);
|
|
184
|
+
listeners.beforeAll.add(listener);
|
|
185
|
+
return () => listeners.beforeAll.delete(listener);
|
|
186
|
+
}
|
|
187
|
+
|
|
151
188
|
addInputListenerBeforeViewport(listener: TuiInputListener): () => void {
|
|
152
189
|
const listeners = btwInputListeners.get(this);
|
|
153
190
|
if (!listeners) return super.addInputListener(listener);
|
|
@@ -161,33 +198,208 @@ class BtwTuiAltScreen extends TuiAltScreen {
|
|
|
161
198
|
super.removeInputListener(listener);
|
|
162
199
|
return;
|
|
163
200
|
}
|
|
201
|
+
listeners.beforeAll.delete(listener);
|
|
164
202
|
listeners.beforeViewport.delete(listener);
|
|
165
203
|
listeners.regular.delete(listener);
|
|
166
204
|
}
|
|
167
205
|
}
|
|
168
206
|
|
|
207
|
+
const BRACKETED_PASTE_START = "\u001b[200~";
|
|
208
|
+
const BRACKETED_PASTE_END = "\u001b[201~";
|
|
209
|
+
|
|
210
|
+
// TuiAltScreen evaluates these actions before bottom, so shared keys cannot jump to latest.
|
|
211
|
+
const ALT_SCREEN_ACTIONS_BEFORE_BOTTOM = [
|
|
212
|
+
"tui.altScreen.search",
|
|
213
|
+
"tui.altScreen.searchNext",
|
|
214
|
+
"tui.altScreen.searchPrevious",
|
|
215
|
+
"tui.altScreen.searchClose",
|
|
216
|
+
"tui.altScreen.pageUp",
|
|
217
|
+
"tui.altScreen.pageDown",
|
|
218
|
+
"tui.altScreen.halfPageUp",
|
|
219
|
+
"tui.altScreen.halfPageDown",
|
|
220
|
+
"tui.altScreen.lineUp",
|
|
221
|
+
"tui.altScreen.lineDown",
|
|
222
|
+
"tui.altScreen.previousPrompt",
|
|
223
|
+
"tui.altScreen.nextPrompt",
|
|
224
|
+
"tui.altScreen.top",
|
|
225
|
+
] as const;
|
|
226
|
+
const KEY_MODIFIER_ORDER = ["shift", "ctrl", "alt", "super"] as const;
|
|
227
|
+
const MATCHABLE_SPECIAL_KEYS = new Set([
|
|
228
|
+
"space",
|
|
229
|
+
"tab",
|
|
230
|
+
"enter",
|
|
231
|
+
"backspace",
|
|
232
|
+
"delete",
|
|
233
|
+
"insert",
|
|
234
|
+
"home",
|
|
235
|
+
"end",
|
|
236
|
+
"pageup",
|
|
237
|
+
"pagedown",
|
|
238
|
+
"up",
|
|
239
|
+
"down",
|
|
240
|
+
"left",
|
|
241
|
+
"right",
|
|
242
|
+
]);
|
|
243
|
+
const MATCHABLE_SYMBOL_KEYS = new Set("`-=[]\\;',./!@#$%^&*()_+|~{}:<>?");
|
|
244
|
+
|
|
245
|
+
function normalizedKeyId(key: string): string {
|
|
246
|
+
const parts = key.toLowerCase().split("+");
|
|
247
|
+
const base = parts.at(-1);
|
|
248
|
+
if (!base) return "";
|
|
249
|
+
const normalizedBase = base === "esc" ? "escape" : base === "return" ? "enter" : base;
|
|
250
|
+
const modifiers = KEY_MODIFIER_ORDER.filter((modifier) => parts.includes(modifier));
|
|
251
|
+
return [...modifiers, normalizedBase].join("+");
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function formatEffectiveKeyLabel(key: string): string {
|
|
255
|
+
const parts = key.split("+");
|
|
256
|
+
const base = parts.at(-1);
|
|
257
|
+
if (base === "pageup") parts[parts.length - 1] = "pageUp";
|
|
258
|
+
if (base === "pagedown") parts[parts.length - 1] = "pageDown";
|
|
259
|
+
return formatKeyLabel(parts.join("+"));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function canMatchKeyInput(key: string): boolean {
|
|
263
|
+
const parts = key.split("+");
|
|
264
|
+
const base = parts.at(-1) ?? "";
|
|
265
|
+
const modifiers = parts.slice(0, -1);
|
|
266
|
+
if (base === "escape") return modifiers.length === 0;
|
|
267
|
+
if (base === "clear") {
|
|
268
|
+
return (
|
|
269
|
+
modifiers.length === 0 ||
|
|
270
|
+
(modifiers.length === 1 && (modifiers[0] === "shift" || modifiers[0] === "ctrl"))
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
if (/^f(?:[1-9]|1[0-2])$/u.test(base)) return modifiers.length === 0;
|
|
274
|
+
return (
|
|
275
|
+
MATCHABLE_SPECIAL_KEYS.has(base) ||
|
|
276
|
+
(base.length === 1 && (/^[a-z0-9]$/u.test(base) || MATCHABLE_SYMBOL_KEYS.has(base)))
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function rawCtrlInput(base: string): string | undefined {
|
|
281
|
+
if (base.length !== 1) return undefined;
|
|
282
|
+
const rawBase = base === "-" ? "_" : base;
|
|
283
|
+
if (!"abcdefghijklmnopqrstuvwxyz[\\]_".includes(rawBase)) return undefined;
|
|
284
|
+
return String.fromCharCode(rawBase.charCodeAt(0) & 0x1f);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function legacyRawInput(key: string): string | undefined {
|
|
288
|
+
const parts = key.split("+");
|
|
289
|
+
const base = parts.at(-1) ?? "";
|
|
290
|
+
if (parts.length === 2 && parts[0] === "ctrl") return rawCtrlInput(base);
|
|
291
|
+
if (isKittyProtocolActive()) return undefined;
|
|
292
|
+
if (parts.length === 2 && parts[0] === "alt" && base.length === 1) return `\u001b${base}`;
|
|
293
|
+
if (parts.length === 3 && parts[0] === "ctrl" && parts[1] === "alt") {
|
|
294
|
+
const input = rawCtrlInput(base);
|
|
295
|
+
return input ? `\u001b${input}` : undefined;
|
|
296
|
+
}
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Mirror matchesKey(), using parseKey() to canonicalize IDs that share legacy raw input.
|
|
301
|
+
function keyInputIdentity(key: string): string {
|
|
302
|
+
const identity = normalizedKeyId(key);
|
|
303
|
+
const input = legacyRawInput(identity);
|
|
304
|
+
return input ? normalizedKeyId(parseKey(input) ?? identity) : identity;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function hasManualSelectionCopyApi(): boolean {
|
|
308
|
+
return (
|
|
309
|
+
typeof TuiAltScreen.prototype.hasActiveSelection === "function" &&
|
|
310
|
+
typeof TuiAltScreen.prototype.copyActiveSelectionToClipboard === "function"
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
169
314
|
function createBtwFullscreenTui(
|
|
170
315
|
parent: TUI,
|
|
171
316
|
theme: Theme,
|
|
317
|
+
keybindings: KeybindingsManager,
|
|
318
|
+
copyOnSelect: boolean,
|
|
319
|
+
manualSelectionCopySupported: boolean,
|
|
172
320
|
openUrl: (url: string) => void,
|
|
173
321
|
copyToClipboard: (text: string) => Promise<void>,
|
|
174
322
|
): BtwFullscreenTui {
|
|
323
|
+
if (!copyOnSelect && !manualSelectionCopySupported) {
|
|
324
|
+
throw new Error(
|
|
325
|
+
"Manual fullscreen selection copying is unavailable in this Pi version; update Pi or enable automatic selection copying.",
|
|
326
|
+
);
|
|
327
|
+
}
|
|
175
328
|
const styleSearchMatch = (text: string) =>
|
|
176
329
|
theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
330
|
+
const fullscreen = new BtwTuiAltScreen(
|
|
331
|
+
parent.terminal,
|
|
332
|
+
parent.getShowHardwareCursor(),
|
|
333
|
+
undefined,
|
|
334
|
+
{
|
|
335
|
+
mouse: true,
|
|
336
|
+
copyOnSelect,
|
|
337
|
+
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
338
|
+
scrollToEndIndicator: () => {
|
|
339
|
+
const unavailableKeyIdentities = new Set<string>([keyInputIdentity(Key.ctrl("c"))]);
|
|
340
|
+
for (const action of ALT_SCREEN_ACTIONS_BEFORE_BOTTOM) {
|
|
341
|
+
for (const actionKey of keybindings.getKeys(action)) {
|
|
342
|
+
unavailableKeyIdentities.add(keyInputIdentity(String(actionKey)));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (!copyOnSelect) {
|
|
346
|
+
for (const copyKey of keybindings.getKeys("app.message.copy")) {
|
|
347
|
+
unavailableKeyIdentities.add(keyInputIdentity(String(copyKey)));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const key = keybindings
|
|
351
|
+
.getKeys("tui.altScreen.bottom")
|
|
352
|
+
.map((candidate) => keyInputIdentity(String(candidate)))
|
|
353
|
+
.find(
|
|
354
|
+
(identity) =>
|
|
355
|
+
identity &&
|
|
356
|
+
canMatchKeyInput(identity) &&
|
|
357
|
+
!unavailableKeyIdentities.has(identity) &&
|
|
358
|
+
formatEffectiveKeyLabel(identity),
|
|
359
|
+
);
|
|
360
|
+
const label = theme.fg("text", " ↓ Jump to latest message");
|
|
361
|
+
const shortcut = key ? theme.fg("muted", ` · ${formatEffectiveKeyLabel(key)}`) : "";
|
|
362
|
+
return theme.bg("selectedBg", `${label}${shortcut} `);
|
|
363
|
+
},
|
|
364
|
+
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
365
|
+
openUrl,
|
|
366
|
+
copySelection: async (text) => {
|
|
367
|
+
try {
|
|
368
|
+
await copyToClipboard(text);
|
|
369
|
+
return true;
|
|
370
|
+
} catch {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
},
|
|
189
374
|
},
|
|
190
|
-
|
|
375
|
+
);
|
|
376
|
+
if (!copyOnSelect) {
|
|
377
|
+
let isInBracketedPaste = false;
|
|
378
|
+
fullscreen.addInputListenerBeforeViewport((data) => {
|
|
379
|
+
const wasInBracketedPaste = isInBracketedPaste;
|
|
380
|
+
const startsBracketedPaste = data.includes(BRACKETED_PASTE_START);
|
|
381
|
+
if (startsBracketedPaste) isInBracketedPaste = true;
|
|
382
|
+
if (isInBracketedPaste && data.includes(BRACKETED_PASTE_END)) {
|
|
383
|
+
isInBracketedPaste = false;
|
|
384
|
+
}
|
|
385
|
+
if (
|
|
386
|
+
wasInBracketedPaste ||
|
|
387
|
+
startsBracketedPaste ||
|
|
388
|
+
fullscreen.hasFocusedOverlay() ||
|
|
389
|
+
isKeyRelease(data) ||
|
|
390
|
+
!keybindings.matches(data, "app.message.copy")
|
|
391
|
+
) {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
if (!fullscreen.hasActiveSelection()) {
|
|
395
|
+
fullscreen.flash("No selection to copy");
|
|
396
|
+
return { consume: true };
|
|
397
|
+
}
|
|
398
|
+
void fullscreen.copyActiveSelectionToClipboard().catch(() => fullscreen.flash("Copy failed"));
|
|
399
|
+
return { consume: true };
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
return fullscreen;
|
|
191
403
|
}
|
|
192
404
|
|
|
193
405
|
// Pi does not export its browser opener, so mirror its shell-free launcher for this isolated TUI.
|
|
@@ -213,9 +425,11 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
213
425
|
private disposed = false;
|
|
214
426
|
private finished = false;
|
|
215
427
|
private parentStopped = false;
|
|
216
|
-
private
|
|
428
|
+
private parentRestoreAttempted = false;
|
|
217
429
|
private fullscreenCreated = false;
|
|
218
430
|
private fullscreenStopped = false;
|
|
431
|
+
private parentRestoreQueued = false;
|
|
432
|
+
private parentRestorePromise: Promise<void> | undefined;
|
|
219
433
|
private cleanupError: unknown;
|
|
220
434
|
|
|
221
435
|
constructor(
|
|
@@ -226,6 +440,7 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
226
440
|
private readonly run: (ctx: ExtensionCommandContext) => Promise<T>,
|
|
227
441
|
private readonly done: (outcome: FullscreenOutcome<T>) => void,
|
|
228
442
|
private readonly createTui: BtwFullscreenTuiFactory,
|
|
443
|
+
private readonly options: BtwFullscreenOptions,
|
|
229
444
|
) {
|
|
230
445
|
queueMicrotask(() => void this.start());
|
|
231
446
|
}
|
|
@@ -255,11 +470,12 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
255
470
|
this.parent.stop({ preserveScreen: true });
|
|
256
471
|
this.parentStopped = true;
|
|
257
472
|
if (this.disposed) throw new FullscreenUiDisposedError();
|
|
258
|
-
this.fullscreen = this.createTui(this.parent, this.theme);
|
|
473
|
+
this.fullscreen = this.createTui(this.parent, this.theme, this.keybindings, this.options);
|
|
259
474
|
this.fullscreenCreated = true;
|
|
260
475
|
this.fullscreen.start();
|
|
261
476
|
// Waiting for the custom promise would leave follow-up keys bound to the side TUI.
|
|
262
477
|
const addHardCancelListener =
|
|
478
|
+
this.fullscreen.addInputListenerBeforeAll?.bind(this.fullscreen) ??
|
|
263
479
|
this.fullscreen.addInputListenerBeforeViewport?.bind(this.fullscreen) ??
|
|
264
480
|
this.fullscreen.addInputListener.bind(this.fullscreen);
|
|
265
481
|
this.removeHardCancelListener = addHardCancelListener((data) => {
|
|
@@ -267,7 +483,11 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
267
483
|
try {
|
|
268
484
|
this.hardCancelActiveCustom?.();
|
|
269
485
|
} finally {
|
|
270
|
-
|
|
486
|
+
// ProcessTerminal.stop() destroys its active input buffer. Keep cancellation
|
|
487
|
+
// synchronous, then drain input before the physical Windows terminal handoff.
|
|
488
|
+
// Pi has no public input injection, so do not replay bytes already coalesced
|
|
489
|
+
// behind the hard-cancel key.
|
|
490
|
+
this.queueParentRestore();
|
|
271
491
|
}
|
|
272
492
|
return { consume: true };
|
|
273
493
|
});
|
|
@@ -281,15 +501,35 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
281
501
|
} catch (error) {
|
|
282
502
|
this.cleanupError ??= error;
|
|
283
503
|
}
|
|
284
|
-
this.
|
|
504
|
+
if (this.parentRestorePromise) await this.parentRestorePromise;
|
|
505
|
+
else this.restoreParent();
|
|
285
506
|
if (this.cleanupError !== undefined) outcome = { kind: "failed", error: this.cleanupError };
|
|
286
507
|
this.finished = true;
|
|
287
508
|
this.done(outcome);
|
|
288
509
|
}
|
|
289
510
|
|
|
511
|
+
private queueParentRestore(): void {
|
|
512
|
+
if (this.parentRestoreQueued || this.parentRestoreAttempted) return;
|
|
513
|
+
this.parentRestoreQueued = true;
|
|
514
|
+
this.parentRestorePromise = Promise.resolve().then(async () => {
|
|
515
|
+
try {
|
|
516
|
+
await this.fullscreen?.terminal.drainInput?.();
|
|
517
|
+
} catch (error) {
|
|
518
|
+
this.cleanupError ??= error;
|
|
519
|
+
}
|
|
520
|
+
this.parentRestoreQueued = false;
|
|
521
|
+
this.restoreParent();
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
290
525
|
private restoreParent(): void {
|
|
291
|
-
this.removeHardCancelListener
|
|
526
|
+
const removeHardCancelListener = this.removeHardCancelListener;
|
|
292
527
|
this.removeHardCancelListener = undefined;
|
|
528
|
+
try {
|
|
529
|
+
removeHardCancelListener?.();
|
|
530
|
+
} catch (error) {
|
|
531
|
+
this.cleanupError ??= error;
|
|
532
|
+
}
|
|
293
533
|
if (this.fullscreenCreated && !this.fullscreenStopped) {
|
|
294
534
|
this.fullscreenStopped = true;
|
|
295
535
|
try {
|
|
@@ -298,7 +538,7 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
298
538
|
this.cleanupError ??= error;
|
|
299
539
|
}
|
|
300
540
|
}
|
|
301
|
-
if (!this.parentStopped || this.
|
|
541
|
+
if (!this.parentStopped || this.parentRestoreAttempted) return;
|
|
302
542
|
const parentOverlay = this.parentOverlay;
|
|
303
543
|
this.parentOverlay = undefined;
|
|
304
544
|
try {
|
|
@@ -307,8 +547,8 @@ class BtwFullscreenHost<T> implements Component {
|
|
|
307
547
|
this.cleanupError ??= error;
|
|
308
548
|
}
|
|
309
549
|
try {
|
|
550
|
+
this.parentRestoreAttempted = true;
|
|
310
551
|
this.parent.start();
|
|
311
|
-
this.parentRestarted = true;
|
|
312
552
|
this.parent.renderNow(false);
|
|
313
553
|
} catch (error) {
|
|
314
554
|
this.cleanupError ??= error;
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type BtwSettings,
|
|
10
10
|
type BtwSettingsPatch,
|
|
11
11
|
btwSettingsPath,
|
|
12
|
+
effectiveFullscreenCopyOnSelect,
|
|
12
13
|
effectiveRememberThinkingLevelChanges,
|
|
13
14
|
readBtwSettings,
|
|
14
15
|
type UpdateBtwSettingsOptions,
|
|
@@ -48,7 +49,13 @@ export type BtwCommandMenuResult =
|
|
|
48
49
|
| { kind: "resume"; threadId: string };
|
|
49
50
|
|
|
50
51
|
type BtwMenuScreen = "main" | "resume" | "settings" | "invalid";
|
|
51
|
-
type BtwMenuAction =
|
|
52
|
+
type BtwMenuAction =
|
|
53
|
+
| "start"
|
|
54
|
+
| "start-tree"
|
|
55
|
+
| "resume"
|
|
56
|
+
| "set-thinking"
|
|
57
|
+
| "set-remember"
|
|
58
|
+
| "set-fullscreen-copy";
|
|
52
59
|
const SAME_AS_MAIN_THREAD = "Same as main thread";
|
|
53
60
|
type BtwCustomOptions = Parameters<ExtensionCommandContext["ui"]["custom"]>[1];
|
|
54
61
|
|
|
@@ -111,6 +118,7 @@ export async function showBtwCommandMenu(
|
|
|
111
118
|
title: "Pi BTW",
|
|
112
119
|
lines: [
|
|
113
120
|
`Thinking: ${displayThinkingSummary(state.settings)} · Remember changes: ${displayRememberSummary(state.settings)}`,
|
|
121
|
+
`Copy on select: ${effectiveFullscreenCopyOnSelect(state.settings) ? "On" : "Off"}`,
|
|
114
122
|
],
|
|
115
123
|
items: [
|
|
116
124
|
{
|
|
@@ -138,7 +146,7 @@ export async function showBtwCommandMenu(
|
|
|
138
146
|
{
|
|
139
147
|
id: "settings",
|
|
140
148
|
label: "Settings",
|
|
141
|
-
description: "Choose
|
|
149
|
+
description: "Choose thinking, shortcut memory, and selection copying",
|
|
142
150
|
to: state.kind === "invalid" ? "invalid" : "settings",
|
|
143
151
|
},
|
|
144
152
|
],
|
|
@@ -178,6 +186,15 @@ export async function showBtwCommandMenu(
|
|
|
178
186
|
values: ["On", "Off"],
|
|
179
187
|
action: "set-remember",
|
|
180
188
|
},
|
|
189
|
+
{
|
|
190
|
+
id: "fullscreenCopyOnSelect",
|
|
191
|
+
label: "Copy selection automatically",
|
|
192
|
+
description:
|
|
193
|
+
"Copy mouse selections immediately instead of with the configured copy key.",
|
|
194
|
+
currentValue: effectiveFullscreenCopyOnSelect(state.settings) ? "On" : "Off",
|
|
195
|
+
values: ["On", "Off"],
|
|
196
|
+
action: "set-fullscreen-copy",
|
|
197
|
+
},
|
|
181
198
|
],
|
|
182
199
|
}),
|
|
183
200
|
invalid: ({ state }) => ({
|
|
@@ -240,6 +257,21 @@ export async function showBtwCommandMenu(
|
|
|
240
257
|
return { kind: "rejected" };
|
|
241
258
|
}
|
|
242
259
|
},
|
|
260
|
+
"set-fullscreen-copy": async ({ value, signal }) => {
|
|
261
|
+
if (value !== "On" && value !== "Off") return { kind: "rejected" };
|
|
262
|
+
try {
|
|
263
|
+
await updateSettings(
|
|
264
|
+
{ fullscreenCopyOnSelect: value === "On" },
|
|
265
|
+
{ settingsPath, signal },
|
|
266
|
+
);
|
|
267
|
+
if (signal.aborted) return { kind: "rejected" };
|
|
268
|
+
notifySafely(ctx, `Copy selection automatically: ${value}.`, "info");
|
|
269
|
+
return { kind: "stay" };
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (!signal.aborted) notifySaveFailure(ctx, error);
|
|
272
|
+
return { kind: "rejected" };
|
|
273
|
+
}
|
|
274
|
+
},
|
|
243
275
|
},
|
|
244
276
|
});
|
|
245
277
|
|