@linxiraos/pi-tui 1.1.26 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -491
- package/README.md +13 -7
- package/dist/types/active-terminal.d.ts +30 -0
- package/dist/types/app-keybindings.d.ts +7 -2
- package/dist/types/apps/cleanse-board.d.ts +1 -1
- package/dist/types/apps/debug/log-viewer.d.ts +4 -0
- package/dist/types/apps/debug/raw-sse-buffer.d.ts +2 -0
- package/dist/types/apps/git/git-tui.d.ts +5 -4
- package/dist/types/apps/git/help.d.ts +3 -1
- package/dist/types/apps/if-bench-board.d.ts +2 -2
- package/dist/types/apps/setup-model-picker.d.ts +1 -1
- package/dist/types/autocomplete.d.ts +5 -2
- package/dist/types/charts/chart-plan.d.ts +674 -0
- package/dist/types/charts/chart-svg.d.ts +59 -0
- package/dist/types/charts/table-data.d.ts +175 -0
- package/dist/types/chat/assistant-message.d.ts +11 -1
- package/dist/types/chat/chat-transcript-builder.d.ts +3 -0
- package/dist/types/chat/compaction-summary-message.d.ts +1 -0
- package/dist/types/chat/execution-shared.d.ts +6 -5
- package/dist/types/chat/fence-figure.d.ts +42 -0
- package/dist/types/chat/figure-markdown.d.ts +42 -0
- package/dist/types/chat/messages.d.ts +1 -1
- package/dist/types/chat/svg-figure.d.ts +33 -0
- package/dist/types/chat/svg-source.d.ts +49 -0
- package/dist/types/chat/table-chart.d.ts +89 -0
- package/dist/types/chat/tool-execution.d.ts +2 -0
- package/dist/types/chat/user-message.d.ts +17 -4
- package/dist/types/chrome/shared.d.ts +9 -0
- package/dist/types/chrome/status-notice.d.ts +15 -3
- package/dist/types/chrome/transcript-container.d.ts +6 -0
- package/dist/types/components/box.d.ts +1 -0
- package/dist/types/components/disclosure.d.ts +1 -0
- package/dist/types/components/editor.d.ts +23 -5
- package/dist/types/components/form.d.ts +1 -0
- package/dist/types/components/image.d.ts +26 -44
- package/dist/types/components/input.d.ts +2 -1
- package/dist/types/components/key-value-list.d.ts +1 -0
- package/dist/types/components/layout/row.d.ts +1 -0
- package/dist/types/components/layout/stack.d.ts +1 -0
- package/dist/types/components/loader.d.ts +5 -6
- package/dist/types/components/markdown.d.ts +47 -5
- package/dist/types/components/scroll-view.d.ts +1 -0
- package/dist/types/components/section.d.ts +1 -0
- package/dist/types/components/table.d.ts +1 -0
- package/dist/types/components/text.d.ts +1 -0
- package/dist/types/components/tree-view.d.ts +7 -0
- package/dist/types/components/truncated-text.d.ts +1 -0
- package/dist/types/fuzzy.d.ts +29 -1
- package/dist/types/key-hint-format.d.ts +9 -1
- package/dist/types/keybinding-matchers.d.ts +3 -0
- package/dist/types/keybindings.d.ts +1 -1
- package/dist/types/kitty-graphics.d.ts +2 -2
- package/dist/types/loop-watchdog.d.ts +8 -7
- package/dist/types/multiplexers/cmux.d.ts +16 -0
- package/dist/types/multiplexers/herdr.d.ts +26 -0
- package/dist/types/multiplexers/notify.d.ts +13 -0
- package/dist/types/multiplexers/orca.d.ts +12 -0
- package/dist/types/multiplexers/screen.d.ts +9 -0
- package/dist/types/multiplexers/tmux.d.ts +29 -0
- package/dist/types/multiplexers/types.d.ts +100 -0
- package/dist/types/multiplexers/wmux.d.ts +12 -0
- package/dist/types/multiplexers/zellij.d.ts +12 -0
- package/dist/types/native/apply.d.ts +4 -3
- package/dist/types/native/backend.d.ts +5 -4
- package/dist/types/native/blobs.d.ts +7 -3
- package/dist/types/native/encode.d.ts +8 -6
- package/dist/types/native/node.d.ts +36 -5
- package/dist/types/native/overlay.d.ts +8 -3
- package/dist/types/native/settle.d.ts +4 -3
- package/dist/types/overlays/agent-hub-projection.d.ts +6 -5
- package/dist/types/overlays/agent-hub-types.d.ts +9 -0
- package/dist/types/overlays/agent-hub.d.ts +3 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +12 -2
- package/dist/types/overlays/agents-hub.d.ts +9 -0
- package/dist/types/overlays/annotation-overlay.d.ts +19 -2
- package/dist/types/overlays/annotation-types.d.ts +2 -0
- package/dist/types/overlays/ask-dialog.d.ts +5 -2
- package/dist/types/overlays/btw-history-panel.d.ts +13 -1
- package/dist/types/overlays/copy-selector.d.ts +1 -0
- package/dist/types/overlays/history-search.d.ts +10 -0
- package/dist/types/overlays/hook-selector.d.ts +9 -4
- package/dist/types/overlays/hub-frame.d.ts +4 -1
- package/dist/types/overlays/jobs-panel.d.ts +1 -7
- package/dist/types/overlays/model-browser.d.ts +54 -7
- package/dist/types/overlays/model-hub.d.ts +37 -0
- package/dist/types/overlays/oauth-selector.d.ts +0 -1
- package/dist/types/overlays/plan-review-overlay.d.ts +3 -2
- package/dist/types/overlays/report-panel.d.ts +68 -0
- package/dist/types/overlays/rewind-selector.d.ts +1 -0
- package/dist/types/overlays/session-observer-registry.d.ts +2 -0
- package/dist/types/overlays/session-selector.d.ts +6 -2
- package/dist/types/overlays/settings-defs.d.ts +1 -0
- package/dist/types/overlays/thinking-selector.d.ts +2 -2
- package/dist/types/overlays/usage-dashboard.d.ts +12 -8
- package/dist/types/overlays/usage-display.d.ts +3 -1
- package/dist/types/prompt/composer-cache.d.ts +0 -16
- package/dist/types/prompt/composer.d.ts +18 -3
- package/dist/types/prompt/custom-editor.d.ts +25 -13
- package/dist/types/prompt/image-source.d.ts +2 -1
- package/dist/types/prompt/interactive-context-helpers.d.ts +5 -1
- package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
- package/dist/types/prompt/welcome.d.ts +7 -2
- package/dist/types/render/composite.d.ts +2 -0
- package/dist/types/render/output-pane.d.ts +1 -0
- package/dist/types/render/render-utils.d.ts +1 -0
- package/dist/types/render/tool-card.d.ts +6 -0
- package/dist/types/render/width-aware-text.d.ts +2 -0
- package/dist/types/setup/scenes/types.d.ts +1 -0
- package/dist/types/space-hold.d.ts +40 -25
- package/dist/types/status-line/component.d.ts +3 -0
- package/dist/types/status-line/context-usage.d.ts +7 -5
- package/dist/types/status-line/host.d.ts +4 -1
- package/dist/types/status-line/segments.d.ts +2 -0
- package/dist/types/status-line/types.d.ts +14 -3
- package/dist/types/terminal-capabilities.d.ts +41 -36
- package/dist/types/terminal-multiplexer.d.ts +127 -14
- package/dist/types/terminal.d.ts +23 -16
- package/dist/types/theme/active-symbols.d.ts +1 -1
- package/dist/types/theme/schema.d.ts +11 -2
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme-class.d.ts +7 -0
- package/dist/types/theme/theme.d.ts +1 -1
- package/dist/types/theme/tui-adapters.d.ts +3 -0
- package/dist/types/thinking.d.ts +1 -1
- package/dist/types/tmux.d.ts +11 -4
- package/dist/types/tools/bash.d.ts +7 -2
- package/dist/types/tools/daemon.d.ts +1 -1
- package/dist/types/tools/eval.d.ts +22 -3
- package/dist/types/tools/glob.d.ts +4 -0
- package/dist/types/tools/native-view.d.ts +47 -0
- package/dist/types/tools/output-meta.d.ts +11 -1
- package/dist/types/tools/renderer.d.ts +37 -2
- package/dist/types/tools/streaming-output.d.ts +20 -1
- package/dist/types/tools/task.d.ts +2 -3
- package/dist/types/tools/write.d.ts +9 -2
- package/dist/types/tools/yield.d.ts +22 -0
- package/dist/types/tui.d.ts +36 -0
- package/dist/types/utils.d.ts +6 -0
- package/dist/types/vim.d.ts +19 -5
- package/dist/types/windows-input-mode.d.ts +33 -1
- package/package.json +13 -9
- package/src/active-terminal.d.ts +30 -0
- package/src/active-terminal.ts +80 -0
- package/src/app-keybindings.d.ts +7 -2
- package/src/app-keybindings.ts +7 -2
- package/src/apps/autoresearch-dashboard.ts +7 -3
- package/src/apps/cleanse-board.ts +2 -2
- package/src/apps/cleanse-picker.ts +1 -1
- package/src/apps/debug/log-viewer.ts +188 -72
- package/src/apps/debug/raw-sse-buffer.ts +8 -0
- package/src/apps/debug/raw-sse.ts +39 -5
- package/src/apps/git/diff-pane.ts +96 -23
- package/src/apps/git/git-tui.ts +23 -16
- package/src/apps/git/help.ts +29 -20
- package/src/apps/git/sidebar.ts +8 -3
- package/src/apps/if-bench-board.ts +2 -2
- package/src/apps/ps-top.ts +2 -2
- package/src/apps/setup-model-picker.ts +1 -1
- package/src/autocomplete.d.ts +5 -2
- package/src/autocomplete.ts +67 -43
- package/src/bracketed-paste.ts +10 -3
- package/src/charts/chart-plan.d.ts +674 -0
- package/src/charts/chart-plan.ts +5081 -0
- package/src/charts/chart-svg.d.ts +59 -0
- package/src/charts/chart-svg.ts +4657 -0
- package/src/charts/table-data.d.ts +175 -0
- package/src/charts/table-data.ts +1478 -0
- package/src/chat/advisor-message.ts +11 -0
- package/src/chat/assistant-message.d.ts +11 -1
- package/src/chat/assistant-message.ts +245 -54
- package/src/chat/chat-transcript-builder.d.ts +3 -0
- package/src/chat/chat-transcript-builder.ts +9 -3
- package/src/chat/compaction-summary-message.d.ts +1 -0
- package/src/chat/compaction-summary-message.ts +8 -0
- package/src/chat/execution-shared.d.ts +6 -5
- package/src/chat/execution-shared.ts +42 -25
- package/src/chat/fence-figure.d.ts +42 -0
- package/src/chat/fence-figure.ts +80 -0
- package/src/chat/figure-markdown.d.ts +42 -0
- package/src/chat/figure-markdown.ts +174 -0
- package/src/chat/messages.d.ts +1 -1
- package/src/chat/messages.ts +1 -1
- package/src/chat/read-tool-group.ts +6 -4
- package/src/chat/skill-message.ts +6 -0
- package/src/chat/svg-figure.d.ts +33 -0
- package/src/chat/svg-figure.ts +399 -0
- package/src/chat/svg-source.d.ts +49 -0
- package/src/chat/svg-source.ts +188 -0
- package/src/chat/table-chart.d.ts +89 -0
- package/src/chat/table-chart.ts +294 -0
- package/src/chat/tool-execution.d.ts +2 -0
- package/src/chat/tool-execution.ts +86 -7
- package/src/chat/user-message.d.ts +17 -4
- package/src/chat/user-message.ts +71 -8
- package/src/chrome/shared.ts +53 -0
- package/src/chrome/status-notice.ts +25 -7
- package/src/chrome/transcript-container.d.ts +6 -0
- package/src/chrome/transcript-container.ts +162 -19
- package/src/components/box.d.ts +1 -0
- package/src/components/box.ts +5 -0
- package/src/components/disclosure.d.ts +1 -0
- package/src/components/disclosure.ts +8 -0
- package/src/components/editor.d.ts +23 -5
- package/src/components/editor.ts +316 -47
- package/src/components/form.d.ts +1 -0
- package/src/components/form.ts +8 -0
- package/src/components/image.d.ts +26 -44
- package/src/components/image.ts +115 -152
- package/src/components/input.d.ts +2 -1
- package/src/components/input.ts +17 -7
- package/src/components/key-value-list.d.ts +1 -0
- package/src/components/key-value-list.ts +4 -0
- package/src/components/layout/row.d.ts +1 -0
- package/src/components/layout/row.ts +7 -0
- package/src/components/layout/stack.d.ts +1 -0
- package/src/components/layout/stack.ts +7 -0
- package/src/components/loader.d.ts +5 -6
- package/src/components/loader.ts +10 -17
- package/src/components/markdown.d.ts +47 -5
- package/src/components/markdown.ts +786 -303
- package/src/components/scroll-view.d.ts +1 -0
- package/src/components/scroll-view.ts +5 -0
- package/src/components/section.d.ts +1 -0
- package/src/components/section.ts +5 -0
- package/src/components/settings-list.ts +2 -0
- package/src/components/table.d.ts +1 -0
- package/src/components/table.ts +4 -0
- package/src/components/text.d.ts +1 -0
- package/src/components/text.ts +7 -0
- package/src/components/tree-view.d.ts +7 -0
- package/src/components/tree-view.ts +38 -8
- package/src/components/truncated-text.d.ts +1 -0
- package/src/components/truncated-text.ts +5 -0
- package/src/desktop-notify.ts +3 -2
- package/src/fuzzy.d.ts +29 -1
- package/src/fuzzy.ts +115 -30
- package/src/hotkeys-markdown.ts +9 -2
- package/src/key-hint-format.d.ts +9 -1
- package/src/key-hint-format.ts +26 -11
- package/src/keybinding-matchers.d.ts +3 -0
- package/src/keybinding-matchers.ts +6 -2
- package/src/keybindings.d.ts +1 -1
- package/src/keybindings.ts +1 -1
- package/src/kitty-graphics.d.ts +2 -2
- package/src/kitty-graphics.ts +11 -5
- package/src/loop-watchdog.d.ts +8 -7
- package/src/loop-watchdog.ts +18 -13
- package/src/multiplexers/cmux.d.ts +16 -0
- package/src/multiplexers/cmux.ts +29 -0
- package/src/multiplexers/herdr.d.ts +26 -0
- package/src/multiplexers/herdr.ts +64 -0
- package/src/multiplexers/notify.d.ts +13 -0
- package/src/multiplexers/notify.ts +24 -0
- package/src/multiplexers/orca.d.ts +12 -0
- package/src/multiplexers/orca.ts +16 -0
- package/src/multiplexers/screen.d.ts +9 -0
- package/src/multiplexers/screen.ts +15 -0
- package/src/multiplexers/tmux.d.ts +29 -0
- package/src/multiplexers/tmux.ts +40 -0
- package/src/multiplexers/types.d.ts +100 -0
- package/src/multiplexers/types.ts +110 -0
- package/src/multiplexers/wmux.d.ts +12 -0
- package/src/multiplexers/wmux.ts +16 -0
- package/src/multiplexers/zellij.d.ts +12 -0
- package/src/multiplexers/zellij.ts +24 -0
- package/src/native/apply.d.ts +4 -3
- package/src/native/apply.ts +4 -3
- package/src/native/backend.d.ts +5 -4
- package/src/native/backend.ts +199 -22
- package/src/native/blobs.d.ts +7 -3
- package/src/native/blobs.ts +47 -12
- package/src/native/encode.d.ts +8 -6
- package/src/native/encode.ts +76 -34
- package/src/native/node.d.ts +36 -5
- package/src/native/node.ts +31 -5
- package/src/native/overlay.d.ts +8 -3
- package/src/native/overlay.ts +17 -4
- package/src/native/reconcile.ts +16 -4
- package/src/native/settle.d.ts +4 -3
- package/src/native/settle.ts +4 -3
- package/src/overlays/agent-hub-projection.d.ts +6 -5
- package/src/overlays/agent-hub-projection.ts +124 -13
- package/src/overlays/agent-hub-types.d.ts +9 -0
- package/src/overlays/agent-hub-types.ts +6 -0
- package/src/overlays/agent-hub.ts +48 -29
- package/src/overlays/agent-transcript-viewer.ts +93 -23
- package/src/overlays/agents-hub.ts +28 -8
- package/src/overlays/annotation-overlay.ts +293 -90
- package/src/overlays/annotation-types.ts +2 -0
- package/src/overlays/ask-dialog.ts +10 -17
- package/src/overlays/btw-history-panel.ts +136 -39
- package/src/overlays/copy-selector.d.ts +1 -0
- package/src/overlays/copy-selector.ts +32 -8
- package/src/overlays/extensions/extension-dashboard.ts +4 -4
- package/src/overlays/extensions/state-manager.ts +52 -12
- package/src/overlays/history-search.ts +100 -7
- package/src/overlays/hook-selector.ts +12 -5
- package/src/overlays/hub-frame.ts +19 -1
- package/src/overlays/jobs-panel.ts +33 -52
- package/src/overlays/model-browser.ts +367 -122
- package/src/overlays/model-hub.ts +435 -59
- package/src/overlays/move-overlay.ts +28 -14
- package/src/overlays/oauth-selector.ts +54 -70
- package/src/overlays/pause-screen.ts +2 -2
- package/src/overlays/plan-review-overlay.ts +208 -70
- package/src/overlays/plugin-settings.ts +3 -3
- package/src/overlays/report-panel.ts +191 -0
- package/src/overlays/rewind-selector.ts +88 -17
- package/src/overlays/session-observer-registry.d.ts +2 -0
- package/src/overlays/session-observer-registry.ts +10 -2
- package/src/overlays/session-selector.ts +64 -26
- package/src/overlays/settings-defs.ts +5 -4
- package/src/overlays/settings-selector.ts +33 -8
- package/src/overlays/snapcompact-shape-preview.ts +2 -3
- package/src/overlays/stats-notice.ts +1 -1
- package/src/overlays/thinking-selector.ts +11 -10
- package/src/overlays/tree-selector.ts +41 -20
- package/src/overlays/usage-dashboard.ts +117 -58
- package/src/overlays/usage-display.ts +10 -4
- package/src/overlays/usage-row.ts +22 -8
- package/src/prompt/attachment-chips.ts +15 -11
- package/src/prompt/composer-attachments.ts +6 -4
- package/src/prompt/composer-cache.ts +7 -86
- package/src/prompt/composer.ts +37 -26
- package/src/prompt/custom-editor.ts +282 -105
- package/src/prompt/image-source.ts +2 -1
- package/src/prompt/interactive-context-helpers.ts +32 -18
- package/src/prompt/macos-spelling.ts +7 -0
- package/src/prompt/magic-keywords.ts +11 -2
- package/src/prompt/model-mention-autocomplete.ts +14 -14
- package/src/prompt/queued-messages.ts +6 -4
- package/src/prompt/tips.txt +13 -9
- package/src/prompt/welcome.ts +64 -46
- package/src/prompt/word-completion.ts +38 -5
- package/src/render/composite.d.ts +2 -0
- package/src/render/composite.ts +64 -0
- package/src/render/output-pane.d.ts +1 -0
- package/src/render/output-pane.ts +6 -0
- package/src/render/render-utils.d.ts +1 -0
- package/src/render/render-utils.ts +16 -1
- package/src/render/tool-card.d.ts +6 -0
- package/src/render/tool-card.ts +15 -0
- package/src/render/width-aware-text.d.ts +2 -0
- package/src/render/width-aware-text.ts +5 -0
- package/src/setup/scenes/model.ts +2 -1
- package/src/setup/scenes/types.ts +1 -0
- package/src/space-hold.d.ts +40 -25
- package/src/space-hold.ts +156 -52
- package/src/status-line/component.ts +292 -78
- package/src/status-line/context-usage.ts +21 -46
- package/src/status-line/host.ts +4 -1
- package/src/status-line/segments.ts +40 -22
- package/src/status-line/types.ts +15 -3
- package/src/terminal-capabilities.d.ts +41 -36
- package/src/terminal-capabilities.ts +137 -213
- package/src/terminal-multiplexer.d.ts +127 -14
- package/src/terminal-multiplexer.ts +112 -33
- package/src/terminal.d.ts +23 -16
- package/src/terminal.ts +203 -99
- package/src/theme/active-symbols.d.ts +1 -1
- package/src/theme/active-symbols.ts +1 -1
- package/src/theme/schema-validation.ts +1 -0
- package/src/theme/schema.d.ts +11 -2
- package/src/theme/schema.ts +14 -1
- package/src/theme/session-color.ts +1 -7
- package/src/theme/symbols.d.ts +1 -1
- package/src/theme/symbols.ts +11 -4
- package/src/theme/theme-class.d.ts +7 -0
- package/src/theme/theme-class.ts +26 -1
- package/src/theme/theme-schema.json +5 -1
- package/src/theme/theme.d.ts +1 -1
- package/src/theme/theme.ts +8 -4
- package/src/theme/tui-adapters.d.ts +3 -0
- package/src/theme/tui-adapters.ts +68 -0
- package/src/thinking.d.ts +1 -1
- package/src/thinking.ts +1 -1
- package/src/tmux.d.ts +11 -4
- package/src/tmux.ts +47 -11
- package/src/tools/ast-edit.ts +59 -35
- package/src/tools/ast-grep.ts +35 -16
- package/src/tools/bash.d.ts +7 -2
- package/src/tools/bash.ts +116 -47
- package/src/tools/daemon.d.ts +1 -1
- package/src/tools/daemon.ts +1 -1
- package/src/tools/default-renderer.ts +35 -19
- package/src/tools/edit.ts +142 -120
- package/src/tools/eval-format/index.ts +28 -2
- package/src/tools/eval-format/javascript.ts +7 -5
- package/src/tools/eval.d.ts +22 -3
- package/src/tools/eval.ts +364 -208
- package/src/tools/fetch.ts +5 -6
- package/src/tools/glob.d.ts +4 -0
- package/src/tools/glob.ts +33 -65
- package/src/tools/grep.ts +81 -48
- package/src/tools/index.ts +2 -0
- package/src/tools/lsp.ts +81 -59
- package/src/tools/mcp.ts +111 -68
- package/src/tools/native-view.d.ts +47 -0
- package/src/tools/native-view.ts +102 -1
- package/src/tools/output-meta.d.ts +11 -1
- package/src/tools/output-meta.ts +23 -3
- package/src/tools/read.ts +7 -5
- package/src/tools/renderer.d.ts +37 -2
- package/src/tools/renderer.ts +42 -2
- package/src/tools/streaming-output.d.ts +20 -1
- package/src/tools/streaming-output.ts +154 -50
- package/src/tools/task.d.ts +2 -3
- package/src/tools/task.ts +115 -62
- package/src/tools/todo.ts +119 -30
- package/src/tools/wait.ts +34 -24
- package/src/tools/web-search.ts +1 -1
- package/src/tools/write.d.ts +9 -2
- package/src/tools/write.ts +147 -95
- package/src/tools/yield.d.ts +22 -0
- package/src/tools/yield.ts +225 -0
- package/src/tui.d.ts +36 -0
- package/src/tui.ts +666 -217
- package/src/utils.d.ts +6 -0
- package/src/utils.ts +19 -0
- package/src/vim.d.ts +19 -5
- package/src/vim.ts +333 -18
- package/src/windows-input-mode.d.ts +33 -1
- package/src/windows-input-mode.ts +212 -6
|
@@ -84,6 +84,7 @@ export interface RewindSelectorDeps {
|
|
|
84
84
|
cwd: string;
|
|
85
85
|
hideThinkingBlock?: () => boolean;
|
|
86
86
|
proseOnlyThinking?: () => boolean;
|
|
87
|
+
expandThinkingBlocks?: () => boolean;
|
|
87
88
|
linkTargets?: ReadonlyMap<string, string>;
|
|
88
89
|
requestRender: () => void;
|
|
89
90
|
/** Sibling branch paths of `entryId`'s turn (excluding the turn itself). */
|
|
@@ -157,6 +158,14 @@ const STRIP_GAP = 2;
|
|
|
157
158
|
/** Duration of the branch-swap camera slide. */
|
|
158
159
|
const SLIDE_MS = 160;
|
|
159
160
|
|
|
161
|
+
/** Same length and identical elements (or both undefined). */
|
|
162
|
+
function sameElements<T>(a: readonly T[] | undefined, b: readonly T[] | undefined): boolean {
|
|
163
|
+
if (a === b) return true;
|
|
164
|
+
if (a === undefined || b === undefined || a.length !== b.length) return false;
|
|
165
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
160
169
|
export class RewindSelectorComponent implements Component {
|
|
161
170
|
#builder: ChatTranscriptBuilder;
|
|
162
171
|
#browser: TranscriptBrowser;
|
|
@@ -195,6 +204,24 @@ export class RewindSelectorComponent implements Component {
|
|
|
195
204
|
#rowText = new WeakMap<readonly string[], string>();
|
|
196
205
|
/** Native filter haystack: lowercased turn text per main target (no rendered rows under TSP). */
|
|
197
206
|
#nativeTexts: { targets: OutlineTarget[]; texts: string[] } | undefined;
|
|
207
|
+
/** Compiled word patterns of the last filter query. */
|
|
208
|
+
#filterPatterns: { query: string; patterns: RegExp[] } | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* Last {@link #filterMatches} result and its inputs. Frames hand back fresh
|
|
211
|
+
* outer `mainRows`/`mainVisible` arrays over cached per-child rows, so those
|
|
212
|
+
* compare element-wise.
|
|
213
|
+
*/
|
|
214
|
+
#filterMemo:
|
|
215
|
+
| {
|
|
216
|
+
query: string;
|
|
217
|
+
targets: OutlineTarget[];
|
|
218
|
+
expanded: boolean;
|
|
219
|
+
native: boolean;
|
|
220
|
+
mainRows: readonly (readonly string[])[];
|
|
221
|
+
mainVisible: readonly boolean[] | undefined;
|
|
222
|
+
matches: readonly number[];
|
|
223
|
+
}
|
|
224
|
+
| undefined;
|
|
198
225
|
/** Last described bar and the state it was built from. */
|
|
199
226
|
#bar: { memo: string; targets: OutlineTarget[]; node: NativeNode } | undefined;
|
|
200
227
|
|
|
@@ -227,6 +254,7 @@ export class RewindSelectorComponent implements Component {
|
|
|
227
254
|
cwd: this.deps.cwd,
|
|
228
255
|
hideThinkingBlock: this.deps.hideThinkingBlock,
|
|
229
256
|
proseOnlyThinking: this.deps.proseOnlyThinking,
|
|
257
|
+
expandThinkingBlocks: this.deps.expandThinkingBlocks,
|
|
230
258
|
linkTargets: this.deps.linkTargets,
|
|
231
259
|
requestRender: this.deps.requestRender,
|
|
232
260
|
});
|
|
@@ -527,14 +555,25 @@ export class RewindSelectorComponent implements Component {
|
|
|
527
555
|
* Matching the rendered rows keeps results honest: collapsed tool output
|
|
528
556
|
* only matches once Ctrl+O expands it.
|
|
529
557
|
*/
|
|
530
|
-
#filterMatches(): number[] {
|
|
531
|
-
const
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
558
|
+
#filterMatches(): readonly number[] {
|
|
559
|
+
const query = this.#filter ?? "";
|
|
560
|
+
const native = isNativeRendering();
|
|
561
|
+
const memo = this.#filterMemo;
|
|
562
|
+
if (
|
|
563
|
+
memo &&
|
|
564
|
+
memo.query === query &&
|
|
565
|
+
memo.targets === this.#targets &&
|
|
566
|
+
memo.expanded === this.#expanded &&
|
|
567
|
+
memo.native === native &&
|
|
568
|
+
sameElements(memo.mainVisible, this.#mainVisible) &&
|
|
569
|
+
(native || sameElements(memo.mainRows, this.#mainRows))
|
|
570
|
+
) {
|
|
571
|
+
memo.mainRows = this.#mainRows;
|
|
572
|
+
memo.mainVisible = this.#mainVisible;
|
|
573
|
+
return memo.matches;
|
|
574
|
+
}
|
|
575
|
+
const patterns = this.#compileFilter(query);
|
|
576
|
+
if (native && this.#nativeTexts?.targets !== this.#targets) {
|
|
538
577
|
this.#nativeTexts = {
|
|
539
578
|
targets: this.#targets,
|
|
540
579
|
// Turn text plus its commands and tool output, like the expanded rendered rows.
|
|
@@ -549,21 +588,53 @@ export class RewindSelectorComponent implements Component {
|
|
|
549
588
|
for (let index = 0; index < this.#targets.length; index++) {
|
|
550
589
|
if (!this.#isMainSelectable(index)) continue;
|
|
551
590
|
const target = this.#targets[index]!;
|
|
552
|
-
const
|
|
553
|
-
?
|
|
554
|
-
:
|
|
555
|
-
let
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
this.#rowText.set(rows, text);
|
|
591
|
+
const matched = native
|
|
592
|
+
? patterns.every(pattern => pattern.test(this.#nativeTexts?.texts[index] ?? ""))
|
|
593
|
+
: patterns.every(pattern => {
|
|
594
|
+
for (let child = target.start; child < target.end; child++) {
|
|
595
|
+
const rows = this.#mainRows[child];
|
|
596
|
+
if (rows !== undefined && pattern.test(this.#plainRowText(rows))) return true;
|
|
559
597
|
}
|
|
560
|
-
return
|
|
598
|
+
return false;
|
|
561
599
|
});
|
|
562
|
-
if (
|
|
600
|
+
if (matched) matches.push(index);
|
|
563
601
|
}
|
|
602
|
+
this.#filterMemo = {
|
|
603
|
+
query,
|
|
604
|
+
targets: this.#targets,
|
|
605
|
+
expanded: this.#expanded,
|
|
606
|
+
native,
|
|
607
|
+
mainRows: this.#mainRows,
|
|
608
|
+
mainVisible: this.#mainVisible,
|
|
609
|
+
matches,
|
|
610
|
+
};
|
|
564
611
|
return matches;
|
|
565
612
|
}
|
|
566
613
|
|
|
614
|
+
/** Word patterns of `query`, compiled once per distinct query. */
|
|
615
|
+
#compileFilter(query: string): RegExp[] {
|
|
616
|
+
const cached = this.#filterPatterns;
|
|
617
|
+
if (cached?.query === query) return cached.patterns;
|
|
618
|
+
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
619
|
+
const patterns = words.map(word =>
|
|
620
|
+
/^[\p{Script=Latin}\p{N}_]+$/u.test(word)
|
|
621
|
+
? new RegExp(`(?<![\\p{L}\\p{N}_])${RegExp.escape(word)}(?![\\p{L}\\p{N}_])`, "u")
|
|
622
|
+
: new RegExp(RegExp.escape(word), "u"),
|
|
623
|
+
);
|
|
624
|
+
this.#filterPatterns = { query, patterns };
|
|
625
|
+
return patterns;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** Lowercased plain text of one rendered child row array, cached by array identity. */
|
|
629
|
+
#plainRowText(rows: readonly string[]): string {
|
|
630
|
+
let text = this.#rowText.get(rows);
|
|
631
|
+
if (text === undefined) {
|
|
632
|
+
text = Bun.stripANSI(rows.join("\n")).toLowerCase();
|
|
633
|
+
this.#rowText.set(rows, text);
|
|
634
|
+
}
|
|
635
|
+
return text;
|
|
636
|
+
}
|
|
637
|
+
|
|
567
638
|
/** Up/Down: step within the active column; leaving a sibling column's top exits the strip. */
|
|
568
639
|
#moveVertical(delta: -1 | 1): void {
|
|
569
640
|
if (this.#activeVariant > 0) {
|
|
@@ -58,6 +58,8 @@ export interface ObservableSession {
|
|
|
58
58
|
lastUpdate: number;
|
|
59
59
|
/** Latest progress snapshot from the subagent executor */
|
|
60
60
|
progress?: AgentProgress;
|
|
61
|
+
/** `Date.now()` when {@link progress} arrived; its `durationMs` was current then. */
|
|
62
|
+
progressAt?: number;
|
|
61
63
|
}
|
|
62
64
|
/** Coarse source of an observer change; callers use it to separate lifecycle work from high-frequency progress. */
|
|
63
65
|
export type SessionObserverChangeKind = "main" | "reset" | "lifecycle" | "progress";
|
|
@@ -64,6 +64,8 @@ export interface ObservableSession {
|
|
|
64
64
|
lastUpdate: number;
|
|
65
65
|
/** Latest progress snapshot from the subagent executor */
|
|
66
66
|
progress?: AgentProgress;
|
|
67
|
+
/** `Date.now()` when {@link progress} arrived; its `durationMs` was current then. */
|
|
68
|
+
progressAt?: number;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
/** Coarse source of an observer change; callers use it to separate lifecycle work from high-frequency progress. */
|
|
@@ -225,6 +227,9 @@ export class SessionObserverRegistry {
|
|
|
225
227
|
existing.detached = payload.detached ?? existing.detached;
|
|
226
228
|
if (payload.description) existing.description = payload.description;
|
|
227
229
|
if (payload.sessionFile) existing.sessionFile = payload.sessionFile;
|
|
230
|
+
// A kept-alive or parked agent's next turn restarts its clock; the prior turn's
|
|
231
|
+
// snapshot must not be aged across the idle gap before the new turn's first frame.
|
|
232
|
+
if (payload.status === "started") existing.progressAt = undefined;
|
|
228
233
|
} else {
|
|
229
234
|
this.#sessions.set(payload.id, {
|
|
230
235
|
id: payload.id,
|
|
@@ -256,12 +261,14 @@ export class SessionObserverRegistry {
|
|
|
256
261
|
|
|
257
262
|
const sortOrder = this.#ensureSortOrder(id);
|
|
258
263
|
this.#ensureParentSortOrder(payload.parentToolCallId, sortOrder);
|
|
264
|
+
const now = Date.now();
|
|
259
265
|
if (existing) {
|
|
260
|
-
existing.lastUpdate =
|
|
266
|
+
existing.lastUpdate = now;
|
|
261
267
|
existing.index = payload.index;
|
|
262
268
|
existing.parentToolCallId = payload.parentToolCallId ?? existing.parentToolCallId;
|
|
263
269
|
existing.detached = payload.detached ?? existing.detached;
|
|
264
270
|
existing.progress = progress;
|
|
271
|
+
existing.progressAt = now;
|
|
265
272
|
if (progress.description) existing.description = progress.description;
|
|
266
273
|
if (payload.sessionFile) existing.sessionFile = payload.sessionFile;
|
|
267
274
|
} else {
|
|
@@ -276,8 +283,9 @@ export class SessionObserverRegistry {
|
|
|
276
283
|
parentToolCallId: payload.parentToolCallId,
|
|
277
284
|
detached: payload.detached,
|
|
278
285
|
index: payload.index,
|
|
279
|
-
lastUpdate:
|
|
286
|
+
lastUpdate: now,
|
|
280
287
|
progress,
|
|
288
|
+
progressAt: now,
|
|
281
289
|
});
|
|
282
290
|
}
|
|
283
291
|
this.#notifyListeners("progress");
|
|
@@ -179,9 +179,13 @@ function sessionPreview(session: SessionSelectorEntry, forkedFrom: string | unde
|
|
|
179
179
|
]);
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
/** Absolute dates of week-old sessions; `toLocaleDateString` goes through Intl on every call. */
|
|
183
|
+
const localeDateCache = new WeakMap<Date, { time: number; text: string }>();
|
|
184
|
+
|
|
182
185
|
/** Relative age of a session's last modification (`"3 hours ago"`), falling back to the date after a week. */
|
|
183
186
|
function formatSessionDate(date: Date): string {
|
|
184
|
-
const
|
|
187
|
+
const time = date.getTime();
|
|
188
|
+
const diffMs = Date.now() - time;
|
|
185
189
|
const diffMins = Math.floor(diffMs / 60000);
|
|
186
190
|
const diffHours = Math.floor(diffMs / 3600000);
|
|
187
191
|
const diffDays = Math.floor(diffMs / 86400000);
|
|
@@ -192,7 +196,11 @@ function formatSessionDate(date: Date): string {
|
|
|
192
196
|
if (diffDays === 1) return "1 day ago";
|
|
193
197
|
if (diffDays < 7) return `${diffDays} days ago`;
|
|
194
198
|
|
|
195
|
-
|
|
199
|
+
const cached = localeDateCache.get(date);
|
|
200
|
+
if (cached?.time === time) return cached.text;
|
|
201
|
+
const text = date.toLocaleDateString();
|
|
202
|
+
localeDateCache.set(date, { time, text });
|
|
203
|
+
return text;
|
|
196
204
|
}
|
|
197
205
|
|
|
198
206
|
/** A cached native session item and the inputs it was built from. */
|
|
@@ -517,12 +525,12 @@ class SessionList<T extends SessionSelectorEntry> implements Component {
|
|
|
517
525
|
readonly #getCurrentSessionPath: () => string | undefined;
|
|
518
526
|
readonly #historyMatcher?: SessionHistoryMatcher;
|
|
519
527
|
#historyMergeTimer: NodeJS.Timeout | undefined;
|
|
520
|
-
/** Re-render hook for async list updates (fuzzy scan
|
|
528
|
+
/** Re-render hook for async list updates (fuzzy scan completion, history merge). */
|
|
521
529
|
onRequestRender?: () => void;
|
|
522
530
|
|
|
523
531
|
// ── Incremental search state ──────────────────────────────────────────
|
|
524
532
|
// The menu's visible list is always composed from these three inputs (see
|
|
525
|
-
// #composeFiltered), so late-
|
|
533
|
+
// #composeFiltered), so a late-finishing fuzzy scan and the debounced
|
|
526
534
|
// history merge can land in any order without clobbering each other.
|
|
527
535
|
/** Recency-ranked sessions whose text contains every query token verbatim. */
|
|
528
536
|
#literalRanked: RankedSessionMatch<T>[] = [];
|
|
@@ -535,8 +543,8 @@ class SessionList<T extends SessionSelectorEntry> implements Component {
|
|
|
535
543
|
#scanTimer: NodeJS.Timeout | undefined;
|
|
536
544
|
/**
|
|
537
545
|
* True once the user moved the selection for the current query; blocks the
|
|
538
|
-
* history merge from reordering the list under their cursor. (
|
|
539
|
-
* only
|
|
546
|
+
* history merge from reordering the list under their cursor. (The fuzzy scan
|
|
547
|
+
* only appends below the literal group, which never shifts existing rows.)
|
|
540
548
|
*/
|
|
541
549
|
#selectionMoved = false;
|
|
542
550
|
/** True after a nonempty query; empty refilter restores current only then. */
|
|
@@ -545,6 +553,8 @@ class SessionList<T extends SessionSelectorEntry> implements Component {
|
|
|
545
553
|
#lastFilterQuery = "";
|
|
546
554
|
/** Bumped whenever the visible session set may have changed (native memo key). */
|
|
547
555
|
#itemsVersion = 0;
|
|
556
|
+
/** Per-row line heights of the visible list for the ANSI window, reused until the set changes. */
|
|
557
|
+
#rowHeights: { items: readonly T[]; length: number; version: number; heights: readonly number[] } | undefined;
|
|
548
558
|
#itemsNative:
|
|
549
559
|
| { version: number; showCwd: boolean; currentPath: string | undefined; items: NativeNode[] }
|
|
550
560
|
| undefined;
|
|
@@ -677,38 +687,48 @@ class SessionList<T extends SessionSelectorEntry> implements Component {
|
|
|
677
687
|
// Fuzzy pass: building a fuzzy index per session is too expensive to run
|
|
678
688
|
// across a huge listing inside one keystroke, so scan a bounded slice now
|
|
679
689
|
// and spill the remainder into async chunks.
|
|
680
|
-
|
|
690
|
+
const inlineEnd = Math.min(rest.length, FUZZY_SCAN_INLINE_COUNT);
|
|
691
|
+
this.#scoreFuzzy(tokens, rest, 0, inlineEnd);
|
|
681
692
|
this.#composeFiltered();
|
|
693
|
+
if (inlineEnd < rest.length) {
|
|
694
|
+
this.#scheduleFuzzyScan(this.#scanGeneration, tokens, rest, inlineEnd, this.#fuzzyRanked.length);
|
|
695
|
+
}
|
|
682
696
|
// New query rebuilds ranking from scratch. Same-query refilter (delete)
|
|
683
|
-
// and async compose (fuzzy
|
|
697
|
+
// and async compose (fuzzy scan / history merge) only clamp so an
|
|
684
698
|
// arrow selection survives. A live-session index > 0 would otherwise
|
|
685
699
|
// land on a lower-ranked match after the first keystroke.
|
|
686
700
|
if (queryChanged) this.#menu.setSelectedIndex(0);
|
|
687
701
|
this.#scheduleHistoryMerge(query);
|
|
688
702
|
}
|
|
689
703
|
|
|
690
|
-
/**
|
|
691
|
-
|
|
692
|
-
* unfiltered list), then schedule the remainder on a macrotask so pending
|
|
693
|
-
* input events run first. Chunks that added matches recompose the visible
|
|
694
|
-
* list and request a render; a stale generation aborts silently.
|
|
695
|
-
*/
|
|
696
|
-
#scanFuzzySlice(generation: number, tokens: string[], rest: number[], start: number, budget: number): void {
|
|
704
|
+
/** Fuzzy-score `rest[start..end)` (indexes into the unfiltered list) into {@link #fuzzyRanked}. */
|
|
705
|
+
#scoreFuzzy(tokens: string[], rest: number[], start: number, end: number): void {
|
|
697
706
|
const all = this.#allSessions;
|
|
698
|
-
const end = Math.min(rest.length, start + budget);
|
|
699
707
|
for (let i = start; i < end; i++) {
|
|
700
708
|
const index = rest[i]!;
|
|
701
709
|
const session = all[index]!;
|
|
702
710
|
const match = scoreFuzzySession(session, index, tokens, new FuzzyText(sessionTextLower(session)));
|
|
703
711
|
if (match) this.#fuzzyRanked.push(match);
|
|
704
712
|
}
|
|
705
|
-
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Score `rest[start..]` in {@link FUZZY_SCAN_CHUNK_COUNT}-session macrotask
|
|
717
|
+
* chunks so pending input runs between them, then recompose the visible
|
|
718
|
+
* list once if the scan matched more than the `shown` fuzzy hits already
|
|
719
|
+
* composed. Publishing per chunk reorders the list dozens of times per
|
|
720
|
+
* keystroke on a large listing, and the native picker replays its
|
|
721
|
+
* row-arrival animation on every reorder. A stale generation aborts silently.
|
|
722
|
+
*/
|
|
723
|
+
#scheduleFuzzyScan(generation: number, tokens: string[], rest: number[], start: number, shown: number): void {
|
|
706
724
|
this.#scanTimer = setTimeout(() => {
|
|
707
725
|
this.#scanTimer = undefined;
|
|
708
726
|
if (generation !== this.#scanGeneration) return;
|
|
709
|
-
const
|
|
710
|
-
this.#
|
|
711
|
-
if (
|
|
727
|
+
const end = Math.min(rest.length, start + FUZZY_SCAN_CHUNK_COUNT);
|
|
728
|
+
this.#scoreFuzzy(tokens, rest, start, end);
|
|
729
|
+
if (end < rest.length) {
|
|
730
|
+
this.#scheduleFuzzyScan(generation, tokens, rest, end, shown);
|
|
731
|
+
} else if (this.#fuzzyRanked.length > shown) {
|
|
712
732
|
this.#composeFiltered();
|
|
713
733
|
this.onRequestRender?.();
|
|
714
734
|
}
|
|
@@ -730,7 +750,7 @@ class SessionList<T extends SessionSelectorEntry> implements Component {
|
|
|
730
750
|
tokenizeSessionQuery(this.#searchInput.getValue()),
|
|
731
751
|
this.#literalRanked,
|
|
732
752
|
);
|
|
733
|
-
//
|
|
753
|
+
// The fuzzy scan and the history merge only clamp the cursor so an arrow
|
|
734
754
|
// selection survives recomposition; the query path resets explicitly.
|
|
735
755
|
const keepIndex = Math.min(this.#menu.selectedIndex, Math.max(0, composed.length - 1));
|
|
736
756
|
this.#menu.setItems(composed, keepIndex >= 0 ? composed[keepIndex]?.path : undefined);
|
|
@@ -1130,14 +1150,13 @@ class SessionList<T extends SessionSelectorEntry> implements Component {
|
|
|
1130
1150
|
// worst-case count-based window would leave (then padded by
|
|
1131
1151
|
// fill-height).
|
|
1132
1152
|
const filtered = this.#menu.visibleItems;
|
|
1133
|
-
const itemHeight = (session: T): number => (session.title ? 4 : 3);
|
|
1134
1153
|
const budget = this.#lineBudget();
|
|
1135
1154
|
const {
|
|
1136
1155
|
startIndex,
|
|
1137
1156
|
endIndex,
|
|
1138
1157
|
rowOffset: offsetRows,
|
|
1139
1158
|
totalRows: rawTotalRows,
|
|
1140
|
-
} = getMenuWindow(filtered
|
|
1159
|
+
} = getMenuWindow(this.#visibleRowHeights(filtered), this.#menu.selectedIndex, budget);
|
|
1141
1160
|
|
|
1142
1161
|
// Each session block is built into sessionLines, then wrapped by ScrollView
|
|
1143
1162
|
// so the right-edge scrollbar is proportional at the physical-line level.
|
|
@@ -1232,6 +1251,17 @@ class SessionList<T extends SessionSelectorEntry> implements Component {
|
|
|
1232
1251
|
return lines;
|
|
1233
1252
|
}
|
|
1234
1253
|
|
|
1254
|
+
/** Line height per visible session (3, or 4 when a title adds a preview line), memoized per visible set. */
|
|
1255
|
+
#visibleRowHeights(items: readonly T[]): readonly number[] {
|
|
1256
|
+
const cached = this.#rowHeights;
|
|
1257
|
+
if (cached?.items === items && cached.length === items.length && cached.version === this.#itemsVersion) {
|
|
1258
|
+
return cached.heights;
|
|
1259
|
+
}
|
|
1260
|
+
const heights = items.map(session => (session.title ? 4 : 3));
|
|
1261
|
+
this.#rowHeights = { items, length: items.length, version: this.#itemsVersion, heights };
|
|
1262
|
+
return heights;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1235
1265
|
handleInput(keyData: string): void {
|
|
1236
1266
|
// Delete key — or Backspace on an empty search query — request delete
|
|
1237
1267
|
// confirmation from the parent. macOS laptops have no dedicated Forward
|
|
@@ -1310,7 +1340,11 @@ export interface SessionSelectorOptions<T extends SessionSelectorEntry = Session
|
|
|
1310
1340
|
title?: string;
|
|
1311
1341
|
/** Fixed scope label, or false to omit the scope suffix. */
|
|
1312
1342
|
scopeLabel?: string | false;
|
|
1313
|
-
/**
|
|
1343
|
+
/**
|
|
1344
|
+
* Show each session's working directory in the list. Defaults to on when
|
|
1345
|
+
* the session files live in more than one directory (e.g. the folder scope
|
|
1346
|
+
* merging a repository's worktrees).
|
|
1347
|
+
*/
|
|
1314
1348
|
showCwd?: boolean;
|
|
1315
1349
|
/**
|
|
1316
1350
|
* Reads the live terminal height so the visible window fits the viewport.
|
|
@@ -1354,6 +1388,7 @@ export class SessionSelectorComponent<T extends SessionSelectorEntry = SessionSe
|
|
|
1354
1388
|
#onRequestRender?: () => void;
|
|
1355
1389
|
readonly #loadAllSessions?: () => Promise<T[]>;
|
|
1356
1390
|
#folderSessions: T[];
|
|
1391
|
+
readonly #folderShowCwd: boolean;
|
|
1357
1392
|
#globalSessions: T[] | null = null;
|
|
1358
1393
|
#scope: "folder" | "all" = "folder";
|
|
1359
1394
|
#toggling = false;
|
|
@@ -1420,6 +1455,9 @@ export class SessionSelectorComponent<T extends SessionSelectorEntry = SessionSe
|
|
|
1420
1455
|
this.#onDelete = options.onDelete;
|
|
1421
1456
|
this.#loadAllSessions = options.loadAllSessions;
|
|
1422
1457
|
this.#folderSessions = sessions;
|
|
1458
|
+
// Storage directory, not recorded cwd: one folder's sessions may record
|
|
1459
|
+
// symlink aliases of the same path, which must not turn the column on.
|
|
1460
|
+
this.#folderShowCwd = options.showCwd ?? new Set(sessions.map(session => path.dirname(session.path))).size > 1;
|
|
1423
1461
|
this.#globalSessions = options.allSessions ?? null;
|
|
1424
1462
|
this.#getTerminalRows = options.getTerminalRows ?? (() => 24);
|
|
1425
1463
|
this.#fillHeight = options.fillHeight ?? false;
|
|
@@ -1437,7 +1475,7 @@ export class SessionSelectorComponent<T extends SessionSelectorEntry = SessionSe
|
|
|
1437
1475
|
// projects' history (issue #3099).
|
|
1438
1476
|
this.#sessionList = new SessionList(
|
|
1439
1477
|
sessions,
|
|
1440
|
-
|
|
1478
|
+
this.#folderShowCwd,
|
|
1441
1479
|
options.historyMatcher,
|
|
1442
1480
|
options.getTerminalRows,
|
|
1443
1481
|
options.pinnedIds,
|
|
@@ -1510,7 +1548,7 @@ export class SessionSelectorComponent<T extends SessionSelectorEntry = SessionSe
|
|
|
1510
1548
|
this.#sessionList.setSessions(global, true);
|
|
1511
1549
|
} else {
|
|
1512
1550
|
this.#scope = "folder";
|
|
1513
|
-
this.#sessionList.setSessions(this.#folderSessions,
|
|
1551
|
+
this.#sessionList.setSessions(this.#folderSessions, this.#folderShowCwd);
|
|
1514
1552
|
}
|
|
1515
1553
|
this.title = this.#headerLabel();
|
|
1516
1554
|
this.#onRequestRender?.();
|
|
@@ -133,7 +133,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
|
|
|
133
133
|
],
|
|
134
134
|
context: ["General", "Compaction", "Zeta Context Cache", "Rules (TTSR)", "Experimental"],
|
|
135
135
|
memory: ["General", "Auto-Learn", "Mnemopi", "Hindsight", "Sharpshooter"],
|
|
136
|
-
files: ["Editing", "Reading", "Read Summaries", "LSP"],
|
|
136
|
+
files: ["Editing", "Reading", "Read Summaries", "LSP", "Session Trash"],
|
|
137
137
|
shell: ["Bash", "Eval & Runtimes"],
|
|
138
138
|
tools: [
|
|
139
139
|
"Available Tools",
|
|
@@ -180,6 +180,7 @@ export type AnyUiMetadata = UiBase & {
|
|
|
180
180
|
options?: ReadonlyArray<SubmenuOption> | "runtime";
|
|
181
181
|
secret?: boolean;
|
|
182
182
|
ordered?: boolean;
|
|
183
|
+
editor?: "json";
|
|
183
184
|
};
|
|
184
185
|
|
|
185
186
|
/** Structural schema entries supplied by the application host. */
|
|
@@ -330,9 +331,9 @@ function entryToSettingDef(entry: SettingsDisplayEntry): SettingDef | null {
|
|
|
330
331
|
}
|
|
331
332
|
|
|
332
333
|
if (schemaType === "array") {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (
|
|
334
|
+
if (ui.editor === "json") return { ...base, type: "text", secret: entry.credential === true };
|
|
335
|
+
if (!options) return null;
|
|
336
|
+
if (options === "runtime") return null;
|
|
336
337
|
return { ...base, type: "multiselect", options, ordered: ui.ordered === true };
|
|
337
338
|
}
|
|
338
339
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@linxiraos/pi-agent-core";
|
|
2
2
|
import type { Effort } from "@linxiraos/pi-ai";
|
|
3
|
+
import { CLI_BIN_NAME } from "@linxiraos/pi-utils";
|
|
3
4
|
import {
|
|
4
5
|
type Component,
|
|
5
6
|
Container,
|
|
6
7
|
extractPrintableText,
|
|
7
|
-
|
|
8
|
+
FuzzyCorpus,
|
|
8
9
|
getKeybindings,
|
|
9
10
|
getSettingItemFilterText,
|
|
10
11
|
type ImageBudget,
|
|
@@ -630,6 +631,12 @@ export class SettingsSelectorComponent implements Component {
|
|
|
630
631
|
#searchMatchCount = 0;
|
|
631
632
|
/** First matching item id per tab id, for Tab-key jumps while searching. */
|
|
632
633
|
#searchFirstMatch = new Map<string, string>();
|
|
634
|
+
/**
|
|
635
|
+
* Per-tab search candidates prepared once per search session. Dropped on
|
|
636
|
+
* search start/end and whenever a value or condition may have changed
|
|
637
|
+
* (`#refreshItems`, `#onSearchSettingChange`); rebuilt lazily by `#setSearchQuery`.
|
|
638
|
+
*/
|
|
639
|
+
#searchCorpora: { tab: SettingTab; corpus: FuzzyCorpus<SettingItem> }[] | null = null;
|
|
633
640
|
#textInputActive = false;
|
|
634
641
|
#hasSectionJump = false;
|
|
635
642
|
// Frame geometry from the last render, for mouse hit-testing (the
|
|
@@ -872,7 +879,7 @@ export class SettingsSelectorComponent implements Component {
|
|
|
872
879
|
}
|
|
873
880
|
|
|
874
881
|
const props: TspPrefsProps = {
|
|
875
|
-
title:
|
|
882
|
+
title: `${CLI_BIN_NAME} settings`,
|
|
876
883
|
pages,
|
|
877
884
|
page: searching ? this.#preSearchTabId : this.#currentTabId,
|
|
878
885
|
lead: searching ? undefined : (pluginPage?.lead ?? (tab ? TAB_LEADS[tab] : undefined)),
|
|
@@ -1111,8 +1118,10 @@ export class SettingsSelectorComponent implements Component {
|
|
|
1111
1118
|
|
|
1112
1119
|
/** Rebuilds the visible items after a change made outside a list's own `onChange` (changed flags, conditions). */
|
|
1113
1120
|
#refreshItems(): void {
|
|
1114
|
-
if (this.#searchList)
|
|
1115
|
-
|
|
1121
|
+
if (this.#searchList) {
|
|
1122
|
+
this.#searchCorpora = null;
|
|
1123
|
+
this.#setSearchQuery(this.#searchQuery);
|
|
1124
|
+
} else if (this.#currentTabId !== "plugins")
|
|
1116
1125
|
this.#refreshCurrentTabItems(getSettingsForTab(this.#context.settings.entries, this.#currentTabId));
|
|
1117
1126
|
}
|
|
1118
1127
|
|
|
@@ -1281,6 +1290,7 @@ export class SettingsSelectorComponent implements Component {
|
|
|
1281
1290
|
/** Swap the tab content for the global search result list. */
|
|
1282
1291
|
#startSearch(initialQuery: string): void {
|
|
1283
1292
|
this.#preSearchTabId = this.#currentTabId;
|
|
1293
|
+
this.#searchCorpora = null;
|
|
1284
1294
|
this.#searchInput = new Input();
|
|
1285
1295
|
this.#searchInput.prompt = "";
|
|
1286
1296
|
this.#searchInput.setValue(initialQuery);
|
|
@@ -1324,13 +1334,17 @@ export class SettingsSelectorComponent implements Component {
|
|
|
1324
1334
|
const tabResults: { tab: SettingTab; matched: SettingItem[]; bestScore: number; order: number }[] = [];
|
|
1325
1335
|
this.#searchFirstMatch.clear();
|
|
1326
1336
|
let total = 0;
|
|
1327
|
-
|
|
1337
|
+
this.#searchCorpora ??= SETTING_TABS.map(tab => {
|
|
1328
1338
|
const candidates: SettingItem[] = [];
|
|
1329
1339
|
for (const def of getSettingsForTab(this.#context.settings.entries, tab)) {
|
|
1330
1340
|
const item = this.#defToItem(def);
|
|
1331
1341
|
if (item) candidates.push(item);
|
|
1332
1342
|
}
|
|
1333
|
-
|
|
1343
|
+
return { tab, corpus: new FuzzyCorpus(candidates, getSettingItemFilterText) };
|
|
1344
|
+
});
|
|
1345
|
+
for (let order = 0; order < this.#searchCorpora.length; order++) {
|
|
1346
|
+
const { tab, corpus } = this.#searchCorpora[order]!;
|
|
1347
|
+
const ranked = corpus.rank(query);
|
|
1334
1348
|
const matched = ranked.map(result => result.item);
|
|
1335
1349
|
counts.set(tab, matched.length);
|
|
1336
1350
|
if (matched.length === 0) continue;
|
|
@@ -1339,7 +1353,7 @@ export class SettingsSelectorComponent implements Component {
|
|
|
1339
1353
|
tab,
|
|
1340
1354
|
matched,
|
|
1341
1355
|
bestScore: ranked[0]?.score ?? 0,
|
|
1342
|
-
order
|
|
1356
|
+
order,
|
|
1343
1357
|
});
|
|
1344
1358
|
}
|
|
1345
1359
|
|
|
@@ -1378,6 +1392,7 @@ export class SettingsSelectorComponent implements Component {
|
|
|
1378
1392
|
const targetTab: SettingTab | "plugins" = selectedDef?.tab ?? this.#preSearchTabId;
|
|
1379
1393
|
|
|
1380
1394
|
this.#searchQuery = "";
|
|
1395
|
+
this.#searchCorpora = null;
|
|
1381
1396
|
this.#searchFirstMatch.clear();
|
|
1382
1397
|
this.#searchMatchCount = 0;
|
|
1383
1398
|
this.#tabs = getSettingsTabs();
|
|
@@ -1441,6 +1456,7 @@ export class SettingsSelectorComponent implements Component {
|
|
|
1441
1456
|
}
|
|
1442
1457
|
// Values feed the searchable text and condition gates may have flipped:
|
|
1443
1458
|
// recompute results in place (selection is preserved by item id).
|
|
1459
|
+
this.#searchCorpora = null;
|
|
1444
1460
|
this.#setSearchQuery(this.#searchQuery);
|
|
1445
1461
|
}
|
|
1446
1462
|
|
|
@@ -1648,7 +1664,7 @@ export class SettingsSelectorComponent implements Component {
|
|
|
1648
1664
|
}
|
|
1649
1665
|
|
|
1650
1666
|
/**
|
|
1651
|
-
* Create a text input submenu for a
|
|
1667
|
+
* Create a text input submenu for a string or structured setting.
|
|
1652
1668
|
*/
|
|
1653
1669
|
#createTextInput(
|
|
1654
1670
|
def: SettingDef & { type: "text" },
|
|
@@ -1751,6 +1767,15 @@ export class SettingsSelectorComponent implements Component {
|
|
|
1751
1767
|
this.#context.settings.set(path, -1);
|
|
1752
1768
|
} else if (path === "compaction.thresholdTokens" && value === "default") {
|
|
1753
1769
|
this.#context.settings.set(path, -1);
|
|
1770
|
+
} else if (schemaType === "array") {
|
|
1771
|
+
let parsed: unknown;
|
|
1772
|
+
try {
|
|
1773
|
+
parsed = JSON.parse(value);
|
|
1774
|
+
} catch {
|
|
1775
|
+
throw new Error(`Invalid array JSON for ${path}`);
|
|
1776
|
+
}
|
|
1777
|
+
if (!Array.isArray(parsed)) throw new Error(`Invalid array JSON for ${path}`);
|
|
1778
|
+
this.#context.settings.set(path, parsed);
|
|
1754
1779
|
} else if (schemaType === "record") {
|
|
1755
1780
|
let parsed: unknown;
|
|
1756
1781
|
try {
|
|
@@ -31,7 +31,7 @@ import { theme } from "../theme/theme";
|
|
|
31
31
|
import sampleDoc from "./snapcompact-shape-preview-doc.md" with { type: "text" };
|
|
32
32
|
import type { DescribeContext, NativeNode } from "../native/node";
|
|
33
33
|
import { col, node, span, text } from "../native/describe";
|
|
34
|
-
import {
|
|
34
|
+
import { nativeImageNode } from "../native/blobs";
|
|
35
35
|
|
|
36
36
|
/** Mini-frame edge in px — a small page from the real rasterizer ≈ a zoomed crop. */
|
|
37
37
|
const SRC_FRAME_PX = 128;
|
|
@@ -100,8 +100,7 @@ export class SnapcompactShapePreview implements Component {
|
|
|
100
100
|
else if (entry.state === "rendering") sample = node("spinner", { label: [span("rendering sample…", "dim")] });
|
|
101
101
|
else if (entry.state === "failed") sample = text([span("(sample render failed)", "dim")]);
|
|
102
102
|
else {
|
|
103
|
-
sample =
|
|
104
|
-
blob: registerNativeBlob(entry.bytes, "image/png"),
|
|
103
|
+
sample = nativeImageNode(entry.bytes, "image/png", {
|
|
105
104
|
alt: `snapcompact ${label} sample`,
|
|
106
105
|
w: entry.edgePx,
|
|
107
106
|
h: entry.edgePx,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `/stats` result:
|
|
2
|
+
* `/stats` result: zetacode serves the stats dashboard in the browser. ANSI keeps
|
|
3
3
|
* the dim status line; natively it is an inline notice with an
|
|
4
4
|
* `Open dashboard ↗` button that opens the URL terminal-side.
|
|
5
5
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { AUTO_THINKING, type ConfiguredThinkingLevel, getConfiguredThinkingLevelMetadata } from "../thinking";
|
|
1
2
|
import type { Effort } from "@linxiraos/pi-ai";
|
|
2
3
|
import { tuiText } from "../i18n";
|
|
3
4
|
import { type SelectItem, SelectList, type SgrMouseEvent } from "../index";
|
|
4
5
|
import { getSelectListTheme } from "../theme/theme";
|
|
5
|
-
import { getThinkingLevelMetadata } from "../thinking";
|
|
6
6
|
import { OverlayPanel } from "../chrome/overlay-box";
|
|
7
7
|
import { routeSelectListMouseWithTopBorder } from "../chrome/select-list-mouse-routing";
|
|
8
8
|
import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node";
|
|
@@ -16,14 +16,14 @@ export class ThinkingSelectorComponent extends OverlayPanel {
|
|
|
16
16
|
#sheet: SelectListSheet;
|
|
17
17
|
|
|
18
18
|
constructor(
|
|
19
|
-
currentLevel:
|
|
20
|
-
availableLevels:
|
|
21
|
-
onSelect: (level:
|
|
19
|
+
currentLevel: ConfiguredThinkingLevel | undefined,
|
|
20
|
+
availableLevels: ConfiguredThinkingLevel[],
|
|
21
|
+
onSelect: (level: ConfiguredThinkingLevel) => void,
|
|
22
22
|
onCancel: () => void,
|
|
23
23
|
) {
|
|
24
24
|
super(tuiText("thinkingSelectorTitle", "Thinking Level"), "zeta.overlay.thinking");
|
|
25
25
|
|
|
26
|
-
const thinkingLevels: SelectItem[] = availableLevels.map(
|
|
26
|
+
const thinkingLevels: SelectItem[] = availableLevels.map(getConfiguredThinkingLevelMetadata);
|
|
27
27
|
|
|
28
28
|
// Create selector
|
|
29
29
|
this.#selectList = new SelectList(thinkingLevels, thinkingLevels.length, getSelectListTheme());
|
|
@@ -35,7 +35,7 @@ export class ThinkingSelectorComponent extends OverlayPanel {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
this.#selectList.onSelect = item => {
|
|
38
|
-
onSelect(item.value as
|
|
38
|
+
onSelect(item.value as ConfiguredThinkingLevel);
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
this.#selectList.onCancel = () => {
|
|
@@ -47,10 +47,11 @@ export class ThinkingSelectorComponent extends OverlayPanel {
|
|
|
47
47
|
title: "Thinking level",
|
|
48
48
|
icon: "brain",
|
|
49
49
|
noun: "levels",
|
|
50
|
-
current: [currentLevel],
|
|
51
|
-
decorate: item =>
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
current: currentLevel ? [currentLevel] : [],
|
|
51
|
+
decorate: item =>
|
|
52
|
+
item.value === AUTO_THINKING
|
|
53
|
+
? {}
|
|
54
|
+
: { chips: [{ text: "", dot: `thinking${item.value.charAt(0).toUpperCase()}${item.value.slice(1)}` }] },
|
|
54
55
|
});
|
|
55
56
|
}
|
|
56
57
|
|