@bubblebrain-ai/bubble 0.0.52 → 0.0.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -12
- package/dist/agent/routing-catalog.js +3 -2
- package/dist/agent/subagent/router.js +5 -0
- package/dist/agent/subagent/runtime.d.ts +1 -0
- package/dist/agent/subagent/runtime.js +13 -1
- package/dist/agent/subagent-control.d.ts +4 -0
- package/dist/agent/subagent-control.js +1 -0
- package/dist/agent/subagent-store.js +2 -0
- package/dist/agent/workflow/control.d.ts +2 -0
- package/dist/agent/workflow/runs.d.ts +1 -0
- package/dist/agent/workflow/runs.js +3 -0
- package/dist/agent.d.ts +11 -1
- package/dist/agent.js +158 -57
- package/dist/bin.js +2 -44
- package/dist/config.d.ts +14 -1
- package/dist/config.js +45 -4
- package/dist/context/budget.d.ts +7 -1
- package/dist/context/budget.js +25 -2
- package/dist/context/compact-llm.d.ts +1 -1
- package/dist/context/compact-llm.js +5 -2
- package/dist/context/overflow.js +2 -0
- package/dist/context/projector.d.ts +8 -0
- package/dist/context/projector.js +55 -6
- package/dist/context/tool-output-truncate.d.ts +17 -1
- package/dist/context/tool-output-truncate.js +171 -49
- package/dist/feishu/agent-host/run-driver.js +3 -0
- package/dist/feishu/serve.js +2 -0
- package/dist/goal/command.d.ts +1 -1
- package/dist/goal/command.js +7 -2
- package/dist/main.d.ts +1 -1
- package/dist/main.js +38 -8
- package/dist/memory/db.js +0 -7
- package/dist/model-catalog.js +25 -6
- package/dist/model-config.js +3 -2
- package/dist/model-pricing.d.ts +24 -5
- package/dist/model-pricing.js +0 -0
- package/dist/model-selection.js +13 -4
- package/dist/network/chatgpt-transport.js +0 -34
- package/dist/network/errors.d.ts +19 -0
- package/dist/network/errors.js +24 -0
- package/dist/network/provider-transport.d.ts +0 -4
- package/dist/network/provider-transport.js +3 -35
- package/dist/network/retry.d.ts +6 -1
- package/dist/network/retry.js +9 -0
- package/dist/provider-model-policy.d.ts +16 -0
- package/dist/provider-model-policy.js +33 -0
- package/dist/provider-registry.d.ts +1 -1
- package/dist/provider-registry.js +73 -10
- package/dist/provider-transform.js +32 -4
- package/dist/provider.d.ts +6 -2
- package/dist/provider.js +95 -6
- package/dist/question/controller.js +5 -4
- package/dist/question/normalize.d.ts +6 -0
- package/dist/question/normalize.js +11 -0
- package/dist/rewind.d.ts +20 -0
- package/dist/rewind.js +29 -0
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.js +11 -3
- package/dist/session.d.ts +7 -0
- package/dist/session.js +20 -0
- package/dist/skills/discovery.js +18 -0
- package/dist/skills/registry.d.ts +12 -2
- package/dist/skills/registry.js +40 -5
- package/dist/skills/types.d.ts +2 -0
- package/dist/slash-commands/commands.js +120 -81
- package/dist/slash-commands/registry.d.ts +2 -1
- package/dist/slash-commands/registry.js +3 -0
- package/dist/slash-commands/types.d.ts +22 -4
- package/dist/stats/usage.d.ts +3 -1
- package/dist/stats/usage.js +22 -13
- package/dist/tasks/manager.d.ts +4 -2
- package/dist/tasks/manager.js +34 -9
- package/dist/tasks/promotion.d.ts +2 -2
- package/dist/tasks/promotion.js +2 -2
- package/dist/tools/agent-lifecycle.js +4 -0
- package/dist/tools/bash.d.ts +3 -1
- package/dist/tools/bash.js +13 -2
- package/dist/tools/grep.d.ts +5 -1
- package/dist/tools/grep.js +318 -69
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/question.js +5 -4
- package/dist/tools/read.js +40 -21
- package/dist/tui/animation-clock.d.ts +14 -0
- package/dist/tui/animation-clock.js +34 -0
- package/dist/tui/app.d.ts +176 -0
- package/dist/tui/app.js +1647 -0
- package/dist/tui/components/approval-dialog.d.ts +43 -0
- package/dist/tui/components/approval-dialog.js +247 -0
- package/dist/tui/components/bottom-sheet.d.ts +9 -0
- package/dist/tui/components/bottom-sheet.js +32 -0
- package/dist/tui/components/context-info.d.ts +54 -0
- package/dist/tui/components/context-info.js +388 -0
- package/dist/tui/components/feedback-dialog.d.ts +56 -0
- package/dist/tui/components/feedback-dialog.js +262 -0
- package/dist/tui/components/feishu-setup-dialog.d.ts +65 -0
- package/dist/tui/components/feishu-setup-dialog.js +332 -0
- package/dist/tui/components/image-preview.d.ts +32 -0
- package/dist/tui/components/image-preview.js +130 -0
- package/dist/tui/components/plan-dialog.d.ts +34 -0
- package/dist/tui/components/plan-dialog.js +196 -0
- package/dist/tui/components/provider-key-input.d.ts +18 -0
- package/dist/tui/components/provider-key-input.js +49 -0
- package/dist/tui/components/question-dialog.d.ts +25 -0
- package/dist/tui/components/question-dialog.js +263 -0
- package/dist/tui/components/responsive-transcript.d.ts +31 -0
- package/dist/tui/components/responsive-transcript.js +77 -0
- package/dist/tui/components/rewind-picker.d.ts +62 -0
- package/dist/tui/components/rewind-picker.js +317 -0
- package/dist/tui/components/session-picker.d.ts +49 -0
- package/dist/tui/components/session-picker.js +303 -0
- package/dist/tui/components/skills-panel.d.ts +59 -0
- package/dist/tui/components/skills-panel.js +568 -0
- package/dist/tui/components/stats-panel.d.ts +40 -0
- package/dist/tui/components/stats-panel.js +301 -0
- package/dist/tui/components/streaming-message.d.ts +69 -0
- package/dist/tui/components/streaming-message.js +393 -0
- package/dist/tui/components/subagent-inspector.d.ts +28 -0
- package/dist/tui/components/subagent-inspector.js +127 -0
- package/dist/tui/components/task-inspector.d.ts +26 -0
- package/dist/tui/components/task-inspector.js +70 -0
- package/dist/tui/components/tasks-pane.d.ts +88 -0
- package/dist/tui/components/tasks-pane.js +416 -0
- package/dist/tui/components/transcript.d.ts +96 -0
- package/dist/tui/components/transcript.js +771 -0
- package/dist/tui/components/welcome.d.ts +20 -0
- package/dist/tui/components/welcome.js +133 -0
- package/dist/tui/components/workflow-inspector.d.ts +30 -0
- package/dist/tui/components/workflow-inspector.js +136 -0
- package/dist/tui/composer-autocomplete.d.ts +61 -0
- package/dist/tui/composer-autocomplete.js +383 -0
- package/dist/tui/composer-style.d.ts +7 -0
- package/dist/tui/composer-style.js +29 -0
- package/dist/tui/controller/agent-event-reducer.d.ts +75 -0
- package/dist/tui/controller/agent-event-reducer.js +238 -0
- package/dist/tui/controller/composer-controller.d.ts +95 -0
- package/dist/tui/controller/composer-controller.js +502 -0
- package/dist/tui/controller/controller.d.ts +194 -0
- package/dist/tui/controller/controller.js +1035 -0
- package/dist/tui/controller/effects.d.ts +71 -0
- package/dist/tui/controller/effects.js +1 -0
- package/dist/tui/controller/goal-runtime-controller.d.ts +47 -0
- package/dist/tui/controller/goal-runtime-controller.js +202 -0
- package/dist/tui/controller/input-queue-machine.d.ts +71 -0
- package/dist/tui/controller/input-queue-machine.js +121 -0
- package/dist/tui/controller/intents.d.ts +56 -0
- package/dist/tui/controller/intents.js +6 -0
- package/dist/tui/controller/overlay-controller.d.ts +64 -0
- package/dist/tui/controller/overlay-controller.js +129 -0
- package/dist/tui/controller/ports.d.ts +57 -0
- package/dist/tui/controller/ports.js +9 -0
- package/dist/tui/controller/session-transition.d.ts +54 -0
- package/dist/tui/controller/session-transition.js +68 -0
- package/dist/tui/controller/state.d.ts +14 -0
- package/dist/tui/controller/state.js +36 -0
- package/dist/tui/controller/task-runtime-controller.d.ts +66 -0
- package/dist/tui/controller/task-runtime-controller.js +158 -0
- package/dist/tui/footer.d.ts +37 -0
- package/dist/tui/footer.js +80 -0
- package/dist/{tui-ink → tui/formatting}/edit-diff.d.ts +1 -1
- package/dist/{tui-ink → tui/formatting}/edit-diff.js +1 -1
- package/dist/{tui-ink/app-helpers.d.ts → tui/formatting/summary.d.ts} +4 -4
- package/dist/{tui-ink/app-helpers.js → tui/formatting/summary.js} +2 -2
- package/dist/tui/fullscreen.d.ts +48 -0
- package/dist/tui/fullscreen.js +255 -0
- package/dist/tui/image-display.d.ts +4 -0
- package/dist/tui/image-display.js +30 -12
- package/dist/tui/markdown-style.d.ts +5 -0
- package/dist/tui/markdown-style.js +23 -0
- package/dist/{tui-ink → tui/model}/composer-buffer.js +1 -1
- package/dist/tui/model/composer-types.d.ts +17 -0
- package/dist/tui/model/composer-types.js +1 -0
- package/dist/{tui-ink → tui/model}/display-history.d.ts +7 -1
- package/dist/{tui-ink → tui/model}/display-reconstruct.d.ts +1 -1
- package/dist/{tui-ink → tui/model}/display-reconstruct.js +54 -6
- package/dist/{tui-ink → tui/model}/file-mentions.d.ts +2 -0
- package/dist/{tui-ink → tui/model}/file-mentions.js +4 -0
- package/dist/tui/model/image-attachment.d.ts +15 -0
- package/dist/tui/model/image-attachment.js +1 -0
- package/dist/{tui-ink → tui/model}/image-paste.d.ts +7 -1
- package/dist/{tui-ink → tui/model}/image-paste.js +46 -0
- package/dist/tui/model/input-history.d.ts +1 -0
- package/dist/tui/model/input-history.js +1 -0
- package/dist/{tui-ink → tui/model}/input-queue.d.ts +1 -1
- package/dist/{tui-ink → tui/model}/recent-activity.js +1 -1
- package/dist/{tui-ink → tui/model}/subagent-view.d.ts +17 -2
- package/dist/{tui-ink → tui/model}/subagent-view.js +125 -4
- package/dist/{tui-ink → tui/model}/submit-dedupe.d.ts +1 -1
- package/dist/tui/model/theme-style.d.ts +10 -0
- package/dist/tui/model/theme-style.js +79 -0
- package/dist/{tui-ink → tui/model}/theme.d.ts +11 -23
- package/dist/{tui-ink → tui/model}/theme.js +23 -38
- package/dist/{tui-ink → tui/model}/trace-groups.d.ts +2 -0
- package/dist/{tui-ink → tui/model}/trace-groups.js +91 -30
- package/dist/tui/model/trace-interaction.d.ts +63 -0
- package/dist/tui/model/trace-interaction.js +87 -0
- package/dist/tui/model-picker-data.d.ts +1 -2
- package/dist/tui/model-picker-data.js +8 -1
- package/dist/tui/model-switch.js +2 -0
- package/dist/{tui-ink → tui}/run-session-picker.d.ts +2 -3
- package/dist/tui/run-session-picker.js +64 -0
- package/dist/tui/run.d.ts +72 -0
- package/dist/tui/run.js +139 -0
- package/dist/tui/terminal/multiplexer.d.ts +5 -0
- package/dist/tui/terminal/multiplexer.js +14 -0
- package/dist/tui/testing/fake-agent.d.ts +24 -0
- package/dist/tui/testing/fake-agent.js +22 -0
- package/dist/tui/testing/spy-host.d.ts +21 -0
- package/dist/tui/testing/spy-host.js +55 -0
- package/dist/tui/trace-groups.js +27 -6
- package/dist/types.d.ts +7 -0
- package/dist/update/index.d.ts +1 -0
- package/dist/update/index.js +22 -17
- package/node_modules/@bubblebrain-ai/pi-tui/LICENSE +21 -0
- package/node_modules/@bubblebrain-ai/pi-tui/README.md +854 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts +26 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js +121 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts +86 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js +649 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts +13 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js +36 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts +22 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js +99 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts +22 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js +36 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts +330 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js +2344 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts +7 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js +43 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts +28 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js +82 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts +48 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js +390 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts +31 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js +67 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts +103 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js +797 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts +56 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js +179 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts +53 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js +175 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts +50 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js +174 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts +12 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js +22 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts +32 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js +109 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts +19 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js +81 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts +13 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js +48 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts +8 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js +26 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts +39 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js +2 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts +16 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js +110 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts +31 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js +42 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts +273 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js +231 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts +184 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js +1174 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts +28 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js +46 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts +10 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js +1268 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts +40 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js +6 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts +42 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js +335 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts +3 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js +60 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts +56 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js +363 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts +10 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js +59 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts +116 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js +518 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts +142 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js +544 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts +11 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js +11 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts +140 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js +1334 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts +39 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js +536 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts +338 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js +1016 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts +17 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js +27 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts +96 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js +1193 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts +25 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js +96 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts +73 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js +188 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/README.md +20 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/build.sh +62 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/src/darwin-modifiers.c +70 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/README.md +22 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/build.mjs +229 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/src/win32-console-mode.c +119 -0
- package/node_modules/@bubblebrain-ai/pi-tui/package.json +66 -0
- package/node_modules/get-east-asian-width/index.d.ts +60 -0
- package/node_modules/get-east-asian-width/index.js +30 -0
- package/node_modules/get-east-asian-width/license +9 -0
- package/node_modules/get-east-asian-width/lookup-data.js +21 -0
- package/node_modules/get-east-asian-width/lookup.js +138 -0
- package/node_modules/get-east-asian-width/package.json +71 -0
- package/node_modules/get-east-asian-width/readme.md +65 -0
- package/node_modules/get-east-asian-width/utilities.js +24 -0
- package/node_modules/marked/LICENSE +44 -0
- package/node_modules/marked/README.md +115 -0
- package/node_modules/marked/bin/main.js +284 -0
- package/node_modules/marked/bin/marked.js +16 -0
- package/node_modules/marked/lib/marked.d.ts +760 -0
- package/node_modules/marked/lib/marked.esm.js +77 -0
- package/node_modules/marked/lib/marked.esm.js.map +7 -0
- package/node_modules/marked/lib/marked.umd.js +79 -0
- package/node_modules/marked/lib/marked.umd.js.map +7 -0
- package/node_modules/marked/man/marked.1 +113 -0
- package/node_modules/marked/man/marked.1.md +93 -0
- package/node_modules/marked/package.json +103 -0
- package/package.json +25 -10
- package/dist/tui-ink/app.d.ts +0 -76
- package/dist/tui-ink/app.js +0 -2377
- package/dist/tui-ink/approval/approval-dialog.d.ts +0 -13
- package/dist/tui-ink/approval/approval-dialog.js +0 -184
- package/dist/tui-ink/approval/diff-view.d.ts +0 -7
- package/dist/tui-ink/approval/diff-view.js +0 -44
- package/dist/tui-ink/approval/select.d.ts +0 -35
- package/dist/tui-ink/approval/select.js +0 -98
- package/dist/tui-ink/code-highlight.d.ts +0 -8
- package/dist/tui-ink/code-highlight.js +0 -121
- package/dist/tui-ink/command-palette.d.ts +0 -32
- package/dist/tui-ink/command-palette.js +0 -158
- package/dist/tui-ink/compaction-progress.d.ts +0 -19
- package/dist/tui-ink/compaction-progress.js +0 -74
- package/dist/tui-ink/detect-theme.d.ts +0 -1
- package/dist/tui-ink/detect-theme.js +0 -1
- package/dist/tui-ink/feedback-dialog.d.ts +0 -19
- package/dist/tui-ink/feedback-dialog.js +0 -133
- package/dist/tui-ink/feishu-setup-picker.d.ts +0 -5
- package/dist/tui-ink/feishu-setup-picker.js +0 -271
- package/dist/tui-ink/footer.d.ts +0 -26
- package/dist/tui-ink/footer.js +0 -51
- package/dist/tui-ink/input-box.d.ts +0 -189
- package/dist/tui-ink/input-box.js +0 -1279
- package/dist/tui-ink/input-history.d.ts +0 -1
- package/dist/tui-ink/input-history.js +0 -1
- package/dist/tui-ink/markdown.d.ts +0 -103
- package/dist/tui-ink/markdown.js +0 -747
- package/dist/tui-ink/message-list.d.ts +0 -49
- package/dist/tui-ink/message-list.js +0 -841
- package/dist/tui-ink/model-picker.d.ts +0 -143
- package/dist/tui-ink/model-picker.js +0 -966
- package/dist/tui-ink/plan-confirm.d.ts +0 -7
- package/dist/tui-ink/plan-confirm.js +0 -115
- package/dist/tui-ink/question-dialog.d.ts +0 -8
- package/dist/tui-ink/question-dialog.js +0 -135
- package/dist/tui-ink/rewind-picker.d.ts +0 -8
- package/dist/tui-ink/rewind-picker.js +0 -102
- package/dist/tui-ink/run-session-picker.js +0 -22
- package/dist/tui-ink/run.d.ts +0 -62
- package/dist/tui-ink/run.js +0 -103
- package/dist/tui-ink/session-picker.d.ts +0 -10
- package/dist/tui-ink/session-picker.js +0 -113
- package/dist/tui-ink/stats-panel.d.ts +0 -11
- package/dist/tui-ink/stats-panel.js +0 -65
- package/dist/tui-ink/subagent-inspector.d.ts +0 -21
- package/dist/tui-ink/subagent-inspector.js +0 -205
- package/dist/tui-ink/use-terminal-size.d.ts +0 -4
- package/dist/tui-ink/use-terminal-size.js +0 -48
- package/dist/tui-ink/waiting-indicator.d.ts +0 -12
- package/dist/tui-ink/waiting-indicator.js +0 -129
- package/dist/tui-ink/welcome.d.ts +0 -25
- package/dist/tui-ink/welcome.js +0 -88
- package/dist/tui-ink/wordmark.d.ts +0 -8
- package/dist/tui-ink/wordmark.js +0 -103
- /package/dist/{tui-ink → tui/model}/composer-buffer.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/display-history.js +0 -0
- /package/dist/{tui-ink → tui/model}/input-queue.js +0 -0
- /package/dist/{tui-ink → tui/model}/key-events.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/key-events.js +0 -0
- /package/dist/{tui-ink → tui/model}/paste-operation-tracker.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/paste-operation-tracker.js +0 -0
- /package/dist/{tui-ink → tui/model}/recent-activity.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/submit-dedupe.js +0 -0
- /package/dist/{tui-ink → tui/model}/sync-output.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/sync-output.js +0 -0
- /package/dist/{tui-ink → tui/model}/terminal-env.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/terminal-env.js +0 -0
- /package/dist/{tui-ink → tui/model}/terminal-mouse.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/terminal-mouse.js +0 -0
- /package/dist/{tui-ink → tui/model}/width.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/width.js +0 -0
|
@@ -1,966 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect, useMemo, useRef } from "react";
|
|
3
|
-
import { Box, Text, useInput, usePaste, useStdout } from "ink";
|
|
4
|
-
import { isKeyReleaseEvent } from "./key-events.js";
|
|
5
|
-
import { useTheme } from "./theme.js";
|
|
6
|
-
import { encodeModel, decodeModel, displayModel, isUserVisibleProvider, } from "../provider-registry.js";
|
|
7
|
-
import { listBuiltinModels } from "../model-catalog.js";
|
|
8
|
-
import { padVisual, truncateVisual } from "../text-display.js";
|
|
9
|
-
import { hasTerminalMouseSequence } from "./terminal-mouse.js";
|
|
10
|
-
import { getAvailableThinkingLevels, isThinkingOnlyLevels, isThinkingToggleModel, normalizeThinkingLevel } from "../provider-transform.js";
|
|
11
|
-
export { padVisual, truncateVisual } from "../text-display.js";
|
|
12
|
-
export function resolvePickerKeyAction(input, key) {
|
|
13
|
-
if (key.escape)
|
|
14
|
-
return "escape";
|
|
15
|
-
if (key.return)
|
|
16
|
-
return "enter";
|
|
17
|
-
if (key.upArrow)
|
|
18
|
-
return "up";
|
|
19
|
-
if (key.downArrow)
|
|
20
|
-
return "down";
|
|
21
|
-
if (key.backspace)
|
|
22
|
-
return "backspace";
|
|
23
|
-
if (key.delete)
|
|
24
|
-
return "delete";
|
|
25
|
-
// Batched chunks: slow ptys (web-terminal bridges, SSH, paste) can deliver
|
|
26
|
-
// several keys in ONE input event, in which case ink sets no key flags at
|
|
27
|
-
// all ("\r/quit\r" arrives with return=false). Act on the chunk's leading
|
|
28
|
-
// key instead of swallowing it — exact-match anchors would drop the whole
|
|
29
|
-
// chunk.
|
|
30
|
-
if (input.startsWith("\r") || input.startsWith("\n"))
|
|
31
|
-
return "enter";
|
|
32
|
-
const sequence = normalizeEscapeSequence(input);
|
|
33
|
-
if (/^(?:O|\[[\d;:]*)A/.test(sequence))
|
|
34
|
-
return "up";
|
|
35
|
-
if (/^(?:O|\[[\d;:]*)B/.test(sequence))
|
|
36
|
-
return "down";
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
export function isPrintablePickerInput(input) {
|
|
40
|
-
if (!input)
|
|
41
|
-
return false;
|
|
42
|
-
if (hasTerminalMouseSequence(input))
|
|
43
|
-
return false;
|
|
44
|
-
if (input.startsWith("\x1b"))
|
|
45
|
-
return false;
|
|
46
|
-
if (isRawEscapeTail(input))
|
|
47
|
-
return false;
|
|
48
|
-
return !/[\x00-\x1f\x7f]/.test(input);
|
|
49
|
-
}
|
|
50
|
-
export function formatSkillPickerRow(skill, options) {
|
|
51
|
-
const width = Math.max(12, options.width);
|
|
52
|
-
const marker = options.selected ? "> " : " ";
|
|
53
|
-
const nameBudget = Math.max(8, Math.min(28, Math.floor(width * 0.35)));
|
|
54
|
-
const name = truncateVisual(skill.name, nameBudget);
|
|
55
|
-
const nameCell = padVisual(name, nameBudget);
|
|
56
|
-
const description = (skill.description ?? "").replace(/\s+/g, " ").trim();
|
|
57
|
-
const row = description
|
|
58
|
-
? `${marker}${nameCell} ${description}`
|
|
59
|
-
: `${marker}${nameCell}`;
|
|
60
|
-
return padVisual(truncateVisual(row, width), width);
|
|
61
|
-
}
|
|
62
|
-
export const MODEL_PICKER_MAX_BODY_ROWS = 10;
|
|
63
|
-
export const MODEL_PICKER_CHROME_ROWS = 14;
|
|
64
|
-
export const MODEL_PICKER_RECENT_FAMILY_LIMIT = 3;
|
|
65
|
-
export function modelPickerBodyRows(termHeight) {
|
|
66
|
-
const rows = Number.isFinite(termHeight) ? Math.floor(termHeight) : 24;
|
|
67
|
-
return Math.max(1, Math.min(MODEL_PICKER_MAX_BODY_ROWS, rows - MODEL_PICKER_CHROME_ROWS));
|
|
68
|
-
}
|
|
69
|
-
export function clampPickerIndex(index, length) {
|
|
70
|
-
if (length <= 0)
|
|
71
|
-
return 0;
|
|
72
|
-
return Math.max(0, Math.min(length - 1, index));
|
|
73
|
-
}
|
|
74
|
-
export function pickerWindowStart(selectedIndex, length, visibleRows) {
|
|
75
|
-
const rows = Math.max(1, Math.floor(visibleRows));
|
|
76
|
-
const safeIndex = clampPickerIndex(selectedIndex, length);
|
|
77
|
-
const maxStart = Math.max(0, length - rows);
|
|
78
|
-
return Math.max(0, Math.min(maxStart, safeIndex - Math.floor(rows / 2)));
|
|
79
|
-
}
|
|
80
|
-
export function padPickerRows(rows, bodyRows, width) {
|
|
81
|
-
const rowCount = Math.max(1, Math.floor(bodyRows));
|
|
82
|
-
const rowWidth = Math.max(1, Math.floor(width));
|
|
83
|
-
const padded = rows.slice(0, rowCount).map((row) => padVisual(truncateVisual(row, rowWidth), rowWidth));
|
|
84
|
-
while (padded.length < rowCount) {
|
|
85
|
-
padded.push(padVisual("", rowWidth));
|
|
86
|
-
}
|
|
87
|
-
return padded;
|
|
88
|
-
}
|
|
89
|
-
export function modelPickerRecentKey(recent) {
|
|
90
|
-
return recent.join("\u0000");
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Returns a provider-scoped family key for model ordering.
|
|
94
|
-
*
|
|
95
|
-
* Keep this deliberately conservative: suffixes such as `mini`, `codex`,
|
|
96
|
-
* `spark`, or `highspeed` often describe distinct products rather than
|
|
97
|
-
* interchangeable siblings. The Codex Sol/Terra/Luna trio is the one family
|
|
98
|
-
* whose relationship is explicitly declared by the provider catalog.
|
|
99
|
-
*/
|
|
100
|
-
export function modelPickerFamilyKey(provider, modelId) {
|
|
101
|
-
const isCodexOAuth = provider.id === "openai-codex"
|
|
102
|
-
|| (provider.id === "openai" && provider.authType === "oauth");
|
|
103
|
-
if (isCodexOAuth) {
|
|
104
|
-
const match = /^(gpt-\d+(?:\.\d+)+)-(sol|terra|luna)$/i.exec(modelId);
|
|
105
|
-
if (match)
|
|
106
|
-
return `${provider.id}:${match[1].toLowerCase()}:sol-terra-luna`;
|
|
107
|
-
}
|
|
108
|
-
return `${provider.id}:${modelId}`;
|
|
109
|
-
}
|
|
110
|
-
export function buildModelPickerDisplayItems(options) {
|
|
111
|
-
const items = [];
|
|
112
|
-
let previousGroup;
|
|
113
|
-
let headerIndex = 0;
|
|
114
|
-
for (const option of options) {
|
|
115
|
-
if (option.group !== previousGroup) {
|
|
116
|
-
previousGroup = option.group;
|
|
117
|
-
items.push({
|
|
118
|
-
kind: "header",
|
|
119
|
-
key: `header:${headerIndex++}:${option.group}`,
|
|
120
|
-
label: option.group,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
items.push({ kind: "model", key: option.id, option });
|
|
124
|
-
}
|
|
125
|
-
return items;
|
|
126
|
-
}
|
|
127
|
-
export function resolveModelPickerDisplayIndex(items, selectedId) {
|
|
128
|
-
if (selectedId) {
|
|
129
|
-
const selectedIndex = items.findIndex((item) => item.kind === "model" && item.option.id === selectedId);
|
|
130
|
-
if (selectedIndex >= 0)
|
|
131
|
-
return selectedIndex;
|
|
132
|
-
}
|
|
133
|
-
const firstModelIndex = items.findIndex((item) => item.kind === "model");
|
|
134
|
-
return firstModelIndex >= 0 ? firstModelIndex : 0;
|
|
135
|
-
}
|
|
136
|
-
export function filterAndRankModelPickerOptions(options, query) {
|
|
137
|
-
const normalized = query.trim().toLowerCase();
|
|
138
|
-
if (!normalized)
|
|
139
|
-
return [...options];
|
|
140
|
-
return options
|
|
141
|
-
.map((option, index) => {
|
|
142
|
-
const { modelId } = decodeModel(option.id);
|
|
143
|
-
const fields = [option.label, modelId, option.id, option.providerBadge]
|
|
144
|
-
.map((value) => value.toLowerCase());
|
|
145
|
-
const rank = fields.some((value) => value === normalized)
|
|
146
|
-
? 0
|
|
147
|
-
: fields.some((value) => value.startsWith(normalized))
|
|
148
|
-
? 1
|
|
149
|
-
: fields.some((value) => value.includes(normalized))
|
|
150
|
-
? 2
|
|
151
|
-
: Number.POSITIVE_INFINITY;
|
|
152
|
-
return { option, index, rank };
|
|
153
|
-
})
|
|
154
|
-
.filter((entry) => Number.isFinite(entry.rank))
|
|
155
|
-
.sort((left, right) => left.rank - right.rank || left.index - right.index)
|
|
156
|
-
.map((entry) => entry.option);
|
|
157
|
-
}
|
|
158
|
-
function isThinkingToggleOption(option) {
|
|
159
|
-
const decoded = decodeModel(option.id);
|
|
160
|
-
return isThinkingToggleModel(decoded.providerId || option.providerBadge, decoded.modelId);
|
|
161
|
-
}
|
|
162
|
-
export function formatReasoningLevelsLabel(levels, asToggle = false) {
|
|
163
|
-
if (levels.length === 0)
|
|
164
|
-
return "checking";
|
|
165
|
-
if (isThinkingOnlyLevels(levels))
|
|
166
|
-
return "on";
|
|
167
|
-
if (asToggle)
|
|
168
|
-
return "thinking on/off";
|
|
169
|
-
return levels.join("/");
|
|
170
|
-
}
|
|
171
|
-
export function formatModelPickerRow(option, options) {
|
|
172
|
-
const width = Math.max(24, options.width);
|
|
173
|
-
const marker = options.selected ? "> " : " ";
|
|
174
|
-
const label = option.label.replace(/\s+/g, " ").trim();
|
|
175
|
-
const provider = option.providerBadge.replace(/\s+/g, " ").trim();
|
|
176
|
-
const effort = option.pending
|
|
177
|
-
? "checking"
|
|
178
|
-
: (option.available === false || option.reasoningLevels.length === 0)
|
|
179
|
-
? "unavailable"
|
|
180
|
-
: formatReasoningLevelsLabel(option.reasoningLevels, isThinkingToggleOption(option));
|
|
181
|
-
const current = options.current ? " ●" : "";
|
|
182
|
-
const providerWidth = Math.max(6, Math.min(16, Math.floor(width * 0.18)));
|
|
183
|
-
const effortWidth = Math.max(12, Math.min(30, Math.floor(width * 0.32)));
|
|
184
|
-
const labelWidth = Math.max(6, width - marker.length - providerWidth - effortWidth - 4 - current.length);
|
|
185
|
-
const row = [
|
|
186
|
-
marker,
|
|
187
|
-
padVisual(truncateVisual(label, labelWidth), labelWidth),
|
|
188
|
-
" ",
|
|
189
|
-
padVisual(truncateVisual(provider, providerWidth), providerWidth),
|
|
190
|
-
" ",
|
|
191
|
-
truncateVisual(effort, effortWidth),
|
|
192
|
-
current,
|
|
193
|
-
].join("");
|
|
194
|
-
return padVisual(truncateVisual(row, width), width);
|
|
195
|
-
}
|
|
196
|
-
export function formatEffortPickerRow(level, options) {
|
|
197
|
-
const width = Math.max(24, options.width);
|
|
198
|
-
const marker = options.selected ? "> " : " ";
|
|
199
|
-
const name = options.asToggle ? (level === "off" ? "off" : "on") : level;
|
|
200
|
-
const row = `${marker}${name} ${effortDescription(level, options.asToggle)}`;
|
|
201
|
-
return padVisual(truncateVisual(row, width), width);
|
|
202
|
-
}
|
|
203
|
-
export function formatNoModelResultsRow(query, width) {
|
|
204
|
-
const rowWidth = Math.max(24, width);
|
|
205
|
-
const normalizedQuery = query.replace(/\s+/g, " ").trim();
|
|
206
|
-
const row = normalizedQuery
|
|
207
|
-
? ` No models match "${normalizedQuery}"`
|
|
208
|
-
: " No models available";
|
|
209
|
-
return padVisual(truncateVisual(row, rowWidth), rowWidth);
|
|
210
|
-
}
|
|
211
|
-
export function preferredEffortIndex(option, currentThinkingLevel) {
|
|
212
|
-
const currentIndex = option.reasoningLevels.indexOf(currentThinkingLevel);
|
|
213
|
-
if (currentIndex >= 0)
|
|
214
|
-
return currentIndex;
|
|
215
|
-
if (option.defaultReasoningLevel) {
|
|
216
|
-
const defaultIndex = option.reasoningLevels.indexOf(option.defaultReasoningLevel);
|
|
217
|
-
if (defaultIndex >= 0)
|
|
218
|
-
return defaultIndex;
|
|
219
|
-
}
|
|
220
|
-
const preferred = normalizeThinkingLevel(currentThinkingLevel, option.reasoningLevels);
|
|
221
|
-
const index = option.reasoningLevels.indexOf(preferred);
|
|
222
|
-
return index >= 0 ? index : 0;
|
|
223
|
-
}
|
|
224
|
-
export function shouldOpenEffortPicker(option) {
|
|
225
|
-
return option.reasoningLevels.length > 1;
|
|
226
|
-
}
|
|
227
|
-
export function isModelOptionSelectable(option) {
|
|
228
|
-
return option.available && !option.pending && option.reasoningLevels.length > 0;
|
|
229
|
-
}
|
|
230
|
-
export function resolveSelectedModelId(options, selectedId) {
|
|
231
|
-
if (selectedId) {
|
|
232
|
-
const selected = options.find((option) => option.id === selectedId);
|
|
233
|
-
if (selected && isModelOptionSelectable(selected))
|
|
234
|
-
return selected.id;
|
|
235
|
-
}
|
|
236
|
-
return options.find(isModelOptionSelectable)?.id;
|
|
237
|
-
}
|
|
238
|
-
export function moveModelSelection(options, selectedId, direction) {
|
|
239
|
-
const selectableIndexes = options
|
|
240
|
-
.map((option, index) => isModelOptionSelectable(option) ? index : -1)
|
|
241
|
-
.filter((index) => index >= 0);
|
|
242
|
-
if (selectableIndexes.length === 0)
|
|
243
|
-
return undefined;
|
|
244
|
-
const currentIndex = options.findIndex((option) => option.id === selectedId);
|
|
245
|
-
if (currentIndex < 0) {
|
|
246
|
-
return options[direction > 0 ? selectableIndexes[0] : selectableIndexes[selectableIndexes.length - 1]]?.id;
|
|
247
|
-
}
|
|
248
|
-
const target = direction > 0
|
|
249
|
-
? selectableIndexes.find((index) => index > currentIndex)
|
|
250
|
-
: [...selectableIndexes].reverse().find((index) => index < currentIndex);
|
|
251
|
-
return options[target ?? currentIndex]?.id ?? selectedId;
|
|
252
|
-
}
|
|
253
|
-
export function reconcileModelPickerPhase(phase, options, currentThinkingLevel) {
|
|
254
|
-
if (phase.kind !== "effort")
|
|
255
|
-
return phase;
|
|
256
|
-
const latest = options.find((option) => option.id === phase.model.id);
|
|
257
|
-
if (!latest || !isModelOptionSelectable(latest))
|
|
258
|
-
return { kind: "model" };
|
|
259
|
-
const selectedLevel = phase.model.reasoningLevels[phase.selectedIndex];
|
|
260
|
-
const selectedIndex = selectedLevel && latest.reasoningLevels.includes(selectedLevel)
|
|
261
|
-
? latest.reasoningLevels.indexOf(selectedLevel)
|
|
262
|
-
: preferredEffortIndex(latest, currentThinkingLevel);
|
|
263
|
-
if (selectedIndex === phase.selectedIndex && sameModelOptionMetadata(phase.model, latest))
|
|
264
|
-
return phase;
|
|
265
|
-
return { kind: "effort", model: latest, selectedIndex };
|
|
266
|
-
}
|
|
267
|
-
function effortDescription(level, asToggle) {
|
|
268
|
-
if (asToggle)
|
|
269
|
-
return level === "off" ? "thinking disabled" : "thinking enabled";
|
|
270
|
-
switch (level) {
|
|
271
|
-
case "off":
|
|
272
|
-
return "no reasoning effort";
|
|
273
|
-
case "minimal":
|
|
274
|
-
return "fastest reasoning";
|
|
275
|
-
case "low":
|
|
276
|
-
return "light reasoning";
|
|
277
|
-
case "medium":
|
|
278
|
-
return "balanced reasoning";
|
|
279
|
-
case "high":
|
|
280
|
-
return "deeper reasoning";
|
|
281
|
-
case "xhigh":
|
|
282
|
-
return "extra high reasoning";
|
|
283
|
-
case "max":
|
|
284
|
-
return "maximum provider effort";
|
|
285
|
-
case "ultra":
|
|
286
|
-
return "maximum effort with delegation";
|
|
287
|
-
default:
|
|
288
|
-
return "reasoning effort";
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
function normalizeEscapeSequence(input) {
|
|
292
|
-
return input.startsWith("\x1b") ? input.slice(1) : input;
|
|
293
|
-
}
|
|
294
|
-
function isRawEscapeTail(input) {
|
|
295
|
-
return /^(?:O[ABCDHF]|\[[\d;:]*[A-Za-z~])$/.test(input);
|
|
296
|
-
}
|
|
297
|
-
export function ModelPicker({ registry, current, currentThinkingLevel, recent, onSelect, onCancel }) {
|
|
298
|
-
const theme = useTheme();
|
|
299
|
-
const { stdout } = useStdout();
|
|
300
|
-
const termHeight = stdout?.rows || 24;
|
|
301
|
-
const terminalColumns = stdout?.columns || 80;
|
|
302
|
-
const bodyRows = modelPickerBodyRows(termHeight);
|
|
303
|
-
const rowWidth = Math.max(36, Math.min(110, terminalColumns - 6));
|
|
304
|
-
const recentKey = modelPickerRecentKey(recent);
|
|
305
|
-
const enabledSnapshot = registry.getEnabled().filter((provider) => isUserVisibleProvider(provider.id));
|
|
306
|
-
const providerKey = enabledSnapshot
|
|
307
|
-
.map((provider) => [
|
|
308
|
-
provider.id,
|
|
309
|
-
provider.name,
|
|
310
|
-
provider.baseURL,
|
|
311
|
-
provider.authType ?? "",
|
|
312
|
-
provider.protocol ?? "",
|
|
313
|
-
].join("\u0000"))
|
|
314
|
-
.join("\u0001");
|
|
315
|
-
const enabledProviders = useMemo(() => registry.getEnabled().filter((provider) => isUserVisibleProvider(provider.id)), [registry, providerKey]);
|
|
316
|
-
const localModelsByProvider = useMemo(() => new Map(enabledProviders.map((provider) => [
|
|
317
|
-
provider.id,
|
|
318
|
-
localModelsForProvider(registry, provider),
|
|
319
|
-
])), [registry, providerKey]);
|
|
320
|
-
const [discoveries, setDiscoveries] = useState(() => new Map());
|
|
321
|
-
const [refreshGeneration, setRefreshGeneration] = useState(0);
|
|
322
|
-
const requestGenerationRef = useRef(0);
|
|
323
|
-
const rawOptions = useMemo(() => buildMergedModelOptions({
|
|
324
|
-
providers: enabledProviders,
|
|
325
|
-
localModelsByProvider,
|
|
326
|
-
discoveries,
|
|
327
|
-
current,
|
|
328
|
-
recent,
|
|
329
|
-
}), [enabledProviders, localModelsByProvider, discoveries, current, recentKey]);
|
|
330
|
-
const [query, setQuery] = useState("");
|
|
331
|
-
const [selectedId, setSelectedId] = useState(() => resolveSelectedModelId(buildLocalModelOptions(registry, current, recent), current));
|
|
332
|
-
const [phase, setPhase] = useState({ kind: "model" });
|
|
333
|
-
useEffect(() => {
|
|
334
|
-
const generation = ++requestGenerationRef.current;
|
|
335
|
-
setDiscoveries((previous) => {
|
|
336
|
-
const next = new Map();
|
|
337
|
-
for (const provider of enabledProviders) {
|
|
338
|
-
const prior = previous.get(provider.id);
|
|
339
|
-
next.set(provider.id, {
|
|
340
|
-
status: "pending",
|
|
341
|
-
models: prior?.models ?? [],
|
|
342
|
-
source: prior?.source,
|
|
343
|
-
authoritative: prior?.authoritative ?? false,
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
return next;
|
|
347
|
-
});
|
|
348
|
-
for (const provider of enabledProviders) {
|
|
349
|
-
void discoverModelsForPicker(registry, provider, refreshGeneration > 0)
|
|
350
|
-
.then((result) => {
|
|
351
|
-
if (requestGenerationRef.current !== generation)
|
|
352
|
-
return;
|
|
353
|
-
setDiscoveries((previous) => {
|
|
354
|
-
const next = new Map(previous);
|
|
355
|
-
next.set(provider.id, { status: "complete", ...result });
|
|
356
|
-
return next;
|
|
357
|
-
});
|
|
358
|
-
})
|
|
359
|
-
.catch((error) => {
|
|
360
|
-
if (requestGenerationRef.current !== generation)
|
|
361
|
-
return;
|
|
362
|
-
setDiscoveries((previous) => {
|
|
363
|
-
const next = new Map(previous);
|
|
364
|
-
next.set(provider.id, {
|
|
365
|
-
status: "complete",
|
|
366
|
-
models: localModelsByProvider.get(provider.id) ?? [],
|
|
367
|
-
source: "fallback",
|
|
368
|
-
authoritative: false,
|
|
369
|
-
error: error instanceof Error ? error.message : String(error),
|
|
370
|
-
});
|
|
371
|
-
return next;
|
|
372
|
-
});
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
return () => {
|
|
376
|
-
if (requestGenerationRef.current === generation)
|
|
377
|
-
requestGenerationRef.current++;
|
|
378
|
-
};
|
|
379
|
-
}, [registry, providerKey, refreshGeneration]);
|
|
380
|
-
const options = useMemo(() => {
|
|
381
|
-
return filterAndRankModelPickerOptions(rawOptions, query);
|
|
382
|
-
}, [rawOptions, query]);
|
|
383
|
-
const resolvedSelectedId = resolveSelectedModelId(options, selectedId);
|
|
384
|
-
const selectedIndex = resolvedSelectedId
|
|
385
|
-
? options.findIndex((option) => option.id === resolvedSelectedId)
|
|
386
|
-
: -1;
|
|
387
|
-
useEffect(() => {
|
|
388
|
-
if (selectedId !== resolvedSelectedId)
|
|
389
|
-
setSelectedId(resolvedSelectedId);
|
|
390
|
-
}, [selectedId, resolvedSelectedId]);
|
|
391
|
-
useEffect(() => {
|
|
392
|
-
setPhase((currentPhase) => reconcileModelPickerPhase(currentPhase, rawOptions, currentThinkingLevel));
|
|
393
|
-
}, [rawOptions, currentThinkingLevel]);
|
|
394
|
-
const discoveryStatus = formatModelDiscoveryStatus(enabledProviders, discoveries);
|
|
395
|
-
useInput((input, key) => {
|
|
396
|
-
if (isKeyReleaseEvent(key))
|
|
397
|
-
return;
|
|
398
|
-
const action = resolvePickerKeyAction(input, key);
|
|
399
|
-
if (phase.kind === "effort") {
|
|
400
|
-
const levels = phase.model.reasoningLevels;
|
|
401
|
-
if (action === "escape" || action === "backspace" || action === "delete") {
|
|
402
|
-
setPhase({ kind: "model" });
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
if (action === "enter") {
|
|
406
|
-
const level = levels[clampPickerIndex(phase.selectedIndex, levels.length)];
|
|
407
|
-
if (level)
|
|
408
|
-
onSelect(phase.model.id, level);
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
if (action === "up") {
|
|
412
|
-
setPhase((currentPhase) => currentPhase.kind === "effort"
|
|
413
|
-
? { ...currentPhase, selectedIndex: clampPickerIndex(currentPhase.selectedIndex - 1, levels.length) }
|
|
414
|
-
: currentPhase);
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
if (action === "down") {
|
|
418
|
-
setPhase((currentPhase) => currentPhase.kind === "effort"
|
|
419
|
-
? { ...currentPhase, selectedIndex: clampPickerIndex(currentPhase.selectedIndex + 1, levels.length) }
|
|
420
|
-
: currentPhase);
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
if (key.ctrl && (input.toLowerCase() === "r" || input === "\x12")) {
|
|
426
|
-
setRefreshGeneration((generation) => generation + 1);
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
if (action === "escape") {
|
|
430
|
-
onCancel();
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
if (action === "enter") {
|
|
434
|
-
const opt = selectedIndex >= 0 ? options[selectedIndex] : undefined;
|
|
435
|
-
if (opt && isModelOptionSelectable(opt)) {
|
|
436
|
-
if (shouldOpenEffortPicker(opt)) {
|
|
437
|
-
setPhase({
|
|
438
|
-
kind: "effort",
|
|
439
|
-
model: opt,
|
|
440
|
-
selectedIndex: preferredEffortIndex(opt, currentThinkingLevel),
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
const level = opt.reasoningLevels[0];
|
|
445
|
-
if (level)
|
|
446
|
-
onSelect(opt.id, level);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
if (action === "up") {
|
|
452
|
-
setSelectedId((id) => moveModelSelection(options, id, -1));
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
if (action === "down") {
|
|
456
|
-
setSelectedId((id) => moveModelSelection(options, id, 1));
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
if (action === "backspace" || action === "delete") {
|
|
460
|
-
setQuery((q) => {
|
|
461
|
-
const next = q.slice(0, -1);
|
|
462
|
-
setSelectedId(undefined);
|
|
463
|
-
return next;
|
|
464
|
-
});
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
if (isPrintablePickerInput(input) && !key.ctrl && !key.meta) {
|
|
468
|
-
setQuery((q) => {
|
|
469
|
-
const next = q + input;
|
|
470
|
-
setSelectedId(undefined);
|
|
471
|
-
return next;
|
|
472
|
-
});
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
const displayItems = buildModelPickerDisplayItems(options);
|
|
477
|
-
const safeSelectedDisplayIndex = resolveModelPickerDisplayIndex(displayItems, resolvedSelectedId);
|
|
478
|
-
const start = pickerWindowStart(safeSelectedDisplayIndex, displayItems.length, bodyRows);
|
|
479
|
-
const visible = displayItems.slice(start, start + bodyRows);
|
|
480
|
-
const rawModelRows = options.length === 0
|
|
481
|
-
? [{
|
|
482
|
-
key: "no-results",
|
|
483
|
-
row: formatNoModelResultsRow(query, rowWidth),
|
|
484
|
-
selected: false,
|
|
485
|
-
header: false,
|
|
486
|
-
}]
|
|
487
|
-
: visible.map((item) => {
|
|
488
|
-
if (item.kind === "header") {
|
|
489
|
-
return {
|
|
490
|
-
key: item.key,
|
|
491
|
-
row: padVisual(truncateVisual(` ${item.label}`, rowWidth), rowWidth),
|
|
492
|
-
selected: false,
|
|
493
|
-
header: true,
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
const isSelected = item.option.id === resolvedSelectedId;
|
|
497
|
-
return {
|
|
498
|
-
key: item.key,
|
|
499
|
-
row: formatModelPickerRow(item.option, {
|
|
500
|
-
selected: isSelected,
|
|
501
|
-
current: item.option.id === current,
|
|
502
|
-
width: rowWidth,
|
|
503
|
-
}),
|
|
504
|
-
selected: isSelected,
|
|
505
|
-
header: false,
|
|
506
|
-
};
|
|
507
|
-
});
|
|
508
|
-
const modelRows = padPickerRows(rawModelRows.map((row) => row.row), bodyRows, rowWidth).map((row, index) => ({
|
|
509
|
-
key: rawModelRows[index]?.key ?? `blank-${index}`,
|
|
510
|
-
row,
|
|
511
|
-
selected: rawModelRows[index]?.selected ?? false,
|
|
512
|
-
header: rawModelRows[index]?.header ?? false,
|
|
513
|
-
}));
|
|
514
|
-
const highlightedOption = selectedIndex >= 0 ? options[selectedIndex] : undefined;
|
|
515
|
-
const enterAction = highlightedOption
|
|
516
|
-
? (shouldOpenEffortPicker(highlightedOption)
|
|
517
|
-
? (isThinkingToggleOption(highlightedOption) ? "choose mode" : "choose effort")
|
|
518
|
-
: "select")
|
|
519
|
-
: "wait";
|
|
520
|
-
const effortTitle = phase.kind === "effort" && isThinkingToggleOption(phase.model)
|
|
521
|
-
? "Select Thinking Mode"
|
|
522
|
-
: "Select Reasoning Effort";
|
|
523
|
-
return (_jsxs(Box, { flexDirection: "column", marginY: 1, paddingX: 1, borderStyle: "round", borderColor: theme.borderActive, children: [_jsx(Text, { bold: true, color: theme.accent, children: phase.kind === "effort" ? effortTitle : "Select Model" }), phase.kind === "effort" ? (_jsx(EffortPickerView, { model: phase.model, selectedIndex: phase.selectedIndex, bodyRows: bodyRows, rowWidth: rowWidth })) : (_jsxs(_Fragment, { children: [_jsx(SearchField, { query: query, placeholder: "Type to search models...", width: rowWidth }), _jsxs(Text, { color: theme.muted, children: ["\u2191/\u2193 navigate \u00B7 Enter ", enterAction, " \u00B7 Esc cancel \u00B7 Ctrl+R retry"] }), _jsx(Box, { height: 1, overflow: "hidden", children: _jsx(Text, { color: discoveryStatus.hasError ? theme.warning : theme.muted, children: discoveryStatus.text || " " }) })] })), phase.kind === "model" && _jsx(Box, { flexDirection: "column", height: bodyRows, overflow: "hidden", marginTop: 1, children: modelRows.map(({ key, row, selected, header }) => (_jsx(Box, { height: 1, overflow: "hidden", children: _jsx(Text, { color: selected ? theme.accent : (header || key === "no-results" ? theme.muted : undefined), bold: selected || header, children: row }) }, key))) })] }));
|
|
524
|
-
}
|
|
525
|
-
function EffortPickerView({ model, selectedIndex, bodyRows, rowWidth, }) {
|
|
526
|
-
const theme = useTheme();
|
|
527
|
-
const safeSelectedIndex = clampPickerIndex(selectedIndex, model.reasoningLevels.length);
|
|
528
|
-
const rawRows = model.reasoningLevels.map((level, index) => ({
|
|
529
|
-
key: level,
|
|
530
|
-
row: formatEffortPickerRow(level, {
|
|
531
|
-
selected: index === safeSelectedIndex,
|
|
532
|
-
width: rowWidth,
|
|
533
|
-
asToggle: isThinkingToggleOption(model),
|
|
534
|
-
}),
|
|
535
|
-
selected: index === safeSelectedIndex,
|
|
536
|
-
}));
|
|
537
|
-
const effortRows = padPickerRows(rawRows.map((row) => row.row), bodyRows, rowWidth).map((row, index) => ({
|
|
538
|
-
key: rawRows[index]?.key ?? `blank-${index}`,
|
|
539
|
-
row,
|
|
540
|
-
selected: rawRows[index]?.selected ?? false,
|
|
541
|
-
}));
|
|
542
|
-
const modelDetail = padVisual(truncateVisual(`${model.label} · ${model.providerBadge}`, rowWidth), rowWidth);
|
|
543
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Box, { height: 1, overflow: "hidden", children: _jsx(Text, { color: theme.muted, children: modelDetail }) }), _jsx(Text, { color: theme.muted, children: "\u2191/\u2193 navigate \u00B7 Enter select \u00B7 Esc back" }), _jsx(Box, { flexDirection: "column", height: bodyRows, overflow: "hidden", marginTop: 1, children: effortRows.map(({ key, row, selected }) => (_jsx(Box, { height: 1, overflow: "hidden", children: _jsx(Text, { color: selected ? theme.accent : undefined, bold: selected, children: row }) }, key))) })] }));
|
|
544
|
-
}
|
|
545
|
-
function SearchField({ query, placeholder, width }) {
|
|
546
|
-
const theme = useTheme();
|
|
547
|
-
const [cursorVisible, setCursorVisible] = useState(true);
|
|
548
|
-
useEffect(() => {
|
|
549
|
-
const t = setInterval(() => setCursorVisible((v) => !v), 500);
|
|
550
|
-
return () => clearInterval(t);
|
|
551
|
-
}, []);
|
|
552
|
-
const contentBudget = width ? Math.max(1, width - 3) : undefined;
|
|
553
|
-
const visibleQuery = contentBudget ? truncateVisual(query, contentBudget) : query;
|
|
554
|
-
const visiblePlaceholder = !query
|
|
555
|
-
? (contentBudget ? truncateVisual(` ${placeholder}`, contentBudget) : ` ${placeholder}`)
|
|
556
|
-
: "";
|
|
557
|
-
return (_jsxs(Box, { height: 1, overflow: "hidden", marginTop: 1, children: [_jsx(Text, { color: theme.accent, children: "❯ " }), _jsx(Text, { children: visibleQuery }), _jsx(Text, { color: theme.accent, inverse: cursorVisible, children: " " }), visiblePlaceholder && _jsx(Text, { color: theme.muted, dimColor: true, children: visiblePlaceholder })] }));
|
|
558
|
-
}
|
|
559
|
-
export function buildLocalModelOptions(registry, current, recent) {
|
|
560
|
-
const providers = registry.getEnabled().filter((provider) => isUserVisibleProvider(provider.id));
|
|
561
|
-
const localModelsByProvider = new Map(providers.map((provider) => [
|
|
562
|
-
provider.id,
|
|
563
|
-
localModelsForProvider(registry, provider),
|
|
564
|
-
]));
|
|
565
|
-
return buildMergedModelOptions({
|
|
566
|
-
providers,
|
|
567
|
-
localModelsByProvider,
|
|
568
|
-
discoveries: new Map(),
|
|
569
|
-
current,
|
|
570
|
-
recent,
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
export function buildMergedModelOptions(input) {
|
|
574
|
-
const opts = [];
|
|
575
|
-
const seen = new Set();
|
|
576
|
-
const providerById = new Map(input.providers.map((provider) => [provider.id, provider]));
|
|
577
|
-
const effectiveModelsByProvider = new Map(input.providers.map((provider) => {
|
|
578
|
-
const localModels = input.localModelsByProvider.get(provider.id) ?? [];
|
|
579
|
-
const discovery = input.discoveries.get(provider.id);
|
|
580
|
-
return [provider.id, effectiveProviderModels(localModels, discovery)];
|
|
581
|
-
}));
|
|
582
|
-
const promotedFamilies = new Set();
|
|
583
|
-
const promotedRecentProviderIds = [];
|
|
584
|
-
const familyKeyForId = (id) => {
|
|
585
|
-
const { providerId, modelId } = decodeModel(id);
|
|
586
|
-
const provider = providerId ? providerById.get(providerId) : undefined;
|
|
587
|
-
return provider
|
|
588
|
-
? modelPickerFamilyKey(provider, modelId)
|
|
589
|
-
: `unknown:${id}`;
|
|
590
|
-
};
|
|
591
|
-
const appendPinned = (id, group) => {
|
|
592
|
-
appendModelOption(opts, seen, pinnedModelOption({
|
|
593
|
-
id,
|
|
594
|
-
group,
|
|
595
|
-
providers: input.providers,
|
|
596
|
-
localModelsByProvider: input.localModelsByProvider,
|
|
597
|
-
discoveries: input.discoveries,
|
|
598
|
-
}));
|
|
599
|
-
};
|
|
600
|
-
const appendFamily = (seedId, group) => {
|
|
601
|
-
const { providerId, modelId } = decodeModel(seedId);
|
|
602
|
-
const provider = providerId ? providerById.get(providerId) : undefined;
|
|
603
|
-
if (!provider) {
|
|
604
|
-
appendPinned(seedId, group);
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
const familyKey = modelPickerFamilyKey(provider, modelId);
|
|
608
|
-
const familyModels = (effectiveModelsByProvider.get(provider.id) ?? [])
|
|
609
|
-
.filter((model) => modelPickerFamilyKey(provider, model.id) === familyKey);
|
|
610
|
-
for (const model of familyModels) {
|
|
611
|
-
appendModelOption(opts, seen, {
|
|
612
|
-
...modelOptionMetadata(encodeModel(provider.id, model.id), model, true),
|
|
613
|
-
group,
|
|
614
|
-
providerBadge: provider.name,
|
|
615
|
-
available: true,
|
|
616
|
-
pending: false,
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
// Keep an exact Current/Recent pin visible even when an authoritative
|
|
620
|
-
// catalog no longer contains it. It remains unavailable and never causes
|
|
621
|
-
// local siblings to be reintroduced.
|
|
622
|
-
if (!familyModels.some((model) => model.id === modelId))
|
|
623
|
-
appendPinned(seedId, group);
|
|
624
|
-
};
|
|
625
|
-
if (input.current) {
|
|
626
|
-
appendPinned(input.current, "Current");
|
|
627
|
-
const currentFamilyKey = familyKeyForId(input.current);
|
|
628
|
-
promotedFamilies.add(currentFamilyKey);
|
|
629
|
-
appendFamily(input.current, "Current");
|
|
630
|
-
}
|
|
631
|
-
let recentFamilyCount = 0;
|
|
632
|
-
for (const recentId of [...new Set(input.recent)]) {
|
|
633
|
-
if (!recentId || recentId === input.current)
|
|
634
|
-
continue;
|
|
635
|
-
const familyKey = familyKeyForId(recentId);
|
|
636
|
-
if (promotedFamilies.has(familyKey)) {
|
|
637
|
-
// A previously promoted family may not contain this exact Recent model
|
|
638
|
-
// in an authoritative catalog. Preserve that pin as unavailable; when
|
|
639
|
-
// it is present, exact-ID deduplication makes this a no-op.
|
|
640
|
-
appendPinned(recentId, "Recent");
|
|
641
|
-
continue;
|
|
642
|
-
}
|
|
643
|
-
promotedFamilies.add(familyKey);
|
|
644
|
-
recentFamilyCount += 1;
|
|
645
|
-
const { providerId } = decodeModel(recentId);
|
|
646
|
-
if (providerId && providerById.has(providerId) && !promotedRecentProviderIds.includes(providerId)) {
|
|
647
|
-
promotedRecentProviderIds.push(providerId);
|
|
648
|
-
}
|
|
649
|
-
appendFamily(recentId, "Recent");
|
|
650
|
-
if (recentFamilyCount >= MODEL_PICKER_RECENT_FAMILY_LIMIT)
|
|
651
|
-
break;
|
|
652
|
-
}
|
|
653
|
-
const orderedProviderIds = [];
|
|
654
|
-
const addProviderId = (providerId) => {
|
|
655
|
-
if (providerId && providerById.has(providerId) && !orderedProviderIds.includes(providerId)) {
|
|
656
|
-
orderedProviderIds.push(providerId);
|
|
657
|
-
}
|
|
658
|
-
};
|
|
659
|
-
addProviderId(decodeModel(input.current).providerId);
|
|
660
|
-
for (const providerId of promotedRecentProviderIds)
|
|
661
|
-
addProviderId(providerId);
|
|
662
|
-
for (const provider of input.providers)
|
|
663
|
-
addProviderId(provider.id);
|
|
664
|
-
for (const providerId of orderedProviderIds) {
|
|
665
|
-
const provider = providerById.get(providerId);
|
|
666
|
-
if (!provider)
|
|
667
|
-
continue;
|
|
668
|
-
for (const model of effectiveModelsByProvider.get(provider.id) ?? []) {
|
|
669
|
-
appendModelOption(opts, seen, {
|
|
670
|
-
...modelOptionMetadata(encodeModel(provider.id, model.id), model, true),
|
|
671
|
-
group: provider.name,
|
|
672
|
-
providerBadge: provider.name,
|
|
673
|
-
available: true,
|
|
674
|
-
pending: false,
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
return opts;
|
|
679
|
-
}
|
|
680
|
-
function localModelsForProvider(registry, provider) {
|
|
681
|
-
const customModels = registry.getModelConfig().getCustomModels(provider.id);
|
|
682
|
-
if (customModels.length > 0)
|
|
683
|
-
return customModels;
|
|
684
|
-
const builtinProviderId = provider.id === "openai" && provider.authType === "oauth"
|
|
685
|
-
? "openai-codex"
|
|
686
|
-
: provider.id;
|
|
687
|
-
return listBuiltinModels(builtinProviderId).map((model) => ({
|
|
688
|
-
id: model.id,
|
|
689
|
-
name: model.name,
|
|
690
|
-
providerId: provider.id,
|
|
691
|
-
reasoningLevels: model.reasoningLevels,
|
|
692
|
-
defaultReasoningLevel: model.defaultReasoningLevel,
|
|
693
|
-
contextWindow: model.contextWindow,
|
|
694
|
-
toolOutputTokenLimit: model.toolOutputTokenLimit,
|
|
695
|
-
}));
|
|
696
|
-
}
|
|
697
|
-
function appendModelOption(options, seen, option) {
|
|
698
|
-
if (seen.has(option.id))
|
|
699
|
-
return;
|
|
700
|
-
seen.add(option.id);
|
|
701
|
-
options.push(option);
|
|
702
|
-
}
|
|
703
|
-
function pinnedModelOption(input) {
|
|
704
|
-
const { providerId, modelId } = decodeModel(input.id);
|
|
705
|
-
const provider = input.providers.find((item) => item.id === providerId);
|
|
706
|
-
if (!provider) {
|
|
707
|
-
return {
|
|
708
|
-
...modelOptionMetadata(input.id, undefined, false),
|
|
709
|
-
group: input.group,
|
|
710
|
-
providerBadge: providerId || "",
|
|
711
|
-
available: false,
|
|
712
|
-
pending: false,
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
const local = input.localModelsByProvider.get(provider.id) ?? [];
|
|
716
|
-
const discovery = input.discoveries.get(provider.id);
|
|
717
|
-
const discoveredModel = discovery?.models.find((model) => model.id === modelId);
|
|
718
|
-
const localModel = local.find((model) => model.id === modelId);
|
|
719
|
-
const metadata = discoveredModel ?? localModel;
|
|
720
|
-
const hasKnownMetadata = !!metadata;
|
|
721
|
-
let available = hasKnownMetadata;
|
|
722
|
-
let pending = false;
|
|
723
|
-
if (!discovery || discovery.status === "pending") {
|
|
724
|
-
pending = !hasKnownMetadata;
|
|
725
|
-
}
|
|
726
|
-
else if (discovery.authoritative) {
|
|
727
|
-
available = !!discoveredModel;
|
|
728
|
-
}
|
|
729
|
-
return {
|
|
730
|
-
...modelOptionMetadata(input.id, metadata, hasKnownMetadata),
|
|
731
|
-
group: input.group,
|
|
732
|
-
providerBadge: provider.name,
|
|
733
|
-
available,
|
|
734
|
-
pending,
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
function effectiveProviderModels(localModels, discovery) {
|
|
738
|
-
if (!discovery)
|
|
739
|
-
return localModels;
|
|
740
|
-
if (discovery.authoritative)
|
|
741
|
-
return discovery.models;
|
|
742
|
-
return discovery.models.length > 0 ? discovery.models : localModels;
|
|
743
|
-
}
|
|
744
|
-
function modelOptionMetadata(id, rawModel, known) {
|
|
745
|
-
const model = rawModel;
|
|
746
|
-
const reasoningLevels = model?.reasoningLevels
|
|
747
|
-
? [...model.reasoningLevels]
|
|
748
|
-
: (known ? reasoningLevelsForModel(id) : []);
|
|
749
|
-
const defaultReasoningLevel = model?.defaultReasoningLevel
|
|
750
|
-
&& reasoningLevels.includes(model.defaultReasoningLevel)
|
|
751
|
-
? model.defaultReasoningLevel
|
|
752
|
-
: undefined;
|
|
753
|
-
return {
|
|
754
|
-
id,
|
|
755
|
-
label: model?.name || displayModel(id),
|
|
756
|
-
reasoningLevels,
|
|
757
|
-
defaultReasoningLevel,
|
|
758
|
-
contextWindow: model?.contextWindow,
|
|
759
|
-
toolOutputTokenLimit: model?.toolOutputTokenLimit,
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
async function discoverModelsForPicker(registry, provider, forceRefresh) {
|
|
763
|
-
return registry.discoverModels(provider, { forceRefresh });
|
|
764
|
-
}
|
|
765
|
-
export function formatModelDiscoveryStatus(providers, discoveries) {
|
|
766
|
-
if (providers.some((provider) => discoveries.get(provider.id)?.status !== "complete")) {
|
|
767
|
-
return { text: "Refreshing model catalog…", hasError: false };
|
|
768
|
-
}
|
|
769
|
-
const failed = providers.filter((provider) => discoveries.get(provider.id)?.error);
|
|
770
|
-
if (failed.length > 0) {
|
|
771
|
-
const names = failed.slice(0, 2).map((provider) => provider.name).join(", ");
|
|
772
|
-
const suffix = failed.length > 2 ? ` +${failed.length - 2}` : "";
|
|
773
|
-
return { text: `Showing fallback models for ${names}${suffix} · Ctrl+R retry`, hasError: true };
|
|
774
|
-
}
|
|
775
|
-
return { text: "", hasError: false };
|
|
776
|
-
}
|
|
777
|
-
function sameModelOptionMetadata(left, right) {
|
|
778
|
-
return left.id === right.id
|
|
779
|
-
&& left.label === right.label
|
|
780
|
-
&& left.group === right.group
|
|
781
|
-
&& left.providerBadge === right.providerBadge
|
|
782
|
-
&& left.defaultReasoningLevel === right.defaultReasoningLevel
|
|
783
|
-
&& left.contextWindow === right.contextWindow
|
|
784
|
-
&& left.toolOutputTokenLimit === right.toolOutputTokenLimit
|
|
785
|
-
&& left.available === right.available
|
|
786
|
-
&& left.pending === right.pending
|
|
787
|
-
&& left.reasoningLevels.length === right.reasoningLevels.length
|
|
788
|
-
&& left.reasoningLevels.every((level, index) => level === right.reasoningLevels[index]);
|
|
789
|
-
}
|
|
790
|
-
function reasoningLevelsForModel(model) {
|
|
791
|
-
const { providerId, modelId } = decodeModel(model);
|
|
792
|
-
return getAvailableThinkingLevels(providerId || "openai", modelId);
|
|
793
|
-
}
|
|
794
|
-
export function ProviderPicker({ providers, current, onSelect, onCancel, title, onHighlight }) {
|
|
795
|
-
const theme = useTheme();
|
|
796
|
-
const { stdout } = useStdout();
|
|
797
|
-
const termHeight = stdout?.rows || 24;
|
|
798
|
-
const maxVisible = Math.max(5, termHeight - 8);
|
|
799
|
-
const [selectedIndex, setSelectedIndex] = useState(() => {
|
|
800
|
-
const idx = providers.findIndex((p) => p.id === current);
|
|
801
|
-
return idx >= 0 ? idx : 0;
|
|
802
|
-
});
|
|
803
|
-
// Authoritative selection lives in a ref, mutated synchronously by the
|
|
804
|
-
// input handler; state only mirrors it for display. Slow ptys (web-terminal
|
|
805
|
-
// bridges) can deliver several key events before React commits a re-render,
|
|
806
|
-
// and a state-only selection would leave the handler closure stale — Enter
|
|
807
|
-
// would then select the pre-batch highlight.
|
|
808
|
-
const selectedIndexRef = useRef(selectedIndex);
|
|
809
|
-
const moveSelection = (next) => {
|
|
810
|
-
selectedIndexRef.current = next;
|
|
811
|
-
setSelectedIndex(next);
|
|
812
|
-
};
|
|
813
|
-
useEffect(() => {
|
|
814
|
-
const p = providers[selectedIndex];
|
|
815
|
-
if (p)
|
|
816
|
-
onHighlight?.(p.id);
|
|
817
|
-
}, [selectedIndex]);
|
|
818
|
-
useInput((input, key) => {
|
|
819
|
-
if (isKeyReleaseEvent(key))
|
|
820
|
-
return;
|
|
821
|
-
const action = resolvePickerKeyAction(input, key);
|
|
822
|
-
if (action === "escape") {
|
|
823
|
-
onCancel();
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
if (action === "enter") {
|
|
827
|
-
const p = providers[selectedIndexRef.current];
|
|
828
|
-
if (p)
|
|
829
|
-
onSelect(p.id);
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
if (action === "up") {
|
|
833
|
-
moveSelection((selectedIndexRef.current - 1 + providers.length) % providers.length);
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
if (action === "down") {
|
|
837
|
-
moveSelection((selectedIndexRef.current + 1) % providers.length);
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
840
|
-
if (isPrintablePickerInput(input) && input.length === 1 && /[a-z]/i.test(input)) {
|
|
841
|
-
const char = input.toLowerCase();
|
|
842
|
-
const from = selectedIndexRef.current;
|
|
843
|
-
for (let i = from + 1; i < providers.length; i++) {
|
|
844
|
-
if (providers[i].name.toLowerCase().startsWith(char)) {
|
|
845
|
-
moveSelection(i);
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
for (let i = 0; i <= from; i++) {
|
|
850
|
-
if (providers[i].name.toLowerCase().startsWith(char)) {
|
|
851
|
-
moveSelection(i);
|
|
852
|
-
return;
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
});
|
|
857
|
-
const start = Math.max(0, Math.min(selectedIndex, providers.length - maxVisible));
|
|
858
|
-
const visible = providers.slice(start, start + maxVisible);
|
|
859
|
-
return (
|
|
860
|
-
// backgroundColor lives on the picker's OWN root box, below its state:
|
|
861
|
-
// a canvas toggle (theme preview across a forced-theme mismatch) then
|
|
862
|
-
// reshapes only this stateless output subtree. On an ancestor wrapper it
|
|
863
|
-
// would remount the whole picker and reset selectedIndex mid-navigation.
|
|
864
|
-
_jsxs(Box, { flexDirection: "column", marginY: 1, paddingX: 1, borderStyle: "round", borderColor: theme.borderActive, backgroundColor: theme.background, children: [_jsx(Text, { bold: true, color: theme.accent, children: title || "Select Provider" }), _jsx(Text, { color: theme.muted, children: "\u2191/\u2193 navigate \u00B7 Enter select \u00B7 Esc cancel \u00B7 type letter to jump" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: visible.map((p, i) => {
|
|
865
|
-
const actualIndex = start + i;
|
|
866
|
-
const isSelected = actualIndex === selectedIndex;
|
|
867
|
-
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? theme.accent : undefined, children: [isSelected ? "> " : " ", p.name, p.id === current ? " (current)" : "", !p.enabled ? " [disabled]" : ""] }) }, p.id));
|
|
868
|
-
}) })] }));
|
|
869
|
-
}
|
|
870
|
-
export function KeyPicker({ providerName, onSubmit, onCancel }) {
|
|
871
|
-
const theme = useTheme();
|
|
872
|
-
const [value, setValue] = useState("");
|
|
873
|
-
useInput((input, key) => {
|
|
874
|
-
if (isKeyReleaseEvent(key))
|
|
875
|
-
return;
|
|
876
|
-
const action = resolvePickerKeyAction(input, key);
|
|
877
|
-
if (action === "escape") {
|
|
878
|
-
onCancel();
|
|
879
|
-
return;
|
|
880
|
-
}
|
|
881
|
-
if (action === "enter") {
|
|
882
|
-
if (value.trim())
|
|
883
|
-
onSubmit(value.trim());
|
|
884
|
-
return;
|
|
885
|
-
}
|
|
886
|
-
if (action === "backspace" || action === "delete") {
|
|
887
|
-
setValue((v) => v.slice(0, -1));
|
|
888
|
-
return;
|
|
889
|
-
}
|
|
890
|
-
if (isPrintablePickerInput(input) && !key.ctrl && !key.meta) {
|
|
891
|
-
setValue((v) => v + input);
|
|
892
|
-
}
|
|
893
|
-
});
|
|
894
|
-
// Append pasted clipboard content directly into the key field. Without
|
|
895
|
-
// this the paste falls through to whichever other hook (InputBox's
|
|
896
|
-
// usePaste) is active, and the key ends up in the main input area.
|
|
897
|
-
usePaste((pasted) => {
|
|
898
|
-
const clean = pasted.replace(/[\r\n\t]/g, "").trim();
|
|
899
|
-
if (clean)
|
|
900
|
-
setValue((v) => v + clean);
|
|
901
|
-
});
|
|
902
|
-
return (_jsxs(Box, { flexDirection: "column", marginY: 1, paddingX: 1, borderStyle: "round", borderColor: theme.borderActive, children: [_jsxs(Text, { bold: true, color: theme.accent, children: ["Enter API Key for ", providerName] }), _jsx(Text, { color: theme.muted, children: "Paste or type the key \u00B7 Enter to submit \u00B7 Esc to cancel" }), _jsx(SearchField, { query: value.replace(/./g, "*"), placeholder: "Paste your key here..." })] }));
|
|
903
|
-
}
|
|
904
|
-
export function SkillPicker({ skills, onSelect, onCancel }) {
|
|
905
|
-
const theme = useTheme();
|
|
906
|
-
const { stdout } = useStdout();
|
|
907
|
-
const termHeight = stdout?.rows || 24;
|
|
908
|
-
const terminalColumns = stdout?.columns || 80;
|
|
909
|
-
const maxVisible = Math.max(5, termHeight - 8);
|
|
910
|
-
const rowWidth = Math.max(36, Math.min(96, terminalColumns - 6));
|
|
911
|
-
const [query, setQuery] = useState("");
|
|
912
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
913
|
-
const options = useMemo(() => {
|
|
914
|
-
const q = query.trim().toLowerCase();
|
|
915
|
-
if (!q)
|
|
916
|
-
return skills;
|
|
917
|
-
return skills.filter((skill) => skill.name.toLowerCase().includes(q) || skill.description.toLowerCase().includes(q));
|
|
918
|
-
}, [query, skills]);
|
|
919
|
-
useInput((input, key) => {
|
|
920
|
-
if (isKeyReleaseEvent(key))
|
|
921
|
-
return;
|
|
922
|
-
const action = resolvePickerKeyAction(input, key);
|
|
923
|
-
if (action === "escape") {
|
|
924
|
-
onCancel();
|
|
925
|
-
return;
|
|
926
|
-
}
|
|
927
|
-
if (action === "enter") {
|
|
928
|
-
const skill = options[selectedIndex];
|
|
929
|
-
if (skill)
|
|
930
|
-
onSelect(skill.name);
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
if (action === "up") {
|
|
934
|
-
setSelectedIndex((i) => Math.max(0, i - 1));
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
|
-
if (action === "down") {
|
|
938
|
-
setSelectedIndex((i) => Math.min(Math.max(0, options.length - 1), i + 1));
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
941
|
-
if (action === "backspace" || action === "delete") {
|
|
942
|
-
setQuery((q) => {
|
|
943
|
-
const next = q.slice(0, -1);
|
|
944
|
-
setSelectedIndex(0);
|
|
945
|
-
return next;
|
|
946
|
-
});
|
|
947
|
-
return;
|
|
948
|
-
}
|
|
949
|
-
if (isPrintablePickerInput(input) && !key.ctrl && !key.meta) {
|
|
950
|
-
setQuery((q) => {
|
|
951
|
-
const next = q + input;
|
|
952
|
-
setSelectedIndex(0);
|
|
953
|
-
return next;
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
});
|
|
957
|
-
const maxStart = Math.max(0, options.length - maxVisible);
|
|
958
|
-
const start = Math.max(0, Math.min(maxStart, selectedIndex - Math.floor(maxVisible / 2)));
|
|
959
|
-
const visible = options.slice(start, start + maxVisible);
|
|
960
|
-
return (_jsxs(Box, { flexDirection: "column", marginY: 1, paddingX: 1, borderStyle: "round", borderColor: theme.borderActive, children: [_jsx(Text, { bold: true, color: theme.accent, children: "Select Skill" }), _jsx(SearchField, { query: query, placeholder: "Type to search skills..." }), _jsx(Text, { color: theme.muted, children: "\u2191/\u2193 navigate \u00B7 Enter load \u00B7 Esc cancel \u00B7 Backspace clear" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [options.length === 0 && (_jsxs(Text, { color: theme.muted, children: ["No skills match \"", query, "\""] })), visible.map((skill, i) => {
|
|
961
|
-
const actualIndex = start + i;
|
|
962
|
-
const isSelected = actualIndex === selectedIndex;
|
|
963
|
-
const row = formatSkillPickerRow(skill, { selected: isSelected, width: rowWidth });
|
|
964
|
-
return (_jsx(Box, { children: _jsx(Text, { inverse: isSelected, color: isSelected ? theme.accent : undefined, bold: isSelected, children: row }) }, skill.name));
|
|
965
|
-
})] })] }));
|
|
966
|
-
}
|