@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
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* to the session's `local://` root, and generated video contact-sheet previews.
|
|
5
5
|
* Symbol metadata stays out of serialized/model-bound image data while traveling
|
|
6
6
|
* with the draft object, until AgentSession creates the hidden companion message
|
|
7
|
-
* that tells the model the path
|
|
7
|
+
* that tells the model the path. The model keeps `local://` references across
|
|
8
|
+
* `/move`; chips for internal URLs instead use a stable blob copy.
|
|
8
9
|
*/
|
|
9
10
|
import type { ImageContent } from "@linxiraos/pi-ai";
|
|
10
11
|
import { isRecord } from "@linxiraos/pi-utils";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Shared assistant transcript construction and model-authored link caching. */
|
|
2
|
-
import type { AssistantMessage, Model } from "@linxiraos/pi-ai";
|
|
2
|
+
import type { AssistantMessage, Model, TextContent } from "@linxiraos/pi-ai";
|
|
3
3
|
import { getMarkdownLinkUrls } from "../index";
|
|
4
4
|
import { EMPTY_LINK_TARGETS } from "../render/render-utils";
|
|
5
5
|
import type { ImageBudget } from "../components/image";
|
|
@@ -16,11 +16,15 @@ export interface AssistantMessageHost {
|
|
|
16
16
|
readonly viewSession: AssistantMessageSession;
|
|
17
17
|
readonly effectiveHideThinkingBlock: boolean;
|
|
18
18
|
readonly proseOnlyThinking: boolean;
|
|
19
|
+
readonly expandThinkingBlocks: boolean;
|
|
19
20
|
readonly assistantImagesVisible: boolean;
|
|
21
|
+
/** Whether the viewed session's tables get charts: the main session's do, a focused subagent's do not. */
|
|
22
|
+
readonly tableChartsVisible: boolean;
|
|
20
23
|
readonly hideToolActivity: boolean;
|
|
21
24
|
readonly toolOutputExpanded: boolean;
|
|
22
25
|
readonly ui: { requestRender(): void; readonly imageBudget: ImageBudget };
|
|
23
|
-
|
|
26
|
+
/** Resolve already-extracted Markdown link destinations to local targets. */
|
|
27
|
+
resolveAssistantMessageLinkHrefs(hrefs: readonly string[]): Promise<ReadonlyMap<string, string>>;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
const kMarkdownLinkTargets = Symbol("markdownLinkTargets");
|
|
@@ -28,14 +32,26 @@ type SessionWithMarkdownLinkTargets = AssistantMessageSession & {
|
|
|
28
32
|
[kMarkdownLinkTargets]?: ReadonlyMap<string, string>;
|
|
29
33
|
};
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
/** Link destinations per text block, revalidated against the block's text (streaming blocks grow in place). */
|
|
36
|
+
const textBlockHrefs = new WeakMap<TextContent, { text: string; hrefs: readonly string[] }>();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Distinct Markdown link destinations authored in `message`'s text blocks.
|
|
40
|
+
* Each block is lexed once per text revision; transcript rebuilds and the
|
|
41
|
+
* message_end refresh reuse the result.
|
|
42
|
+
*/
|
|
43
|
+
function assistantMessageLinkHrefs(message: AssistantMessage): string[] {
|
|
44
|
+
const hrefs = new Set<string>();
|
|
45
|
+
for (const content of message.content) {
|
|
46
|
+
if (content.type !== "text") continue;
|
|
47
|
+
let memo = textBlockHrefs.get(content);
|
|
48
|
+
if (memo?.text !== content.text) {
|
|
49
|
+
memo = { text: content.text, hrefs: getMarkdownLinkUrls(content.text) };
|
|
50
|
+
textBlockHrefs.set(content, memo);
|
|
36
51
|
}
|
|
52
|
+
for (const href of memo.hrefs) hrefs.add(href);
|
|
37
53
|
}
|
|
38
|
-
return
|
|
54
|
+
return [...hrefs];
|
|
39
55
|
}
|
|
40
56
|
|
|
41
57
|
/**
|
|
@@ -50,13 +66,12 @@ export async function refreshAssistantMessageLinkTargets(
|
|
|
50
66
|
): Promise<ReadonlyMap<string, string>> {
|
|
51
67
|
const session: SessionWithMarkdownLinkTargets = ctx.viewSession;
|
|
52
68
|
const previous = session[kMarkdownLinkTargets] ?? EMPTY_LINK_TARGETS;
|
|
53
|
-
const texts = assistantTextBlocks(messages);
|
|
54
69
|
const hrefs = new Set<string>();
|
|
55
|
-
for (const
|
|
56
|
-
for (const href of
|
|
70
|
+
for (const message of messages) {
|
|
71
|
+
for (const href of assistantMessageLinkHrefs(message)) hrefs.add(href);
|
|
57
72
|
}
|
|
58
73
|
if (hrefs.size === 0) return previous;
|
|
59
|
-
const resolved = await ctx.
|
|
74
|
+
const resolved = await ctx.resolveAssistantMessageLinkHrefs([...hrefs]);
|
|
60
75
|
let changed = false;
|
|
61
76
|
for (const href of hrefs) {
|
|
62
77
|
if (previous.get(href) !== resolved.get(href)) {
|
|
@@ -84,12 +99,9 @@ export function assistantMessageLinkTargets(
|
|
|
84
99
|
targets: ReadonlyMap<string, string>,
|
|
85
100
|
): ReadonlyMap<string, string> {
|
|
86
101
|
const selected = new Map<string, string>();
|
|
87
|
-
for (const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const target = targets.get(href);
|
|
91
|
-
if (target) selected.set(href, target);
|
|
92
|
-
}
|
|
102
|
+
for (const href of assistantMessageLinkHrefs(message)) {
|
|
103
|
+
const target = targets.get(href);
|
|
104
|
+
if (target) selected.set(href, target);
|
|
93
105
|
}
|
|
94
106
|
return selected;
|
|
95
107
|
}
|
|
@@ -112,8 +124,10 @@ export function createAssistantMessageComponent(
|
|
|
112
124
|
ctx.ui.imageBudget,
|
|
113
125
|
ctx.proseOnlyThinking,
|
|
114
126
|
linkTargets,
|
|
127
|
+
ctx.expandThinkingBlocks,
|
|
115
128
|
);
|
|
116
129
|
component.setImagesVisible(ctx.assistantImagesVisible);
|
|
130
|
+
component.setTableChartsVisible(ctx.tableChartsVisible);
|
|
117
131
|
component.setToolResultImagesVisible(!ctx.hideToolActivity);
|
|
118
132
|
component.setExpanded(ctx.toolOutputExpanded);
|
|
119
133
|
// A wire the `stream-revision` axis marks `possible` can rewrite text it has
|
|
@@ -56,6 +56,8 @@ export class MacOSSpellingProvider implements EditorTextAssistProvider {
|
|
|
56
56
|
#availabilityChecked = false;
|
|
57
57
|
#cacheGeneration = 0;
|
|
58
58
|
#typoCache = new Map<string, readonly native.SpellingRange[]>();
|
|
59
|
+
/** {@link #projectTypoRanges} answers for the current {@link #typoCache}; segments re-ask every frame. */
|
|
60
|
+
#projectionMemo = new Map<string, readonly native.SpellingRange[] | undefined>();
|
|
59
61
|
#typoInFlight = new Map<string, Promise<readonly native.SpellingRange[]>>();
|
|
60
62
|
#automaticTypoActive = false;
|
|
61
63
|
#automaticTypoQueue = new Map<string, string>();
|
|
@@ -237,6 +239,7 @@ export class MacOSSpellingProvider implements EditorTextAssistProvider {
|
|
|
237
239
|
return request;
|
|
238
240
|
}
|
|
239
241
|
#projectTypoRanges(text: string): readonly native.SpellingRange[] | undefined {
|
|
242
|
+
if (this.#projectionMemo.has(text)) return this.#projectionMemo.get(text);
|
|
240
243
|
let projected: native.SpellingRange[] | undefined;
|
|
241
244
|
let matchLength = -1;
|
|
242
245
|
for (const [previous, ranges] of this.#typoCache) {
|
|
@@ -265,6 +268,8 @@ export class MacOSSpellingProvider implements EditorTextAssistProvider {
|
|
|
265
268
|
});
|
|
266
269
|
matchLength = prefix + suffix;
|
|
267
270
|
}
|
|
271
|
+
if (this.#projectionMemo.size >= CACHE_LIMIT) this.#projectionMemo.clear();
|
|
272
|
+
this.#projectionMemo.set(text, projected);
|
|
268
273
|
return projected;
|
|
269
274
|
}
|
|
270
275
|
|
|
@@ -316,6 +321,7 @@ export class MacOSSpellingProvider implements EditorTextAssistProvider {
|
|
|
316
321
|
const hadProjectedRanges = (this.#projectTypoRanges(text)?.length ?? 0) > 0;
|
|
317
322
|
if (this.#typoCache.size >= CACHE_LIMIT) this.#typoCache.clear();
|
|
318
323
|
this.#typoCache.set(text, ranges);
|
|
324
|
+
this.#projectionMemo.clear();
|
|
319
325
|
if (ranges.length > 0 || hadProjectedRanges) this.onUpdate?.();
|
|
320
326
|
return ranges;
|
|
321
327
|
}
|
|
@@ -323,6 +329,7 @@ export class MacOSSpellingProvider implements EditorTextAssistProvider {
|
|
|
323
329
|
#clearCaches(): void {
|
|
324
330
|
this.#cacheGeneration++;
|
|
325
331
|
this.#typoCache.clear();
|
|
332
|
+
this.#projectionMemo.clear();
|
|
326
333
|
this.#typoInFlight.clear();
|
|
327
334
|
this.#automaticTypoQueue.clear();
|
|
328
335
|
}
|
|
@@ -148,6 +148,9 @@ export function magicKeywordRanges(text: string): { from: number; to: number }[]
|
|
|
148
148
|
return ranges.sort((a, b) => a.from - b.from);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/** Last {@link hasMagicKeyword} answer: the live editor asks once per layout segment with the same buffer. */
|
|
152
|
+
let lastProbe: { text: string; registry: readonly RegisteredKeyword[]; found: boolean } | undefined;
|
|
153
|
+
|
|
151
154
|
/**
|
|
152
155
|
* Cheap test for "does this text contain any registered keyword as standalone
|
|
153
156
|
* prose?". Short-circuits on a substring probe before paying for the
|
|
@@ -155,8 +158,14 @@ export function magicKeywordRanges(text: string): { from: number; to: number }[]
|
|
|
155
158
|
* `String#includes` per word. Used by the live editor to gate the shimmer timer.
|
|
156
159
|
*/
|
|
157
160
|
export function hasMagicKeyword(text: string): boolean {
|
|
161
|
+
if (lastProbe?.text === text && lastProbe.registry === registry) return lastProbe.found;
|
|
162
|
+
let found = false;
|
|
158
163
|
for (const keyword of registry) {
|
|
159
|
-
if (text.includes(keyword.word) && containsMagicKeyword(text, keyword.word))
|
|
164
|
+
if (text.includes(keyword.word) && containsMagicKeyword(text, keyword.word)) {
|
|
165
|
+
found = true;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
160
168
|
}
|
|
161
|
-
|
|
169
|
+
lastProbe = { text, registry, found };
|
|
170
|
+
return found;
|
|
162
171
|
}
|
|
@@ -5,9 +5,7 @@ import { modelMentionDisplayName } from "./model-mention-syntax";
|
|
|
5
5
|
import type {
|
|
6
6
|
buildSearchAffinity as BuildSearchAffinity,
|
|
7
7
|
ModelBrowserItem,
|
|
8
|
-
|
|
9
|
-
SearchAffinity,
|
|
10
|
-
SessionModelScope,
|
|
8
|
+
ModelItemRanker as ModelItemRankerClass,
|
|
11
9
|
SessionModelScopeCache as SessionModelScopeCacheClass,
|
|
12
10
|
} from "../overlays/model-browser";
|
|
13
11
|
import { theme } from "../theme/theme";
|
|
@@ -21,7 +19,7 @@ export type ModelMentionCandidateSource = (query: string) => ReadonlyArray<Model
|
|
|
21
19
|
interface ModelBrowserModules {
|
|
22
20
|
SessionModelScopeCache: typeof SessionModelScopeCacheClass;
|
|
23
21
|
buildSearchAffinity: typeof BuildSearchAffinity;
|
|
24
|
-
|
|
22
|
+
ModelItemRanker: typeof ModelItemRankerClass;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
/** Synchronous first-use boundary for the interactive model browser implementation. */
|
|
@@ -80,7 +78,7 @@ export function applyModelMentionCompletion(
|
|
|
80
78
|
|
|
81
79
|
/**
|
|
82
80
|
* Create a session-scoped model candidate lookup using picker ordering. The
|
|
83
|
-
* scope and
|
|
81
|
+
* scope and its prepared ranker are reused across queries until their inputs change.
|
|
84
82
|
*/
|
|
85
83
|
export function createModelMentionSource(host: {
|
|
86
84
|
source: ModelBrowserSource;
|
|
@@ -88,20 +86,22 @@ export function createModelMentionSource(host: {
|
|
|
88
86
|
scopedModels: () => ReadonlyArray<Model>;
|
|
89
87
|
}): ModelMentionCandidateSource {
|
|
90
88
|
let scopeCache: SessionModelScopeCacheClass | undefined;
|
|
91
|
-
let
|
|
92
|
-
let
|
|
93
|
-
let affinity: SearchAffinity | undefined;
|
|
89
|
+
let ranker: ModelItemRankerClass | undefined;
|
|
90
|
+
let rankerProviderOrder: readonly string[] | undefined;
|
|
94
91
|
return query => {
|
|
95
|
-
const { SessionModelScopeCache, buildSearchAffinity,
|
|
92
|
+
const { SessionModelScopeCache, buildSearchAffinity, ModelItemRanker } = loadModelBrowser();
|
|
96
93
|
scopeCache ??= new SessionModelScopeCache(host.source, host.registry);
|
|
97
94
|
const scope = scopeCache.get(host.scopedModels());
|
|
98
95
|
if (!query.trim()) return scope.items;
|
|
99
96
|
const providerOrder = host.source.modelProviderOrder;
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
if (ranker?.items !== scope.items || rankerProviderOrder !== providerOrder) {
|
|
98
|
+
ranker = new ModelItemRanker(scope.items, {
|
|
99
|
+
roles: scope.roles,
|
|
100
|
+
mruOrder: scope.mruOrder,
|
|
101
|
+
affinity: buildSearchAffinity(providerOrder, scope.roles, scope.mruOrder),
|
|
102
|
+
});
|
|
103
|
+
rankerProviderOrder = providerOrder;
|
|
104
104
|
}
|
|
105
|
-
return
|
|
105
|
+
return ranker.rank(query);
|
|
106
106
|
};
|
|
107
107
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Container } from "../tui";
|
|
2
2
|
import { Spacer } from "../components/spacer";
|
|
3
3
|
import { TruncatedText } from "../components/truncated-text";
|
|
4
|
-
import { formatKeyHint } from "../key-hint-format";
|
|
4
|
+
import { formatKeyHint, formatTooltipKey } from "../key-hint-format";
|
|
5
5
|
import type { KeyId } from "../keys";
|
|
6
6
|
import { kbd, node, text } from "../native/describe";
|
|
7
7
|
import type { NativeNode, NativeUiEvent } from "../native/node";
|
|
@@ -39,8 +39,10 @@ export class QueuedMessagesBand extends Container {
|
|
|
39
39
|
this.addChild(new TruncatedText(theme.fg("dim", ` ${index + 1}. ${message}`), 1, 0));
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
this.addChild(
|
|
43
|
+
new TruncatedText(theme.fg("dim", ` ${theme.tree.hook} ${formatKeyHint(dequeueKey)} to edit`), 1, 0),
|
|
44
|
+
);
|
|
45
|
+
const editTitle = `Edit ${formatTooltipKey(dequeueKey)}`;
|
|
44
46
|
|
|
45
47
|
const count = groups.reduce((sum, group) => sum + group.messages.length, 0);
|
|
46
48
|
const pills: NativeNode[] = [];
|
|
@@ -70,7 +72,7 @@ export class QueuedMessagesBand extends Container {
|
|
|
70
72
|
role: "zeta.queue.edit",
|
|
71
73
|
gap: "xs",
|
|
72
74
|
align: "center",
|
|
73
|
-
title:
|
|
75
|
+
title: editTitle,
|
|
74
76
|
actions: { click: "queue.edit" },
|
|
75
77
|
},
|
|
76
78
|
[kbd(dequeueKey), text("Edit")],
|
package/src/prompt/tips.txt
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
Tired of typing "keep going"? Just send a '.'
|
|
2
|
+
Type `#` anywhere in your prompt for quick actions — copy the line or the whole prompt, undo, jump the cursor
|
|
3
|
+
Type `/` at the start of your prompt to browse every command
|
|
4
|
+
Start your prompt with `!` to run it as bash — `!!` runs it without adding the output to context
|
|
5
|
+
Start your prompt with `$ ` to run Python — `$$ ` runs it without adding the output to context
|
|
2
6
|
You can /btw to ask a side question
|
|
3
7
|
Use /tan to fork the current conversation into a background agent
|
|
4
8
|
With default keybindings, {key:ctrl+d} deletes the character under the cursor instead of quitting your draft — see /hotkeys for your exit key
|
|
5
|
-
Find out which model you emotionally abuse the most with `
|
|
9
|
+
Find out which model you emotionally abuse the most with `zetacode stats`
|
|
6
10
|
Try task isolation to create CoW worktrees
|
|
7
|
-
Need a cheap nested model call? Use `completion(x...)
|
|
11
|
+
Need a cheap nested model call? Use `completion(x...)`
|
|
8
12
|
Spaghetti code? Try complaining with /omfg
|
|
9
|
-
Did you know? Each kitty/tmux/cmux/zellij/wezterm split keeps its own session — `
|
|
13
|
+
Did you know? Each kitty/tmux/cmux/zellij/wezterm split keeps its own session — `zetacode -c` resumes the right one
|
|
10
14
|
Drop the word `ultrathink` in your message for harder multi-step reasoning — watch it glow rainbow as you type
|
|
11
15
|
Say `orchestrate` in your message to drive a multi-phase task with parallel subagents — watch it glow as you type
|
|
12
16
|
Say `workflowz` in your message to drive the task with parallel subagents in eval — watch it glow as you type
|
|
13
17
|
Bulk data to classify or triage? Say `jevify` in your message — the eval kernel's `judge()` processes the batch and you read only what it flags — watch it glow as you type
|
|
14
|
-
Log in to several accounts of the same provider — `/login` again — and
|
|
15
|
-
Run `
|
|
18
|
+
Log in to several accounts of the same provider — `/login` again — and zeta load-balances across them automatically
|
|
19
|
+
Run `zetacode auth-broker serve` once and every machine pulls live tokens over the wire — refresh keys never leave the host; `zetacode auth-gateway` fronts it as a drop-in proxy any OpenAI-compatible client can hit
|
|
16
20
|
Press {key:alt+p} (or /switch) to switch provider, and {key:ctrl+p} to cycle role models smol -> slow -> etc
|
|
17
21
|
Press {key:ctrl+r} to search your prompt history and reuse a past message
|
|
18
22
|
`/force read` pins the next turn to one specific tool when the model keeps reaching for the wrong one
|
|
19
23
|
`/copy code` grabs the last code block to your clipboard — `/copy cmd` grabs the last shell/python command
|
|
20
24
|
`/shake` rips heavy tool results out of context to reclaim tokens without a full /compact — `/shake images` drops just images
|
|
21
|
-
Pair up live: `/collab` shares your session through an end-to-end encrypted relay link — a teammate runs `/join <link>` to watch tool calls stream and prompt the agent from their own
|
|
25
|
+
Pair up live: `/collab` shares your session through an end-to-end encrypted relay link — a teammate runs `/join <link>` to watch tool calls stream and prompt the agent from their own zeta
|
|
22
26
|
Press {tap:left} to drill into a running or finished agent and inspect its tool calls and transcript
|
|
23
27
|
Hit a Codex rate limit? `/usage reset` spends a saved reset credit to immediately restore your quota
|
|
24
|
-
No native tool_calling? Inference provider botches parsing them? `PI_DIALECT=glm|kimi|anthropic…` rolls it locally for them!
|
|
25
28
|
Turn on `/advisor` to attach a second model that reviews every turn and quietly injects advice
|
|
26
29
|
Try starting your prompt with a ->, and writing a list (1. Do X, 2. Do Y)
|
|
27
30
|
Press {key:shift+tab} to cycle through reasoning effort levels
|
|
28
|
-
|
|
29
|
-
Lint/type errors piling up? `
|
|
31
|
+
zeta can reconfigure and extend itself — ask any questions you have!
|
|
32
|
+
Lint/type errors piling up? `zetacode cleanse` (or /cleanse right here) hunts project diagnostics and fixes them with parallel subagents — {key:escape} cancels
|
|
33
|
+
[NO-TERN] Psst, zeta has a natural habitat 🐦 [tern](https://scl.so/tern)
|
package/src/prompt/welcome.ts
CHANGED
|
@@ -3,21 +3,26 @@ import type { TspSpan } from "@linxiraos/pi-wire";
|
|
|
3
3
|
import { formatDoubleTap, formatKeyHint, formatKeyHints, type KeyName } from "../app-keybindings";
|
|
4
4
|
import { editorKey } from "../chrome/keybinding-hints";
|
|
5
5
|
import { getKeybindings, type Keybinding } from "../keybindings";
|
|
6
|
-
import { registerNativeBlob } from "../native/blobs";
|
|
7
6
|
import { card, col, kbd, keyed, node, row, span, text } from "../native/describe";
|
|
8
7
|
import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
|
|
9
8
|
import { runTranscriptAction } from "../chat/transcript-actions";
|
|
10
|
-
import { plainLine } from "../native/spans";
|
|
9
|
+
import { compactText, plainLine, plainText } from "../native/spans";
|
|
10
|
+
|
|
11
11
|
import { isNativeRendering } from "../native/state";
|
|
12
|
+
import { isHyperlinkEnabled, urlHyperlink, urlLinkSpan } from "../render/hyperlink";
|
|
12
13
|
import { TERMINAL } from "../terminal-capabilities";
|
|
13
14
|
import { theme } from "../theme/theme";
|
|
14
15
|
import type { Component } from "../tui";
|
|
15
16
|
import { padding, replaceTabs, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../utils";
|
|
16
17
|
import tipsText from "./tips.txt" with { type: "text" };
|
|
17
18
|
|
|
18
|
-
/**
|
|
19
|
+
/** Leading marker for a tip that pitches Tern: shown only while no TSP surface
|
|
20
|
+
* is live, since a live one means the user is already in Tern. */
|
|
21
|
+
const NO_TERN_TIP_MARKER = /^\[NO-TERN\]\s*/;
|
|
22
|
+
|
|
23
|
+
/** tips.txt lines embedded at build time; blanks dropped. Key placeholders
|
|
19
24
|
* (see {@link expandTipKeys}) stay raw until render time. */
|
|
20
|
-
const
|
|
25
|
+
const TIP_LINES: readonly string[] = tipsText
|
|
21
26
|
.split("\n")
|
|
22
27
|
.map(line => line.trim())
|
|
23
28
|
.filter(line => line.length > 0);
|
|
@@ -34,6 +39,12 @@ export const WELCOME_SESSION_SLOTS = 4;
|
|
|
34
39
|
*/
|
|
35
40
|
export const WELCOME_LSP_SLOTS = 4;
|
|
36
41
|
|
|
42
|
+
/** Tips for the classic renderer: every tip, {@link NO_TERN_TIP_MARKER} stripped. */
|
|
43
|
+
const TIPS: readonly string[] = TIP_LINES.map(line => line.replace(NO_TERN_TIP_MARKER, ""));
|
|
44
|
+
|
|
45
|
+
/** Tips for a live TSP surface: those marked {@link NO_TERN_TIP_MARKER} dropped. */
|
|
46
|
+
const TSP_TIPS: readonly string[] = TIP_LINES.filter(line => !NO_TERN_TIP_MARKER.test(line));
|
|
47
|
+
|
|
37
48
|
/** Trailing marker that flags a tip as a "what's new" callout. Stripped before
|
|
38
49
|
* wrapping (with any preceding whitespace) and replaced by {@link NEW_TAG_TEXT}
|
|
39
50
|
* painted as a shimmering rainbow. Non-global so `.test` stays stateless. */
|
|
@@ -121,14 +132,44 @@ function expandTipKeys(tip: string): string {
|
|
|
121
132
|
});
|
|
122
133
|
}
|
|
123
134
|
|
|
124
|
-
|
|
135
|
+
/** Markdown-style links in tips.txt: `[Tern](https://scl.so/tern)` shows the label, linked to the URL. */
|
|
136
|
+
const TIP_LINK = /\[([^\]]+)\]\(([^\s)]+)\)/g;
|
|
137
|
+
|
|
138
|
+
/** Tip links as underlined OSC 8 labels, or `label (url)` where the terminal cannot link. */
|
|
139
|
+
function linkTipText(tip: string): string {
|
|
140
|
+
const linkable = isHyperlinkEnabled();
|
|
141
|
+
return tip.replace(TIP_LINK, (_link, label: string, url: string) =>
|
|
142
|
+
linkable ? urlHyperlink(url, theme.underline(label)) : `${label} (${url})`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** The tip as native text: escapes dropped, links as `href` spans. */
|
|
147
|
+
function tipSpans(tip: string): TspSpan[] {
|
|
148
|
+
const spans: TspSpan[] = [];
|
|
149
|
+
let cursor = 0;
|
|
150
|
+
for (const match of tip.matchAll(TIP_LINK)) {
|
|
151
|
+
const [link, label, url] = match;
|
|
152
|
+
if (match.index > cursor) spans.push(span(plainText(tip.slice(cursor, match.index))));
|
|
153
|
+
spans.push(urlLinkSpan(url, label));
|
|
154
|
+
cursor = match.index + link.length;
|
|
155
|
+
}
|
|
156
|
+
if (cursor < tip.length) spans.push(span(plainText(tip.slice(cursor))));
|
|
157
|
+
return spans;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The welcome tip as lines of at most `width` columns: `Tip:` and the body
|
|
162
|
+
* wrapped together, with no indent, so the banner can center each line.
|
|
163
|
+
* `[]` when `width` leaves no room for a useful line.
|
|
164
|
+
*/
|
|
165
|
+
export function renderWelcomeTip(tip: string, width: number, phase = 0): string[] {
|
|
125
166
|
const label = "Tip: ";
|
|
126
167
|
const labelWidth = visibleWidth(label);
|
|
127
|
-
const bodyBudget =
|
|
168
|
+
const bodyBudget = width - labelWidth;
|
|
128
169
|
if (bodyBudget < 8) return [];
|
|
129
170
|
|
|
130
171
|
const isNew = NEW_TIP_MARKER.test(tip);
|
|
131
|
-
const body = expandTipKeys(isNew ? tip.replace(NEW_TIP_MARKER, "") : tip);
|
|
172
|
+
const body = linkTipText(expandTipKeys(isNew ? tip.replace(NEW_TIP_MARKER, "") : tip));
|
|
132
173
|
|
|
133
174
|
const wrappedBody = wrapTextWithAnsi(replaceTabs(body), bodyBudget);
|
|
134
175
|
if (wrappedBody.length === 0) return [];
|
|
@@ -141,8 +182,8 @@ export function renderWelcomeTip(tip: string, boxWidth: number, phase = 0): stri
|
|
|
141
182
|
|
|
142
183
|
const lines = wrappedBody.map((line, index) => {
|
|
143
184
|
const styledBody = theme.fg("muted", line);
|
|
144
|
-
const content = index === 0 ? `${styledLabel}${styledBody}` : `${
|
|
145
|
-
return
|
|
185
|
+
const content = index === 0 ? `${styledLabel}${styledBody}` : `${styledBody}`;
|
|
186
|
+
return theme.italic(content);
|
|
146
187
|
});
|
|
147
188
|
|
|
148
189
|
if (isNew) {
|
|
@@ -153,10 +194,10 @@ export function renderWelcomeTip(tip: string, boxWidth: number, phase = 0): stri
|
|
|
153
194
|
const tag = renderNewTag(phase, encoding);
|
|
154
195
|
const tagWidth = 1 + visibleWidth(NEW_TAG_TEXT); // 1 = space separator
|
|
155
196
|
const lastLine = lines[lines.length - 1];
|
|
156
|
-
if (lastLine !== undefined && visibleWidth(lastLine) + tagWidth <=
|
|
197
|
+
if (lastLine !== undefined && visibleWidth(lastLine) + tagWidth <= width) {
|
|
157
198
|
lines[lines.length - 1] = `${lastLine} ${tag}`;
|
|
158
199
|
} else {
|
|
159
|
-
lines.push(
|
|
200
|
+
lines.push(continuationIndent + tag);
|
|
160
201
|
}
|
|
161
202
|
}
|
|
162
203
|
|
|
@@ -184,9 +225,10 @@ export class WelcomeComponent implements Component {
|
|
|
184
225
|
#animTimer: Timer | null = null;
|
|
185
226
|
#requestRender: (() => void) | null = null;
|
|
186
227
|
// Tip randomness is latched once so the tip is stable across renders, but
|
|
187
|
-
// the nerdfont-nag gate re-reads the live preset
|
|
188
|
-
// run under the default "unicode"
|
|
189
|
-
//
|
|
228
|
+
// the nerdfont-nag gate re-reads the live preset and the pool re-reads the
|
|
229
|
+
// live TSP state: the startup prepaint can run under the default "unicode"
|
|
230
|
+
// preset before settings resolve the real one, and an optimistic Tern start
|
|
231
|
+
// falls back to the classic renderer when `hello` never confirms.
|
|
190
232
|
#nagRoll: number | undefined;
|
|
191
233
|
#tipRoll: number | undefined;
|
|
192
234
|
// Render cache: the welcome box is the first transcript-area component, so
|
|
@@ -198,8 +240,8 @@ export class WelcomeComponent implements Component {
|
|
|
198
240
|
|
|
199
241
|
constructor(
|
|
200
242
|
private version: string,
|
|
201
|
-
private modelName: string,
|
|
202
|
-
private providerName: string,
|
|
243
|
+
private modelName: string = "",
|
|
244
|
+
private providerName: string = "",
|
|
203
245
|
private recentSessions: RecentSession[] = [],
|
|
204
246
|
/** Detected project servers; `null` means LSP is disabled and hides the section. */
|
|
205
247
|
private lspServers: LspServerInfo[] | null = [],
|
|
@@ -210,7 +252,7 @@ export class WelcomeComponent implements Component {
|
|
|
210
252
|
if (theme.getSymbolPreset() === "unicode" && this.#nagRoll < 0.1) {
|
|
211
253
|
return "Please use nerdfont 😭.";
|
|
212
254
|
}
|
|
213
|
-
return pickWeightedTip(TIPS, this.#tipRoll) || undefined;
|
|
255
|
+
return pickWeightedTip(isNativeRendering() ? TSP_TIPS : TIPS, this.#tipRoll) || undefined;
|
|
214
256
|
}
|
|
215
257
|
|
|
216
258
|
invalidate(): void {
|
|
@@ -339,7 +381,7 @@ export class WelcomeComponent implements Component {
|
|
|
339
381
|
];
|
|
340
382
|
if (tip) {
|
|
341
383
|
const isNew = NEW_TIP_MARKER.test(tip);
|
|
342
|
-
const tipText =
|
|
384
|
+
const tipText = compactText(tipSpans(expandTipKeys(isNew ? tip.replace(NEW_TIP_MARKER, "") : tip)));
|
|
343
385
|
const tipRow: NativeChild[] = [
|
|
344
386
|
node("icon", { name: "lightbulb", role: "zeta.welcome.tip-icon" }),
|
|
345
387
|
text(tipText, { wrap: "word", role: "zeta.welcome.tip-text" }),
|
|
@@ -587,7 +629,9 @@ export class WelcomeComponent implements Component {
|
|
|
587
629
|
}
|
|
588
630
|
|
|
589
631
|
// Randomly picked tip, rendered directly beneath the box.
|
|
590
|
-
|
|
632
|
+
// The banner centers every tip line, so the tip drops out entirely on
|
|
633
|
+
// narrow boxes rather than wrapping into unreadable slivers.
|
|
634
|
+
if (termWidth >= 16) lines.push(...this.#renderTip(boxWidth));
|
|
591
635
|
|
|
592
636
|
return lines;
|
|
593
637
|
}
|
|
@@ -680,34 +724,8 @@ export class WelcomeComponent implements Component {
|
|
|
680
724
|
}
|
|
681
725
|
}
|
|
682
726
|
|
|
683
|
-
/**
|
|
684
|
-
* {@link ZETA_LOGO} as SVG for the native welcome, on the terminal's grid: a
|
|
685
|
-
* cell is 3×6 units, so the 12×5-cell art spans 36×30 from (14,16). The left
|
|
686
|
-
* leg's `▒▒` tail is a half-opacity cell; the gradient spans the whole art in
|
|
687
|
-
* user space (per-axis normalized, like {@link gradientLogo}) so the tail
|
|
688
|
-
* keeps its colour. Tern mounts SVG blobs as live DOM, so the classes are
|
|
689
|
-
* animation hooks: `trace` (the outline, `pathLength=1` for a draw-on), `mark`
|
|
690
|
-
* (the fills) and the gradient stops `s0`–`s2`.
|
|
691
|
-
*/
|
|
692
|
-
const WELCOME_LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="10 12 44 38">
|
|
693
|
-
<defs><linearGradient id="g" gradientUnits="userSpaceOnUse" gradientTransform="matrix(36 0 0 30 14 16)" x1="0" y1="0" x2="1" y2="1">
|
|
694
|
-
<stop class="s0" offset="0" stop-color="#ed4abf"/><stop class="s1" offset=".5" stop-color="#9b4dff"/><stop class="s2" offset="1" stop-color="#5ad8e6"/>
|
|
695
|
-
</linearGradient></defs>
|
|
696
|
-
<path class="mark" fill="url(#g)" d="M14 16h36v6h-9v24h-6V22h-6v12h-6V22h-9z"/>
|
|
697
|
-
<rect class="mark" fill="url(#g)" opacity=".5" x="23" y="34" width="6" height="6"/>
|
|
698
|
-
<path class="trace" fill="none" stroke="url(#g)" stroke-width="1" stroke-linejoin="round" pathLength="1" d="M14 16h36v6h-9v24h-6V22h-6v18h-6V22h-9z"/>
|
|
699
|
-
</svg>`;
|
|
700
|
-
|
|
701
|
-
let welcomeLogoId: string | undefined;
|
|
702
|
-
|
|
703
|
-
/** The registered blob id of {@link WELCOME_LOGO_SVG}. */
|
|
704
|
-
function welcomeLogoBlob(): string {
|
|
705
|
-
welcomeLogoId ??= registerNativeBlob(new TextEncoder().encode(WELCOME_LOGO_SVG), "image/svg+xml");
|
|
706
|
-
return welcomeLogoId;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
727
|
/** Block-grid brand mark shared by the welcome and setup surfaces. */
|
|
710
|
-
export const ZETA_LOGO = ["
|
|
728
|
+
export const ZETA_LOGO = ["█████████╗", "╚════████║", " ████╔═╝", " ████╔═╝ ", "█████████╗", "╚════════╝"];
|
|
711
729
|
|
|
712
730
|
/** The block-grid brand mark as accent lines; `shimmer` declares the terminal-clocked shine sweep. */
|
|
713
731
|
export function logoNode(lines: readonly string[], shimmer: boolean): NativeNode {
|
|
@@ -56,9 +56,20 @@ interface WordQuery extends WordCompletionQuery {
|
|
|
56
56
|
|
|
57
57
|
/** Editor text before the word starting at `start`, truncated to its last {@link BEFORE_LIMIT} code units. */
|
|
58
58
|
function textBefore(lines: readonly string[], cursorLine: number, start: number): string {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const head = (lines[cursorLine] ?? "").slice(0, start);
|
|
60
|
+
// Find the first line the capped tail reaches, then join once.
|
|
61
|
+
let length = head.length;
|
|
62
|
+
let first = cursorLine;
|
|
63
|
+
while (first > 0 && length < BEFORE_LIMIT) {
|
|
64
|
+
first--;
|
|
65
|
+
length += (lines[first] ?? "").length + 1;
|
|
66
|
+
}
|
|
67
|
+
let text = head;
|
|
68
|
+
if (first < cursorLine) {
|
|
69
|
+
const parts: string[] = [];
|
|
70
|
+
for (let line = first; line < cursorLine; line++) parts.push(lines[line] ?? "");
|
|
71
|
+
parts.push(head);
|
|
72
|
+
text = parts.join("\n");
|
|
62
73
|
}
|
|
63
74
|
if (text.length <= BEFORE_LIMIT) return text;
|
|
64
75
|
let cut = text.length - BEFORE_LIMIT;
|
|
@@ -111,6 +122,16 @@ export class WordCompletionProvider implements EditorTextAssistProvider {
|
|
|
111
122
|
#lastShown: { before: string; word: string } | undefined;
|
|
112
123
|
/** What the last `getWordCompletion` returned, so a fetch repaints only when it changes that. */
|
|
113
124
|
#displayed: { key: string; suffix: string | null } | undefined;
|
|
125
|
+
/** Last {@link #query} input snapshot (lines are copied: the editor mutates its array in place). */
|
|
126
|
+
#queryMemo:
|
|
127
|
+
| {
|
|
128
|
+
method: WordCompletionMethod;
|
|
129
|
+
lines: readonly string[];
|
|
130
|
+
cursorLine: number;
|
|
131
|
+
cursorCol: number;
|
|
132
|
+
query: WordQuery | undefined;
|
|
133
|
+
}
|
|
134
|
+
| undefined;
|
|
114
135
|
|
|
115
136
|
/** Invoked when an asynchronous answer changes the rendered ghost text. */
|
|
116
137
|
onUpdate: (() => void) | undefined;
|
|
@@ -171,11 +192,23 @@ export class WordCompletionProvider implements EditorTextAssistProvider {
|
|
|
171
192
|
|
|
172
193
|
#query(lines: readonly string[], cursorLine: number, cursorCol: number): WordQuery | undefined {
|
|
173
194
|
if (this.#method === "off") return undefined;
|
|
195
|
+
// Rendering asks every frame; reuse the answer until the buffer, caret, or engine changes.
|
|
196
|
+
const memo = this.#queryMemo;
|
|
197
|
+
if (
|
|
198
|
+
memo?.method === this.#method &&
|
|
199
|
+
memo.cursorLine === cursorLine &&
|
|
200
|
+
memo.cursorCol === cursorCol &&
|
|
201
|
+
memo.lines.length === lines.length &&
|
|
202
|
+
memo.lines.every((line, index) => line === lines[index])
|
|
203
|
+
) {
|
|
204
|
+
return memo.query;
|
|
205
|
+
}
|
|
174
206
|
const query = wordCompletionQuery(lines, cursorLine, cursorCol, this.#prose);
|
|
175
|
-
if (!query) return undefined;
|
|
176
207
|
// The method keeps a request still in flight for the previous engine from
|
|
177
208
|
// shadowing the same word state on the new one.
|
|
178
|
-
|
|
209
|
+
const result = query && { key: `${this.#method}\u0000${query.prefix}\u0000${query.before}`, ...query };
|
|
210
|
+
this.#queryMemo = { method: this.#method, lines: lines.slice(), cursorLine, cursorCol, query: result };
|
|
211
|
+
return result;
|
|
179
212
|
}
|
|
180
213
|
|
|
181
214
|
#project(query: WordQuery): string | null {
|