@astralyn/pi 0.82.7 → 0.84.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/CHANGELOG.md +78 -5099
- package/README.md +12 -111
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js.map +1 -1
- package/dist/bun/register-bedrock.d.ts.map +1 -1
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
- package/dist/bun/restore-sandbox-env.js.map +1 -1
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +28 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +2 -2
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/credential-print.d.ts +23 -0
- package/dist/cli/credential-print.d.ts.map +1 -0
- package/dist/cli/credential-print.js +117 -0
- package/dist/cli/credential-print.js.map +1 -0
- package/dist/cli/experimental/auth.d.ts +16 -0
- package/dist/cli/experimental/auth.d.ts.map +1 -0
- package/dist/cli/experimental/auth.js +13 -0
- package/dist/cli/experimental/auth.js.map +1 -0
- package/dist/cli/experimental/cli.d.ts +6 -0
- package/dist/cli/experimental/cli.d.ts.map +1 -0
- package/dist/cli/experimental/cli.js +5 -0
- package/dist/cli/experimental/cli.js.map +1 -0
- package/dist/cli/experimental/command-options.d.ts +17 -0
- package/dist/cli/experimental/command-options.d.ts.map +1 -0
- package/dist/cli/experimental/command-options.js +35 -0
- package/dist/cli/experimental/command-options.js.map +1 -0
- package/dist/cli/experimental/command.d.ts +63 -0
- package/dist/cli/experimental/command.d.ts.map +1 -0
- package/dist/cli/experimental/command.js +127 -0
- package/dist/cli/experimental/command.js.map +1 -0
- package/dist/cli/experimental/commands/client.d.ts +13 -0
- package/dist/cli/experimental/commands/client.d.ts.map +1 -0
- package/dist/cli/experimental/commands/client.js +25 -0
- package/dist/cli/experimental/commands/client.js.map +1 -0
- package/dist/cli/experimental/commands/pi.d.ts +15 -0
- package/dist/cli/experimental/commands/pi.d.ts.map +1 -0
- package/dist/cli/experimental/commands/pi.js +28 -0
- package/dist/cli/experimental/commands/pi.js.map +1 -0
- package/dist/cli/experimental/commands/server.d.ts +13 -0
- package/dist/cli/experimental/commands/server.d.ts.map +1 -0
- package/dist/cli/experimental/commands/server.js +25 -0
- package/dist/cli/experimental/commands/server.js.map +1 -0
- package/dist/cli/experimental/transport-address.d.ts +10 -0
- package/dist/cli/experimental/transport-address.d.ts.map +1 -0
- package/dist/cli/experimental/transport-address.js +38 -0
- package/dist/cli/experimental/transport-address.js.map +1 -0
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/initial-message.d.ts +1 -1
- package/dist/cli/initial-message.d.ts.map +1 -1
- package/dist/cli/initial-message.js.map +1 -1
- package/dist/cli/list-models.d.ts +1 -1
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +2 -2
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli/startup-ui.d.ts +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +2 -2
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/cli.js.map +1 -1
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/remote-session.d.ts +53 -0
- package/dist/client/remote-session.d.ts.map +1 -0
- package/dist/client/remote-session.js +340 -0
- package/dist/client/remote-session.js.map +1 -0
- package/dist/client/transcript.d.ts +12 -0
- package/dist/client/transcript.d.ts.map +1 -0
- package/dist/client/transcript.js +98 -0
- package/dist/client/transcript.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +3 -8
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +31 -11
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +189 -192
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-guidance.d.ts.map +1 -1
- package/dist/core/auth-guidance.js.map +1 -1
- package/dist/core/auth-storage.d.ts +15 -9
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +159 -41
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/cache-stats.d.ts.map +1 -1
- package/dist/core/cache-stats.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
- package/dist/core/export-html/ansi-to-html.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +43 -20
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +31 -28
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +28 -3
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +10 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +18 -19
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +5 -0
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/keybindings.d.ts +157 -125
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +0 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-config.d.ts +37 -13
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +4 -2
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts +5 -3
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +13 -11
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +4 -3
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +41 -10
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +30 -13
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +239 -91
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/models-store.d.ts +12 -7
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +84 -16
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/output-guard.d.ts.map +1 -1
- package/dist/core/output-guard.js.map +1 -1
- package/dist/core/package-manager.d.ts +4 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +76 -48
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-manifest.d.ts +8 -0
- package/dist/core/pi-manifest.d.ts.map +1 -0
- package/dist/core/pi-manifest.js +25 -0
- package/dist/core/pi-manifest.js.map +1 -0
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/provider-composer.d.ts +4 -1
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js +36 -15
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/radius.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +73 -67
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts +15 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +68 -51
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-credentials.d.ts +5 -5
- package/dist/core/runtime-credentials.d.ts.map +1 -1
- package/dist/core/runtime-credentials.js +12 -10
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-cwd.d.ts.map +1 -1
- package/dist/core/session-cwd.js +0 -1
- package/dist/core/session-cwd.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +10 -12
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +12 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +47 -33
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/source-info.d.ts.map +1 -1
- package/dist/core/source-info.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/telemetry.d.ts.map +1 -1
- package/dist/core/telemetry.js.map +1 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +12 -10
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +4 -3
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/find.d.ts +3 -0
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +47 -20
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +12 -18
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +0 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +48 -10
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/usage-totals.d.ts.map +1 -1
- package/dist/core/usage-totals.js.map +1 -1
- package/dist/extensions/biu/index.d.ts +19 -0
- package/dist/extensions/biu/index.d.ts.map +1 -0
- package/dist/extensions/biu/index.js +350 -0
- package/dist/extensions/biu/index.js.map +1 -0
- package/dist/extensions/biu/prompts.d.ts +26 -0
- package/dist/extensions/biu/prompts.d.ts.map +1 -0
- package/dist/extensions/biu/prompts.js +260 -0
- package/dist/extensions/biu/prompts.js.map +1 -0
- package/dist/extensions/biu/workspace.d.ts +122 -0
- package/dist/extensions/biu/workspace.d.ts.map +1 -0
- package/dist/extensions/biu/workspace.js +404 -0
- package/dist/extensions/biu/workspace.js.map +1 -0
- package/dist/extensions/deepwiki/client.d.ts.map +1 -1
- package/dist/extensions/deepwiki/client.js.map +1 -1
- package/dist/extensions/deepwiki/constants.d.ts.map +1 -1
- package/dist/extensions/deepwiki/constants.js.map +1 -1
- package/dist/extensions/deepwiki/contents.d.ts.map +1 -1
- package/dist/extensions/deepwiki/contents.js.map +1 -1
- package/dist/extensions/deepwiki/execute.d.ts.map +1 -1
- package/dist/extensions/deepwiki/execute.js.map +1 -1
- package/dist/extensions/deepwiki/http.d.ts.map +1 -1
- package/dist/extensions/deepwiki/http.js.map +1 -1
- package/dist/extensions/deepwiki/index.d.ts.map +1 -1
- package/dist/extensions/deepwiki/index.js.map +1 -1
- package/dist/extensions/deepwiki/render.d.ts.map +1 -1
- package/dist/extensions/deepwiki/render.js.map +1 -1
- package/dist/extensions/deepwiki/schema.d.ts +1 -1
- package/dist/extensions/deepwiki/schema.d.ts.map +1 -1
- package/dist/extensions/deepwiki/schema.js.map +1 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +2 -2
- package/dist/extensions/index.js.map +1 -1
- package/dist/extensions/llama/client.d.ts.map +1 -1
- package/dist/extensions/llama/client.js +0 -2
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/huggingface.d.ts.map +1 -1
- package/dist/extensions/llama/huggingface.js +0 -2
- package/dist/extensions/llama/huggingface.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +11 -2
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +22 -8
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/extensions/llama/ui.d.ts.map +1 -1
- package/dist/extensions/llama/ui.js +12 -28
- package/dist/extensions/llama/ui.js.map +1 -1
- package/dist/extensions/question/constants.d.ts.map +1 -1
- package/dist/extensions/question/constants.js.map +1 -1
- package/dist/extensions/question/dialog-primitives.d.ts.map +1 -1
- package/dist/extensions/question/dialog-primitives.js.map +1 -1
- package/dist/extensions/question/dialog.d.ts.map +1 -1
- package/dist/extensions/question/dialog.js.map +1 -1
- package/dist/extensions/question/index.d.ts.map +1 -1
- package/dist/extensions/question/index.js.map +1 -1
- package/dist/extensions/question/limits.d.ts.map +1 -1
- package/dist/extensions/question/render-cache.d.ts.map +1 -1
- package/dist/extensions/question/render-cache.js +3 -4
- package/dist/extensions/question/render-cache.js.map +1 -1
- package/dist/extensions/question/render.d.ts.map +1 -1
- package/dist/extensions/question/render.js.map +1 -1
- package/dist/extensions/question/results.d.ts.map +1 -1
- package/dist/extensions/question/results.js.map +1 -1
- package/dist/extensions/question/schema.d.ts.map +1 -1
- package/dist/extensions/question/schema.js.map +1 -1
- package/dist/extensions/question/state.d.ts.map +1 -1
- package/dist/extensions/question/state.js.map +1 -1
- package/dist/extensions/question/types.d.ts.map +1 -1
- package/dist/extensions/rewind/anchor.d.ts.map +1 -1
- package/dist/extensions/rewind/anchor.js.map +1 -1
- package/dist/extensions/rewind/config.d.ts.map +1 -1
- package/dist/extensions/rewind/config.js.map +1 -1
- package/dist/extensions/rewind/engine.d.ts.map +1 -1
- package/dist/extensions/rewind/engine.js.map +1 -1
- package/dist/extensions/rewind/gc.d.ts.map +1 -1
- package/dist/extensions/rewind/gc.js.map +1 -1
- package/dist/extensions/rewind/index.d.ts.map +1 -1
- package/dist/extensions/rewind/index.js.map +1 -1
- package/dist/extensions/rewind/menu.d.ts.map +1 -1
- package/dist/extensions/rewind/menu.js.map +1 -1
- package/dist/extensions/rewind/paths.d.ts.map +1 -1
- package/dist/extensions/rewind/paths.js.map +1 -1
- package/dist/extensions/rewind/restore.d.ts.map +1 -1
- package/dist/extensions/rewind/restore.js.map +1 -1
- package/dist/extensions/rewind/snapshot.d.ts.map +1 -1
- package/dist/extensions/rewind/snapshot.js.map +1 -1
- package/dist/extensions/rewind/storage.d.ts.map +1 -1
- package/dist/extensions/rewind/storage.js.map +1 -1
- package/dist/extensions/rewind/text.d.ts.map +1 -1
- package/dist/extensions/rewind/text.js.map +1 -1
- package/dist/extensions/rewind/tool-path.d.ts.map +1 -1
- package/dist/extensions/rewind/tool-path.js.map +1 -1
- package/dist/extensions/router/constants.d.ts +3 -0
- package/dist/extensions/router/constants.d.ts.map +1 -1
- package/dist/extensions/router/constants.js +3 -0
- package/dist/extensions/router/constants.js.map +1 -1
- package/dist/extensions/router/dialog.d.ts +14 -7
- package/dist/extensions/router/dialog.d.ts.map +1 -1
- package/dist/extensions/router/dialog.js +70 -132
- package/dist/extensions/router/dialog.js.map +1 -1
- package/dist/extensions/router/index.d.ts.map +1 -1
- package/dist/extensions/router/index.js.map +1 -1
- package/dist/extensions/router/presets.d.ts +8 -3
- package/dist/extensions/router/presets.d.ts.map +1 -1
- package/dist/extensions/router/presets.js +33 -17
- package/dist/extensions/router/presets.js.map +1 -1
- package/dist/extensions/router/probe.d.ts.map +1 -1
- package/dist/extensions/router/probe.js.map +1 -1
- package/dist/extensions/router/register.d.ts +3 -3
- package/dist/extensions/router/register.d.ts.map +1 -1
- package/dist/extensions/router/register.js.map +1 -1
- package/dist/extensions/router/store.d.ts.map +1 -1
- package/dist/extensions/router/store.js.map +1 -1
- package/dist/extensions/router/stream.d.ts.map +1 -1
- package/dist/extensions/router/stream.js.map +1 -1
- package/dist/extensions/router/types.d.ts.map +1 -1
- package/dist/extensions/router/ui.d.ts +11 -2
- package/dist/extensions/router/ui.d.ts.map +1 -1
- package/dist/extensions/router/ui.js +316 -171
- package/dist/extensions/router/ui.js.map +1 -1
- package/dist/extensions/statusline/index.d.ts.map +1 -1
- package/dist/extensions/statusline/index.js +17 -9
- package/dist/extensions/statusline/index.js.map +1 -1
- package/dist/extensions/subagent/activity.d.ts.map +1 -1
- package/dist/extensions/subagent/activity.js.map +1 -1
- package/dist/extensions/subagent/agents.d.ts.map +1 -1
- package/dist/extensions/subagent/agents.js.map +1 -1
- package/dist/extensions/subagent/constants.d.ts.map +1 -1
- package/dist/extensions/subagent/index.d.ts +3 -0
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +83 -12
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/subagent/picker.d.ts.map +1 -1
- package/dist/extensions/subagent/picker.js +2 -9
- package/dist/extensions/subagent/picker.js.map +1 -1
- package/dist/extensions/subagent/render.d.ts.map +1 -1
- package/dist/extensions/subagent/render.js +33 -9
- package/dist/extensions/subagent/render.js.map +1 -1
- package/dist/extensions/subagent/resolve.d.ts.map +1 -1
- package/dist/extensions/subagent/resolve.js.map +1 -1
- package/dist/extensions/subagent/retry.d.ts.map +1 -1
- package/dist/extensions/subagent/retry.js.map +1 -1
- package/dist/extensions/subagent/runner.d.ts +2 -1
- package/dist/extensions/subagent/runner.d.ts.map +1 -1
- package/dist/extensions/subagent/runner.js +10 -12
- package/dist/extensions/subagent/runner.js.map +1 -1
- package/dist/extensions/subagent/schema.d.ts.map +1 -1
- package/dist/extensions/subagent/schema.js.map +1 -1
- package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
- package/dist/extensions/subagent/sdk-runner.js +41 -1
- package/dist/extensions/subagent/sdk-runner.js.map +1 -1
- package/dist/extensions/subagent/settings.d.ts.map +1 -1
- package/dist/extensions/subagent/settings.js +46 -36
- package/dist/extensions/subagent/settings.js.map +1 -1
- package/dist/extensions/subagent/types.d.ts +0 -1
- package/dist/extensions/subagent/types.d.ts.map +1 -1
- package/dist/extensions/subagent/types.js.map +1 -1
- package/dist/extensions/todo/constants.d.ts.map +1 -1
- package/dist/extensions/todo/constants.js.map +1 -1
- package/dist/extensions/todo/index.d.ts.map +1 -1
- package/dist/extensions/todo/index.js.map +1 -1
- package/dist/extensions/todo/overlay.d.ts.map +1 -1
- package/dist/extensions/todo/overlay.js +6 -10
- package/dist/extensions/todo/overlay.js.map +1 -1
- package/dist/extensions/todo/schema.d.ts +2 -2
- package/dist/extensions/todo/schema.d.ts.map +1 -1
- package/dist/extensions/todo/state.d.ts.map +1 -1
- package/dist/extensions/todo/state.js.map +1 -1
- package/dist/extensions/todo/view.d.ts.map +1 -1
- package/dist/extensions/todo/view.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +57 -5
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js +6 -10
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +5 -2
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +13 -11
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +4 -10
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +0 -3
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +1 -3
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +1 -3
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +5 -22
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js +1 -7
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +8 -8
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-entry.js +1 -4
- package/dist/modes/interactive/components/custom-entry.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +1 -7
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
- package/dist/modes/interactive/components/daxnuts.js +6 -8
- package/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/dist/modes/interactive/components/diff.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +0 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +1 -7
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +2 -8
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +1 -9
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +2 -4
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +2 -4
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +3 -9
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/markdown-transform.d.ts +3 -0
- package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -0
- package/dist/modes/interactive/components/markdown-transform.js +19 -0
- package/dist/modes/interactive/components/markdown-transform.js.map +1 -0
- package/dist/modes/interactive/components/mermaid.d.ts +11 -0
- package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
- package/dist/modes/interactive/components/mermaid.js +75 -0
- package/dist/modes/interactive/components/mermaid.js.map +1 -0
- package/dist/modes/interactive/components/model-selector.d.ts +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +32 -30
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +3 -11
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +4 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +34 -13
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +35 -60
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +8 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +30 -14
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +0 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +1 -3
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-indicator.js +0 -2
- package/dist/modes/interactive/components/status-indicator.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +0 -2
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +0 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +18 -36
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-group.js +2 -3
- package/dist/modes/interactive/components/tool-group.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +19 -39
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/trust-selector.js +0 -6
- package/dist/modes/interactive/components/trust-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +3 -6
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +3 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +8 -5
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +40 -3
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +46 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +538 -275
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-search.d.ts.map +1 -1
- package/dist/modes/interactive/model-search.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +1 -0
- package/dist/modes/interactive/theme/light.json +1 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +3 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +11 -8
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +5 -1
- package/dist/modes/interactive/theme/theme.d.ts +4 -4
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +13 -9
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +28 -0
- package/dist/modes/json-event.d.ts.map +1 -0
- package/dist/modes/json-event.js +12 -0
- package/dist/modes/json-event.js.map +1 -0
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +12 -2
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/dist/modes/rpc/jsonl.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +5 -4
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -8
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +17 -3
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +11 -9
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/rpc-entry.d.ts.map +1 -1
- package/dist/rpc-entry.js +1 -0
- package/dist/rpc-entry.js.map +1 -1
- package/dist/utils/abort.d.ts +5 -0
- package/dist/utils/abort.d.ts.map +1 -0
- package/dist/utils/abort.js +48 -0
- package/dist/utils/abort.js.map +1 -0
- package/dist/utils/ansi.d.ts.map +1 -1
- package/dist/utils/ansi.js.map +1 -1
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +22 -2
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/coalescing-terminal.d.ts +1 -0
- package/dist/utils/coalescing-terminal.d.ts.map +1 -1
- package/dist/utils/coalescing-terminal.js +16 -8
- package/dist/utils/coalescing-terminal.js.map +1 -1
- package/dist/utils/deprecation.d.ts.map +1 -1
- package/dist/utils/deprecation.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/html.d.ts.map +1 -1
- package/dist/utils/html.js.map +1 -1
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-process.d.ts.map +1 -1
- package/dist/utils/image-process.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/image-resize-worker.d.ts.map +1 -1
- package/dist/utils/image-resize-worker.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/json.d.ts.map +1 -1
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/management-http.d.ts +23 -0
- package/dist/utils/management-http.d.ts.map +1 -0
- package/dist/utils/management-http.js +50 -0
- package/dist/utils/management-http.js.map +1 -0
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js.map +1 -1
- package/dist/utils/open-browser.d.ts.map +1 -1
- package/dist/utils/open-browser.js.map +1 -1
- package/dist/utils/paths.d.ts +5 -3
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +25 -4
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/photon.d.ts.map +1 -1
- package/dist/utils/photon.js.map +1 -1
- package/dist/utils/pi-user-agent.d.ts.map +1 -1
- package/dist/utils/pi-user-agent.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/sleep.d.ts.map +1 -1
- package/dist/utils/sleep.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/tool-result-images.d.ts +19 -0
- package/dist/utils/tool-result-images.d.ts.map +1 -0
- package/dist/utils/tool-result-images.js +45 -0
- package/dist/utils/tool-result-images.js.map +1 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +4 -6
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +4 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +20 -2
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/windows-self-update.d.ts.map +1 -1
- package/dist/utils/windows-self-update.js.map +1 -1
- package/docs/bundled/README.md +14 -16
- package/docs/bundled/extensions/biu.md +63 -0
- package/docs/bundled/extensions/router.md +20 -14
- package/docs/bundled/extensions/statusline.md +7 -5
- package/docs/bundled/themes.md +1 -1
- package/docs/compaction.md +25 -30
- package/docs/custom-provider.md +303 -467
- package/docs/development.md +83 -0
- package/docs/docs.json +6 -10
- package/docs/extensions.md +139 -119
- package/docs/index.md +18 -74
- package/docs/json.md +40 -15
- package/docs/keybindings.md +38 -10
- package/docs/models.md +210 -150
- package/docs/providers.md +3 -1
- package/docs/quickstart.md +5 -3
- package/docs/rpc.md +177 -134
- package/docs/sdk.md +165 -178
- package/docs/security.md +2 -2
- package/docs/session-format.md +139 -119
- package/docs/settings.md +4 -1
- package/docs/skills.md +0 -1
- package/docs/themes.md +5 -3
- package/docs/tui.md +411 -549
- package/docs/usage.md +11 -11
- package/examples/README.md +6 -1
- package/examples/extensions/README.md +13 -33
- package/examples/extensions/custom-compaction.ts +1 -16
- package/examples/extensions/custom-provider-anthropic/index.ts +11 -4
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -1
- package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
- package/examples/extensions/handoff.ts +2 -11
- package/examples/extensions/qna.ts +3 -7
- package/examples/extensions/summarize.ts +8 -18
- package/examples/extensions/truncated-tool.ts +2 -2
- package/examples/rpc-extension-ui.ts +11 -2
- package/examples/sdk/02-custom-model.ts +1 -2
- package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
- package/examples/sdk/12-full-control.ts +3 -1
- package/examples/sdk/README.md +1 -0
- package/npm-shrinkwrap.json +199 -270
- package/package.json +16 -9
- package/dist/extensions/plan/constants.d.ts +0 -68
- package/dist/extensions/plan/constants.d.ts.map +0 -1
- package/dist/extensions/plan/constants.js +0 -100
- package/dist/extensions/plan/constants.js.map +0 -1
- package/dist/extensions/plan/index.d.ts +0 -30
- package/dist/extensions/plan/index.d.ts.map +0 -1
- package/dist/extensions/plan/index.js +0 -429
- package/dist/extensions/plan/index.js.map +0 -1
- package/dist/extensions/plan/schema.d.ts +0 -20
- package/dist/extensions/plan/schema.d.ts.map +0 -1
- package/dist/extensions/plan/schema.js +0 -13
- package/dist/extensions/plan/schema.js.map +0 -1
- package/dist/extensions/plan/state.d.ts +0 -38
- package/dist/extensions/plan/state.d.ts.map +0 -1
- package/dist/extensions/plan/state.js +0 -96
- package/dist/extensions/plan/state.js.map +0 -1
- package/dist/extensions/plan/storage.d.ts +0 -26
- package/dist/extensions/plan/storage.d.ts.map +0 -1
- package/dist/extensions/plan/storage.js +0 -83
- package/dist/extensions/plan/storage.js.map +0 -1
- package/dist/extensions/plan/view.d.ts +0 -44
- package/dist/extensions/plan/view.d.ts.map +0 -1
- package/dist/extensions/plan/view.js +0 -149
- package/dist/extensions/plan/view.js.map +0 -1
- package/docs/bundled/extensions/README.md +0 -19
- package/docs/bundled/extensions/plan.md +0 -34
- package/examples/extensions/plan-mode/README.md +0 -66
- package/examples/extensions/plan-mode/index.ts +0 -390
- package/examples/extensions/plan-mode/utils.ts +0 -168
package/docs/tui.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
# TUI Components
|
|
4
4
|
|
|
5
|
-
Extensions and custom tools can render custom
|
|
5
|
+
Extensions and custom tools can render custom terminal UI in interactive mode. This page documents the public TUI package API that Pi exposes to extensions, plus Pi's extension UI helpers.
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**API reference:** the public declarations shipped by [`@earendil-works/pi-tui`](https://www.npmjs.com/package/@earendil-works/pi-tui) are authoritative. Pi builds on the [earendil-works/pi project](https://github.com/earendil-works/pi); Pi-specific helpers below are exported by `@astralyn/pi`.
|
|
8
8
|
|
|
9
9
|
## Component Interface
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Every rendered component implements the public `Component` interface:
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
14
|
interface Component {
|
|
@@ -19,56 +19,51 @@ interface Component {
|
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
|
23
|
-
|
|
24
|
-
| `render(width)` | Return
|
|
25
|
-
| `handleInput?(data)` |
|
|
26
|
-
| `wantsKeyRelease?` |
|
|
27
|
-
| `invalidate()` | Clear cached render state.
|
|
22
|
+
| Member | Description |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `render(width)` | Return one string per line. Every line must be at most `width` terminal columns wide. |
|
|
25
|
+
| `handleInput?(data)` | Receives raw terminal input while this component has focus. |
|
|
26
|
+
| `wantsKeyRelease?` | Set to `true` only when the component needs Kitty keyboard key-release events. Releases are otherwise filtered. |
|
|
27
|
+
| `invalidate()` | Clear cached render state. TUI calls it when the theme changes; call it yourself before rendering changed cached state. |
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
`invalidate()` is required even for an uncached component; an empty implementation is fine. `invalidate()` does **not** schedule a frame. After application state changes, call the injected `tui.requestRender()`.
|
|
30
30
|
|
|
31
|
-
## Focusable
|
|
31
|
+
## Focusable Components and IME
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
A component that renders a text cursor and needs IME candidate-window placement implements `Focusable` as well as `Component`:
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
36
|
import { CURSOR_MARKER, type Component, type Focusable } from "@earendil-works/pi-tui";
|
|
37
37
|
|
|
38
|
-
class
|
|
39
|
-
focused
|
|
40
|
-
|
|
38
|
+
class CursorCell implements Component, Focusable {
|
|
39
|
+
focused = false;
|
|
40
|
+
|
|
41
41
|
render(width: number): string[] {
|
|
42
|
+
if (width < 1) return [];
|
|
43
|
+
// Put the zero-width marker immediately before the visual cursor.
|
|
42
44
|
const marker = this.focused ? CURSOR_MARKER : "";
|
|
43
|
-
|
|
44
|
-
return [`> ${beforeCursor}${marker}\x1b[7m${atCursor}\x1b[27m${afterCursor}`];
|
|
45
|
+
return [`${marker}\x1b[7m \x1b[27m`];
|
|
45
46
|
}
|
|
47
|
+
|
|
48
|
+
invalidate(): void {}
|
|
46
49
|
}
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
When a
|
|
50
|
-
1. Sets `focused = true` on the component
|
|
51
|
-
2. Scans rendered output for `CURSOR_MARKER` (a zero-width APC escape sequence)
|
|
52
|
-
3. Positions the hardware terminal cursor at that location
|
|
53
|
-
4. Shows the hardware cursor only when `showHardwareCursor` is enabled
|
|
54
|
-
|
|
55
|
-
The cursor remains hidden by default. This keeps the fake cursor rendering, while still positioning the hardware cursor for terminals that track IME candidate windows with hidden cursors. Some terminals require a visible hardware cursor for IME positioning; enable it with `showHardwareCursor`, `setShowHardwareCursor(true)`, or `PI_HARDWARE_CURSOR=1`. The `Editor` and `Input` built-in components already implement this interface.
|
|
56
|
-
|
|
57
|
-
### Container Components with Embedded Inputs
|
|
52
|
+
When a focused component has a `focused` property, TUI sets it and looks for `CURSOR_MARKER` in its rendered lines to position the hardware cursor. `Input` and `Editor` already implement this. An extension can query or change the injected TUI's hardware-cursor setting with `tui.getShowHardwareCursor()` and `tui.setShowHardwareCursor(enabled)`; changing it affects the whole application UI.
|
|
58
53
|
|
|
59
|
-
|
|
54
|
+
A container that owns an `Input` or `Editor` must forward both focus and input to its child. `Container` only groups and renders children; it does not route input for them.
|
|
60
55
|
|
|
61
56
|
```typescript
|
|
62
57
|
import { Container, type Focusable, Input } from "@earendil-works/pi-tui";
|
|
63
58
|
|
|
64
59
|
class SearchDialog extends Container implements Focusable {
|
|
65
|
-
private searchInput: Input;
|
|
66
|
-
|
|
67
|
-
// Focusable implementation - propagate to child input for IME cursor positioning
|
|
60
|
+
private readonly searchInput: Input;
|
|
68
61
|
private _focused = false;
|
|
62
|
+
|
|
69
63
|
get focused(): boolean {
|
|
70
64
|
return this._focused;
|
|
71
65
|
}
|
|
66
|
+
|
|
72
67
|
set focused(value: boolean) {
|
|
73
68
|
this._focused = value;
|
|
74
69
|
this.searchInput.focused = value;
|
|
@@ -79,297 +74,255 @@ class SearchDialog extends Container implements Focusable {
|
|
|
79
74
|
this.searchInput = new Input();
|
|
80
75
|
this.addChild(this.searchInput);
|
|
81
76
|
}
|
|
77
|
+
|
|
78
|
+
handleInput(data: string): void {
|
|
79
|
+
this.searchInput.handleInput(data);
|
|
80
|
+
}
|
|
82
81
|
}
|
|
83
82
|
```
|
|
84
83
|
|
|
85
|
-
Without
|
|
84
|
+
Without that propagation, an IME can place its candidate window at the wrong screen location.
|
|
86
85
|
|
|
87
|
-
##
|
|
86
|
+
## Show a Component in Pi
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
`ctx.ui.custom()` replaces the editor until its `done` callback is called. It is available only in TUI mode, so guard commands or tools that use it with `ctx.mode === "tui"`.
|
|
90
89
|
|
|
91
90
|
```typescript
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
|
|
91
|
+
if (ctx.mode !== "tui") return;
|
|
92
|
+
|
|
93
|
+
const result = await ctx.ui.custom<string | null>((tui, theme, keybindings, done) =>
|
|
94
|
+
new MyComponent({
|
|
95
|
+
theme,
|
|
96
|
+
keybindings,
|
|
97
|
+
onChange: () => tui.requestRender(),
|
|
98
|
+
onSelect: (value) => done(value),
|
|
99
|
+
onCancel: () => done(null),
|
|
100
|
+
})
|
|
101
|
+
);
|
|
103
102
|
```
|
|
104
103
|
|
|
105
|
-
|
|
104
|
+
The factory receives Pi's current `TUI`, semantic `Theme`, and configured `KeybindingsManager`. It may return a component synchronously or as a promise. The `custom()` promise resolves only when the component calls `done(result)`.
|
|
106
105
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
keybindings,
|
|
113
|
-
onChange: () => tui.requestRender(),
|
|
114
|
-
onSelect: (value) => done(value),
|
|
115
|
-
onCancel: () => done(null),
|
|
116
|
-
})
|
|
117
|
-
);
|
|
118
|
-
// Use result...
|
|
119
|
-
}
|
|
120
|
-
```
|
|
106
|
+
### Disposal
|
|
107
|
+
|
|
108
|
+
A component returned from `ctx.ui.custom()`, `setWidget()`, `setFooter()`, or `setHeader()` may also implement `dispose(): void`. Use it to stop timers, abort owned work, and unsubscribe listeners. Pi calls it after a custom component finishes, or when a widget, footer, or header is replaced or cleared.
|
|
109
|
+
|
|
110
|
+
Do not reuse a component after it has called `done`. Create a fresh instance if it must be shown again. In particular, an overlay handle's `hide()` only removes that overlay; it does not resolve the surrounding `ctx.ui.custom()` promise or dispose the component. Have the component call `done` to close its Pi UI lifecycle.
|
|
121
111
|
|
|
122
112
|
## Overlays
|
|
123
113
|
|
|
124
|
-
|
|
114
|
+
Pass `{ overlay: true }` to leave the existing UI visible and show a component above it:
|
|
125
115
|
|
|
126
116
|
```typescript
|
|
127
117
|
const result = await ctx.ui.custom<string | null>(
|
|
128
|
-
(
|
|
118
|
+
(_tui, theme, _keybindings, done) => new MyDialog({ theme, onClose: () => done(null) }),
|
|
129
119
|
{ overlay: true }
|
|
130
120
|
);
|
|
131
121
|
```
|
|
132
122
|
|
|
133
|
-
|
|
123
|
+
Use `overlayOptions` to choose its layout and focus behavior:
|
|
134
124
|
|
|
135
125
|
```typescript
|
|
136
|
-
|
|
137
|
-
(
|
|
126
|
+
await ctx.ui.custom<void>(
|
|
127
|
+
(_tui, theme, _keybindings, done) => new SidePanel({ theme, onClose: () => done() }),
|
|
138
128
|
{
|
|
139
129
|
overlay: true,
|
|
140
130
|
overlayOptions: {
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// Position: anchor-based (default: "center")
|
|
147
|
-
anchor: "right-center", // 9 positions: center, top-left, top-center, etc.
|
|
148
|
-
offsetX: -2, // offset from anchor
|
|
131
|
+
width: "50%", // number of columns or a percentage string
|
|
132
|
+
minWidth: 40,
|
|
133
|
+
maxHeight: "80%", // number of rows or a percentage string
|
|
134
|
+
anchor: "right-center", // center plus eight edge/corner anchors
|
|
135
|
+
offsetX: -2,
|
|
149
136
|
offsetY: 0,
|
|
150
|
-
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// Margins
|
|
156
|
-
margin: 2, // all sides, or { top, right, bottom, left }
|
|
157
|
-
|
|
158
|
-
// Responsive: hide on narrow terminals
|
|
159
|
-
visible: (termWidth, termHeight) => termWidth >= 80,
|
|
137
|
+
row: "25%", // absolute number or percentage from the top
|
|
138
|
+
col: 10, // absolute number or percentage from the left
|
|
139
|
+
margin: 2, // number, or { top, right, bottom, left }
|
|
140
|
+
visible: (termWidth, _termHeight) => termWidth >= 80,
|
|
141
|
+
nonCapturing: false, // set true for a visible overlay that never takes input
|
|
160
142
|
},
|
|
161
|
-
// Get handle for programmatic focus and visibility control
|
|
162
143
|
onHandle: (handle) => {
|
|
163
|
-
// handle.focus()
|
|
164
|
-
// handle.unfocus() - release input to normal fallback
|
|
165
|
-
// handle.unfocus({ target }) - release input to a specific component or null
|
|
166
|
-
// handle.setHidden(true/false) - toggle visibility
|
|
167
|
-
// handle.hide() - permanently remove
|
|
144
|
+
// handle.focus(), handle.unfocus(), handle.setHidden(), and handle.hide()
|
|
168
145
|
},
|
|
169
146
|
}
|
|
170
147
|
);
|
|
171
148
|
```
|
|
172
149
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
A focused visible overlay keeps input ownership across temporary non-overlay UI. If an overlay opens another `ctx.ui.custom()` component without `{ overlay: true }`, that replacement UI receives input while it is active; when it closes, the focused overlay can reclaim input.
|
|
150
|
+
The anchors are `"center"`, `"top-left"`, `"top-center"`, `"top-right"`, `"left-center"`, `"right-center"`, `"bottom-left"`, `"bottom-center"`, and `"bottom-right"`. An `overlayOptions` function is also accepted when the options need to be computed at the time the overlay is shown.
|
|
176
151
|
|
|
177
|
-
|
|
152
|
+
`onHandle` receives the public `OverlayHandle` after the overlay is shown:
|
|
178
153
|
|
|
179
|
-
|
|
154
|
+
- `focus()` gives the overlay input and brings it to the visual front.
|
|
155
|
+
- `unfocus()` releases input to another visible capturing overlay or the prior focus target. Use `unfocus({ target })` to focus a particular component, or `unfocus({ target: null })` to leave nothing focused.
|
|
156
|
+
- `setHidden(true)` temporarily hides the overlay; `setHidden(false)` shows it again. `isHidden()` reports that state.
|
|
157
|
+
- `hide()` permanently removes the overlay. `isFocused()` reports whether it owns focus.
|
|
180
158
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
// Wrong - stale reference
|
|
185
|
-
let menu: MenuComponent;
|
|
186
|
-
await ctx.ui.custom((_, __, ___, done) => {
|
|
187
|
-
menu = new MenuComponent(done);
|
|
188
|
-
return menu;
|
|
189
|
-
}, { overlay: true });
|
|
190
|
-
setActiveComponent(menu); // Disposed
|
|
191
|
-
|
|
192
|
-
// Correct - re-call to re-show
|
|
193
|
-
const showMenu = () => ctx.ui.custom((_, __, ___, done) =>
|
|
194
|
-
new MenuComponent(done), { overlay: true });
|
|
195
|
-
|
|
196
|
-
await showMenu(); // First show
|
|
197
|
-
await showMenu(); // "Back" = just call again
|
|
198
|
-
```
|
|
159
|
+
A visible focused overlay can regain focus after a temporary non-overlay `ctx.ui.custom()` view closes. Use `unfocus()` when it should stop capturing input instead.
|
|
199
160
|
|
|
200
|
-
See [overlay-qa-tests.ts](../examples/extensions/overlay-qa-tests.ts) for
|
|
161
|
+
See [overlay-qa-tests.ts](../examples/extensions/overlay-qa-tests.ts) for anchors, margins, stacking, responsive visibility, and focus examples.
|
|
201
162
|
|
|
202
163
|
## Built-in Components
|
|
203
164
|
|
|
204
|
-
Import from `@earendil-works/pi-tui
|
|
165
|
+
Import TUI primitives from `@earendil-works/pi-tui`. In Pi extension factories, use the supplied `theme` and its semantic helpers rather than hard-coded ANSI colors.
|
|
205
166
|
|
|
206
|
-
|
|
207
|
-
import { Text, Box, Container, Spacer, Markdown } from "@earendil-works/pi-tui";
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Text
|
|
211
|
-
|
|
212
|
-
Multi-line text with word wrapping.
|
|
167
|
+
### Layout and text
|
|
213
168
|
|
|
214
169
|
```typescript
|
|
215
|
-
|
|
216
|
-
"Hello World", // content
|
|
217
|
-
1, // paddingX (default: 1)
|
|
218
|
-
1, // paddingY (default: 1)
|
|
219
|
-
(s) => bgGray(s) // optional background function
|
|
220
|
-
);
|
|
221
|
-
text.setText("Updated");
|
|
222
|
-
```
|
|
170
|
+
import { Box, Container, Spacer, Text, TruncatedText } from "@earendil-works/pi-tui";
|
|
223
171
|
|
|
224
|
-
|
|
172
|
+
const background = (text: string) => theme.bg("selectedBg", text);
|
|
225
173
|
|
|
226
|
-
|
|
174
|
+
const text = new Text("Hello World", 1, 1, background);
|
|
175
|
+
text.setText("Updated");
|
|
176
|
+
text.setCustomBgFn((value: string) => theme.bg("toolPendingBg", value));
|
|
227
177
|
|
|
228
|
-
|
|
229
|
-
const box = new Box(
|
|
230
|
-
1, // paddingX
|
|
231
|
-
1, // paddingY
|
|
232
|
-
(s) => bgGray(s) // background function
|
|
233
|
-
);
|
|
178
|
+
const box = new Box(1, 1, background);
|
|
234
179
|
box.addChild(new Text("Content", 0, 0));
|
|
235
|
-
box.setBgFn((
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Container
|
|
239
|
-
|
|
240
|
-
Groups child components vertically.
|
|
180
|
+
box.setBgFn((value: string) => theme.bg("selectedBg", value));
|
|
241
181
|
|
|
242
|
-
```typescript
|
|
243
182
|
const container = new Container();
|
|
244
|
-
container.addChild(
|
|
245
|
-
container.addChild(
|
|
246
|
-
container.removeChild(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
### Spacer
|
|
250
|
-
|
|
251
|
-
Empty vertical space.
|
|
183
|
+
container.addChild(box);
|
|
184
|
+
container.addChild(new Spacer(1));
|
|
185
|
+
container.removeChild(box);
|
|
186
|
+
container.clear();
|
|
252
187
|
|
|
253
|
-
|
|
254
|
-
const spacer = new Spacer(2); // 2 empty lines
|
|
188
|
+
const singleLine = new TruncatedText("A long status line", 0, 0);
|
|
255
189
|
```
|
|
256
190
|
|
|
257
|
-
|
|
191
|
+
`Text` wraps multi-line text. `TruncatedText` renders text constrained to one line. `Box` applies padding and an optional background function around its children. `Spacer(lines?)` creates empty lines and supports `setLines(lines)`.
|
|
258
192
|
|
|
259
|
-
|
|
193
|
+
### Markdown and images
|
|
260
194
|
|
|
261
|
-
|
|
262
|
-
const md = new Markdown(
|
|
263
|
-
"# Title\n\nSome **bold** text",
|
|
264
|
-
1, // paddingX
|
|
265
|
-
1, // paddingY
|
|
266
|
-
theme // MarkdownTheme (see below)
|
|
267
|
-
);
|
|
268
|
-
md.setText("Updated markdown");
|
|
269
|
-
```
|
|
195
|
+
Pi exports a semantic Markdown theme builder. `Markdown` requires its text, horizontal padding, vertical padding, and a `MarkdownTheme`.
|
|
270
196
|
|
|
271
|
-
|
|
197
|
+
```typescript
|
|
198
|
+
import { getMarkdownTheme } from "@astralyn/pi";
|
|
199
|
+
import { Image, Markdown } from "@earendil-works/pi-tui";
|
|
272
200
|
|
|
273
|
-
|
|
201
|
+
const markdown = new Markdown("# Title\n\nSome **bold** text", 1, 1, getMarkdownTheme());
|
|
202
|
+
markdown.setText("Updated markdown");
|
|
274
203
|
|
|
275
|
-
```typescript
|
|
276
204
|
const image = new Image(
|
|
277
|
-
base64Data,
|
|
278
|
-
"image/png",
|
|
279
|
-
theme,
|
|
205
|
+
base64Data,
|
|
206
|
+
"image/png",
|
|
207
|
+
{ fallbackColor: (text: string) => theme.fg("muted", text) },
|
|
280
208
|
{ maxWidthCells: 80, maxHeightCells: 24 }
|
|
281
209
|
);
|
|
282
210
|
```
|
|
283
211
|
|
|
212
|
+
`Image` accepts base64 data, a MIME type, an `ImageTheme` with `fallbackColor`, and optional sizing, filename, or reusable `imageId` options. It renders a text fallback where inline images are unsupported.
|
|
213
|
+
|
|
214
|
+
### Inputs, selectors, and loaders
|
|
215
|
+
|
|
216
|
+
`Input` is a single-line focused editor. `Editor` is the multi-line editor component; for Pi's main editor, extend Pi's `CustomEditor` instead of constructing a bare `Editor` so app shortcuts continue to work.
|
|
217
|
+
|
|
218
|
+
`SelectList` takes `(items, maxVisible, theme, layout?)`; `SettingsList` takes `(items, maxVisible, theme, onChange, onCancel, options?)`. Both use Pi's configured selection bindings. `Loader` takes `(tui, spinnerColorFn, messageColorFn, message?, indicator?)`; `CancellableLoader` adds `signal`, `aborted`, `onAbort`, and `dispose()`. Pi's `BorderedLoader` wraps the latter with Pi-native framing and cancellation hints; see [Pattern 2](#pattern-2-async-operation-with-cancel-borderedloader).
|
|
219
|
+
|
|
284
220
|
## Keyboard Input
|
|
285
221
|
|
|
286
|
-
Use `matchesKey()` for
|
|
222
|
+
Use `matchesKey()` and `Key` for fixed, extension-specific keys. They handle legacy terminal sequences and Kitty keyboard protocol.
|
|
287
223
|
|
|
288
224
|
```typescript
|
|
289
|
-
import {
|
|
225
|
+
import { Key, matchesKey } from "@earendil-works/pi-tui";
|
|
290
226
|
|
|
291
|
-
handleInput(data: string) {
|
|
227
|
+
handleInput(data: string): void {
|
|
292
228
|
if (matchesKey(data, Key.up)) {
|
|
293
|
-
this.
|
|
229
|
+
this.moveUp();
|
|
294
230
|
} else if (matchesKey(data, Key.enter)) {
|
|
295
|
-
this.
|
|
231
|
+
this.confirm();
|
|
296
232
|
} else if (matchesKey(data, Key.escape)) {
|
|
297
|
-
this.
|
|
233
|
+
this.cancel();
|
|
298
234
|
} else if (matchesKey(data, Key.ctrl("c"))) {
|
|
299
|
-
|
|
235
|
+
this.cancel();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`Key` provides basic, navigation, function, and symbol keys, plus `ctrl`, `shift`, `alt`, `super`, and combined-modifier helpers. A `KeyId` string such as `"enter"`, `"ctrl+c"`, or `"ctrl+shift+p"` also works.
|
|
241
|
+
|
|
242
|
+
For behavior that follows a Pi-configurable binding, use the `keybindings` callback argument instead of checking a default key directly:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import type { KeybindingsManager } from "@astralyn/pi";
|
|
246
|
+
|
|
247
|
+
class MyDialog {
|
|
248
|
+
constructor(private readonly keybindings: KeybindingsManager) {}
|
|
249
|
+
|
|
250
|
+
handleInput(data: string): void {
|
|
251
|
+
if (this.keybindings.matches(data, "tui.select.cancel")) {
|
|
252
|
+
this.close();
|
|
253
|
+
}
|
|
300
254
|
}
|
|
301
255
|
}
|
|
302
256
|
```
|
|
303
257
|
|
|
304
|
-
|
|
305
|
-
- Basic keys: `Key.enter`, `Key.escape`, `Key.tab`, `Key.space`, `Key.backspace`, `Key.delete`, `Key.home`, `Key.end`
|
|
306
|
-
- Arrow keys: `Key.up`, `Key.down`, `Key.left`, `Key.right`
|
|
307
|
-
- With modifiers: `Key.ctrl("c")`, `Key.shift("tab")`, `Key.alt("left")`, `Key.ctrlShift("p")`
|
|
308
|
-
- String format also works: `"enter"`, `"ctrl+c"`, `"shift+tab"`, `"ctrl+shift+p"`
|
|
258
|
+
Pi supplies `tui.*` IDs such as `tui.select.up`, `tui.select.confirm`, and `tui.select.cancel`, along with `app.*` IDs such as `app.interrupt`. Use Pi's `keyHint()` helper when displaying a configured binding in UI text; it formats the current binding rather than hard-coding a key label.
|
|
309
259
|
|
|
310
260
|
## Line Width
|
|
311
261
|
|
|
312
|
-
|
|
262
|
+
Every string returned by `render(width)` must fit within `width` visible terminal columns. ANSI styling does not count toward display width.
|
|
313
263
|
|
|
314
264
|
```typescript
|
|
315
|
-
import { visibleWidth,
|
|
265
|
+
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
316
266
|
|
|
317
267
|
render(width: number): string[] {
|
|
318
|
-
|
|
319
|
-
|
|
268
|
+
const line = truncateToWidth(this.text, width);
|
|
269
|
+
const wrapped = wrapTextWithAnsi(this.longText, width);
|
|
270
|
+
return visibleWidth(line) <= width ? [line, ...wrapped] : [truncateToWidth(line, width)];
|
|
320
271
|
}
|
|
321
272
|
```
|
|
322
273
|
|
|
323
|
-
|
|
324
|
-
- `visibleWidth(str)` - Get display width (ignores ANSI codes)
|
|
325
|
-
- `truncateToWidth(str, width, ellipsis?)` - Truncate with optional ellipsis
|
|
326
|
-
- `wrapTextWithAnsi(str, width)` - Word wrap preserving ANSI codes
|
|
274
|
+
`truncateToWidth(text, width, ellipsis?, pad?)` preserves ANSI sequences and can use `""` as its ellipsis. `wrapTextWithAnsi()` preserves active ANSI styles across wrapped lines.
|
|
327
275
|
|
|
328
|
-
## Creating Custom
|
|
276
|
+
## Creating a Custom Component
|
|
329
277
|
|
|
330
|
-
|
|
278
|
+
This selector uses Pi's configured list bindings, semantic colors, width-safe output, caching, and explicit render requests without a forwarding wrapper:
|
|
331
279
|
|
|
332
280
|
```typescript
|
|
333
|
-
import {
|
|
334
|
-
|
|
335
|
-
truncateToWidth, visibleWidth
|
|
336
|
-
} from "@earendil-works/pi-tui";
|
|
281
|
+
import type { KeybindingsManager, Theme } from "@astralyn/pi";
|
|
282
|
+
import { type Component, truncateToWidth } from "@earendil-works/pi-tui";
|
|
337
283
|
|
|
338
|
-
class MySelector {
|
|
339
|
-
private items: string[];
|
|
284
|
+
class MySelector implements Component {
|
|
340
285
|
private selected = 0;
|
|
341
286
|
private cachedWidth?: number;
|
|
342
287
|
private cachedLines?: string[];
|
|
343
|
-
|
|
344
|
-
public onSelect?: (item: string) => void;
|
|
345
|
-
public onCancel?: () => void;
|
|
346
288
|
|
|
347
|
-
constructor(
|
|
348
|
-
|
|
349
|
-
|
|
289
|
+
constructor(
|
|
290
|
+
private readonly items: readonly string[],
|
|
291
|
+
private readonly theme: Theme,
|
|
292
|
+
private readonly keybindings: KeybindingsManager,
|
|
293
|
+
private readonly requestRender: () => void,
|
|
294
|
+
private readonly onSelect: (item: string) => void,
|
|
295
|
+
private readonly onCancel: () => void,
|
|
296
|
+
) {}
|
|
350
297
|
|
|
351
298
|
handleInput(data: string): void {
|
|
352
|
-
if (
|
|
299
|
+
if (this.keybindings.matches(data, "tui.select.up") && this.selected > 0) {
|
|
353
300
|
this.selected--;
|
|
354
|
-
|
|
355
|
-
} else if (matchesKey(data, Key.down) && this.selected < this.items.length - 1) {
|
|
301
|
+
} else if (this.keybindings.matches(data, "tui.select.down") && this.selected < this.items.length - 1) {
|
|
356
302
|
this.selected++;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
this.onSelect
|
|
360
|
-
|
|
361
|
-
|
|
303
|
+
} else if (this.keybindings.matches(data, "tui.select.confirm")) {
|
|
304
|
+
const item = this.items[this.selected];
|
|
305
|
+
if (item !== undefined) this.onSelect(item);
|
|
306
|
+
return;
|
|
307
|
+
} else if (this.keybindings.matches(data, "tui.select.cancel")) {
|
|
308
|
+
this.onCancel();
|
|
309
|
+
return;
|
|
310
|
+
} else {
|
|
311
|
+
return;
|
|
362
312
|
}
|
|
313
|
+
|
|
314
|
+
this.invalidate();
|
|
315
|
+
this.requestRender();
|
|
363
316
|
}
|
|
364
317
|
|
|
365
318
|
render(width: number): string[] {
|
|
366
|
-
if (this.cachedLines && this.cachedWidth === width)
|
|
367
|
-
return this.cachedLines;
|
|
368
|
-
}
|
|
319
|
+
if (this.cachedLines && this.cachedWidth === width) return this.cachedLines;
|
|
369
320
|
|
|
370
|
-
this.cachedLines = this.items.map((item,
|
|
371
|
-
const
|
|
372
|
-
|
|
321
|
+
this.cachedLines = this.items.map((item, index) => {
|
|
322
|
+
const selected = index === this.selected;
|
|
323
|
+
const prefix = selected ? this.theme.fg("accent", "› ") : " ";
|
|
324
|
+
const label = this.theme.fg(selected ? "accent" : "text", item);
|
|
325
|
+
return truncateToWidth(prefix + label, width);
|
|
373
326
|
});
|
|
374
327
|
this.cachedWidth = width;
|
|
375
328
|
return this.cachedLines;
|
|
@@ -382,55 +335,34 @@ class MySelector {
|
|
|
382
335
|
}
|
|
383
336
|
```
|
|
384
337
|
|
|
385
|
-
|
|
338
|
+
Use it directly from an extension command:
|
|
386
339
|
|
|
387
340
|
```typescript
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const selected = await ctx.ui.custom<string | null>((tui, _theme, _keybindings, done) => {
|
|
393
|
-
const selector = new MySelector(items);
|
|
394
|
-
selector.onSelect = done;
|
|
395
|
-
selector.onCancel = () => done(null);
|
|
396
|
-
|
|
397
|
-
return {
|
|
398
|
-
render: (width) => selector.render(width),
|
|
399
|
-
handleInput: (data) => {
|
|
400
|
-
selector.handleInput(data);
|
|
401
|
-
tui.requestRender();
|
|
402
|
-
},
|
|
403
|
-
invalidate: () => selector.invalidate(),
|
|
404
|
-
};
|
|
405
|
-
});
|
|
341
|
+
const items = ["Option A", "Option B", "Option C"];
|
|
342
|
+
const selected = await ctx.ui.custom<string | null>((tui, theme, keybindings, done) =>
|
|
343
|
+
new MySelector(items, theme, keybindings, () => tui.requestRender(), done, () => done(null))
|
|
344
|
+
);
|
|
406
345
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
});
|
|
346
|
+
if (selected !== null) {
|
|
347
|
+
ctx.ui.notify(`Selected: ${selected}`, "info");
|
|
348
|
+
}
|
|
412
349
|
```
|
|
413
350
|
|
|
414
351
|
## Theming
|
|
415
352
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
**In `renderCall`/`renderResult`**, use the `theme` parameter:
|
|
353
|
+
Use the `Theme` passed to a UI factory or renderer. `theme.fg(color, text)` and `theme.bg(color, text)` use the user's semantic theme instead of fixed ANSI colors.
|
|
419
354
|
|
|
420
355
|
```typescript
|
|
421
|
-
renderResult(
|
|
422
|
-
|
|
423
|
-
return new Text(
|
|
424
|
-
|
|
425
|
-
// Use theme.bg() for background colors
|
|
426
|
-
const styled = theme.bg("toolPendingBg", theme.fg("accent", "text"));
|
|
356
|
+
renderResult(_result, _options, theme, _context) {
|
|
357
|
+
const text = theme.bg("toolSuccessBg", theme.fg("success", "Done!"));
|
|
358
|
+
return new Text(text, 0, 0);
|
|
427
359
|
}
|
|
428
360
|
```
|
|
429
361
|
|
|
430
|
-
|
|
362
|
+
Foreground color names:
|
|
431
363
|
|
|
432
364
|
| Category | Colors |
|
|
433
|
-
|
|
365
|
+
|---|---|
|
|
434
366
|
| General | `text`, `accent`, `muted`, `dim` |
|
|
435
367
|
| Status | `success`, `error`, `warning` |
|
|
436
368
|
| Borders | `border`, `borderAccent`, `borderMuted` |
|
|
@@ -439,59 +371,66 @@ renderResult(result, options, theme, context) {
|
|
|
439
371
|
| Diffs | `toolDiffAdded`, `toolDiffRemoved`, `toolDiffContext` |
|
|
440
372
|
| Markdown | `mdHeading`, `mdLink`, `mdLinkUrl`, `mdCode`, `mdCodeBlock`, `mdCodeBlockBorder`, `mdQuote`, `mdQuoteBorder`, `mdHr`, `mdListBullet` |
|
|
441
373
|
| Syntax | `syntaxComment`, `syntaxKeyword`, `syntaxFunction`, `syntaxVariable`, `syntaxString`, `syntaxNumber`, `syntaxType`, `syntaxOperator`, `syntaxPunctuation` |
|
|
442
|
-
| Thinking | `thinkingOff`, `thinkingMinimal`, `thinkingLow`, `thinkingMedium`, `thinkingHigh`, `thinkingXhigh`, `thinkingMax` |
|
|
443
|
-
|
|
|
374
|
+
| Thinking | `thinkingText`, `thinkingOff`, `thinkingMinimal`, `thinkingLow`, `thinkingMedium`, `thinkingHigh`, `thinkingXhigh`, `thinkingMax` |
|
|
375
|
+
| Mode | `bashMode` |
|
|
444
376
|
|
|
445
|
-
|
|
377
|
+
Background color names: `selectedBg`, `userMessageBg`, `customMessageBg`, `toolPendingBg`, `toolSuccessBg`, and `toolErrorBg`.
|
|
446
378
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
**For Markdown**, use `getMarkdownTheme()`:
|
|
379
|
+
For Markdown, use Pi's public helper:
|
|
450
380
|
|
|
451
381
|
```typescript
|
|
452
382
|
import { getMarkdownTheme } from "@astralyn/pi";
|
|
453
383
|
import { Markdown } from "@earendil-works/pi-tui";
|
|
454
384
|
|
|
455
|
-
renderResult(result,
|
|
456
|
-
|
|
457
|
-
return new Markdown(result.details.markdown, 0, 0, mdTheme);
|
|
385
|
+
renderResult(result, _options, _theme, _context) {
|
|
386
|
+
return new Markdown(result.details.markdown, 0, 0, getMarkdownTheme());
|
|
458
387
|
}
|
|
459
388
|
```
|
|
460
389
|
|
|
461
|
-
|
|
390
|
+
## Invalidation and Theme Changes
|
|
391
|
+
|
|
392
|
+
If a component caches lines that contain styled strings, it must clear that cache in `invalidate()`. If it creates a `Text`, `SettingsList`, or other child with already styled strings, clear the child cache **and recreate those strings** on invalidation. Calling `Container.invalidate()` alone cannot change ANSI sequences already stored in a child.
|
|
462
393
|
|
|
463
394
|
```typescript
|
|
464
|
-
|
|
465
|
-
selected: (s: string) => string;
|
|
466
|
-
normal: (s: string) => string;
|
|
467
|
-
}
|
|
468
|
-
```
|
|
395
|
+
import { Container, Text } from "@earendil-works/pi-tui";
|
|
469
396
|
|
|
470
|
-
|
|
397
|
+
// Inside a ctx.ui.custom() factory, where `theme` is supplied:
|
|
398
|
+
const container = new Container();
|
|
399
|
+
const title = new Text("", 1, 0);
|
|
400
|
+
container.addChild(title);
|
|
471
401
|
|
|
472
|
-
|
|
402
|
+
const refreshTheme = () => {
|
|
403
|
+
title.setText(theme.fg("accent", theme.bold("Current title")));
|
|
404
|
+
};
|
|
473
405
|
|
|
474
|
-
|
|
475
|
-
PI_TUI_WRITE_LOG=/tmp/tui-ansi.log npx tsx packages/tui/test/chat-simple.ts
|
|
476
|
-
```
|
|
406
|
+
refreshTheme();
|
|
477
407
|
|
|
478
|
-
|
|
408
|
+
const component = {
|
|
409
|
+
render: (width: number) => container.render(width),
|
|
410
|
+
invalidate: () => {
|
|
411
|
+
refreshTheme();
|
|
412
|
+
container.invalidate();
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
```
|
|
479
416
|
|
|
480
|
-
|
|
417
|
+
For a component that styles its output inside `render()`, simply clearing cached lines is enough:
|
|
481
418
|
|
|
482
419
|
```typescript
|
|
483
|
-
|
|
420
|
+
import type { Theme } from "@astralyn/pi";
|
|
421
|
+
import { type Component, truncateToWidth } from "@earendil-works/pi-tui";
|
|
422
|
+
|
|
423
|
+
class CachedComponent implements Component {
|
|
484
424
|
private cachedWidth?: number;
|
|
485
425
|
private cachedLines?: string[];
|
|
486
426
|
|
|
427
|
+
constructor(private readonly theme: Theme, private readonly text: string) {}
|
|
428
|
+
|
|
487
429
|
render(width: number): string[] {
|
|
488
|
-
if (this.cachedLines && this.cachedWidth === width)
|
|
489
|
-
return this.cachedLines;
|
|
490
|
-
}
|
|
491
|
-
// ... compute lines ...
|
|
430
|
+
if (this.cachedLines && this.cachedWidth === width) return this.cachedLines;
|
|
492
431
|
this.cachedWidth = width;
|
|
493
|
-
this.cachedLines =
|
|
494
|
-
return
|
|
432
|
+
this.cachedLines = [truncateToWidth(this.theme.fg("muted", this.text), width)];
|
|
433
|
+
return this.cachedLines;
|
|
495
434
|
}
|
|
496
435
|
|
|
497
436
|
invalidate(): void {
|
|
@@ -501,192 +440,95 @@ class CachedComponent {
|
|
|
501
440
|
}
|
|
502
441
|
```
|
|
503
442
|
|
|
504
|
-
Call `invalidate()`
|
|
505
|
-
|
|
506
|
-
## Invalidation and Theme Changes
|
|
507
|
-
|
|
508
|
-
When the theme changes, the TUI calls `invalidate()` on all components to clear their caches. Components must properly implement `invalidate()` to ensure theme changes take effect.
|
|
509
|
-
|
|
510
|
-
### The Problem
|
|
511
|
-
|
|
512
|
-
If a component pre-bakes theme colors into strings (via `theme.fg()`, `theme.bg()`, etc.) and caches them, the cached strings contain ANSI escape codes from the old theme. Simply clearing the render cache isn't enough if the component stores the themed content separately.
|
|
513
|
-
|
|
514
|
-
**Wrong approach** (theme colors won't update):
|
|
515
|
-
|
|
516
|
-
```typescript
|
|
517
|
-
class BadComponent extends Container {
|
|
518
|
-
private content: Text;
|
|
519
|
-
|
|
520
|
-
constructor(message: string, theme: Theme) {
|
|
521
|
-
super();
|
|
522
|
-
// Pre-baked theme colors stored in Text component
|
|
523
|
-
this.content = new Text(theme.fg("accent", message), 1, 0);
|
|
524
|
-
this.addChild(this.content);
|
|
525
|
-
}
|
|
526
|
-
// No invalidate override - parent's invalidate only clears
|
|
527
|
-
// child render caches, not the pre-baked content
|
|
528
|
-
}
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
### The Solution
|
|
532
|
-
|
|
533
|
-
Components that build content with theme colors must rebuild that content when `invalidate()` is called:
|
|
534
|
-
|
|
535
|
-
```typescript
|
|
536
|
-
class GoodComponent extends Container {
|
|
537
|
-
private message: string;
|
|
538
|
-
private content: Text;
|
|
539
|
-
|
|
540
|
-
constructor(message: string) {
|
|
541
|
-
super();
|
|
542
|
-
this.message = message;
|
|
543
|
-
this.content = new Text("", 1, 0);
|
|
544
|
-
this.addChild(this.content);
|
|
545
|
-
this.updateDisplay();
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
private updateDisplay(): void {
|
|
549
|
-
// Rebuild content with current theme
|
|
550
|
-
this.content.setText(theme.fg("accent", this.message));
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
override invalidate(): void {
|
|
554
|
-
super.invalidate(); // Clear child caches
|
|
555
|
-
this.updateDisplay(); // Rebuild with new theme
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
### Pattern: Rebuild on Invalidate
|
|
561
|
-
|
|
562
|
-
For components with complex content:
|
|
563
|
-
|
|
564
|
-
```typescript
|
|
565
|
-
class ComplexComponent extends Container {
|
|
566
|
-
private data: SomeData;
|
|
567
|
-
|
|
568
|
-
constructor(data: SomeData) {
|
|
569
|
-
super();
|
|
570
|
-
this.data = data;
|
|
571
|
-
this.rebuild();
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
private rebuild(): void {
|
|
575
|
-
this.clear(); // Remove all children
|
|
576
|
-
|
|
577
|
-
// Build UI with current theme
|
|
578
|
-
this.addChild(new Text(theme.fg("accent", theme.bold("Title")), 1, 0));
|
|
579
|
-
this.addChild(new Spacer(1));
|
|
580
|
-
|
|
581
|
-
for (const item of this.data.items) {
|
|
582
|
-
const color = item.active ? "success" : "muted";
|
|
583
|
-
this.addChild(new Text(theme.fg(color, item.label), 1, 0));
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
override invalidate(): void {
|
|
588
|
-
super.invalidate();
|
|
589
|
-
this.rebuild();
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
### When This Matters
|
|
595
|
-
|
|
596
|
-
This pattern is needed when:
|
|
597
|
-
|
|
598
|
-
1. **Pre-baking theme colors** - Using `theme.fg()` or `theme.bg()` to create styled strings stored in child components
|
|
599
|
-
2. **Syntax highlighting** - Using `highlightCode()` which applies theme-based syntax colors
|
|
600
|
-
3. **Complex layouts** - Building child component trees that embed theme colors
|
|
601
|
-
|
|
602
|
-
This pattern is NOT needed when:
|
|
603
|
-
|
|
604
|
-
1. **Using theme callbacks** - Passing functions like `(text) => theme.fg("accent", text)` that are called during render
|
|
605
|
-
2. **Simple containers** - Just grouping other components without adding themed content
|
|
606
|
-
3. **Stateless render** - Computing themed output fresh in every `render()` call (no caching)
|
|
443
|
+
Call `invalidate()` and then `tui.requestRender()` whenever component state changes outside input handling, such as in a timer, event listener, or async completion. Implement `dispose()` as well when that state is driven by a timer or subscription.
|
|
607
444
|
|
|
608
445
|
## Common Patterns
|
|
609
446
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
### Pattern 1: Selection Dialog (SelectList)
|
|
613
|
-
|
|
614
|
-
For letting users pick from a list of options. Use `SelectList` from `@earendil-works/pi-tui` with `DynamicBorder` for framing.
|
|
447
|
+
### Pattern 1: Selection Dialog (`SelectList`)
|
|
615
448
|
|
|
616
449
|
```typescript
|
|
617
|
-
import
|
|
618
|
-
import { DynamicBorder } from "@astralyn/pi";
|
|
450
|
+
import { DynamicBorder, keyHint } from "@astralyn/pi";
|
|
619
451
|
import { Container, type SelectItem, SelectList, Text } from "@earendil-works/pi-tui";
|
|
620
452
|
|
|
621
453
|
pi.registerCommand("pick", {
|
|
622
454
|
handler: async (_args, ctx) => {
|
|
455
|
+
if (ctx.mode !== "tui") return;
|
|
456
|
+
|
|
623
457
|
const items: SelectItem[] = [
|
|
624
458
|
{ value: "opt1", label: "Option 1", description: "First option" },
|
|
625
459
|
{ value: "opt2", label: "Option 2", description: "Second option" },
|
|
626
|
-
{ value: "opt3", label: "Option 3" },
|
|
460
|
+
{ value: "opt3", label: "Option 3" },
|
|
627
461
|
];
|
|
628
462
|
|
|
629
|
-
const result = await ctx.ui.custom<string | null>((tui, theme,
|
|
463
|
+
const result = await ctx.ui.custom<string | null>((tui, theme, _keybindings, done) => {
|
|
630
464
|
const container = new Container();
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
|
|
634
|
-
|
|
635
|
-
// Title
|
|
636
|
-
container.addChild(new Text(theme.fg("accent", theme.bold("Pick an Option")), 1, 0));
|
|
637
|
-
|
|
638
|
-
// SelectList with theme
|
|
465
|
+
const title = new Text("", 1, 0);
|
|
466
|
+
const hint = new Text("", 1, 0);
|
|
639
467
|
const selectList = new SelectList(items, Math.min(items.length, 10), {
|
|
640
|
-
selectedPrefix: (
|
|
641
|
-
selectedText: (
|
|
642
|
-
description: (
|
|
643
|
-
scrollInfo: (
|
|
644
|
-
noMatch: (
|
|
468
|
+
selectedPrefix: (text: string) => theme.fg("accent", text),
|
|
469
|
+
selectedText: (text: string) => theme.fg("accent", text),
|
|
470
|
+
description: (text: string) => theme.fg("muted", text),
|
|
471
|
+
scrollInfo: (text: string) => theme.fg("dim", text),
|
|
472
|
+
noMatch: (text: string) => theme.fg("warning", text),
|
|
645
473
|
});
|
|
646
474
|
selectList.onSelect = (item) => done(item.value);
|
|
647
475
|
selectList.onCancel = () => done(null);
|
|
648
|
-
container.addChild(selectList);
|
|
649
476
|
|
|
650
|
-
|
|
651
|
-
|
|
477
|
+
const refreshTheme = () => {
|
|
478
|
+
title.setText(theme.fg("accent", theme.bold("Pick an option")));
|
|
479
|
+
hint.setText(
|
|
480
|
+
[
|
|
481
|
+
keyHint("tui.select.up", "up"),
|
|
482
|
+
keyHint("tui.select.down", "down"),
|
|
483
|
+
keyHint("tui.select.confirm", "select"),
|
|
484
|
+
keyHint("tui.select.cancel", "cancel"),
|
|
485
|
+
].join(theme.fg("dim", " • ")),
|
|
486
|
+
);
|
|
487
|
+
};
|
|
652
488
|
|
|
653
|
-
|
|
654
|
-
container.addChild(
|
|
489
|
+
container.addChild(new DynamicBorder((text: string) => theme.fg("borderAccent", text)));
|
|
490
|
+
container.addChild(title);
|
|
491
|
+
container.addChild(selectList);
|
|
492
|
+
container.addChild(hint);
|
|
493
|
+
container.addChild(new DynamicBorder((text: string) => theme.fg("borderAccent", text)));
|
|
494
|
+
refreshTheme();
|
|
655
495
|
|
|
656
496
|
return {
|
|
657
|
-
render: (
|
|
658
|
-
invalidate: () =>
|
|
659
|
-
|
|
497
|
+
render: (width: number) => container.render(width),
|
|
498
|
+
invalidate: () => {
|
|
499
|
+
refreshTheme();
|
|
500
|
+
container.invalidate();
|
|
501
|
+
},
|
|
502
|
+
handleInput: (data: string) => {
|
|
503
|
+
selectList.handleInput(data);
|
|
504
|
+
tui.requestRender();
|
|
505
|
+
},
|
|
660
506
|
};
|
|
661
507
|
});
|
|
662
508
|
|
|
663
|
-
if (result) {
|
|
664
|
-
ctx.ui.notify(`Selected: ${result}`, "info");
|
|
665
|
-
}
|
|
509
|
+
if (result !== null) ctx.ui.notify(`Selected: ${result}`, "info");
|
|
666
510
|
},
|
|
667
511
|
});
|
|
668
512
|
```
|
|
669
513
|
|
|
670
514
|
**Examples:** [preset.ts](../examples/extensions/preset.ts), [tools.ts](../examples/extensions/tools.ts)
|
|
671
515
|
|
|
672
|
-
### Pattern 2: Async Operation with Cancel (BorderedLoader)
|
|
516
|
+
### Pattern 2: Async Operation with Cancel (`BorderedLoader`)
|
|
673
517
|
|
|
674
|
-
|
|
518
|
+
`BorderedLoader` is Pi's public framed wrapper around the TUI package's cancellable loader. Its `dispose()` stops its loader, so returning it directly gives the operation a clean lifecycle.
|
|
675
519
|
|
|
676
520
|
```typescript
|
|
677
521
|
import { BorderedLoader } from "@astralyn/pi";
|
|
678
522
|
|
|
679
523
|
pi.registerCommand("fetch", {
|
|
680
524
|
handler: async (_args, ctx) => {
|
|
681
|
-
|
|
525
|
+
if (ctx.mode !== "tui") return;
|
|
526
|
+
|
|
527
|
+
const result = await ctx.ui.custom<string | null>((tui, theme, _keybindings, done) => {
|
|
682
528
|
const loader = new BorderedLoader(tui, theme, "Fetching data...");
|
|
683
529
|
loader.onAbort = () => done(null);
|
|
684
530
|
|
|
685
|
-
|
|
686
|
-
fetchData(loader.signal)
|
|
687
|
-
.then((data) => done(data))
|
|
688
|
-
.catch(() => done(null));
|
|
689
|
-
|
|
531
|
+
void fetchData(loader.signal).then(done, () => done(null));
|
|
690
532
|
return loader;
|
|
691
533
|
});
|
|
692
534
|
|
|
@@ -701,9 +543,9 @@ pi.registerCommand("fetch", {
|
|
|
701
543
|
|
|
702
544
|
**Examples:** [qna.ts](../examples/extensions/qna.ts), [handoff.ts](../examples/extensions/handoff.ts)
|
|
703
545
|
|
|
704
|
-
### Pattern 3: Settings
|
|
546
|
+
### Pattern 3: Settings and Toggles (`SettingsList`)
|
|
705
547
|
|
|
706
|
-
|
|
548
|
+
Use Pi's semantic settings-list theme. Rebuild this small list on invalidation so its pre-styled cursor also follows a theme change.
|
|
707
549
|
|
|
708
550
|
```typescript
|
|
709
551
|
import { getSettingsListTheme } from "@astralyn/pi";
|
|
@@ -711,32 +553,44 @@ import { Container, type SettingItem, SettingsList, Text } from "@earendil-works
|
|
|
711
553
|
|
|
712
554
|
pi.registerCommand("settings", {
|
|
713
555
|
handler: async (_args, ctx) => {
|
|
556
|
+
if (ctx.mode !== "tui") return;
|
|
557
|
+
|
|
714
558
|
const items: SettingItem[] = [
|
|
715
559
|
{ id: "verbose", label: "Verbose mode", currentValue: "off", values: ["on", "off"] },
|
|
716
560
|
{ id: "color", label: "Color output", currentValue: "on", values: ["on", "off"] },
|
|
717
561
|
];
|
|
718
562
|
|
|
719
|
-
await ctx.ui.custom((
|
|
563
|
+
await ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
720
564
|
const container = new Container();
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
(
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
565
|
+
const title = new Text("", 1, 1);
|
|
566
|
+
container.addChild(title);
|
|
567
|
+
let settingsList: SettingsList | undefined;
|
|
568
|
+
|
|
569
|
+
const rebuild = () => {
|
|
570
|
+
title.setText(theme.fg("accent", theme.bold("Settings")));
|
|
571
|
+
if (settingsList) container.removeChild(settingsList);
|
|
572
|
+
settingsList = new SettingsList(
|
|
573
|
+
items,
|
|
574
|
+
Math.min(items.length + 2, 15),
|
|
575
|
+
getSettingsListTheme(),
|
|
576
|
+
(id, newValue) => ctx.ui.notify(`${id} = ${newValue}`, "info"),
|
|
577
|
+
() => done(undefined),
|
|
578
|
+
{ enableSearch: true },
|
|
579
|
+
);
|
|
580
|
+
container.addChild(settingsList);
|
|
581
|
+
};
|
|
735
582
|
|
|
583
|
+
rebuild();
|
|
736
584
|
return {
|
|
737
|
-
render: (
|
|
738
|
-
invalidate: () =>
|
|
739
|
-
|
|
585
|
+
render: (width: number) => container.render(width),
|
|
586
|
+
invalidate: () => {
|
|
587
|
+
rebuild();
|
|
588
|
+
container.invalidate();
|
|
589
|
+
},
|
|
590
|
+
handleInput: (data: string) => {
|
|
591
|
+
settingsList?.handleInput(data);
|
|
592
|
+
tui.requestRender();
|
|
593
|
+
},
|
|
740
594
|
};
|
|
741
595
|
});
|
|
742
596
|
},
|
|
@@ -747,27 +601,20 @@ pi.registerCommand("settings", {
|
|
|
747
601
|
|
|
748
602
|
### Pattern 4: Persistent Status Indicator
|
|
749
603
|
|
|
750
|
-
Show status in the footer that persists across renders. Good for mode indicators.
|
|
751
|
-
|
|
752
604
|
```typescript
|
|
753
|
-
|
|
754
|
-
ctx.ui.setStatus("my-
|
|
755
|
-
|
|
756
|
-
// Clear status
|
|
757
|
-
ctx.ui.setStatus("my-ext", undefined);
|
|
605
|
+
ctx.ui.setStatus("my-extension", ctx.ui.theme.fg("accent", "● active"));
|
|
606
|
+
ctx.ui.setStatus("my-extension", undefined); // Clear
|
|
758
607
|
```
|
|
759
608
|
|
|
760
|
-
**Examples:** [status-line.ts](../examples/extensions/status-line.ts), [
|
|
609
|
+
**Examples:** [status-line.ts](../examples/extensions/status-line.ts), [preset.ts](../examples/extensions/preset.ts)
|
|
761
610
|
|
|
762
611
|
### Pattern 4b: Working Indicator Customization
|
|
763
612
|
|
|
764
|
-
Customize the inline working indicator shown while pi is streaming a response.
|
|
765
|
-
|
|
766
613
|
```typescript
|
|
767
614
|
// Static indicator
|
|
768
615
|
ctx.ui.setWorkingIndicator({ frames: [ctx.ui.theme.fg("accent", "●")] });
|
|
769
616
|
|
|
770
|
-
// Custom
|
|
617
|
+
// Custom animation. Frames are rendered verbatim, so style them explicitly.
|
|
771
618
|
ctx.ui.setWorkingIndicator({
|
|
772
619
|
frames: [
|
|
773
620
|
ctx.ui.theme.fg("dim", "·"),
|
|
@@ -778,121 +625,145 @@ ctx.ui.setWorkingIndicator({
|
|
|
778
625
|
intervalMs: 120,
|
|
779
626
|
});
|
|
780
627
|
|
|
781
|
-
|
|
782
|
-
ctx.ui.setWorkingIndicator(
|
|
783
|
-
|
|
784
|
-
// Restore pi's default spinner
|
|
785
|
-
ctx.ui.setWorkingIndicator();
|
|
628
|
+
ctx.ui.setWorkingIndicator({ frames: [] }); // Hide
|
|
629
|
+
ctx.ui.setWorkingIndicator(); // Restore Pi's default
|
|
786
630
|
```
|
|
787
631
|
|
|
788
|
-
This only affects the normal streaming working indicator. Compaction and retry loaders keep their built-in styling. Custom frames are rendered verbatim, so extensions must add their own colors when needed.
|
|
789
|
-
|
|
790
632
|
**Examples:** [working-indicator.ts](../examples/extensions/working-indicator.ts)
|
|
791
633
|
|
|
792
|
-
### Pattern 5: Widgets Above
|
|
793
|
-
|
|
794
|
-
Show persistent content above or below the input editor. Good for todo lists, progress.
|
|
634
|
+
### Pattern 5: Widgets Above or Below the Editor
|
|
795
635
|
|
|
796
636
|
```typescript
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
ctx.ui.setWidget("my-widget", (_tui, theme) => {
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
}
|
|
637
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
638
|
+
|
|
639
|
+
const tasks = [
|
|
640
|
+
{ text: "Review changes", done: true },
|
|
641
|
+
{ text: "Run checks", done: false },
|
|
642
|
+
];
|
|
643
|
+
|
|
644
|
+
ctx.ui.setWidget("my-widget", (_tui, theme) => ({
|
|
645
|
+
render(width: number): string[] {
|
|
646
|
+
return tasks.map((task) =>
|
|
647
|
+
truncateToWidth(
|
|
648
|
+
task.done
|
|
649
|
+
? theme.fg("success", "✓ ") + theme.fg("muted", task.text)
|
|
650
|
+
: theme.fg("dim", "○ ") + theme.fg("text", task.text),
|
|
651
|
+
width,
|
|
652
|
+
),
|
|
653
|
+
);
|
|
654
|
+
},
|
|
655
|
+
invalidate(): void {},
|
|
656
|
+
}));
|
|
815
657
|
|
|
816
|
-
// Clear
|
|
817
|
-
ctx.ui.setWidget("my-widget",
|
|
658
|
+
ctx.ui.setWidget("my-widget", undefined); // Clear
|
|
659
|
+
ctx.ui.setWidget("my-widget", ["Below the editor"], { placement: "belowEditor" });
|
|
818
660
|
```
|
|
819
661
|
|
|
820
|
-
|
|
662
|
+
The factory's component is disposed when that widget key is replaced or cleared. If mutable widget state changes, retain the factory's `tui` argument and call `tui.requestRender()`.
|
|
663
|
+
|
|
664
|
+
**Examples:** [widget-placement.ts](../examples/extensions/widget-placement.ts)
|
|
821
665
|
|
|
822
666
|
### Pattern 6: Custom Footer
|
|
823
667
|
|
|
824
|
-
|
|
668
|
+
A footer factory receives branch and extension-status data unavailable elsewhere. Return the unsubscribe function as `dispose` so it is released when the footer changes.
|
|
825
669
|
|
|
826
670
|
```typescript
|
|
671
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
672
|
+
|
|
827
673
|
ctx.ui.setFooter((tui, theme, footerData) => ({
|
|
828
|
-
invalidate() {},
|
|
829
674
|
render(width: number): string[] {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
return [
|
|
675
|
+
const branch = footerData.getGitBranch() ?? "no git";
|
|
676
|
+
const text = theme.fg("dim", `${ctx.model?.id ?? "no model"} (${branch})`);
|
|
677
|
+
return [truncateToWidth(text, width)];
|
|
833
678
|
},
|
|
834
|
-
|
|
679
|
+
invalidate(): void {},
|
|
680
|
+
dispose: footerData.onBranchChange(() => tui.requestRender()),
|
|
835
681
|
}));
|
|
836
682
|
|
|
837
|
-
ctx.ui.setFooter(undefined); //
|
|
683
|
+
ctx.ui.setFooter(undefined); // Restore Pi's default footer
|
|
838
684
|
```
|
|
839
685
|
|
|
840
|
-
|
|
686
|
+
`footerData.getExtensionStatuses()` returns the read-only status map set through `ctx.ui.setStatus()`. Session statistics are available from `ctx.sessionManager` and `ctx.model`.
|
|
841
687
|
|
|
842
688
|
**Examples:** [custom-footer.ts](../examples/extensions/custom-footer.ts)
|
|
843
689
|
|
|
844
|
-
### Pattern 7: Custom Editor
|
|
690
|
+
### Pattern 7: Custom Editor
|
|
845
691
|
|
|
846
|
-
|
|
692
|
+
Extend `CustomEditor` to preserve Pi's application keybindings. This example uses the configured `app.interrupt` binding to enter normal mode and `decodeKittyPrintable()` for vim's printable commands.
|
|
847
693
|
|
|
848
694
|
```typescript
|
|
849
|
-
import { CustomEditor, type ExtensionAPI } from "@astralyn/pi";
|
|
850
|
-
import {
|
|
695
|
+
import { CustomEditor, type ExtensionAPI, type KeybindingsManager } from "@astralyn/pi";
|
|
696
|
+
import {
|
|
697
|
+
decodeKittyPrintable,
|
|
698
|
+
truncateToWidth,
|
|
699
|
+
type EditorTheme,
|
|
700
|
+
type TUI,
|
|
701
|
+
visibleWidth,
|
|
702
|
+
} from "@earendil-works/pi-tui";
|
|
851
703
|
|
|
852
704
|
type Mode = "normal" | "insert";
|
|
853
705
|
|
|
854
706
|
class VimEditor extends CustomEditor {
|
|
855
707
|
private mode: Mode = "insert";
|
|
856
708
|
|
|
709
|
+
constructor(tui: TUI, theme: EditorTheme, private readonly bindings: KeybindingsManager) {
|
|
710
|
+
super(tui, theme, bindings);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
private setMode(mode: Mode): void {
|
|
714
|
+
if (this.mode === mode) return;
|
|
715
|
+
this.mode = mode;
|
|
716
|
+
this.invalidate();
|
|
717
|
+
this.tui.requestRender();
|
|
718
|
+
}
|
|
719
|
+
|
|
857
720
|
handleInput(data: string): void {
|
|
858
|
-
|
|
859
|
-
if (matchesKey(data, "escape")) {
|
|
721
|
+
if (this.bindings.matches(data, "app.interrupt")) {
|
|
860
722
|
if (this.mode === "insert") {
|
|
861
|
-
this.
|
|
862
|
-
|
|
723
|
+
this.setMode("normal");
|
|
724
|
+
} else {
|
|
725
|
+
super.handleInput(data);
|
|
863
726
|
}
|
|
864
|
-
// In normal mode, escape aborts agent (handled by CustomEditor)
|
|
865
|
-
super.handleInput(data);
|
|
866
727
|
return;
|
|
867
728
|
}
|
|
868
729
|
|
|
869
|
-
// Insert mode: pass everything to CustomEditor
|
|
870
730
|
if (this.mode === "insert") {
|
|
871
731
|
super.handleInput(data);
|
|
872
732
|
return;
|
|
873
733
|
}
|
|
874
734
|
|
|
875
|
-
|
|
876
|
-
switch (
|
|
877
|
-
case "i":
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
case "
|
|
881
|
-
|
|
735
|
+
const key = decodeKittyPrintable(data) ?? data;
|
|
736
|
+
switch (key) {
|
|
737
|
+
case "i":
|
|
738
|
+
this.setMode("insert");
|
|
739
|
+
return;
|
|
740
|
+
case "h":
|
|
741
|
+
super.handleInput("\x1b[D");
|
|
742
|
+
return;
|
|
743
|
+
case "j":
|
|
744
|
+
super.handleInput("\x1b[B");
|
|
745
|
+
return;
|
|
746
|
+
case "k":
|
|
747
|
+
super.handleInput("\x1b[A");
|
|
748
|
+
return;
|
|
749
|
+
case "l":
|
|
750
|
+
super.handleInput("\x1b[C");
|
|
751
|
+
return;
|
|
882
752
|
}
|
|
883
|
-
|
|
884
|
-
|
|
753
|
+
|
|
754
|
+
// Keep application controls, but do not insert normal-mode text.
|
|
755
|
+
if (key.length === 1 && key.charCodeAt(0) >= 32) return;
|
|
885
756
|
super.handleInput(data);
|
|
886
757
|
}
|
|
887
758
|
|
|
888
759
|
render(width: number): string[] {
|
|
889
760
|
const lines = super.render(width);
|
|
890
|
-
|
|
891
|
-
if (
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
lines[
|
|
761
|
+
const last = lines.length - 1;
|
|
762
|
+
if (last < 0) return lines;
|
|
763
|
+
|
|
764
|
+
const label = this.mode === "normal" ? " NORMAL " : " INSERT ";
|
|
765
|
+
if (width >= label.length && visibleWidth(lines[last]!) >= label.length) {
|
|
766
|
+
lines[last] = truncateToWidth(lines[last]!, width - label.length, "") + label;
|
|
896
767
|
}
|
|
897
768
|
return lines;
|
|
898
769
|
}
|
|
@@ -900,43 +771,34 @@ class VimEditor extends CustomEditor {
|
|
|
900
771
|
|
|
901
772
|
export default function (pi: ExtensionAPI) {
|
|
902
773
|
pi.on("session_start", (_event, ctx) => {
|
|
903
|
-
|
|
904
|
-
ctx.ui.setEditorComponent((tui, theme, keybindings) =>
|
|
905
|
-
new VimEditor(tui, theme, keybindings)
|
|
906
|
-
);
|
|
774
|
+
ctx.ui.setEditorComponent((tui, theme, keybindings) => new VimEditor(tui, theme, keybindings));
|
|
907
775
|
});
|
|
908
776
|
}
|
|
909
777
|
```
|
|
910
778
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
- **Extend `CustomEditor`** (not base `Editor`) to get app keybindings (escape to abort, ctrl+d to exit, model switching, etc.)
|
|
914
|
-
- **Call `super.handleInput(data)`** for keys you don't handle
|
|
915
|
-
- **Factory pattern**: `setEditorComponent` receives a factory function that gets `tui`, `theme`, and `keybindings`
|
|
916
|
-
- **Pass `undefined`** to restore the default editor: `ctx.ui.setEditorComponent(undefined)`
|
|
779
|
+
Pass `undefined` to `ctx.ui.setEditorComponent()` to restore Pi's default editor.
|
|
917
780
|
|
|
918
781
|
**Examples:** [modal-editor.ts](../examples/extensions/modal-editor.ts)
|
|
919
782
|
|
|
920
|
-
##
|
|
921
|
-
|
|
922
|
-
1. **Always use theme from callback** - Don't import theme directly. Use `theme` from the `ctx.ui.custom((tui, theme, keybindings, done) => ...)` callback.
|
|
783
|
+
## Debug Logging
|
|
923
784
|
|
|
924
|
-
|
|
785
|
+
Set `PI_TUI_WRITE_LOG` to capture the ANSI stream written to stdout.
|
|
925
786
|
|
|
926
|
-
|
|
787
|
+
```bash
|
|
788
|
+
PI_TUI_WRITE_LOG=/tmp/tui-ansi.log pi
|
|
789
|
+
```
|
|
927
790
|
|
|
928
|
-
|
|
791
|
+
## Key Rules
|
|
929
792
|
|
|
930
|
-
|
|
793
|
+
1. Guard terminal-only UI with `ctx.mode === "tui"`.
|
|
794
|
+
2. Use the callback `theme` or `ctx.ui.theme`; use semantic `fg()` and `bg()` helpers, never fixed ANSI colors.
|
|
795
|
+
3. Use `keybindings.matches()` and `keyHint()` for configurable Pi actions. Reserve `matchesKey()` for deliberately fixed extension-specific keys.
|
|
796
|
+
4. Keep every rendered line within the supplied width, including styled lines.
|
|
797
|
+
5. Invalidate cached output and call `tui.requestRender()` after state changes.
|
|
798
|
+
6. Implement `dispose()` for timers, subscriptions, or other owned resources.
|
|
799
|
+
7. Prefer `SelectList`, `SettingsList`, and `BorderedLoader` over rebuilding their behavior.
|
|
931
800
|
|
|
932
|
-
##
|
|
801
|
+
## Additional examples
|
|
933
802
|
|
|
934
|
-
- **
|
|
935
|
-
- **
|
|
936
|
-
- **Settings toggles**: [examples/extensions/tools.ts](../examples/extensions/tools.ts) - SettingsList for tool enable/disable
|
|
937
|
-
- **Status indicators**: [examples/extensions/plan-mode/index.ts](../examples/extensions/plan-mode/index.ts) - setStatus and setWidget
|
|
938
|
-
- **Working indicator**: [examples/extensions/working-indicator.ts](../examples/extensions/working-indicator.ts) - setWorkingIndicator
|
|
939
|
-
- **Custom footer**: [examples/extensions/custom-footer.ts](../examples/extensions/custom-footer.ts) - setFooter with stats
|
|
940
|
-
- **Custom editor**: [examples/extensions/modal-editor.ts](../examples/extensions/modal-editor.ts) - Vim-like modal editing
|
|
941
|
-
- **Snake game**: [examples/extensions/snake.ts](../examples/extensions/snake.ts) - Full game with keyboard input, game loop
|
|
942
|
-
- **Custom tool rendering**: [examples/extensions/todo.ts](../examples/extensions/todo.ts) - renderCall and renderResult
|
|
803
|
+
- **Game loop and disposal:** [examples/extensions/snake.ts](../examples/extensions/snake.ts)
|
|
804
|
+
- **Custom tool rendering:** [examples/extensions/todo.ts](../examples/extensions/todo.ts)
|