@oh-my-pi/pi-tui 18.4.2 → 18.4.4
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 +56 -0
- package/dist/types/apps/cleanse-board.d.ts +17 -0
- package/dist/types/apps/git/help.d.ts +16 -0
- package/dist/types/apps/git/sidebar.d.ts +18 -0
- package/dist/types/apps/live-visualizer.d.ts +10 -0
- package/dist/types/apps/ps-top.d.ts +15 -0
- package/dist/types/apps/standalone-picker.d.ts +3 -0
- package/dist/types/autocomplete.d.ts +8 -0
- package/dist/types/chat/advisor-message.d.ts +12 -2
- package/dist/types/chat/assistant-message.d.ts +20 -0
- package/dist/types/chat/bash-execution.d.ts +9 -0
- package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
- package/dist/types/chat/collab-prompt-message.d.ts +4 -0
- package/dist/types/chat/compaction-summary-message.d.ts +15 -0
- package/dist/types/chat/eval-execution.d.ts +8 -0
- package/dist/types/chat/execution-shared.d.ts +58 -0
- package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
- package/dist/types/chat/read-tool-group.d.ts +15 -0
- package/dist/types/chat/skill-message.d.ts +9 -0
- package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
- package/dist/types/chat/thinking-display.d.ts +2 -0
- package/dist/types/chat/todo-reminder.d.ts +4 -1
- package/dist/types/chat/tool-execution.d.ts +25 -1
- package/dist/types/chat/transcript-actions.d.ts +37 -0
- package/dist/types/chat/transcript-entry.d.ts +3 -1
- package/dist/types/chat/user-message.d.ts +25 -0
- package/dist/types/chrome/collab-qrcode.d.ts +7 -0
- package/dist/types/chrome/context-thresholds.d.ts +11 -0
- package/dist/types/chrome/countdown-timer.d.ts +9 -3
- package/dist/types/chrome/diff.d.ts +12 -0
- package/dist/types/chrome/dynamic-border.d.ts +3 -0
- package/dist/types/chrome/format.d.ts +6 -0
- package/dist/types/chrome/message-divider.d.ts +13 -0
- package/dist/types/chrome/message-frame.d.ts +10 -0
- package/dist/types/chrome/message-notice.d.ts +23 -0
- package/dist/types/chrome/overlay-box.d.ts +11 -2
- package/dist/types/chrome/qrcode.d.ts +7 -0
- package/dist/types/chrome/segment-track.d.ts +7 -0
- package/dist/types/chrome/selector-helpers.d.ts +0 -6
- package/dist/types/chrome/status-notice.d.ts +14 -0
- package/dist/types/chrome/streaming-panel.d.ts +3 -0
- package/dist/types/chrome/tool-activity.d.ts +3 -0
- package/dist/types/chrome/transcript-container.d.ts +13 -0
- package/dist/types/chrome/transcript-status.d.ts +15 -3
- package/dist/types/components/box.d.ts +7 -0
- package/dist/types/components/disclosure.d.ts +9 -0
- package/dist/types/components/editor.d.ts +42 -0
- package/dist/types/components/form.d.ts +14 -0
- package/dist/types/components/image.d.ts +7 -0
- package/dist/types/components/input.d.ts +20 -1
- package/dist/types/components/key-value-list.d.ts +3 -0
- package/dist/types/components/layout/geometry.d.ts +16 -0
- package/dist/types/components/layout/row.d.ts +7 -0
- package/dist/types/components/layout/split-pane.d.ts +8 -0
- package/dist/types/components/layout/stack.d.ts +6 -0
- package/dist/types/components/loader.d.ts +48 -0
- package/dist/types/components/markdown.d.ts +8 -0
- package/dist/types/components/metric.d.ts +7 -0
- package/dist/types/components/progress-bar.d.ts +3 -0
- package/dist/types/components/scroll-view.d.ts +6 -0
- package/dist/types/components/section.d.ts +3 -0
- package/dist/types/components/select-list.d.ts +33 -0
- package/dist/types/components/settings-list.d.ts +68 -0
- package/dist/types/components/spacer.d.ts +4 -0
- package/dist/types/components/tab-bar.d.ts +9 -0
- package/dist/types/components/table.d.ts +7 -0
- package/dist/types/components/text.d.ts +6 -0
- package/dist/types/components/tree-view.d.ts +9 -0
- package/dist/types/components/truncated-text.d.ts +3 -0
- package/dist/types/components/wizard-step.d.ts +6 -0
- package/dist/types/hotkeys-markdown.d.ts +24 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/latex-block.d.ts +7 -0
- package/dist/types/loop-watchdog.d.ts +9 -0
- package/dist/types/native/apply.d.ts +39 -0
- package/dist/types/native/backend.d.ts +94 -0
- package/dist/types/native/blobs.d.ts +29 -0
- package/dist/types/native/describe.d.ts +56 -0
- package/dist/types/native/encode.d.ts +50 -0
- package/dist/types/native/icons.d.ts +18 -0
- package/dist/types/native/memo.d.ts +27 -0
- package/dist/types/native/node.d.ts +107 -0
- package/dist/types/native/overlay.d.ts +64 -0
- package/dist/types/native/picker.d.ts +124 -0
- package/dist/types/native/reconcile.d.ts +41 -0
- package/dist/types/native/settle.d.ts +11 -0
- package/dist/types/native/spans.d.ts +21 -0
- package/dist/types/native/state.d.ts +6 -0
- package/dist/types/native/tone.d.ts +24 -0
- package/dist/types/overlays/advisor-config.d.ts +20 -0
- package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
- package/dist/types/overlays/agent-hub.d.ts +10 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
- package/dist/types/overlays/agents-hub.d.ts +8 -0
- package/dist/types/overlays/annotation-overlay.d.ts +3 -0
- package/dist/types/overlays/ask-dialog.d.ts +23 -0
- package/dist/types/overlays/bordered-loader.d.ts +3 -0
- package/dist/types/overlays/btw-history-panel.d.ts +9 -0
- package/dist/types/overlays/btw-panel.d.ts +3 -0
- package/dist/types/overlays/cleanse-panel.d.ts +5 -2
- package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
- package/dist/types/overlays/copy-selector.d.ts +38 -0
- package/dist/types/overlays/error-banner.d.ts +9 -0
- package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
- package/dist/types/overlays/history-search.d.ts +18 -0
- package/dist/types/overlays/hook-editor.d.ts +28 -1
- package/dist/types/overlays/hook-input.d.ts +3 -0
- package/dist/types/overlays/hook-selector.d.ts +10 -0
- package/dist/types/overlays/hub-frame.d.ts +18 -0
- package/dist/types/overlays/jobs-panel.d.ts +24 -0
- package/dist/types/overlays/login-dialog.d.ts +10 -0
- package/dist/types/overlays/logout-account-selector.d.ts +4 -0
- package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
- package/dist/types/overlays/model-browser.d.ts +69 -0
- package/dist/types/overlays/model-hub.d.ts +7 -0
- package/dist/types/overlays/model-picker.d.ts +9 -1
- package/dist/types/overlays/move-overlay.d.ts +3 -0
- package/dist/types/overlays/oauth-selector.d.ts +4 -0
- package/dist/types/overlays/omfg-panel.d.ts +3 -0
- package/dist/types/overlays/pause-screen.d.ts +12 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
- package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
- package/dist/types/overlays/plugin-selector.d.ts +4 -0
- package/dist/types/overlays/plugin-settings.d.ts +30 -0
- package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
- package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
- package/dist/types/overlays/rewind-selector.d.ts +5 -0
- package/dist/types/overlays/session-info-overlay.d.ts +19 -1
- package/dist/types/overlays/session-selector.d.ts +64 -1
- package/dist/types/overlays/settings-defs.d.ts +14 -0
- package/dist/types/overlays/settings-selector.d.ts +21 -0
- package/dist/types/overlays/show-images-selector.d.ts +4 -0
- package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
- package/dist/types/overlays/stats-notice.d.ts +8 -0
- package/dist/types/overlays/theme-selector.d.ts +11 -0
- package/dist/types/overlays/thinking-selector.d.ts +4 -0
- package/dist/types/overlays/tree-selector.d.ts +60 -2
- package/dist/types/overlays/usage-dashboard.d.ts +26 -3
- package/dist/types/overlays/usage-row.d.ts +36 -2
- package/dist/types/prompt/attachment-chips.d.ts +8 -0
- package/dist/types/prompt/composer-attachments.d.ts +15 -3
- package/dist/types/prompt/composer.d.ts +18 -1
- package/dist/types/prompt/custom-editor.d.ts +48 -2
- package/dist/types/prompt/editor-top-gap.d.ts +2 -0
- package/dist/types/prompt/macos-spelling.d.ts +6 -0
- package/dist/types/prompt/magic-keywords.d.ts +9 -0
- package/dist/types/prompt/queued-messages.d.ts +23 -0
- package/dist/types/prompt/skill-tokens.d.ts +4 -2
- package/dist/types/prompt/welcome.d.ts +16 -0
- package/dist/types/render/code-cell.d.ts +10 -0
- package/dist/types/render/file-list.d.ts +8 -0
- package/dist/types/render/hyperlink.d.ts +14 -0
- package/dist/types/render/output-block.d.ts +43 -0
- package/dist/types/render/output-pane.d.ts +17 -0
- package/dist/types/render/tool-card.d.ts +8 -0
- package/dist/types/render/tree-list.d.ts +8 -0
- package/dist/types/render/width-aware-text.d.ts +7 -0
- package/dist/types/setup/scenes/outro.d.ts +7 -0
- package/dist/types/setup/scenes/sign-in.d.ts +16 -9
- package/dist/types/setup/scenes/splash.d.ts +8 -0
- package/dist/types/setup/scenes/types.d.ts +6 -27
- package/dist/types/setup/wizard-overlay.d.ts +9 -0
- package/dist/types/status-line/component.d.ts +49 -5
- package/dist/types/status-line/context-usage.d.ts +20 -0
- package/dist/types/status-line/footer.d.ts +8 -0
- package/dist/types/status-line/metrics.d.ts +1 -2
- package/dist/types/status-line/segments.d.ts +10 -1
- package/dist/types/status-line/types.d.ts +40 -1
- package/dist/types/stdin-buffer.d.ts +10 -7
- package/dist/types/terminal-capabilities.d.ts +1 -1
- package/dist/types/terminal.d.ts +36 -0
- package/dist/types/theme/defaults/index.d.ts +100 -0
- package/dist/types/theme/schema.d.ts +2 -0
- package/dist/types/theme/shimmer.d.ts +8 -0
- package/dist/types/theme/theme-class.d.ts +2 -0
- package/dist/types/theme/theme.d.ts +33 -1
- package/dist/types/tools/ask.d.ts +3 -0
- package/dist/types/tools/ast-edit.d.ts +10 -1
- package/dist/types/tools/ast-grep.d.ts +10 -1
- package/dist/types/tools/autoresearch.d.ts +21 -4
- package/dist/types/tools/bash.d.ts +30 -1
- package/dist/types/tools/cfg-render.d.ts +5 -1
- package/dist/types/tools/debug.d.ts +3 -1
- package/dist/types/tools/default-renderer.d.ts +6 -1
- package/dist/types/tools/edit.d.ts +14 -1
- package/dist/types/tools/eval.d.ts +12 -1
- package/dist/types/tools/fetch.d.ts +16 -1
- package/dist/types/tools/find.d.ts +15 -1
- package/dist/types/tools/github.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +14 -1
- package/dist/types/tools/goal.d.ts +3 -1
- package/dist/types/tools/grep.d.ts +14 -1
- package/dist/types/tools/grouped-file-output.d.ts +58 -0
- package/dist/types/tools/json-tree.d.ts +20 -0
- package/dist/types/tools/lsp.d.ts +3 -10
- package/dist/types/tools/mcp.d.ts +16 -1
- package/dist/types/tools/memory.d.ts +26 -0
- package/dist/types/tools/native-view.d.ts +90 -0
- package/dist/types/tools/proc-render.d.ts +7 -1
- package/dist/types/tools/read.d.ts +23 -1
- package/dist/types/tools/renderer.d.ts +57 -0
- package/dist/types/tools/report-tool-issue.d.ts +3 -0
- package/dist/types/tools/resolve.d.ts +14 -1
- package/dist/types/tools/task.d.ts +3 -0
- package/dist/types/tools/think.d.ts +3 -0
- package/dist/types/tools/todo.d.ts +12 -0
- package/dist/types/tools/vibe.d.ts +3 -1
- package/dist/types/tools/wait.d.ts +3 -0
- package/dist/types/tools/web-search-types.d.ts +90 -0
- package/dist/types/tools/web-search.d.ts +9 -168
- package/dist/types/tools/write.d.ts +14 -1
- package/dist/types/tools/xdev.d.ts +13 -0
- package/dist/types/tui.d.ts +49 -0
- package/package.json +9 -9
- package/src/apps/autoresearch-dashboard.ts +362 -4
- package/src/apps/cleanse-board.ts +219 -1
- package/src/apps/cleanse-picker.ts +9 -1
- package/src/apps/debug/log-viewer.ts +15 -15
- package/src/apps/debug/protocol-probe.ts +2 -20
- package/src/apps/git/avatar.ts +2 -22
- package/src/apps/git/git-tui.ts +406 -59
- package/src/apps/git/help.ts +130 -0
- package/src/apps/git/sidebar.ts +491 -0
- package/src/apps/live-visualizer.ts +65 -0
- package/src/apps/ps-top.ts +448 -17
- package/src/apps/session-picker.ts +1 -0
- package/src/apps/setup-model-picker.ts +5 -1
- package/src/apps/standalone-picker.ts +55 -1
- package/src/autocomplete.ts +44 -1
- package/src/chat/advisor-message.ts +72 -3
- package/src/chat/assistant-message.ts +538 -51
- package/src/chat/background-tan-message.ts +4 -4
- package/src/chat/bash-execution.ts +81 -0
- package/src/chat/cache-invalidation-marker.ts +8 -0
- package/src/chat/chat-transcript-builder.ts +37 -6
- package/src/chat/collab-prompt-message.ts +15 -3
- package/src/chat/compaction-summary-message.ts +74 -1
- package/src/chat/custom-message.ts +3 -1
- package/src/chat/eval-execution.ts +57 -0
- package/src/chat/execution-shared.ts +153 -1
- package/src/chat/hook-message.ts +1 -0
- package/src/chat/late-diagnostics-message.ts +85 -2
- package/src/chat/read-tool-group.ts +341 -1
- package/src/chat/served-model-marker.ts +8 -0
- package/src/chat/skill-message.ts +63 -0
- package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
- package/src/chat/thinking-display.ts +73 -11
- package/src/chat/todo-reminder.ts +34 -4
- package/src/chat/tool-execution.ts +385 -10
- package/src/chat/transcript-actions.ts +38 -0
- package/src/chat/transcript-browser.ts +2 -9
- package/src/chat/transcript-entry.ts +10 -1
- package/src/chat/transcript-render-helpers.ts +16 -15
- package/src/chat/ttsr-notification.ts +38 -1
- package/src/chat/user-message.ts +167 -4
- package/src/chrome/collab-qrcode.ts +27 -2
- package/src/chrome/context-thresholds.ts +34 -0
- package/src/chrome/countdown-timer.ts +24 -1
- package/src/chrome/diff.ts +60 -1
- package/src/chrome/dynamic-border.ts +7 -0
- package/src/chrome/format.ts +12 -0
- package/src/chrome/message-divider.ts +33 -1
- package/src/chrome/message-frame.ts +53 -10
- package/src/chrome/message-notice.ts +71 -0
- package/src/chrome/overlay-box.ts +45 -2
- package/src/chrome/qrcode.ts +28 -0
- package/src/chrome/segment-track.ts +26 -0
- package/src/chrome/selector-helpers.ts +1 -17
- package/src/chrome/status-notice.ts +46 -0
- package/src/chrome/streaming-panel.ts +25 -1
- package/src/chrome/tool-activity.ts +20 -0
- package/src/chrome/transcript-container.ts +139 -17
- package/src/chrome/transcript-status.ts +40 -11
- package/src/components/box.ts +51 -0
- package/src/components/disclosure.ts +33 -0
- package/src/components/editor.ts +239 -36
- package/src/components/form.ts +93 -9
- package/src/components/image.ts +30 -0
- package/src/components/input.ts +77 -26
- package/src/components/key-value-list.ts +18 -0
- package/src/components/layout/geometry.ts +44 -0
- package/src/components/layout/row.ts +30 -0
- package/src/components/layout/split-pane.ts +65 -0
- package/src/components/layout/stack.ts +27 -0
- package/src/components/loader.ts +183 -1
- package/src/components/markdown.ts +131 -56
- package/src/components/metric.ts +39 -0
- package/src/components/progress-bar.ts +28 -0
- package/src/components/scroll-view.ts +23 -0
- package/src/components/section.ts +16 -0
- package/src/components/select-list.ts +174 -25
- package/src/components/settings-list.ts +264 -7
- package/src/components/spacer.ts +17 -0
- package/src/components/tab-bar.ts +42 -0
- package/src/components/table.ts +34 -0
- package/src/components/text.ts +23 -0
- package/src/components/tree-view.ts +65 -0
- package/src/components/truncated-text.ts +14 -0
- package/src/components/wizard-step.ts +18 -9
- package/src/debug-server.ts +14 -7
- package/src/hotkeys-markdown.ts +251 -69
- package/src/index.ts +6 -0
- package/src/latex-block.ts +11 -0
- package/src/loop-watchdog.ts +38 -10
- package/src/native/apply.ts +275 -0
- package/src/native/backend.ts +682 -0
- package/src/native/blobs.ts +70 -0
- package/src/native/describe.ts +124 -0
- package/src/native/encode.ts +233 -0
- package/src/native/icons.ts +184 -0
- package/src/native/memo.ts +64 -0
- package/src/native/node.ts +112 -0
- package/src/native/overlay.ts +131 -0
- package/src/native/picker.ts +333 -0
- package/src/native/reconcile.ts +1063 -0
- package/src/native/settle.ts +23 -0
- package/src/native/spans.ts +308 -0
- package/src/native/state.ts +28 -0
- package/src/native/tone.ts +79 -0
- package/src/overlays/advisor-config.ts +420 -0
- package/src/overlays/agent-hub-renderer.ts +155 -18
- package/src/overlays/agent-hub.ts +1037 -44
- package/src/overlays/agent-transcript-viewer.ts +129 -0
- package/src/overlays/agents-hub.ts +688 -53
- package/src/overlays/annotation-overlay.ts +391 -52
- package/src/overlays/ask-dialog.ts +435 -24
- package/src/overlays/bordered-loader.ts +17 -0
- package/src/overlays/btw-history-panel.ts +182 -0
- package/src/overlays/btw-panel.ts +68 -2
- package/src/overlays/cleanse-panel.ts +105 -15
- package/src/overlays/codex-reset-fireworks.ts +65 -5
- package/src/overlays/composer-shape-preview.ts +26 -1
- package/src/overlays/copy-selector.ts +545 -23
- package/src/overlays/error-banner.ts +44 -0
- package/src/overlays/extensions/extension-list.ts +23 -17
- package/src/overlays/history-search.ts +175 -33
- package/src/overlays/hook-editor.ts +113 -6
- package/src/overlays/hook-input.ts +22 -1
- package/src/overlays/hook-selector.ts +311 -22
- package/src/overlays/hub-frame.ts +80 -0
- package/src/overlays/jobs-panel.ts +112 -0
- package/src/overlays/login-dialog.ts +183 -1
- package/src/overlays/logout-account-selector.ts +102 -4
- package/src/overlays/mcp-add-wizard.ts +232 -53
- package/src/overlays/model-browser.ts +681 -22
- package/src/overlays/model-hub.ts +1402 -78
- package/src/overlays/model-picker.ts +184 -7
- package/src/overlays/move-overlay.ts +45 -0
- package/src/overlays/oauth-selector.ts +227 -23
- package/src/overlays/omfg-panel.ts +86 -2
- package/src/overlays/pause-screen.ts +55 -4
- package/src/overlays/plan-review-overlay.ts +352 -0
- package/src/overlays/plan-save-overlay.ts +45 -0
- package/src/overlays/plugin-selector.ts +21 -1
- package/src/overlays/plugin-settings.ts +195 -5
- package/src/overlays/queue-mode-selector.ts +19 -1
- package/src/overlays/reset-usage-selector.ts +110 -40
- package/src/overlays/rewind-selector.ts +458 -44
- package/src/overlays/session-account-selector.ts +1 -1
- package/src/overlays/session-info-overlay.ts +137 -1
- package/src/overlays/session-selector.ts +807 -46
- package/src/overlays/settings-defs.ts +41 -0
- package/src/overlays/settings-selector.ts +585 -33
- package/src/overlays/show-images-selector.ts +18 -1
- package/src/overlays/snapcompact-shape-preview.ts +48 -3
- package/src/overlays/stats-notice.ts +31 -0
- package/src/overlays/theme-selector.ts +31 -1
- package/src/overlays/thinking-selector.ts +22 -1
- package/src/overlays/tree-selector.ts +570 -110
- package/src/overlays/usage-dashboard.ts +587 -12
- package/src/overlays/usage-row.ts +161 -25
- package/src/prompt/attachment-chips.ts +60 -0
- package/src/prompt/composer-attachments.ts +31 -9
- package/src/prompt/composer.ts +55 -2
- package/src/prompt/custom-editor.ts +302 -4
- package/src/prompt/editor-top-gap.ts +8 -0
- package/src/prompt/macos-spelling.ts +24 -9
- package/src/prompt/magic-keywords.ts +22 -1
- package/src/prompt/model-mention-autocomplete.ts +1 -0
- package/src/prompt/queued-messages.ts +101 -0
- package/src/prompt/skill-tokens.ts +5 -3
- package/src/prompt/welcome.ts +213 -6
- package/src/render/code-cell.ts +103 -2
- package/src/render/file-list.ts +29 -0
- package/src/render/hyperlink.ts +32 -0
- package/src/render/output-block.ts +110 -0
- package/src/render/output-pane.ts +37 -0
- package/src/render/tool-card.ts +137 -0
- package/src/render/tree-list.ts +39 -0
- package/src/render/width-aware-text.ts +20 -0
- package/src/setup/scenes/composer.ts +29 -0
- package/src/setup/scenes/glyph.ts +15 -0
- package/src/setup/scenes/model.ts +36 -8
- package/src/setup/scenes/outro.ts +28 -1
- package/src/setup/scenes/sign-in.ts +138 -24
- package/src/setup/scenes/splash.ts +28 -1
- package/src/setup/scenes/theme.ts +88 -3
- package/src/setup/scenes/types.ts +7 -28
- package/src/setup/startup-splash.ts +46 -9
- package/src/setup/wizard-overlay.ts +140 -23
- package/src/setup/wizard.ts +1 -1
- package/src/status-line/component.ts +414 -18
- package/src/status-line/context-usage.ts +279 -50
- package/src/status-line/footer.ts +94 -1
- package/src/status-line/metrics.ts +3 -6
- package/src/status-line/segments.ts +463 -97
- package/src/status-line/separators.ts +3 -16
- package/src/status-line/types.ts +43 -1
- package/src/stdin-buffer.ts +27 -21
- package/src/terminal-capabilities.ts +7 -0
- package/src/terminal.ts +153 -11
- package/src/theme/defaults/dark-neon-noir.json +100 -0
- package/src/theme/defaults/index.ts +2 -0
- package/src/theme/defaults/light-prism.json +5 -5
- package/src/theme/defaults/onyx.json +62 -62
- package/src/theme/mermaid-cache.ts +10 -1
- package/src/theme/schema.ts +15 -0
- package/src/theme/shimmer.ts +33 -0
- package/src/theme/theme-class.ts +24 -19
- package/src/theme/theme.ts +111 -9
- package/src/theme/tui-adapters.ts +14 -1
- package/src/tools/ask.ts +157 -1
- package/src/tools/ast-edit.ts +174 -1
- package/src/tools/ast-grep.ts +82 -2
- package/src/tools/autoresearch.ts +149 -1
- package/src/tools/bash.ts +233 -62
- package/src/tools/cfg-render.ts +66 -1
- package/src/tools/debug.ts +78 -1
- package/src/tools/default-renderer.ts +35 -1
- package/src/tools/edit.ts +357 -31
- package/src/tools/eval.ts +257 -1
- package/src/tools/fetch.ts +102 -3
- package/src/tools/find.ts +127 -1
- package/src/tools/github.ts +119 -10
- package/src/tools/glob.ts +116 -1
- package/src/tools/goal.ts +56 -1
- package/src/tools/grep.ts +83 -2
- package/src/tools/grouped-file-output.ts +205 -0
- package/src/tools/json-tree.ts +116 -1
- package/src/tools/lsp.ts +318 -1
- package/src/tools/mcp.ts +80 -1
- package/src/tools/memory.ts +93 -11
- package/src/tools/native-view.ts +292 -0
- package/src/tools/proc-render.ts +221 -1
- package/src/tools/read.ts +199 -4
- package/src/tools/renderer.ts +61 -0
- package/src/tools/report-tool-issue.ts +7 -1
- package/src/tools/resolve.ts +63 -1
- package/src/tools/streaming-output.ts +79 -16
- package/src/tools/task.ts +406 -4
- package/src/tools/think.ts +19 -1
- package/src/tools/todo.ts +89 -0
- package/src/tools/vibe.ts +230 -2
- package/src/tools/wait.ts +181 -1
- package/src/tools/web-search-types.ts +99 -0
- package/src/tools/web-search.ts +111 -170
- package/src/tools/write.ts +144 -5
- package/src/tools/xdev.ts +64 -1
- package/src/tui.ts +295 -4
- package/dist/types/setup/scenes/providers.d.ts +0 -3
- package/dist/types/setup/scenes/web-search.d.ts +0 -22
- package/src/setup/scenes/providers.ts +0 -110
- package/src/setup/scenes/web-search.ts +0 -165
|
@@ -2,7 +2,6 @@ import { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import {
|
|
3
3
|
type Component,
|
|
4
4
|
Container,
|
|
5
|
-
extractPrintableText,
|
|
6
5
|
fuzzyMatch,
|
|
7
6
|
Input,
|
|
8
7
|
matchesKey,
|
|
@@ -56,7 +55,16 @@ import { resolveAssistantErrorPresentation } from "../chat/transcript-render-hel
|
|
|
56
55
|
import { OverlayPanel, PanelDivider } from "../chrome/overlay-box";
|
|
57
56
|
import { TreeView, type TreeRow } from "../components/tree-view";
|
|
58
57
|
import { formatKeyHint, formatKeyHints } from "../app-keybindings";
|
|
59
|
-
import { editorKeys, interruptKey } from "../chrome/keybinding-hints";
|
|
58
|
+
import { boundKeys, editorKeys, interruptKey } from "../chrome/keybinding-hints";
|
|
59
|
+
import type { TspPickerItem, TspPickerProps, TspSpan, TspText } from "@oh-my-pi/pi-wire";
|
|
60
|
+
import type { ThemeColor } from "../theme/schema";
|
|
61
|
+
import { col, keyed, node, span, text } from "../native/describe";
|
|
62
|
+
import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
|
|
63
|
+
import { actionHint, hintsRow, overlayCard } from "../native/overlay";
|
|
64
|
+
import { CLOSE_ACTION, type PickerEvent, picker, pickerAction, pickerEvent, pickerQuery } from "../native/picker";
|
|
65
|
+
import { plainText } from "../native/spans";
|
|
66
|
+
import type { OutlineTarget } from "../chat/transcript-outline";
|
|
67
|
+
import { collectBlocks, targetCopy, turnPreview } from "./copy-selector";
|
|
60
68
|
|
|
61
69
|
/** Filter mode for tree display */
|
|
62
70
|
type FilterMode = TreeFilterMode;
|
|
@@ -155,13 +163,49 @@ function stripSystemWrapperTags(content: string): string {
|
|
|
155
163
|
/** Per-message cap on text folded into the tree search index. */
|
|
156
164
|
const SEARCH_TEXT_LIMIT = 200;
|
|
157
165
|
|
|
166
|
+
/** One theme-colored piece of an entry's display text. */
|
|
167
|
+
interface EntryPart {
|
|
168
|
+
t: string;
|
|
169
|
+
s?: ThemeColor;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** A cached native tree item and the inputs it was built from. */
|
|
173
|
+
interface TreeItemMemo {
|
|
174
|
+
label: string | undefined;
|
|
175
|
+
branch: boolean;
|
|
176
|
+
node: NativeNode;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** The session tree's `picker` data (see {@link TreeList.pickerView}). */
|
|
180
|
+
interface TreePickerView {
|
|
181
|
+
items: readonly TspPickerItem[];
|
|
182
|
+
order: readonly string[];
|
|
183
|
+
selected: string | null;
|
|
184
|
+
current: readonly string[];
|
|
185
|
+
query: string;
|
|
186
|
+
/** UTF-16 caret into `query`. */
|
|
187
|
+
cursor: number;
|
|
188
|
+
filterMode: FilterMode;
|
|
189
|
+
total: number;
|
|
190
|
+
empty: TspText;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const FILTER_TABS: readonly { id: TreeFilterMode; label: string }[] = [
|
|
194
|
+
{ id: "default", label: "Default" },
|
|
195
|
+
{ id: "no-tools", label: "No tools" },
|
|
196
|
+
{ id: "user-only", label: "User only" },
|
|
197
|
+
{ id: "labeled-only", label: "Labeled" },
|
|
198
|
+
{ id: "all", label: "All" },
|
|
199
|
+
];
|
|
200
|
+
|
|
158
201
|
class TreeList implements Component {
|
|
159
202
|
#tree: TreeView<TreeSelectorNode, string>;
|
|
160
203
|
#roots: TreeSelectorNode[] = [];
|
|
161
204
|
#rootIds: Set<string> = new Set();
|
|
162
205
|
#nodeById: Map<string, TreeSelectorNode> = new Map();
|
|
163
206
|
#filterMode: FilterMode;
|
|
164
|
-
|
|
207
|
+
/** The search field; its value is the fuzzy query. */
|
|
208
|
+
readonly searchInput = new Input();
|
|
165
209
|
#toolCallMap: Map<string, ToolCallInfo> = new Map();
|
|
166
210
|
#multipleRoots = false;
|
|
167
211
|
#activePathIds: Set<string> = new Set();
|
|
@@ -171,6 +215,26 @@ class TreeList implements Component {
|
|
|
171
215
|
onCancel?: () => void;
|
|
172
216
|
onLabelEdit?: (entryId: string, currentLabel: string | undefined) => void;
|
|
173
217
|
|
|
218
|
+
/** Native item nodes by entry id, reused across filter/search changes. */
|
|
219
|
+
#itemCache = new Map<string, TreeItemMemo>();
|
|
220
|
+
#itemsNative: { rows: readonly TreeRow<TreeSelectorNode, string>[]; items: NativeNode[] } | undefined;
|
|
221
|
+
#listNative:
|
|
222
|
+
| {
|
|
223
|
+
items: readonly NativeNode[];
|
|
224
|
+
selected: string | undefined;
|
|
225
|
+
query: string;
|
|
226
|
+
filterMode: FilterMode;
|
|
227
|
+
node: NativeNode;
|
|
228
|
+
}
|
|
229
|
+
| undefined;
|
|
230
|
+
/** Bumped on every label edit: labels are item badges. */
|
|
231
|
+
#labelVersion = 0;
|
|
232
|
+
#pickerItems:
|
|
233
|
+
| { rows: readonly TreeRow<TreeSelectorNode, string>[]; labels: number; items: TspPickerItem[] }
|
|
234
|
+
| undefined;
|
|
235
|
+
#pickerOrder: { rows: readonly TreeRow<TreeSelectorNode, string>[]; order: string[] } | undefined;
|
|
236
|
+
#previewMemo: { node: TreeSelectorNode | undefined; labels: number; preview: NativeChild[] } | undefined;
|
|
237
|
+
|
|
174
238
|
constructor(
|
|
175
239
|
tree: TreeSelectorNode[],
|
|
176
240
|
private readonly currentLeafId: string | null,
|
|
@@ -179,6 +243,8 @@ class TreeList implements Component {
|
|
|
179
243
|
initialSelectedId?: string,
|
|
180
244
|
) {
|
|
181
245
|
this.#filterMode = initialFilterMode;
|
|
246
|
+
this.searchInput.prompt = `${theme.fg("muted", "Search:")} `;
|
|
247
|
+
this.searchInput.placeholder = "Type to search";
|
|
182
248
|
this.#multipleRoots = tree.length > 1;
|
|
183
249
|
this.#indexSession(tree);
|
|
184
250
|
this.#tree = new TreeView<TreeSelectorNode, string>({
|
|
@@ -307,7 +373,7 @@ class TreeList implements Component {
|
|
|
307
373
|
#buildFilter(): (node: TreeSelectorNode, row: TreeRow<TreeSelectorNode, string>) => boolean {
|
|
308
374
|
const filterMode = this.#filterMode;
|
|
309
375
|
const leafId = this.currentLeafId;
|
|
310
|
-
const searchTokens = this
|
|
376
|
+
const searchTokens = this.getSearchQuery().toLowerCase().split(/\s+/).filter(Boolean);
|
|
311
377
|
return node => {
|
|
312
378
|
const entry = node.entry;
|
|
313
379
|
const isCurrentLeaf = entry.id === leafId;
|
|
@@ -473,6 +539,7 @@ class TreeList implements Component {
|
|
|
473
539
|
|
|
474
540
|
invalidate(): void {
|
|
475
541
|
this.#tree.invalidate();
|
|
542
|
+
this.#itemCache.clear();
|
|
476
543
|
}
|
|
477
544
|
|
|
478
545
|
dispose(): void {
|
|
@@ -480,7 +547,203 @@ class TreeList implements Component {
|
|
|
480
547
|
}
|
|
481
548
|
|
|
482
549
|
getSearchQuery(): string {
|
|
483
|
-
return this
|
|
550
|
+
return this.searchInput.getValue();
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
getFilterMode(): FilterMode {
|
|
554
|
+
return this.#filterMode;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/** Native filter tab: switch to that mode, like its alt+key shortcut. */
|
|
558
|
+
setFilterMode(mode: string): void {
|
|
559
|
+
const next = TREE_FILTER_MODES.find(m => m === mode);
|
|
560
|
+
if (next === undefined || next === this.#filterMode) return;
|
|
561
|
+
this.#filterMode = next;
|
|
562
|
+
this.#applyFilter();
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/** Native click on an entry: highlight it, then the same outcome as Enter. */
|
|
566
|
+
activateEntry(entryId: string): void {
|
|
567
|
+
if (!this.#tree.rows.some(row => row.key === entryId)) return;
|
|
568
|
+
this.#tree.setSelectedKey(entryId);
|
|
569
|
+
const selected = this.#tree.selectedItem;
|
|
570
|
+
if (selected && this.onSelect) this.onSelect(selected.entry.id, { summarize: false });
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/** Native row click: highlight the entry without switching. */
|
|
574
|
+
selectEntry(entryId: string): void {
|
|
575
|
+
if (this.#tree.rows.some(row => row.key === entryId)) this.#tree.setSelectedKey(entryId);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* The `tree` picker's data. `items` is every structural entry, rebuilt
|
|
580
|
+
* only when the tree or a label changes; filter modes and typing change
|
|
581
|
+
* only `order` (the visible entries) and the selection.
|
|
582
|
+
*/
|
|
583
|
+
pickerView(): TreePickerView {
|
|
584
|
+
const allRows = this.#tree.allRows;
|
|
585
|
+
if (this.#pickerItems?.rows !== allRows || this.#pickerItems.labels !== this.#labelVersion) {
|
|
586
|
+
// Real branch points get a disclosure; linear chains stay flat at their head's depth.
|
|
587
|
+
const branchPoints = new Set<string>();
|
|
588
|
+
for (const row of allRows) {
|
|
589
|
+
if (row.parentKey !== undefined && row.siblingCount > 1) branchPoints.add(row.parentKey);
|
|
590
|
+
}
|
|
591
|
+
this.#pickerItems = {
|
|
592
|
+
rows: allRows,
|
|
593
|
+
labels: this.#labelVersion,
|
|
594
|
+
items: allRows.map(row => this.#pickerItem(row, branchPoints.has(row.key))),
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
const rows = this.#tree.rows;
|
|
598
|
+
if (this.#pickerOrder?.rows !== rows) this.#pickerOrder = { rows, order: rows.map(row => row.key) };
|
|
599
|
+
return {
|
|
600
|
+
items: this.#pickerItems.items,
|
|
601
|
+
order: this.#pickerOrder.order,
|
|
602
|
+
selected: this.#tree.selectedKey ?? null,
|
|
603
|
+
current: this.currentLeafId === null ? [] : [this.currentLeafId],
|
|
604
|
+
query: this.getSearchQuery(),
|
|
605
|
+
cursor: this.searchInput.getCursor(),
|
|
606
|
+
filterMode: this.#filterMode,
|
|
607
|
+
total: allRows.length,
|
|
608
|
+
empty: this.#emptyText(),
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
#pickerItem(row: TreeRow<TreeSelectorNode, string>, open: boolean): TspPickerItem {
|
|
613
|
+
const item = row.item;
|
|
614
|
+
const entry = item.entry;
|
|
615
|
+
const label = this.#entryParts(item).map(part =>
|
|
616
|
+
span(part.t.includes("\x1b") ? plainText(part.t) : part.t, part.s),
|
|
617
|
+
);
|
|
618
|
+
let kind: TspPickerItem["node"] = "marker";
|
|
619
|
+
let role: string | undefined;
|
|
620
|
+
if (isUserRequestEntry(entry)) {
|
|
621
|
+
kind = "user";
|
|
622
|
+
} else if (entry.type === "message") {
|
|
623
|
+
const message = entry.message;
|
|
624
|
+
if (message.role === "assistant") kind = "assistant";
|
|
625
|
+
else if (message.role === "toolResult") [kind, role] = ["tool", `omp.tool.${message.toolName}`];
|
|
626
|
+
else if (message.role === "bashExecution") [kind, role] = ["tool", "omp.tool.bash"];
|
|
627
|
+
else if (message.role === "pythonExecution") [kind, role] = ["tool", "omp.tool.eval"];
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
id: row.key,
|
|
631
|
+
label,
|
|
632
|
+
depth: row.depth,
|
|
633
|
+
node: kind,
|
|
634
|
+
...(role ? { role } : {}),
|
|
635
|
+
...(open ? { open: true } : {}),
|
|
636
|
+
...(item.label ? { badges: [{ text: plainText(item.label), tone: "warning" as const }] } : {}),
|
|
637
|
+
...(this.#activePathIds.has(row.key) ? { dot: "accent" as const } : {}),
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Preview of the selected entry: a title, its label, and its content as
|
|
643
|
+
* the transcript shows it (markdown prose, commands and output as code).
|
|
644
|
+
*/
|
|
645
|
+
pickerPreview(): readonly NativeChild[] {
|
|
646
|
+
const selected = this.#tree.selectedItem;
|
|
647
|
+
const memo = this.#previewMemo;
|
|
648
|
+
if (memo !== undefined && memo.node === selected && memo.labels === this.#labelVersion) return memo.preview;
|
|
649
|
+
const preview: NativeChild[] = [];
|
|
650
|
+
if (selected) {
|
|
651
|
+
const entry = selected.entry;
|
|
652
|
+
const parts = this.#entryParts(selected)
|
|
653
|
+
.map(part => (part.t.includes("\x1b") ? plainText(part.t) : part.t))
|
|
654
|
+
.join("");
|
|
655
|
+
if (entry.type === "message" || entry.type === "custom_message") {
|
|
656
|
+
const target: OutlineTarget = {
|
|
657
|
+
entryId: entry.id,
|
|
658
|
+
turnId: entry.id,
|
|
659
|
+
isUserTurn: isUserRequestEntry(entry),
|
|
660
|
+
start: 0,
|
|
661
|
+
end: 0,
|
|
662
|
+
entries: [entry],
|
|
663
|
+
};
|
|
664
|
+
const copy = targetCopy(target, collectBlocks(target.entries));
|
|
665
|
+
preview.push(text(`${copy.label[0]!.toUpperCase()}${copy.label.slice(1)}`, { role: "omp.picker.title" }));
|
|
666
|
+
if (selected.label) preview.push(text([span(plainText(selected.label), "warning")]));
|
|
667
|
+
preview.push(turnPreview(target, copy.content || parts));
|
|
668
|
+
} else {
|
|
669
|
+
preview.push(text(parts, { role: "omp.picker.title" }));
|
|
670
|
+
if (selected.label) preview.push(text([span(plainText(selected.label), "warning")]));
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
this.#previewMemo = { node: selected, labels: this.#labelVersion, preview };
|
|
674
|
+
return preview;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* The visible entries as a native `list` keyed `"list"`, items keyed by
|
|
679
|
+
* entry id. Selection stays omp's; the terminal scrolls and virtualizes.
|
|
680
|
+
* Branch heads carry a `branch` icon and the active path an accent bullet
|
|
681
|
+
* instead of drawn tree connectors.
|
|
682
|
+
*/
|
|
683
|
+
describeList(): NativeNode {
|
|
684
|
+
const rows = this.#tree.rows;
|
|
685
|
+
let items = this.#itemsNative;
|
|
686
|
+
if (items?.rows !== rows) {
|
|
687
|
+
items = { rows, items: rows.map(row => this.#describeRow(row)) };
|
|
688
|
+
this.#itemsNative = items;
|
|
689
|
+
}
|
|
690
|
+
const selected = this.#tree.selectedKey;
|
|
691
|
+
const query = this.getSearchQuery();
|
|
692
|
+
const memo = this.#listNative;
|
|
693
|
+
if (
|
|
694
|
+
memo?.items === items.items &&
|
|
695
|
+
memo.selected === selected &&
|
|
696
|
+
memo.query === query &&
|
|
697
|
+
memo.filterMode === this.#filterMode
|
|
698
|
+
) {
|
|
699
|
+
return memo.node;
|
|
700
|
+
}
|
|
701
|
+
const list = node(
|
|
702
|
+
"list",
|
|
703
|
+
{ selected: selected ?? null, filter: query || undefined, empty: this.#emptyText(), virtual: true },
|
|
704
|
+
items.items,
|
|
705
|
+
"list",
|
|
706
|
+
);
|
|
707
|
+
this.#listNative = { items: items.items, selected, query, filterMode: this.#filterMode, node: list };
|
|
708
|
+
return list;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/** The empty-state explanation from {@link render}, as spans. */
|
|
712
|
+
#emptyText(): TspText {
|
|
713
|
+
const total = this.#tree.allRows.length;
|
|
714
|
+
if (total === 0) return [span("No entries found", "muted")];
|
|
715
|
+
const query = this.getSearchQuery();
|
|
716
|
+
if (query.length > 0) {
|
|
717
|
+
return [
|
|
718
|
+
span(`No entries match search "${query}". Press `, "muted"),
|
|
719
|
+
span("backspace", "key"),
|
|
720
|
+
span(" to clear the search", "muted"),
|
|
721
|
+
];
|
|
722
|
+
}
|
|
723
|
+
const filterLabel = this.#getFilterLabel().trim() || "[default]";
|
|
724
|
+
return [
|
|
725
|
+
span(`${total} entries hidden by the current filter ${filterLabel}. Press `, "muted"),
|
|
726
|
+
span("alt+a", "key"),
|
|
727
|
+
span(" to show all, ", "muted"),
|
|
728
|
+
span("alt+d", "key"),
|
|
729
|
+
span(" for default", "muted"),
|
|
730
|
+
];
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
#describeRow(row: TreeRow<TreeSelectorNode, string>): NativeNode {
|
|
734
|
+
const item = row.item;
|
|
735
|
+
const branch = row.parentKey !== undefined && row.siblingCount > 1;
|
|
736
|
+
const cached = this.#itemCache.get(row.key);
|
|
737
|
+
if (cached !== undefined && cached.label === item.label && cached.branch === branch) return cached.node;
|
|
738
|
+
const label: TspSpan[] = [];
|
|
739
|
+
if (this.#activePathIds.has(row.key)) label.push(span(`${theme.md.bullet} `, "accent"));
|
|
740
|
+
if (item.label) label.push(span(`[${plainText(item.label)}] `, "warning"));
|
|
741
|
+
for (const part of this.#entryParts(item)) {
|
|
742
|
+
label.push(span(part.t.includes("\x1b") ? plainText(part.t) : part.t, part.s));
|
|
743
|
+
}
|
|
744
|
+
const described = node("item", { label, icon: branch ? "branch" : undefined }, undefined, row.key);
|
|
745
|
+
this.#itemCache.set(row.key, { label: item.label, branch, node: described });
|
|
746
|
+
return described;
|
|
484
747
|
}
|
|
485
748
|
|
|
486
749
|
getSelectedNode(): TreeSelectorNode | undefined {
|
|
@@ -490,6 +753,7 @@ class TreeList implements Component {
|
|
|
490
753
|
updateNodeLabel(entryId: string, label: string | undefined): void {
|
|
491
754
|
const node = this.#nodeById.get(entryId);
|
|
492
755
|
if (node) node.label = label;
|
|
756
|
+
this.#labelVersion++;
|
|
493
757
|
this.#tree.invalidate();
|
|
494
758
|
}
|
|
495
759
|
|
|
@@ -523,8 +787,8 @@ class TreeList implements Component {
|
|
|
523
787
|
if (totalCount === 0) {
|
|
524
788
|
lines.push(truncateToWidth(theme.fg("muted", "No entries found"), width));
|
|
525
789
|
lines.push(truncateToWidth(theme.fg("muted", `(0/0)${this.#getFilterLabel()}`), width));
|
|
526
|
-
} else if (this
|
|
527
|
-
lines.push(truncateToWidth(theme.fg("muted", `No entries match search "${this
|
|
790
|
+
} else if (this.getSearchQuery().length > 0) {
|
|
791
|
+
lines.push(truncateToWidth(theme.fg("muted", `No entries match search "${this.getSearchQuery()}"`), width));
|
|
528
792
|
lines.push(
|
|
529
793
|
truncateToWidth(theme.fg("muted", `Press ${formatKeyHint("backspace")} to clear the search`), width),
|
|
530
794
|
);
|
|
@@ -660,9 +924,14 @@ class TreeList implements Component {
|
|
|
660
924
|
}
|
|
661
925
|
|
|
662
926
|
#getEntryDisplayText(node: TreeSelectorNode, isSelected: boolean): string {
|
|
663
|
-
|
|
664
|
-
|
|
927
|
+
let result = "";
|
|
928
|
+
for (const part of this.#entryParts(node)) result += part.s ? theme.fg(part.s, part.t) : part.t;
|
|
929
|
+
return isSelected ? theme.bold(result) : result;
|
|
930
|
+
}
|
|
665
931
|
|
|
932
|
+
/** Entry display text as theme-colored parts, shared by the ANSI row and the native item label. */
|
|
933
|
+
#entryParts(node: TreeSelectorNode): readonly EntryPart[] {
|
|
934
|
+
const entry = node.entry;
|
|
666
935
|
const normalize = (s: string) => s.replace(/[\n\t]/g, " ").trim();
|
|
667
936
|
|
|
668
937
|
switch (entry.type) {
|
|
@@ -672,84 +941,76 @@ class TreeList implements Component {
|
|
|
672
941
|
if (role === "user") {
|
|
673
942
|
const msgWithContent = msg as { content?: unknown };
|
|
674
943
|
const content = normalize(this.#extractContent(msgWithContent.content));
|
|
675
|
-
|
|
676
|
-
}
|
|
944
|
+
return [{ t: "user: ", s: "accent" }, { t: content }];
|
|
945
|
+
}
|
|
946
|
+
if (role === "developer") {
|
|
677
947
|
const msgWithContent = msg as { content?: unknown };
|
|
678
948
|
const content = normalize(this.#extractContent(msgWithContent.content));
|
|
679
|
-
|
|
680
|
-
|
|
949
|
+
return [
|
|
950
|
+
{ t: "developer: ", s: "dim" },
|
|
951
|
+
{ t: content, s: "muted" },
|
|
952
|
+
];
|
|
953
|
+
}
|
|
954
|
+
if (role === "assistant") {
|
|
681
955
|
const presentation = resolveAssistantErrorPresentation(msg);
|
|
682
956
|
if (presentation.kind === "compact-recovered") {
|
|
683
|
-
|
|
684
|
-
|
|
957
|
+
return [
|
|
958
|
+
{ t: "assistant: ", s: "success" },
|
|
959
|
+
{ t: presentation.text, s: "dim" },
|
|
960
|
+
];
|
|
685
961
|
}
|
|
686
962
|
const msgWithContent = msg as { content?: unknown; stopReason?: string; errorMessage?: string };
|
|
687
963
|
const textContent = normalize(this.#extractContent(msgWithContent.content));
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
theme.fg("success", "assistant: ") + theme.fg("error", normalize(presentation.text).slice(0, 80));
|
|
693
|
-
} else if (msgWithContent.stopReason === "aborted") {
|
|
694
|
-
result = theme.fg("success", "assistant: ") + theme.fg("muted", "(aborted)");
|
|
695
|
-
} else {
|
|
696
|
-
result = theme.fg("success", "assistant: ") + theme.fg("muted", "(no content)");
|
|
964
|
+
const head: EntryPart = { t: "assistant: ", s: "success" };
|
|
965
|
+
if (textContent) return [head, { t: textContent }];
|
|
966
|
+
if (presentation.kind === "full") {
|
|
967
|
+
return [head, { t: normalize(presentation.text).slice(0, 80), s: "error" }];
|
|
697
968
|
}
|
|
698
|
-
|
|
969
|
+
if (msgWithContent.stopReason === "aborted") return [head, { t: "(aborted)", s: "muted" }];
|
|
970
|
+
return [head, { t: "(no content)", s: "muted" }];
|
|
971
|
+
}
|
|
972
|
+
if (role === "toolResult") {
|
|
699
973
|
const toolMsg = msg as { toolCallId?: string; toolName?: string };
|
|
700
974
|
const toolCall = toolMsg.toolCallId ? this.#toolCallMap.get(toolMsg.toolCallId) : undefined;
|
|
701
|
-
if (toolCall) {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
} else if (role === "bashExecution") {
|
|
975
|
+
if (toolCall) return [{ t: this.#formatToolCall(toolCall.name, toolCall.arguments), s: "muted" }];
|
|
976
|
+
return [{ t: `[${toolMsg.toolName ?? "tool"}]`, s: "muted" }];
|
|
977
|
+
}
|
|
978
|
+
if (role === "bashExecution") {
|
|
707
979
|
const bashMsg = msg as { command?: string };
|
|
708
|
-
|
|
709
|
-
} else {
|
|
710
|
-
result = theme.fg("dim", `[${role}]`);
|
|
980
|
+
return [{ t: `[bash]: ${normalize(bashMsg.command ?? "")}`, s: "dim" }];
|
|
711
981
|
}
|
|
712
|
-
|
|
982
|
+
return [{ t: `[${role}]`, s: "dim" }];
|
|
713
983
|
}
|
|
714
984
|
case "custom_message": {
|
|
715
985
|
if (entry.customType === "advisor") {
|
|
716
986
|
const { qualifier, text } = advisorTreeDisplay(entry.details);
|
|
717
987
|
const label = qualifier ? `advisor (${qualifier}): ` : "advisor: ";
|
|
718
|
-
|
|
719
|
-
break;
|
|
988
|
+
return [{ t: label, s: "customMessageLabel" }, { t: normalize(text) }];
|
|
720
989
|
}
|
|
721
990
|
const content = stripSystemWrapperTags(this.#joinTextContent(entry.content));
|
|
722
|
-
|
|
723
|
-
break;
|
|
991
|
+
return [{ t: `[${entry.customType}]: `, s: "customMessageLabel" }, { t: normalize(content) }];
|
|
724
992
|
}
|
|
725
993
|
case "compaction": {
|
|
726
994
|
const tokens = Math.round(entry.tokensBefore / 1000);
|
|
727
|
-
|
|
728
|
-
break;
|
|
995
|
+
return [{ t: `[compaction: ${tokens}k tokens]`, s: "borderAccent" }];
|
|
729
996
|
}
|
|
730
997
|
case "branch_summary":
|
|
731
|
-
|
|
732
|
-
break;
|
|
998
|
+
return [{ t: `[branch summary]: `, s: "warning" }, { t: normalize(entry.summary) }];
|
|
733
999
|
case "model_change":
|
|
734
|
-
|
|
735
|
-
break;
|
|
1000
|
+
return [{ t: `[model: ${entry.model}]`, s: "dim" }];
|
|
736
1001
|
case "model_usage": {
|
|
737
1002
|
const purpose = sanitizeTreeField(entry.purpose);
|
|
738
1003
|
const role = sanitizeTreeField(entry.role ?? "");
|
|
739
1004
|
const provider = sanitizeTreeField(entry.provider);
|
|
740
1005
|
const model = sanitizeTreeField(entry.model);
|
|
741
|
-
|
|
742
|
-
break;
|
|
1006
|
+
return [{ t: `[model usage: ${purpose} ${role ? `${role} ` : ""}${provider}/${model}]`, s: "dim" }];
|
|
743
1007
|
}
|
|
744
1008
|
case "thinking_level_change":
|
|
745
|
-
|
|
746
|
-
break;
|
|
1009
|
+
return [{ t: `[thinking: ${entry.thinkingLevel ?? ThinkingLevel.Off}]`, s: "dim" }];
|
|
747
1010
|
case "custom":
|
|
748
|
-
|
|
749
|
-
break;
|
|
1011
|
+
return [{ t: `[custom: ${entry.customType}]`, s: "dim" }];
|
|
750
1012
|
case "label":
|
|
751
|
-
|
|
752
|
-
break;
|
|
1013
|
+
return [{ t: `[label: ${entry.label ?? "(cleared)"}]`, s: "dim" }];
|
|
753
1014
|
case "service_tier_change": {
|
|
754
1015
|
// Per-family map, or null when the session went back to the default.
|
|
755
1016
|
const tiers = entry.serviceTier
|
|
@@ -757,27 +1018,21 @@ class TreeList implements Component {
|
|
|
757
1018
|
.map(([family, tier]) => `${family}:${tier}`)
|
|
758
1019
|
.join(" ")
|
|
759
1020
|
: "(default)";
|
|
760
|
-
|
|
761
|
-
break;
|
|
1021
|
+
return [{ t: `[service tier: ${tiers}]`, s: "dim" }];
|
|
762
1022
|
}
|
|
763
1023
|
case "title_change":
|
|
764
|
-
|
|
765
|
-
break;
|
|
1024
|
+
return [{ t: `[title: ${normalize(entry.title)}]`, s: "dim" }];
|
|
766
1025
|
case "mode_change":
|
|
767
|
-
|
|
768
|
-
break;
|
|
1026
|
+
return [{ t: `[mode: ${entry.mode}]`, s: "dim" }];
|
|
769
1027
|
case "credential_pin":
|
|
770
|
-
|
|
771
|
-
break;
|
|
1028
|
+
return [{ t: `[credential pin: ${entry.provider}]`, s: "dim" }];
|
|
772
1029
|
default:
|
|
773
1030
|
// Bookkeeping entries with nothing worth spelling out still get their
|
|
774
1031
|
// type. A row that renders to the empty string is worse than a
|
|
775
1032
|
// useless one: it draws as a bare bullet with no way to tell what it
|
|
776
1033
|
// is or why the tree has a gap in it.
|
|
777
|
-
|
|
1034
|
+
return [{ t: `[${entry.type.replaceAll("_", " ")}]`, s: "dim" }];
|
|
778
1035
|
}
|
|
779
|
-
|
|
780
|
-
return isSelected ? theme.bold(result) : result;
|
|
781
1036
|
}
|
|
782
1037
|
|
|
783
1038
|
#extractContent(content: unknown): string {
|
|
@@ -884,6 +1139,37 @@ class TreeList implements Component {
|
|
|
884
1139
|
}
|
|
885
1140
|
}
|
|
886
1141
|
|
|
1142
|
+
/** Enter (`summarize` false) / Shift+Enter: switch to the selected entry. */
|
|
1143
|
+
confirm(summarize: boolean): void {
|
|
1144
|
+
const selected = this.#tree.selectedItem;
|
|
1145
|
+
if (selected && this.onSelect) this.onSelect(selected.entry.id, { summarize });
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/** Esc: clear the search first, then close. */
|
|
1149
|
+
escape(): void {
|
|
1150
|
+
if (this.getSearchQuery()) this.clearSearch();
|
|
1151
|
+
else this.onCancel?.();
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
clearSearch(): void {
|
|
1155
|
+
this.searchInput.setValue("");
|
|
1156
|
+
this.#applyFilter();
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/** Ctrl+O / Shift+Ctrl+O: default → no-tools → user-only → labeled-only → all → default. */
|
|
1160
|
+
cycleFilter(delta: 1 | -1): void {
|
|
1161
|
+
const modes = TREE_FILTER_MODES;
|
|
1162
|
+
const currentIndex = modes.indexOf(this.#filterMode);
|
|
1163
|
+
this.#filterMode = modes[(currentIndex + delta + modes.length) % modes.length];
|
|
1164
|
+
this.#applyFilter();
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/** Shift+L: edit the selected entry's label. */
|
|
1168
|
+
editLabel(): void {
|
|
1169
|
+
const selected = this.#tree.selectedItem;
|
|
1170
|
+
if (selected && this.onLabelEdit) this.onLabelEdit(selected.entry.id, selected.label);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
887
1173
|
handleInput(keyData: string): void {
|
|
888
1174
|
if (matchesSelectUp(keyData)) {
|
|
889
1175
|
this.#tree.moveSelection(-1, true);
|
|
@@ -918,36 +1204,17 @@ class TreeList implements Component {
|
|
|
918
1204
|
keyData === "\x1b[13;2~" // Shift+Enter legacy CSI ~ form — also accepted by the composer (editor.ts:1466)
|
|
919
1205
|
) {
|
|
920
1206
|
// Summarize-and-switch: fork with a branch summary without the extra prompt.
|
|
921
|
-
|
|
922
|
-
if (selected && this.onSelect) {
|
|
923
|
-
this.onSelect(selected.entry.id, { summarize: true });
|
|
924
|
-
}
|
|
1207
|
+
this.confirm(true);
|
|
925
1208
|
} else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return")) {
|
|
926
|
-
|
|
927
|
-
if (selected && this.onSelect) {
|
|
928
|
-
this.onSelect(selected.entry.id, { summarize: false });
|
|
929
|
-
}
|
|
1209
|
+
this.confirm(false);
|
|
930
1210
|
} else if (matchesAppInterrupt(keyData)) {
|
|
931
|
-
|
|
932
|
-
this.#searchQuery = "";
|
|
933
|
-
this.#applyFilter();
|
|
934
|
-
} else {
|
|
935
|
-
this.onCancel?.();
|
|
936
|
-
}
|
|
1211
|
+
this.escape();
|
|
937
1212
|
} else if (matchesKey(keyData, "ctrl+c")) {
|
|
938
1213
|
this.onCancel?.();
|
|
939
1214
|
} else if (matchesKey(keyData, "shift+ctrl+o") || matchesKey(keyData, "ctrl+shift+o")) {
|
|
940
|
-
|
|
941
|
-
const modes = TREE_FILTER_MODES;
|
|
942
|
-
const currentIndex = modes.indexOf(this.#filterMode);
|
|
943
|
-
this.#filterMode = modes[(currentIndex - 1 + modes.length) % modes.length];
|
|
944
|
-
this.#applyFilter();
|
|
1215
|
+
this.cycleFilter(-1);
|
|
945
1216
|
} else if (matchesKey(keyData, "ctrl+o")) {
|
|
946
|
-
|
|
947
|
-
const modes = TREE_FILTER_MODES;
|
|
948
|
-
const currentIndex = modes.indexOf(this.#filterMode);
|
|
949
|
-
this.#filterMode = modes[(currentIndex + 1) % modes.length];
|
|
950
|
-
this.#applyFilter();
|
|
1217
|
+
this.cycleFilter(1);
|
|
951
1218
|
} else if (matchesKey(keyData, "alt+d")) {
|
|
952
1219
|
this.#filterMode = "default";
|
|
953
1220
|
this.#applyFilter();
|
|
@@ -963,22 +1230,11 @@ class TreeList implements Component {
|
|
|
963
1230
|
} else if (matchesKey(keyData, "alt+a")) {
|
|
964
1231
|
this.#filterMode = "all";
|
|
965
1232
|
this.#applyFilter();
|
|
966
|
-
} else if (matchesKey(keyData, "
|
|
967
|
-
|
|
968
|
-
this.#searchQuery = this.#searchQuery.slice(0, -1);
|
|
969
|
-
this.#applyFilter();
|
|
970
|
-
}
|
|
971
|
-
} else if (matchesKey(keyData, "shift+l") && !this.#searchQuery) {
|
|
972
|
-
const selected = this.#tree.selectedItem;
|
|
973
|
-
if (selected && this.onLabelEdit) {
|
|
974
|
-
this.onLabelEdit(selected.entry.id, selected.label);
|
|
975
|
-
}
|
|
1233
|
+
} else if (matchesKey(keyData, "shift+l") && !this.getSearchQuery()) {
|
|
1234
|
+
this.editLabel();
|
|
976
1235
|
} else {
|
|
977
|
-
const
|
|
978
|
-
if (
|
|
979
|
-
this.#searchQuery += printableText;
|
|
980
|
-
this.#applyFilter();
|
|
981
|
-
}
|
|
1236
|
+
const before = this.getSearchQuery();
|
|
1237
|
+
if (this.searchInput.handleInput(keyData) && this.getSearchQuery() !== before) this.#applyFilter();
|
|
982
1238
|
}
|
|
983
1239
|
}
|
|
984
1240
|
}
|
|
@@ -990,11 +1246,7 @@ class SearchLine implements Component {
|
|
|
990
1246
|
invalidate(): void {}
|
|
991
1247
|
|
|
992
1248
|
render(width: number): readonly string[] {
|
|
993
|
-
|
|
994
|
-
if (query) {
|
|
995
|
-
return [truncateToWidth(`${theme.fg("muted", "Search:")} ${theme.fg("accent", query)}`, width)];
|
|
996
|
-
}
|
|
997
|
-
return [truncateToWidth(theme.fg("muted", "Search:"), width)];
|
|
1249
|
+
return this.treeList.searchInput.render(width);
|
|
998
1250
|
}
|
|
999
1251
|
|
|
1000
1252
|
handleInput(_keyData: string): void {}
|
|
@@ -1006,6 +1258,9 @@ class LabelInput implements Component {
|
|
|
1006
1258
|
onSubmit?: (entryId: string, label: string | undefined) => void;
|
|
1007
1259
|
onCancel?: () => void;
|
|
1008
1260
|
|
|
1261
|
+
#native: NativeNode | undefined;
|
|
1262
|
+
#preview: readonly NativeChild[] | undefined;
|
|
1263
|
+
|
|
1009
1264
|
constructor(
|
|
1010
1265
|
private readonly entryId: string,
|
|
1011
1266
|
currentLabel: string | undefined,
|
|
@@ -1018,6 +1273,35 @@ class LabelInput implements Component {
|
|
|
1018
1273
|
|
|
1019
1274
|
invalidate(): void {}
|
|
1020
1275
|
|
|
1276
|
+
/** Prompt, the label `Input`, and save/cancel hints; the Input describes its own edits. */
|
|
1277
|
+
describe(_cx: DescribeContext): NativeNode {
|
|
1278
|
+
this.#native ??= node("col", { gap: "xs" }, [
|
|
1279
|
+
text([span("Label (empty to remove):", "muted")]),
|
|
1280
|
+
this.#input,
|
|
1281
|
+
hintsRow([
|
|
1282
|
+
{ keys: ["enter"], label: "save" },
|
|
1283
|
+
{ keys: [boundKeys("app.interrupt", ["escape"])[0] ?? "escape"], label: "cancel" },
|
|
1284
|
+
]),
|
|
1285
|
+
]);
|
|
1286
|
+
return this.#native;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/** The picker preview while editing: the prompt and the label `Input` (save/cancel sit in the action bar). */
|
|
1290
|
+
get preview(): readonly NativeChild[] {
|
|
1291
|
+
this.#preview ??= [
|
|
1292
|
+
text("Label", { role: "omp.picker.title" }),
|
|
1293
|
+
text([span("Empty to remove", "muted")]),
|
|
1294
|
+
this.#input,
|
|
1295
|
+
];
|
|
1296
|
+
return this.#preview;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
/** Enter: save the label (empty removes it). */
|
|
1300
|
+
submit(): void {
|
|
1301
|
+
const value = this.#input.getValue().trim();
|
|
1302
|
+
this.onSubmit?.(this.entryId, value || undefined);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1021
1305
|
render(width: number): readonly string[] {
|
|
1022
1306
|
const lines: string[] = [];
|
|
1023
1307
|
lines.push(truncateToWidth(theme.fg("muted", "Label (empty to remove):"), width));
|
|
@@ -1029,8 +1313,7 @@ class LabelInput implements Component {
|
|
|
1029
1313
|
|
|
1030
1314
|
handleInput(keyData: string): void {
|
|
1031
1315
|
if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
1032
|
-
|
|
1033
|
-
this.onSubmit?.(this.entryId, value || undefined);
|
|
1316
|
+
this.submit();
|
|
1034
1317
|
} else if (matchesAppInterrupt(keyData)) {
|
|
1035
1318
|
this.onCancel?.();
|
|
1036
1319
|
} else {
|
|
@@ -1047,6 +1330,23 @@ export class TreeSelectorComponent extends OverlayPanel {
|
|
|
1047
1330
|
#labelInput: LabelInput | null = null;
|
|
1048
1331
|
#labelInputContainer: Container;
|
|
1049
1332
|
#treeContainer: Container;
|
|
1333
|
+
#nativeMemo:
|
|
1334
|
+
| {
|
|
1335
|
+
content: NativeChild;
|
|
1336
|
+
query: string;
|
|
1337
|
+
cursor: number;
|
|
1338
|
+
filterMode: FilterMode;
|
|
1339
|
+
node: NativeNode;
|
|
1340
|
+
}
|
|
1341
|
+
| undefined;
|
|
1342
|
+
#pickerMemo:
|
|
1343
|
+
| {
|
|
1344
|
+
view: TreePickerView;
|
|
1345
|
+
preview: readonly NativeChild[];
|
|
1346
|
+
label: LabelInput | null;
|
|
1347
|
+
node: NativeNode;
|
|
1348
|
+
}
|
|
1349
|
+
| undefined;
|
|
1050
1350
|
|
|
1051
1351
|
constructor(
|
|
1052
1352
|
tree: TreeSelectorNode[],
|
|
@@ -1056,6 +1356,8 @@ export class TreeSelectorComponent extends OverlayPanel {
|
|
|
1056
1356
|
onCancel: () => void,
|
|
1057
1357
|
private readonly onLabelChangeCallback?: (entryId: string, label: string | undefined) => void,
|
|
1058
1358
|
initialFilterMode: FilterMode = "default",
|
|
1359
|
+
/** The picker's subtitle. */
|
|
1360
|
+
private readonly sessionName?: string,
|
|
1059
1361
|
) {
|
|
1060
1362
|
super("Session Tree");
|
|
1061
1363
|
// The outer panel has eight fixed rows around the tree list: top/bottom
|
|
@@ -1109,6 +1411,164 @@ export class TreeSelectorComponent extends OverlayPanel {
|
|
|
1109
1411
|
}
|
|
1110
1412
|
}
|
|
1111
1413
|
|
|
1414
|
+
override describe(cx: DescribeContext): NativeNode {
|
|
1415
|
+
return cx.supports("picker") ? this.#describePicker() : this.#describeCard();
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
/**
|
|
1419
|
+
* The `tree` picker, keyed `picker` inside a column: mounted in the dock,
|
|
1420
|
+
* the reconciler hoists it into `layer` as a sheet. The label editor
|
|
1421
|
+
* takes the preview pane while open.
|
|
1422
|
+
*/
|
|
1423
|
+
#describePicker(): NativeNode {
|
|
1424
|
+
const view = this.#treeList.pickerView();
|
|
1425
|
+
const label = this.#labelInput;
|
|
1426
|
+
const preview = label ? label.preview : this.#treeList.pickerPreview();
|
|
1427
|
+
const memo = this.#pickerMemo;
|
|
1428
|
+
if (
|
|
1429
|
+
memo?.label === label &&
|
|
1430
|
+
memo.preview === preview &&
|
|
1431
|
+
memo.view.items === view.items &&
|
|
1432
|
+
memo.view.order === view.order &&
|
|
1433
|
+
memo.view.selected === view.selected &&
|
|
1434
|
+
memo.view.query === view.query &&
|
|
1435
|
+
memo.view.cursor === view.cursor &&
|
|
1436
|
+
memo.view.filterMode === view.filterMode
|
|
1437
|
+
) {
|
|
1438
|
+
return memo.node;
|
|
1439
|
+
}
|
|
1440
|
+
const props: TspPickerProps = {
|
|
1441
|
+
title: "Session tree",
|
|
1442
|
+
...(this.sessionName ? { subtitle: this.sessionName } : {}),
|
|
1443
|
+
icon: "git-branch",
|
|
1444
|
+
noun: "entries",
|
|
1445
|
+
size: "lg",
|
|
1446
|
+
layout: "tree",
|
|
1447
|
+
preview: "side",
|
|
1448
|
+
...pickerQuery(this.#treeList.searchInput),
|
|
1449
|
+
placeholder: "Search entries…",
|
|
1450
|
+
tabs: FILTER_TABS,
|
|
1451
|
+
tab: view.filterMode,
|
|
1452
|
+
items: view.items,
|
|
1453
|
+
order: view.order,
|
|
1454
|
+
selected: view.selected,
|
|
1455
|
+
current: view.current,
|
|
1456
|
+
total: view.total,
|
|
1457
|
+
empty: view.empty,
|
|
1458
|
+
focus: label ? "preview" : "list",
|
|
1459
|
+
actions: label
|
|
1460
|
+
? [
|
|
1461
|
+
pickerAction("label-save", "Save label", "enter", { primary: true }),
|
|
1462
|
+
{ ...CLOSE_ACTION, label: "Cancel" },
|
|
1463
|
+
]
|
|
1464
|
+
: [
|
|
1465
|
+
pickerAction("switch", "Switch", "enter", { primary: true }),
|
|
1466
|
+
pickerAction("summarize", "Summarize & switch", "shift+enter"),
|
|
1467
|
+
pickerAction("label", "Label", "shift+l"),
|
|
1468
|
+
pickerAction("filter", "Filter", "ctrl+o"),
|
|
1469
|
+
CLOSE_ACTION,
|
|
1470
|
+
],
|
|
1471
|
+
};
|
|
1472
|
+
const result = col([keyed(picker(props, preview), "picker")]);
|
|
1473
|
+
this.#pickerMemo = { view, preview, label, node: result };
|
|
1474
|
+
return result;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/** Picker pointer events run the same paths as their keys; the label editor owns them while open. */
|
|
1478
|
+
#handlePickerEvent(event: PickerEvent): void {
|
|
1479
|
+
const label = this.#labelInput;
|
|
1480
|
+
if (label) {
|
|
1481
|
+
if (event.kind !== "action") return;
|
|
1482
|
+
if (event.act === "label-save") label.submit();
|
|
1483
|
+
else if (event.act === "close") label.onCancel?.();
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
const list = this.#treeList;
|
|
1487
|
+
if (event.kind !== "action") {
|
|
1488
|
+
if (event.kind === "select") list.selectEntry(event.item);
|
|
1489
|
+
else list.activateEntry(event.item);
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
switch (event.act) {
|
|
1493
|
+
case "tab":
|
|
1494
|
+
if (event.value !== undefined) list.setFilterMode(event.value);
|
|
1495
|
+
return;
|
|
1496
|
+
case "switch":
|
|
1497
|
+
list.confirm(false);
|
|
1498
|
+
return;
|
|
1499
|
+
case "summarize":
|
|
1500
|
+
list.confirm(true);
|
|
1501
|
+
return;
|
|
1502
|
+
case "label":
|
|
1503
|
+
if (!list.getSearchQuery()) list.editLabel();
|
|
1504
|
+
return;
|
|
1505
|
+
case "filter":
|
|
1506
|
+
list.cycleFilter(1);
|
|
1507
|
+
return;
|
|
1508
|
+
case "close":
|
|
1509
|
+
list.escape();
|
|
1510
|
+
return;
|
|
1511
|
+
case "clear":
|
|
1512
|
+
list.clearSearch();
|
|
1513
|
+
return;
|
|
1514
|
+
default:
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* Native tree: filter-mode `tabs`, the search query as an `input`, then
|
|
1521
|
+
* the entry `list` (or the label editor in its place) and the key hints.
|
|
1522
|
+
*/
|
|
1523
|
+
#describeCard(): NativeNode {
|
|
1524
|
+
const content: NativeChild = this.#labelInput ?? this.#treeList.describeList();
|
|
1525
|
+
const search = this.#treeList.searchInput;
|
|
1526
|
+
const query = search.getValue();
|
|
1527
|
+
const cursor = search.getCursor();
|
|
1528
|
+
const filterMode = this.#treeList.getFilterMode();
|
|
1529
|
+
const memo = this.#nativeMemo;
|
|
1530
|
+
if (
|
|
1531
|
+
memo?.content === content &&
|
|
1532
|
+
memo.query === query &&
|
|
1533
|
+
memo.cursor === cursor &&
|
|
1534
|
+
memo.filterMode === filterMode
|
|
1535
|
+
) {
|
|
1536
|
+
return memo.node;
|
|
1537
|
+
}
|
|
1538
|
+
const children: NativeChild[] = [
|
|
1539
|
+
node("tabs", { items: FILTER_TABS, active: filterMode }, undefined, "filter"),
|
|
1540
|
+
search,
|
|
1541
|
+
content,
|
|
1542
|
+
hintsRow([
|
|
1543
|
+
{ keys: ["enter"], label: "switch" },
|
|
1544
|
+
{ keys: ["alt+up", "alt+down"], label: "previous/next turn" },
|
|
1545
|
+
actionHint(["tui.select.pageUp", "tui.select.pageDown"], "page"),
|
|
1546
|
+
{ keys: ["home", "end"], label: "first/last" },
|
|
1547
|
+
{ keys: ["shift+enter"], label: "summarize & switch" },
|
|
1548
|
+
{ keys: ["shift+l"], label: "label" },
|
|
1549
|
+
{ keys: ["ctrl+o"], label: "filter" },
|
|
1550
|
+
]),
|
|
1551
|
+
];
|
|
1552
|
+
const result = overlayCard("omp.overlay.tree", "Session Tree", children);
|
|
1553
|
+
this.#nativeMemo = { content, query, cursor, filterMode, node: result };
|
|
1554
|
+
return result;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/** Filter tab → switch mode; entry click → highlight it and switch, exactly like Enter. */
|
|
1558
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
1559
|
+
const picked = pickerEvent(event, "picker");
|
|
1560
|
+
if (picked) {
|
|
1561
|
+
this.#handlePickerEvent(picked);
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
if (event.type !== "select" && event.type !== "activate") return;
|
|
1565
|
+
if (event.key === "filter") {
|
|
1566
|
+
if (event.type === "select") this.#treeList.setFilterMode(event.item);
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
if (event.key === "list" && !this.#labelInput) this.#treeList.activateEntry(event.item);
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1112
1572
|
#showLabelInput(entryId: string, currentLabel: string | undefined): void {
|
|
1113
1573
|
this.#labelInput = new LabelInput(entryId, currentLabel);
|
|
1114
1574
|
this.#labelInput.onSubmit = (id, label) => {
|