@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
package/src/app-keybindings.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
// Subpaths, not the `@linxiraos/pi-utils` barrel: the barrel loads the native
|
|
4
4
|
// addon (file-lock), and key-hint formatting runs on addon-free CLI paths
|
|
5
|
-
// (`
|
|
5
|
+
// (`zetacode --version`, help) through cli/command-help.ts.
|
|
6
6
|
import { getActiveProfile, getAgentDir, getProfileRootDir } from "@linxiraos/pi-utils/dirs";
|
|
7
7
|
import { isEnoent } from "@linxiraos/pi-utils/fs-error";
|
|
8
8
|
import * as logger from "@linxiraos/pi-utils/logger";
|
|
@@ -60,6 +60,7 @@ interface AppKeybindings {
|
|
|
60
60
|
"app.tree.unfoldOrDown": true;
|
|
61
61
|
"app.plan.toggle": true;
|
|
62
62
|
"app.history.search": true;
|
|
63
|
+
"app.stt.pushToTalk": true;
|
|
63
64
|
"app.stt.toggle": true;
|
|
64
65
|
"app.live.toggle": true;
|
|
65
66
|
}
|
|
@@ -237,9 +238,13 @@ export const KEYBINDINGS = {
|
|
|
237
238
|
defaultKeys: "ctrl+r",
|
|
238
239
|
description: "Search history",
|
|
239
240
|
},
|
|
241
|
+
"app.stt.pushToTalk": {
|
|
242
|
+
defaultKeys: "space",
|
|
243
|
+
description: "Hold to record speech-to-text",
|
|
244
|
+
},
|
|
240
245
|
"app.stt.toggle": {
|
|
241
246
|
defaultKeys: [],
|
|
242
|
-
description: "Toggle speech-to-text
|
|
247
|
+
description: "Toggle speech-to-text recording",
|
|
243
248
|
},
|
|
244
249
|
"app.live.toggle": {
|
|
245
250
|
defaultKeys: "ctrl+l",
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
import { formatNum, type ExperimentResult, type ExperimentState } from "../tools/autoresearch";
|
|
18
18
|
import type { TspSpan, TspTableColumn, TspText } from "@linxiraos/pi-wire";
|
|
19
19
|
import { card, col, elapsed, keyed, node, row, span, text } from "../native/describe";
|
|
20
|
-
import type { DescribeContext, NativeNode } from "../native/node";
|
|
21
|
-
import { hintsRow } from "../native/overlay";
|
|
20
|
+
import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node";
|
|
21
|
+
import { actionBar, actionButton, hintsRow } from "../native/overlay";
|
|
22
22
|
import { Memo } from "../native/memo";
|
|
23
23
|
import { isNativeRendering } from "../native/state";
|
|
24
24
|
|
|
@@ -190,9 +190,9 @@ export function createDashboardController(): DashboardController {
|
|
|
190
190
|
{ keys: ["up", "down", "j", "k"], label: "scroll" },
|
|
191
191
|
{ keys: ["pageUp", "pageDown"], label: "page" },
|
|
192
192
|
{ keys: ["g", "shift+g"], label: "top/bottom" },
|
|
193
|
-
{ keys: ["escape"], label: "close" },
|
|
194
193
|
]),
|
|
195
194
|
);
|
|
195
|
+
children.push(actionBar([null, actionButton("Close", "close", { keys: "escape" })]));
|
|
196
196
|
return col(children, { gap: "md", role: "zeta.app.autoresearch" });
|
|
197
197
|
},
|
|
198
198
|
);
|
|
@@ -214,6 +214,10 @@ export function createDashboardController(): DashboardController {
|
|
|
214
214
|
scrollView.setHeight(viewportRows);
|
|
215
215
|
return [header, ...scrollView.render(width), renderOverlayFooter(width, theme)];
|
|
216
216
|
},
|
|
217
|
+
/** The Close button runs Esc's path. */
|
|
218
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
219
|
+
if (event.type === "action" && event.act === "close") done(undefined);
|
|
220
|
+
},
|
|
217
221
|
handleInput(data: string): void {
|
|
218
222
|
if (matchesKey(data, "escape") || matchesKey(data, "esc") || data === "q") {
|
|
219
223
|
done(undefined);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Live status board for `
|
|
2
|
+
* Live status board for `zetacode cleanse`.
|
|
3
3
|
*
|
|
4
4
|
* Interactive terminals get a transient board repainted in place: a phase
|
|
5
5
|
* spinner (model resolution, checker discovery), one row per running checker,
|
|
@@ -59,7 +59,7 @@ const REPAIR_BAR_STYLE: ProgressBarStyle = {
|
|
|
59
59
|
styleEmpty: text => fgOrPlain("dim", text),
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
/** Rendering surface for one `
|
|
62
|
+
/** Rendering surface for one `zetacode cleanse` run. */
|
|
63
63
|
export interface CleanseStatusBoard {
|
|
64
64
|
readonly interactive: boolean;
|
|
65
65
|
/** Print a permanent line above the live area (plain write when non-TTY). */
|
|
@@ -10,7 +10,7 @@ import { Input } from "../../components/input";
|
|
|
10
10
|
import { routeSgrMouseInput, type SgrMouseEvent } from "../../mouse";
|
|
11
11
|
import { padding, truncateToWidth, visibleWidth } from "../../utils";
|
|
12
12
|
import { isRecord, sanitizeText } from "@linxiraos/pi-utils";
|
|
13
|
-
import { theme } from "../../theme/theme";
|
|
13
|
+
import { getThemeEpoch, theme } from "../../theme/theme";
|
|
14
14
|
import { sanitizeDisplayText } from "../../overlays/extensions/display-text";
|
|
15
15
|
import { DebugViewerFrame, type DebugViewerFrameContent, type DebugViewerFrameContext } from "./viewer-frame";
|
|
16
16
|
import { formatDebugLogExpandedLines, parseDebugLogPid, parseDebugLogTimestampMs } from "./log-formatting";
|
|
@@ -28,6 +28,12 @@ export const LOAD_OLDER_LABEL = "### MOVE UP TO LOAD MORE...";
|
|
|
28
28
|
|
|
29
29
|
const INITIAL_LOG_CHUNK = 50;
|
|
30
30
|
const LOAD_OLDER_CHUNK = 50;
|
|
31
|
+
/**
|
|
32
|
+
* Default for {@link DebugLogViewerModelOptions.maxLogEntries}. Once this many
|
|
33
|
+
* entries are loaded, older history stops loading; the newest entries (the
|
|
34
|
+
* current session) are never evicted.
|
|
35
|
+
*/
|
|
36
|
+
const MAX_LOG_ENTRIES = 50_000;
|
|
31
37
|
const MIN_LOG_VIEWER_WIDTH = 48;
|
|
32
38
|
/** Picker item id of the "load older entries" row. */
|
|
33
39
|
const OLDER_ITEM = "older";
|
|
@@ -43,6 +49,24 @@ type LogEntry = {
|
|
|
43
49
|
rawLine: string;
|
|
44
50
|
timestampMs: number | undefined;
|
|
45
51
|
pid: number | undefined;
|
|
52
|
+
/** Lowercased {@link rawLine}, filled on first filter match. */
|
|
53
|
+
lowerLine?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
type SelectionState = {
|
|
57
|
+
rows: readonly ViewerRow[];
|
|
58
|
+
cursor: number;
|
|
59
|
+
anchor: number | undefined;
|
|
60
|
+
indices: number[];
|
|
61
|
+
set: Set<number>;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
type CachedLogRow = {
|
|
65
|
+
width: number;
|
|
66
|
+
selected: boolean;
|
|
67
|
+
active: boolean;
|
|
68
|
+
expanded: boolean;
|
|
69
|
+
lines: string[];
|
|
46
70
|
};
|
|
47
71
|
|
|
48
72
|
type CursorToken = { kind: "log"; logIndex: number } | { kind: "load-older" };
|
|
@@ -52,6 +76,8 @@ type DebugLogViewerModelOptions = {
|
|
|
52
76
|
processPid?: number;
|
|
53
77
|
hasOlderLogs?: () => boolean;
|
|
54
78
|
loadOlderLogs?: (limitDays?: number) => Promise<string>;
|
|
79
|
+
/** Most entries kept; older history beyond it is not loaded. Defaults to {@link MAX_LOG_ENTRIES}. */
|
|
80
|
+
maxLogEntries?: number;
|
|
55
81
|
};
|
|
56
82
|
|
|
57
83
|
/**
|
|
@@ -111,6 +137,17 @@ function getProcessStartMs(): number {
|
|
|
111
137
|
return Date.now() - process.uptime() * 1000;
|
|
112
138
|
}
|
|
113
139
|
|
|
140
|
+
function parseLogEntries(logText: string): LogEntry[] {
|
|
141
|
+
return logText
|
|
142
|
+
.split("\n")
|
|
143
|
+
.filter(line => line.length > 0)
|
|
144
|
+
.map(rawLine => ({
|
|
145
|
+
rawLine,
|
|
146
|
+
timestampMs: parseDebugLogTimestampMs(rawLine),
|
|
147
|
+
pid: parseDebugLogPid(rawLine),
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
|
|
114
151
|
/** Split raw log text into nonempty entries. */
|
|
115
152
|
export function splitLogText(logText: string): string[] {
|
|
116
153
|
return logText.split("\n").filter(line => line.length > 0);
|
|
@@ -126,12 +163,18 @@ export function buildLogCopyPayload(lines: string[]): string {
|
|
|
126
163
|
|
|
127
164
|
/** Filter, paginate, expand, and select captured log entries. */
|
|
128
165
|
export class DebugLogViewerModel {
|
|
166
|
+
/**
|
|
167
|
+
* Loaded entries, newest first, so loading older history appends instead of
|
|
168
|
+
* copying the whole array. Log index `i` (0 = oldest) lives at `length - 1 - i`.
|
|
169
|
+
*/
|
|
129
170
|
#entries: LogEntry[];
|
|
171
|
+
readonly #maxLogEntries: number;
|
|
130
172
|
#rows: ViewerRow[];
|
|
131
173
|
#visibleLogIndices: number[];
|
|
132
174
|
#selectableRowIndices: number[];
|
|
133
175
|
#cursorSelectableIndex = 0;
|
|
134
176
|
#selectionAnchorSelectableIndex: number | undefined;
|
|
177
|
+
#selection: SelectionState | undefined;
|
|
135
178
|
#expandedLogIndices = new Set<number>();
|
|
136
179
|
#filterQuery = "";
|
|
137
180
|
#processStartMs: number;
|
|
@@ -142,12 +185,17 @@ export class DebugLogViewerModel {
|
|
|
142
185
|
#loadOlderLogs?: (limitDays?: number) => Promise<string>;
|
|
143
186
|
|
|
144
187
|
constructor(logText: string, options: DebugLogViewerModelOptions = {}) {
|
|
145
|
-
const {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
188
|
+
const {
|
|
189
|
+
processStartMs = getProcessStartMs(),
|
|
190
|
+
processPid = process.pid,
|
|
191
|
+
hasOlderLogs,
|
|
192
|
+
loadOlderLogs,
|
|
193
|
+
maxLogEntries = MAX_LOG_ENTRIES,
|
|
194
|
+
} = options;
|
|
195
|
+
this.#maxLogEntries = Math.max(1, maxLogEntries);
|
|
196
|
+
this.#entries = parseLogEntries(logText).reverse();
|
|
197
|
+
// Newest first: truncating keeps the newest entries and drops the oldest.
|
|
198
|
+
if (this.#entries.length > this.#maxLogEntries) this.#entries.length = this.#maxLogEntries;
|
|
151
199
|
this.#processStartMs = processStartMs;
|
|
152
200
|
this.#processPid = processPid;
|
|
153
201
|
this.#hasOlderLogs = hasOlderLogs;
|
|
@@ -163,6 +211,11 @@ export class DebugLogViewerModel {
|
|
|
163
211
|
return this.#entries.length;
|
|
164
212
|
}
|
|
165
213
|
|
|
214
|
+
/** True once the entry cap is reached, so older history no longer loads. */
|
|
215
|
+
get historyLimitReached(): boolean {
|
|
216
|
+
return this.#entries.length >= this.#maxLogEntries;
|
|
217
|
+
}
|
|
218
|
+
|
|
166
219
|
get visibleLogCount(): number {
|
|
167
220
|
return this.#visibleLogIndices.length;
|
|
168
221
|
}
|
|
@@ -201,7 +254,7 @@ export class DebugLogViewerModel {
|
|
|
201
254
|
}
|
|
202
255
|
|
|
203
256
|
getRawLine(logIndex: number): string {
|
|
204
|
-
return this.#
|
|
257
|
+
return this.#entryAt(logIndex)?.rawLine ?? "";
|
|
205
258
|
}
|
|
206
259
|
|
|
207
260
|
setFilterQuery(query: string): void {
|
|
@@ -269,38 +322,15 @@ export class DebugLogViewerModel {
|
|
|
269
322
|
}
|
|
270
323
|
|
|
271
324
|
getSelectedLogIndices(): number[] {
|
|
272
|
-
|
|
273
|
-
return [];
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const cursorRow = this.#getCursorRow();
|
|
277
|
-
if (this.#selectionAnchorSelectableIndex === undefined) {
|
|
278
|
-
if (cursorRow?.kind !== "log") {
|
|
279
|
-
return [];
|
|
280
|
-
}
|
|
281
|
-
return [cursorRow.logIndex];
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const min = Math.min(this.#selectionAnchorSelectableIndex, this.#cursorSelectableIndex);
|
|
285
|
-
const max = Math.max(this.#selectionAnchorSelectableIndex, this.#cursorSelectableIndex);
|
|
286
|
-
const selected: number[] = [];
|
|
287
|
-
for (let i = min; i <= max; i++) {
|
|
288
|
-
const rowIndex = this.#selectableRowIndices[i];
|
|
289
|
-
const row = rowIndex === undefined ? undefined : this.#rows[rowIndex];
|
|
290
|
-
if (row?.kind === "log") {
|
|
291
|
-
selected.push(row.logIndex);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
return selected;
|
|
325
|
+
return this.#selectionState().indices.slice();
|
|
295
326
|
}
|
|
296
327
|
|
|
297
328
|
getSelectedCount(): number {
|
|
298
|
-
return this
|
|
329
|
+
return this.#selectionState().indices.length;
|
|
299
330
|
}
|
|
300
331
|
|
|
301
332
|
isSelected(logIndex: number): boolean {
|
|
302
|
-
|
|
303
|
-
return selected.includes(logIndex);
|
|
333
|
+
return this.#selectionState().set.has(logIndex);
|
|
304
334
|
}
|
|
305
335
|
|
|
306
336
|
isExpanded(logIndex: number): boolean {
|
|
@@ -320,8 +350,7 @@ export class DebugLogViewerModel {
|
|
|
320
350
|
}
|
|
321
351
|
|
|
322
352
|
getSelectedRawLines(): string[] {
|
|
323
|
-
|
|
324
|
-
return selectedIndices.map(index => this.getRawLine(index));
|
|
353
|
+
return this.#selectionState().indices.map(index => this.getRawLine(index));
|
|
325
354
|
}
|
|
326
355
|
|
|
327
356
|
selectAllVisible(): void {
|
|
@@ -381,16 +410,17 @@ export class DebugLogViewerModel {
|
|
|
381
410
|
prependLogs(logText: string): number {
|
|
382
411
|
const previousCursor = this.#getCursorToken();
|
|
383
412
|
const previousAnchorLogIndex = this.#getAnchorLogIndex();
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}));
|
|
413
|
+
const room = this.#maxLogEntries - this.#entries.length;
|
|
414
|
+
const parsed = room > 0 ? parseLogEntries(logText) : [];
|
|
415
|
+
// Chronological order: past the cap, keep the chunk's newest entries (nearest the loaded history).
|
|
416
|
+
const newEntries = parsed.length > room ? parsed.slice(parsed.length - room) : parsed;
|
|
389
417
|
if (newEntries.length === 0) {
|
|
390
418
|
return 0;
|
|
391
419
|
}
|
|
392
420
|
const offset = newEntries.length;
|
|
393
|
-
|
|
421
|
+
for (let i = offset - 1; i >= 0; i--) {
|
|
422
|
+
this.#entries.push(newEntries[i]!);
|
|
423
|
+
}
|
|
394
424
|
this.#loadedStartIndex += offset;
|
|
395
425
|
this.#expandedLogIndices = new Set([...this.#expandedLogIndices].map(logIndex => logIndex + offset));
|
|
396
426
|
const adjustedCursor: CursorToken | undefined =
|
|
@@ -421,7 +451,7 @@ export class DebugLogViewerModel {
|
|
|
421
451
|
const query = this.#filterQuery.toLowerCase();
|
|
422
452
|
const visible: number[] = [];
|
|
423
453
|
for (let i = this.#loadedStartIndex; i < this.#entries.length; i++) {
|
|
424
|
-
const entry = this.#
|
|
454
|
+
const entry = this.#entryAt(i);
|
|
425
455
|
if (!entry) {
|
|
426
456
|
continue;
|
|
427
457
|
}
|
|
@@ -438,7 +468,7 @@ export class DebugLogViewerModel {
|
|
|
438
468
|
let olderSeen = false;
|
|
439
469
|
let warningInserted = false;
|
|
440
470
|
for (const logIndex of visible) {
|
|
441
|
-
const timestampMs = this.#
|
|
471
|
+
const timestampMs = this.#entryAt(logIndex)?.timestampMs;
|
|
442
472
|
if (timestampMs !== undefined) {
|
|
443
473
|
if (timestampMs < this.#processStartMs) {
|
|
444
474
|
olderSeen = true;
|
|
@@ -486,7 +516,7 @@ export class DebugLogViewerModel {
|
|
|
486
516
|
}
|
|
487
517
|
|
|
488
518
|
#matchesFilters(entry: LogEntry, query: string): boolean {
|
|
489
|
-
if (query.length > 0 && !entry.rawLine.toLowerCase().includes(query)) {
|
|
519
|
+
if (query.length > 0 && !(entry.lowerLine ??= entry.rawLine.toLowerCase()).includes(query)) {
|
|
490
520
|
return false;
|
|
491
521
|
}
|
|
492
522
|
if (!this.#processFilterEnabled) {
|
|
@@ -503,7 +533,7 @@ export class DebugLogViewerModel {
|
|
|
503
533
|
return false;
|
|
504
534
|
}
|
|
505
535
|
for (let i = 0; i < this.#loadedStartIndex; i++) {
|
|
506
|
-
const entry = this.#
|
|
536
|
+
const entry = this.#entryAt(i);
|
|
507
537
|
if (entry && this.#matchesFilters(entry, query)) {
|
|
508
538
|
return true;
|
|
509
539
|
}
|
|
@@ -512,7 +542,60 @@ export class DebugLogViewerModel {
|
|
|
512
542
|
}
|
|
513
543
|
|
|
514
544
|
#hasExternalOlderLogs(): boolean {
|
|
515
|
-
return this.#hasOlderLogs?.() ?? false;
|
|
545
|
+
return !this.historyLimitReached && (this.#hasOlderLogs?.() ?? false);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
#entryAt(logIndex: number): LogEntry | undefined {
|
|
549
|
+
return this.#entries[this.#entries.length - 1 - logIndex];
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/** Selected log indices, memoized until the rows, cursor or anchor change. */
|
|
553
|
+
#selectionState(): SelectionState {
|
|
554
|
+
const cached = this.#selection;
|
|
555
|
+
if (
|
|
556
|
+
cached &&
|
|
557
|
+
cached.rows === this.#rows &&
|
|
558
|
+
cached.cursor === this.#cursorSelectableIndex &&
|
|
559
|
+
cached.anchor === this.#selectionAnchorSelectableIndex
|
|
560
|
+
) {
|
|
561
|
+
return cached;
|
|
562
|
+
}
|
|
563
|
+
const indices = this.#computeSelectedLogIndices();
|
|
564
|
+
const state: SelectionState = {
|
|
565
|
+
rows: this.#rows,
|
|
566
|
+
cursor: this.#cursorSelectableIndex,
|
|
567
|
+
anchor: this.#selectionAnchorSelectableIndex,
|
|
568
|
+
indices,
|
|
569
|
+
set: new Set(indices),
|
|
570
|
+
};
|
|
571
|
+
this.#selection = state;
|
|
572
|
+
return state;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
#computeSelectedLogIndices(): number[] {
|
|
576
|
+
if (this.#selectableRowIndices.length === 0) {
|
|
577
|
+
return [];
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const cursorRow = this.#getCursorRow();
|
|
581
|
+
if (this.#selectionAnchorSelectableIndex === undefined) {
|
|
582
|
+
if (cursorRow?.kind !== "log") {
|
|
583
|
+
return [];
|
|
584
|
+
}
|
|
585
|
+
return [cursorRow.logIndex];
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const min = Math.min(this.#selectionAnchorSelectableIndex, this.#cursorSelectableIndex);
|
|
589
|
+
const max = Math.max(this.#selectionAnchorSelectableIndex, this.#cursorSelectableIndex);
|
|
590
|
+
const selected: number[] = [];
|
|
591
|
+
for (let i = min; i <= max; i++) {
|
|
592
|
+
const rowIndex = this.#selectableRowIndices[i];
|
|
593
|
+
const row = rowIndex === undefined ? undefined : this.#rows[rowIndex];
|
|
594
|
+
if (row?.kind === "log") {
|
|
595
|
+
selected.push(row.logIndex);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return selected;
|
|
516
599
|
}
|
|
517
600
|
|
|
518
601
|
#getCursorRow(): ViewerRow | undefined {
|
|
@@ -572,9 +655,13 @@ export class DebugLogViewerComponent implements Component {
|
|
|
572
655
|
#loadingOlder = false;
|
|
573
656
|
#bodyLineToRowIndex: Array<number | undefined> = [];
|
|
574
657
|
readonly #native = new Memo();
|
|
575
|
-
/** Picker rows by log index, valid
|
|
658
|
+
/** Picker rows by log index, valid for {@link #nativeItemsGeneration} (a prepend shifts indices). */
|
|
576
659
|
#nativeItems = new Map<number, TspPickerItem>();
|
|
577
|
-
#
|
|
660
|
+
#nativeItemsGeneration = 0;
|
|
661
|
+
/** Formatted ANSI log rows by log index, valid for {@link #rowCacheGeneration} and {@link #rowCacheThemeEpoch}. */
|
|
662
|
+
#rowCache = new Map<number, CachedLogRow>();
|
|
663
|
+
#rowCacheGeneration = 0;
|
|
664
|
+
#rowCacheThemeEpoch = -1;
|
|
578
665
|
|
|
579
666
|
constructor(options: DebugLogViewerComponentOptions) {
|
|
580
667
|
this.#deps = options.deps;
|
|
@@ -744,6 +831,7 @@ export class DebugLogViewerComponent implements Component {
|
|
|
744
831
|
invalidate(): void {
|
|
745
832
|
this.#frame.invalidate();
|
|
746
833
|
this.#native.clear();
|
|
834
|
+
this.#rowCache.clear();
|
|
747
835
|
}
|
|
748
836
|
|
|
749
837
|
dispose(): void {
|
|
@@ -774,6 +862,7 @@ export class DebugLogViewerComponent implements Component {
|
|
|
774
862
|
model.cursorRowIndex,
|
|
775
863
|
selected.join(","),
|
|
776
864
|
model.logCount,
|
|
865
|
+
model.historyLimitReached,
|
|
777
866
|
model.isProcessFilterEnabled(),
|
|
778
867
|
model.canLoadOlder(),
|
|
779
868
|
query,
|
|
@@ -782,9 +871,11 @@ export class DebugLogViewerComponent implements Component {
|
|
|
782
871
|
this.#statusMessage,
|
|
783
872
|
],
|
|
784
873
|
() => {
|
|
785
|
-
|
|
874
|
+
// Entries only grow, and every prepend shifts log indices, so the count keys the item cache.
|
|
875
|
+
const generation = model.logCount;
|
|
876
|
+
if (this.#nativeItemsGeneration !== generation) {
|
|
786
877
|
this.#nativeItems = new Map();
|
|
787
|
-
this.#
|
|
878
|
+
this.#nativeItemsGeneration = generation;
|
|
788
879
|
}
|
|
789
880
|
const items: TspPickerItem[] = [];
|
|
790
881
|
const order: (string | TspPickerGroup)[] = [];
|
|
@@ -817,6 +908,7 @@ export class DebugLogViewerComponent implements Component {
|
|
|
817
908
|
const subtitle = [
|
|
818
909
|
`${model.visibleLogCount}/${model.logCount} entries`,
|
|
819
910
|
selected.length > 1 ? `${selected.length} selected` : undefined,
|
|
911
|
+
model.historyLimitReached ? "history limit reached" : undefined,
|
|
820
912
|
this.#loadingOlder ? "loading older…" : undefined,
|
|
821
913
|
]
|
|
822
914
|
.filter(part => part !== undefined)
|
|
@@ -853,7 +945,11 @@ export class DebugLogViewerComponent implements Component {
|
|
|
853
945
|
pickerAction("all", "Select all", "ctrl+a"),
|
|
854
946
|
pickerAction("pid", "This process", "ctrl+p", { on: model.isProcessFilterEnabled() }),
|
|
855
947
|
pickerAction("older", "Load older", "ctrl+o", {
|
|
856
|
-
disabled: model.canLoadOlder()
|
|
948
|
+
disabled: model.canLoadOlder()
|
|
949
|
+
? undefined
|
|
950
|
+
: model.historyLimitReached
|
|
951
|
+
? "Log history limit reached"
|
|
952
|
+
: "No older log entries",
|
|
857
953
|
}),
|
|
858
954
|
CLOSE_ACTION,
|
|
859
955
|
],
|
|
@@ -945,7 +1041,8 @@ export class DebugLogViewerComponent implements Component {
|
|
|
945
1041
|
#summaryText(): string {
|
|
946
1042
|
const selected = this.#model.getSelectedCount();
|
|
947
1043
|
const expanded = this.#model.expandedCount;
|
|
948
|
-
|
|
1044
|
+
const limitText = this.#model.historyLimitReached ? ` ${theme.fg("warning", "history limit reached")}` : "";
|
|
1045
|
+
return `${theme.fg("muted", "showing")} ${theme.fg("accent", `${this.#model.visibleLogCount}/${this.#model.logCount}`)} ${theme.fg("muted", "selected")} ${theme.fg(selected > 0 ? "accent" : "muted", String(selected))} ${theme.fg("muted", "expanded")} ${theme.fg(expanded > 0 ? "accent" : "muted", String(expanded))}${limitText}`;
|
|
949
1046
|
}
|
|
950
1047
|
|
|
951
1048
|
#controlsText(): string {
|
|
@@ -1017,10 +1114,21 @@ export class DebugLogViewerComponent implements Component {
|
|
|
1017
1114
|
}
|
|
1018
1115
|
|
|
1019
1116
|
#renderRows(innerWidth: number): Array<{ lines: string[]; rowIndex: number }> {
|
|
1117
|
+
const model = this.#model;
|
|
1020
1118
|
const rendered: Array<{ lines: string[]; rowIndex: number }> = [];
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1119
|
+
// Entries only grow, and every prepend shifts log indices, so the count keys the row cache.
|
|
1120
|
+
const generation = model.logCount;
|
|
1121
|
+
const themeEpoch = getThemeEpoch();
|
|
1122
|
+
if (this.#rowCacheGeneration !== generation || this.#rowCacheThemeEpoch !== themeEpoch) {
|
|
1123
|
+
this.#rowCache.clear();
|
|
1124
|
+
this.#rowCacheGeneration = generation;
|
|
1125
|
+
this.#rowCacheThemeEpoch = themeEpoch;
|
|
1126
|
+
}
|
|
1127
|
+
const cursorRowIndex = model.cursorRowIndex;
|
|
1128
|
+
const cursorLogIndex = model.cursorLogIndex;
|
|
1129
|
+
|
|
1130
|
+
for (let rowIndex = 0; rowIndex < model.rows.length; rowIndex++) {
|
|
1131
|
+
const row = model.rows[rowIndex];
|
|
1024
1132
|
if (!row) {
|
|
1025
1133
|
continue;
|
|
1026
1134
|
}
|
|
@@ -1034,7 +1142,7 @@ export class DebugLogViewerComponent implements Component {
|
|
|
1034
1142
|
}
|
|
1035
1143
|
|
|
1036
1144
|
if (row.kind === "load-older") {
|
|
1037
|
-
const active =
|
|
1145
|
+
const active = cursorRowIndex === rowIndex;
|
|
1038
1146
|
const marker = active ? theme.fg("accent", "❯") : " ";
|
|
1039
1147
|
const prefix = `${marker} `;
|
|
1040
1148
|
const contentWidth = Math.max(1, innerWidth - visibleWidth(prefix));
|
|
@@ -1047,32 +1155,40 @@ export class DebugLogViewerComponent implements Component {
|
|
|
1047
1155
|
}
|
|
1048
1156
|
|
|
1049
1157
|
const logIndex = row.logIndex;
|
|
1050
|
-
const selected =
|
|
1051
|
-
const cursorLogIndex = this.#model.cursorLogIndex;
|
|
1158
|
+
const selected = model.isSelected(logIndex);
|
|
1052
1159
|
const active = cursorLogIndex !== undefined && cursorLogIndex === logIndex;
|
|
1053
|
-
const expanded =
|
|
1160
|
+
const expanded = model.isExpanded(logIndex);
|
|
1161
|
+
const cached = this.#rowCache.get(logIndex);
|
|
1162
|
+
if (
|
|
1163
|
+
cached &&
|
|
1164
|
+
cached.width === innerWidth &&
|
|
1165
|
+
cached.selected === selected &&
|
|
1166
|
+
cached.active === active &&
|
|
1167
|
+
cached.expanded === expanded
|
|
1168
|
+
) {
|
|
1169
|
+
rendered.push({ rowIndex, lines: cached.lines });
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1054
1173
|
const marker = active ? theme.fg("accent", "❯") : selected ? theme.fg("accent", "•") : " ";
|
|
1055
1174
|
const fold = expanded ? theme.fg("accent", "▾") : theme.fg("muted", "▸");
|
|
1056
1175
|
const prefix = `${marker}${fold} `;
|
|
1057
1176
|
const contentWidth = Math.max(1, innerWidth - visibleWidth(prefix));
|
|
1058
|
-
|
|
1177
|
+
let lines: string[];
|
|
1059
1178
|
if (expanded) {
|
|
1060
|
-
const wrapped = formatDebugLogExpandedLines(
|
|
1179
|
+
const wrapped = formatDebugLogExpandedLines(model.getRawLine(logIndex), contentWidth);
|
|
1061
1180
|
const indent = padding(visibleWidth(prefix));
|
|
1062
|
-
|
|
1181
|
+
lines = wrapped.map((segment, index) => {
|
|
1063
1182
|
const content = selected ? theme.bold(segment) : segment;
|
|
1064
1183
|
return truncateToWidth(`${index === 0 ? prefix : indent}${content}`, innerWidth);
|
|
1065
1184
|
});
|
|
1066
|
-
|
|
1067
|
-
|
|
1185
|
+
} else {
|
|
1186
|
+
const preview = truncateToWidth(sanitizeDisplayText(model.getRawLine(logIndex)), contentWidth);
|
|
1187
|
+
const content = selected ? theme.bold(preview) : preview;
|
|
1188
|
+
lines = [truncateToWidth(`${prefix}${content}`, innerWidth)];
|
|
1068
1189
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
sanitizeDisplayText(this.#model.getRawLine(logIndex)),
|
|
1072
|
-
Math.max(1, contentWidth),
|
|
1073
|
-
);
|
|
1074
|
-
const content = selected ? theme.bold(preview) : preview;
|
|
1075
|
-
rendered.push({ rowIndex, lines: [truncateToWidth(`${prefix}${content}`, innerWidth)] });
|
|
1190
|
+
this.#rowCache.set(logIndex, { width: innerWidth, selected, active, expanded, lines });
|
|
1191
|
+
rendered.push({ rowIndex, lines });
|
|
1076
1192
|
}
|
|
1077
1193
|
|
|
1078
1194
|
return rendered;
|
|
@@ -271,9 +271,15 @@ export class RawSseDebugBuffer {
|
|
|
271
271
|
#totalEvents = 0;
|
|
272
272
|
#lastUpdatedAt: number | undefined;
|
|
273
273
|
#nextSequence = 1;
|
|
274
|
+
#revision = 0;
|
|
274
275
|
#listeners = new Set<() => void>();
|
|
275
276
|
#emitScheduled = false;
|
|
276
277
|
|
|
278
|
+
/** Bumped on every change to the captured window (append, eviction, clear); equal revisions mean equal snapshots. */
|
|
279
|
+
get revision(): number {
|
|
280
|
+
return this.#revision;
|
|
281
|
+
}
|
|
282
|
+
|
|
277
283
|
subscribe(listener: () => void): () => void {
|
|
278
284
|
this.#listeners.add(listener);
|
|
279
285
|
return () => this.#listeners.delete(listener);
|
|
@@ -359,6 +365,7 @@ export class RawSseDebugBuffer {
|
|
|
359
365
|
this.#droppedChars = 0;
|
|
360
366
|
this.#totalEvents = 0;
|
|
361
367
|
this.#lastUpdatedAt = undefined;
|
|
368
|
+
this.#revision += 1;
|
|
362
369
|
this.#emit();
|
|
363
370
|
}
|
|
364
371
|
|
|
@@ -368,6 +375,7 @@ export class RawSseDebugBuffer {
|
|
|
368
375
|
this.#totalChars += chars;
|
|
369
376
|
this.#lastUpdatedAt = record.timestamp;
|
|
370
377
|
this.#enforceLimits();
|
|
378
|
+
this.#revision += 1;
|
|
371
379
|
this.#emit();
|
|
372
380
|
}
|
|
373
381
|
|
|
@@ -9,7 +9,7 @@ import { actionBar, actionButton } from "../../native/overlay";
|
|
|
9
9
|
import { routeSgrMouseInput, type SgrMouseEvent } from "../../mouse";
|
|
10
10
|
import { truncateToWidth } from "../../utils";
|
|
11
11
|
import { sanitizeDisplayText } from "../../overlays/extensions/display-text";
|
|
12
|
-
import { theme } from "../../theme/theme";
|
|
12
|
+
import { getThemeEpoch, theme } from "../../theme/theme";
|
|
13
13
|
import { DebugViewerFrame } from "./viewer-frame";
|
|
14
14
|
import {
|
|
15
15
|
formatRawSseIsoTime,
|
|
@@ -106,6 +106,14 @@ export class RawSseViewerComponent implements Component {
|
|
|
106
106
|
// Sequences are monotonic; we prune entries below the oldest live record
|
|
107
107
|
// after each render so the cache tracks the buffer's eviction window.
|
|
108
108
|
readonly #prettyLinesCache = new Map<number, string[]>();
|
|
109
|
+
/** Buffer snapshot for {@link #snapshotRevision}; the buffer's revision bumps on every change. */
|
|
110
|
+
#snapshot: RawSseDebugSnapshot | undefined;
|
|
111
|
+
#snapshotRevision = -1;
|
|
112
|
+
/** Rendered body for (buffer revision, width, theme epoch). */
|
|
113
|
+
#bodyLines: string[] = [];
|
|
114
|
+
#bodyRevision = -1;
|
|
115
|
+
#bodyWidth = -1;
|
|
116
|
+
#bodyThemeEpoch = -1;
|
|
109
117
|
|
|
110
118
|
constructor(options: RawSseViewerOptions) {
|
|
111
119
|
this.#deps = options.deps;
|
|
@@ -227,6 +235,7 @@ export class RawSseViewerComponent implements Component {
|
|
|
227
235
|
this.#native.clear();
|
|
228
236
|
this.#nativeHead.clear();
|
|
229
237
|
this.#nativeBody.clear();
|
|
238
|
+
this.#bodyRevision = -1;
|
|
230
239
|
}
|
|
231
240
|
|
|
232
241
|
render(width: number): readonly string[] {
|
|
@@ -249,7 +258,7 @@ export class RawSseViewerComponent implements Component {
|
|
|
249
258
|
* so they never scroll away.
|
|
250
259
|
*/
|
|
251
260
|
describeScreen(): NativeScreen {
|
|
252
|
-
const snapshot = this.#
|
|
261
|
+
const snapshot = this.#currentSnapshot();
|
|
253
262
|
const stream = this.#feedNative(snapshot);
|
|
254
263
|
const head = this.#nativeHead.get(
|
|
255
264
|
[snapshot.totalEvents, snapshot.records.length, snapshot.droppedRecords, snapshot.lastUpdatedAt],
|
|
@@ -372,15 +381,40 @@ export class RawSseViewerComponent implements Component {
|
|
|
372
381
|
this.#onExit();
|
|
373
382
|
}
|
|
374
383
|
|
|
384
|
+
/** The buffer's snapshot, re-taken only when its revision moved. */
|
|
385
|
+
#currentSnapshot(): RawSseDebugSnapshot {
|
|
386
|
+
const revision = this.#buffer.revision;
|
|
387
|
+
if (!this.#snapshot || this.#snapshotRevision !== revision) {
|
|
388
|
+
this.#snapshot = this.#buffer.snapshot();
|
|
389
|
+
this.#snapshotRevision = revision;
|
|
390
|
+
}
|
|
391
|
+
return this.#snapshot;
|
|
392
|
+
}
|
|
393
|
+
|
|
375
394
|
#renderRawLines(innerWidth: number): string[] {
|
|
376
|
-
const snapshot = this.#
|
|
395
|
+
const snapshot = this.#currentSnapshot();
|
|
396
|
+
const themeEpoch = getThemeEpoch();
|
|
397
|
+
if (
|
|
398
|
+
this.#bodyRevision === this.#snapshotRevision &&
|
|
399
|
+
this.#bodyWidth === innerWidth &&
|
|
400
|
+
this.#bodyThemeEpoch === themeEpoch
|
|
401
|
+
) {
|
|
402
|
+
return this.#bodyLines;
|
|
403
|
+
}
|
|
404
|
+
this.#bodyRevision = this.#snapshotRevision;
|
|
405
|
+
this.#bodyWidth = innerWidth;
|
|
406
|
+
this.#bodyThemeEpoch = themeEpoch;
|
|
407
|
+
this.#bodyLines = this.#formatRawLines(snapshot, innerWidth);
|
|
408
|
+
return this.#bodyLines;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
#formatRawLines(snapshot: RawSseDebugSnapshot, innerWidth: number): string[] {
|
|
377
412
|
if (snapshot.records.length === 0) {
|
|
378
413
|
return [
|
|
379
414
|
theme.fg("muted", "No raw SSE frames captured yet."),
|
|
380
415
|
theme.fg("muted", "HTTP SSE providers populate this view while a model response is streaming."),
|
|
381
416
|
];
|
|
382
417
|
}
|
|
383
|
-
|
|
384
418
|
const lines: string[] = [];
|
|
385
419
|
if (snapshot.droppedRecords > 0) {
|
|
386
420
|
lines.push(
|
|
@@ -421,7 +455,7 @@ export class RawSseViewerComponent implements Component {
|
|
|
421
455
|
}
|
|
422
456
|
}
|
|
423
457
|
#summaryText(): string {
|
|
424
|
-
const snapshot = this.#
|
|
458
|
+
const snapshot = this.#currentSnapshot();
|
|
425
459
|
const last = snapshot.lastUpdatedAt
|
|
426
460
|
? `${theme.fg("muted", "last")} ${theme.fg("accent", formatRawSseIsoTime(snapshot.lastUpdatedAt))}`
|
|
427
461
|
: theme.fg("muted", "waiting for first frame");
|