@linxiraos/pi-tui 1.1.27 → 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 +47 -463
- package/README.md +7 -5
- package/dist/types/app-keybindings.d.ts +1 -1
- 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 +2 -2
- 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 +5 -0
- package/dist/types/chat/chat-transcript-builder.d.ts +2 -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 +15 -2
- package/dist/types/chrome/shared.d.ts +9 -0
- 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 +15 -4
- package/dist/types/components/form.d.ts +1 -0
- package/dist/types/components/image.d.ts +12 -44
- 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/markdown.d.ts +36 -3
- 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 +1 -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/backend.d.ts +3 -2
- package/dist/types/native/blobs.d.ts +7 -3
- package/dist/types/native/encode.d.ts +3 -4
- package/dist/types/native/overlay.d.ts +5 -0
- 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 +2 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +11 -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/history-search.d.ts +10 -0
- package/dist/types/overlays/hook-selector.d.ts +9 -4
- 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 +23 -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/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/usage-dashboard.d.ts +8 -4
- package/dist/types/overlays/usage-display.d.ts +3 -1
- package/dist/types/prompt/composer.d.ts +1 -1
- package/dist/types/prompt/custom-editor.d.ts +11 -8
- package/dist/types/prompt/image-source.d.ts +2 -1
- package/dist/types/prompt/interactive-context-helpers.d.ts +4 -1
- package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
- package/dist/types/prompt/welcome.d.ts +6 -1
- 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/status-line/component.d.ts +3 -0
- 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 +27 -35
- package/dist/types/terminal-multiplexer.d.ts +127 -14
- package/dist/types/terminal.d.ts +22 -6
- 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/tmux.d.ts +11 -4
- 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 +0 -4
- package/dist/types/tools/write.d.ts +9 -2
- package/dist/types/tools/yield.d.ts +22 -0
- package/dist/types/tui.d.ts +31 -0
- package/dist/types/utils.d.ts +6 -0
- package/dist/types/vim.d.ts +19 -5
- package/package.json +13 -9
- package/src/app-keybindings.d.ts +1 -1
- package/src/app-keybindings.ts +1 -1
- 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 +26 -5
- package/src/apps/git/git-tui.ts +2 -2
- package/src/apps/git/help.ts +29 -20
- 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/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 +5 -0
- package/src/chat/assistant-message.ts +219 -50
- package/src/chat/chat-transcript-builder.d.ts +2 -0
- package/src/chat/chat-transcript-builder.ts +5 -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 +80 -6
- package/src/chat/user-message.d.ts +15 -2
- package/src/chat/user-message.ts +55 -3
- package/src/chrome/shared.ts +53 -0
- package/src/chrome/transcript-container.d.ts +6 -0
- package/src/chrome/transcript-container.ts +93 -13
- 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 +15 -4
- package/src/components/editor.ts +294 -46
- package/src/components/form.d.ts +1 -0
- package/src/components/form.ts +8 -0
- package/src/components/image.d.ts +12 -44
- package/src/components/image.ts +47 -151
- 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/markdown.d.ts +36 -3
- package/src/components/markdown.ts +433 -318
- 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 +1 -1
- package/src/fuzzy.d.ts +29 -1
- package/src/fuzzy.ts +115 -30
- package/src/key-hint-format.d.ts +1 -1
- package/src/key-hint-format.ts +1 -1
- 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 +4 -4
- 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/backend.d.ts +3 -2
- package/src/native/backend.ts +174 -18
- package/src/native/blobs.d.ts +7 -3
- package/src/native/blobs.ts +47 -12
- package/src/native/encode.d.ts +3 -4
- package/src/native/encode.ts +58 -31
- package/src/native/overlay.d.ts +5 -0
- package/src/native/overlay.ts +13 -0
- package/src/native/reconcile.ts +4 -0
- 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 +44 -29
- package/src/overlays/agent-transcript-viewer.ts +91 -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 +7 -10
- package/src/overlays/btw-history-panel.ts +12 -1
- package/src/overlays/copy-selector.ts +30 -8
- 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/jobs-panel.ts +28 -46
- package/src/overlays/model-browser.ts +341 -110
- package/src/overlays/model-hub.ts +300 -49
- package/src/overlays/move-overlay.ts +28 -14
- package/src/overlays/oauth-selector.ts +54 -70
- package/src/overlays/plan-review-overlay.ts +194 -70
- package/src/overlays/plugin-settings.ts +3 -3
- package/src/overlays/report-panel.ts +2 -2
- package/src/overlays/rewind-selector.ts +86 -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 +4 -3
- 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/tree-selector.ts +41 -20
- package/src/overlays/usage-dashboard.ts +100 -50
- package/src/overlays/usage-display.ts +10 -4
- package/src/prompt/attachment-chips.ts +15 -11
- package/src/prompt/composer-attachments.ts +6 -4
- package/src/prompt/composer.ts +4 -15
- package/src/prompt/custom-editor.ts +54 -26
- package/src/prompt/image-source.ts +2 -1
- package/src/prompt/interactive-context-helpers.ts +30 -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/tips.txt +6 -5
- package/src/prompt/welcome.ts +55 -12
- 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 +11 -0
- 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/status-line/component.ts +292 -78
- 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 +27 -35
- package/src/terminal-capabilities.ts +75 -189
- package/src/terminal-multiplexer.d.ts +127 -14
- package/src/terminal-multiplexer.ts +112 -33
- package/src/terminal.d.ts +22 -6
- package/src/terminal.ts +139 -37
- 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 +6 -1
- 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/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.ts +92 -42
- 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 +149 -50
- package/src/tools/task.d.ts +0 -4
- package/src/tools/task.ts +112 -59
- 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 +31 -0
- package/src/tui.ts +309 -160
- 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.ts +8 -0
package/src/theme/schema.d.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import type { SpinnerFramesOverride } from "./symbols";
|
|
2
2
|
export type ColorValue = string | number;
|
|
3
|
+
/**
|
|
4
|
+
* Tokens a theme is allowed to omit. They resolve to the terminal default
|
|
5
|
+
* foreground, so `Theme.fg()` stays total for callers that paint a token a theme
|
|
6
|
+
* never declared (e.g. a `modelTags.<role>.color` reference) while
|
|
7
|
+
* `Theme.hasColor()` still reports them as unset.
|
|
8
|
+
*/
|
|
9
|
+
export declare const OPTIONAL_THEME_COLOR_RECORD: {
|
|
10
|
+
assistantMessageText: true;
|
|
11
|
+
};
|
|
3
12
|
export interface ThemeJson {
|
|
4
13
|
$schema?: string;
|
|
5
14
|
name: string;
|
|
6
15
|
vars?: Record<string, ColorValue>;
|
|
7
|
-
colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax"> & {
|
|
16
|
+
colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax" | keyof typeof OPTIONAL_THEME_COLOR_RECORD> & Partial<Record<keyof typeof OPTIONAL_THEME_COLOR_RECORD, ColorValue>> & {
|
|
8
17
|
thinkingMax?: ColorValue;
|
|
9
18
|
};
|
|
10
19
|
export?: {
|
|
@@ -36,7 +45,7 @@ export interface ThemeJson {
|
|
|
36
45
|
spinnerFrames?: SpinnerFramesOverride;
|
|
37
46
|
};
|
|
38
47
|
}
|
|
39
|
-
export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "thinkingMax" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
|
|
48
|
+
export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "assistantMessageText" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "thinkingMax" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
|
|
40
49
|
/** Check if a string is a valid ThemeColor value */
|
|
41
50
|
export declare function isValidThemeColor(color: string): color is ThemeColor;
|
|
42
51
|
export type ThemeBg = "selectedBg" | "userMessageBg" | "customMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg" | "statusLineBg";
|
package/src/theme/schema.ts
CHANGED
|
@@ -6,11 +6,22 @@ import type { SpinnerFramesOverride } from "./symbols";
|
|
|
6
6
|
|
|
7
7
|
export type ColorValue = string | number;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Tokens a theme is allowed to omit. They resolve to the terminal default
|
|
11
|
+
* foreground, so `Theme.fg()` stays total for callers that paint a token a theme
|
|
12
|
+
* never declared (e.g. a `modelTags.<role>.color` reference) while
|
|
13
|
+
* `Theme.hasColor()` still reports them as unset.
|
|
14
|
+
*/
|
|
15
|
+
export const OPTIONAL_THEME_COLOR_RECORD = {
|
|
16
|
+
assistantMessageText: true,
|
|
17
|
+
} satisfies Partial<Record<ThemeColor, true>>;
|
|
18
|
+
|
|
9
19
|
export interface ThemeJson {
|
|
10
20
|
$schema?: string;
|
|
11
21
|
name: string;
|
|
12
22
|
vars?: Record<string, ColorValue>;
|
|
13
|
-
colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax"
|
|
23
|
+
colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax" | keyof typeof OPTIONAL_THEME_COLOR_RECORD> &
|
|
24
|
+
Partial<Record<keyof typeof OPTIONAL_THEME_COLOR_RECORD, ColorValue>> & { thinkingMax?: ColorValue };
|
|
14
25
|
export?: {
|
|
15
26
|
pageBg?: ColorValue;
|
|
16
27
|
cardBg?: ColorValue;
|
|
@@ -52,6 +63,7 @@ export type ThemeColor =
|
|
|
52
63
|
| "muted"
|
|
53
64
|
| "dim"
|
|
54
65
|
| "text"
|
|
66
|
+
| "assistantMessageText"
|
|
55
67
|
| "thinkingText"
|
|
56
68
|
| "userMessageText"
|
|
57
69
|
| "customMessageText"
|
|
@@ -115,6 +127,7 @@ const THEME_COLOR_RECORD = {
|
|
|
115
127
|
muted: true,
|
|
116
128
|
dim: true,
|
|
117
129
|
text: true,
|
|
130
|
+
assistantMessageText: true,
|
|
118
131
|
thinkingText: true,
|
|
119
132
|
userMessageText: true,
|
|
120
133
|
customMessageText: true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TERMINAL } from "../terminal-capabilities";
|
|
2
|
-
import { hexToOklch, oklchCusp, oklchToHex, relativeLuminance } from "@linxiraos/pi-utils";
|
|
2
|
+
import { hexToOklch, hueDistance, oklchCusp, oklchToHex, relativeLuminance } from "@linxiraos/pi-utils";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Derive a stable 32-bit hash from a string using djb2.
|
|
@@ -49,12 +49,6 @@ const MIN_HUE_DISTANCE = 10;
|
|
|
49
49
|
/** OKLCH chroma threshold below which hue is meaningless (near-gray). */
|
|
50
50
|
const MIN_CHROMA_FOR_HUE = 0.03;
|
|
51
51
|
|
|
52
|
-
/** Angular distance between two hue values (0-360). */
|
|
53
|
-
function hueDistance(a: number, b: number): number {
|
|
54
|
-
const d = Math.abs(a - b);
|
|
55
|
-
return Math.min(d, 360 - d);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
52
|
/**
|
|
59
53
|
* Parse OKLCH hue (0-360) from a hex color string.
|
|
60
54
|
* Returns undefined for near-gray colors where hue is not meaningful.
|
package/src/theme/symbols.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
|
|
|
2
2
|
/**
|
|
3
3
|
* All available symbol keys organized by category.
|
|
4
4
|
*/
|
|
5
|
-
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "progress.filled" | "progress.empty" | "context.speculation" | "context.compaction" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxDotted.horizontal" | "boxDotted.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.powerlineCapLeft" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.prewalk" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.worktree" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.pin" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.subscription" | "icon.advisor" | "icon.advisorClosed" | "icon.time" | "icon.omp" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.cacheMiss" | "icon.input" | "icon.output" | "icon.throughput" | "icon.intelligence" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.vimNormal" | "icon.vimInsert" | "icon.vimVisual" | "icon.vimVisualLine" | "cmd.action" | "cmd.prompt" | "cmd.extension" | "cmd.settings" | "cmd.gear" | "cmd.shield" | "cmd.wave" | "cmd.compass" | "cmd.inbox" | "cmd.swap" | "cmd.expand" | "cmd.computer" | "cmd.eye" | "cmd.todo" | "cmd.stats" | "cmd.news" | "cmd.keyboard" | "cmd.export" | "cmd.clipboard" | "cmd.share" | "cmd.broadcast" | "cmd.globe" | "cmd.copy" | "cmd.plus" | "cmd.restart" | "cmd.eraser" | "cmd.trash" | "cmd.compress" | "cmd.vibrate" | "cmd.handoff" | "cmd.history" | "cmd.question" | "cmd.rocket" | "cmd.stethoscope" | "cmd.redo" | "cmd.bug" | "cmd.memory" | "cmd.pencil" | "cmd.folderMove" | "cmd.folderPlus" | "cmd.folderMinus" | "cmd.hammer" | "cmd.power" | "cmd.cart" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.max" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "advisor.rail" | "skill.rail" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.julia" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "chip.image" | "chip.video" | "chip.paste" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.launch" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.goal" | "tool.irc" | "tool.delete" | "tool.move" | "key.ctrl" | "key.ctrlMac" | "key.shift" | "key.alt" | "key.altMac" | "key.super" | "key.superMac" | "key.joiner" | "key.enter" | "key.esc" | "key.tab" | "key.backspace" | "key.delete" | "key.space" | "key.up" | "key.down" | "key.left" | "key.right";
|
|
5
|
+
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "progress.filled" | "progress.empty" | "context.speculation" | "context.compaction" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxDotted.horizontal" | "boxDotted.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.powerlineCapLeft" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.prewalk" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.worktree" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.pin" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.subscription" | "icon.advisor" | "icon.advisorClosed" | "icon.time" | "icon.omp" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.cacheMiss" | "icon.input" | "icon.output" | "icon.throughput" | "icon.intelligence" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.vimNormal" | "icon.vimInsert" | "icon.vimVisual" | "icon.vimVisualLine" | "icon.vimReplace" | "cmd.action" | "cmd.prompt" | "cmd.extension" | "cmd.settings" | "cmd.gear" | "cmd.shield" | "cmd.wave" | "cmd.compass" | "cmd.inbox" | "cmd.swap" | "cmd.expand" | "cmd.computer" | "cmd.eye" | "cmd.todo" | "cmd.stats" | "cmd.news" | "cmd.keyboard" | "cmd.export" | "cmd.clipboard" | "cmd.share" | "cmd.broadcast" | "cmd.globe" | "cmd.copy" | "cmd.plus" | "cmd.restart" | "cmd.eraser" | "cmd.trash" | "cmd.compress" | "cmd.vibrate" | "cmd.handoff" | "cmd.history" | "cmd.question" | "cmd.rocket" | "cmd.stethoscope" | "cmd.redo" | "cmd.bug" | "cmd.memory" | "cmd.pencil" | "cmd.folderMove" | "cmd.folderPlus" | "cmd.folderMinus" | "cmd.hammer" | "cmd.power" | "cmd.cart" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.max" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "advisor.rail" | "skill.rail" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.julia" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "chip.image" | "chip.video" | "chip.paste" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.launch" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.goal" | "tool.irc" | "tool.delete" | "tool.move" | "key.ctrl" | "key.ctrlMac" | "key.shift" | "key.alt" | "key.altMac" | "key.super" | "key.superMac" | "key.joiner" | "key.enter" | "key.esc" | "key.tab" | "key.backspace" | "key.delete" | "key.space" | "key.up" | "key.down" | "key.left" | "key.right";
|
|
6
6
|
export type SymbolMap = Record<SymbolKey, string>;
|
|
7
7
|
/**
|
|
8
8
|
* Icon vocabulary for slash-command autocomplete type indicators. Each name
|
package/src/theme/symbols.ts
CHANGED
|
@@ -129,6 +129,7 @@ export type SymbolKey =
|
|
|
129
129
|
| "icon.vimInsert"
|
|
130
130
|
| "icon.vimVisual"
|
|
131
131
|
| "icon.vimVisualLine"
|
|
132
|
+
| "icon.vimReplace"
|
|
132
133
|
// Slash-command type indicators (autocomplete); names without an existing
|
|
133
134
|
// icon.* equivalent — see SlashCommandIconName for the full vocabulary.
|
|
134
135
|
| "cmd.action"
|
|
@@ -511,6 +512,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
511
512
|
"icon.vimInsert": "▎",
|
|
512
513
|
"icon.vimVisual": "◉",
|
|
513
514
|
"icon.vimVisualLine": "≡",
|
|
515
|
+
"icon.vimReplace": "▁",
|
|
514
516
|
// Slash-command type indicators
|
|
515
517
|
"cmd.action": "❯",
|
|
516
518
|
"cmd.prompt": "✎",
|
|
@@ -921,6 +923,8 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
921
923
|
"icon.vimVisual": "\uf06e",
|
|
922
924
|
// pick: (nf-fa-bars) | alt: (nf-fa-align_left)
|
|
923
925
|
"icon.vimVisualLine": "\uf0c9",
|
|
926
|
+
// pick: (nf-fa-exchange) | alt: (nf-fa-repeat)
|
|
927
|
+
"icon.vimReplace": "\uf0ec",
|
|
924
928
|
// Slash-command type indicators
|
|
925
929
|
// pick: (nf-cod-terminal) | alt: (nf-fa-terminal)
|
|
926
930
|
"cmd.action": "\uea85",
|
|
@@ -1258,7 +1262,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
1258
1262
|
"icon.advisor": "(adv)",
|
|
1259
1263
|
"icon.advisorClosed": "(adv)",
|
|
1260
1264
|
"icon.time": "t:",
|
|
1261
|
-
"icon.omp": "
|
|
1265
|
+
"icon.omp": "zeta",
|
|
1262
1266
|
"icon.ghost": "@",
|
|
1263
1267
|
"icon.agents": "AG",
|
|
1264
1268
|
"icon.job": "bg",
|
|
@@ -1289,6 +1293,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
1289
1293
|
"icon.vimInsert": "I",
|
|
1290
1294
|
"icon.vimVisual": "V",
|
|
1291
1295
|
"icon.vimVisualLine": "L",
|
|
1296
|
+
"icon.vimReplace": "R",
|
|
1292
1297
|
// Slash-command type indicators — unused; the icon column is disabled in ASCII mode
|
|
1293
1298
|
"cmd.action": "",
|
|
1294
1299
|
"cmd.prompt": "",
|
|
@@ -54,6 +54,13 @@ export declare class Theme {
|
|
|
54
54
|
* against. Built once per instance and frozen; callers share the object.
|
|
55
55
|
*/
|
|
56
56
|
get sessionAccentInputs(): SessionAccentTheme;
|
|
57
|
+
/**
|
|
58
|
+
* True when the theme paints `color` with a real foreground — i.e. the token
|
|
59
|
+
* exists and is not the empty string (terminal default, `\x1b[39m`). Optional
|
|
60
|
+
* tokens absent from the theme JSON are also false. Callers use this to keep
|
|
61
|
+
* terminal-default behavior byte-for-byte when an optional token is unset.
|
|
62
|
+
*/
|
|
63
|
+
hasColor(color: ThemeColor): boolean;
|
|
57
64
|
fg(color: ThemeColor, text: string): string;
|
|
58
65
|
/** Apply a foreground, replacing terminal-default tokens with the theme's contrast-safe fallback. */
|
|
59
66
|
fgResolved(color: ThemeColor, text: string): string;
|
package/src/theme/theme-class.ts
CHANGED
|
@@ -2,7 +2,13 @@ import { colorLuma, relativeLuminance } from "@linxiraos/pi-utils/color";
|
|
|
2
2
|
import * as logger from "@linxiraos/pi-utils/logger";
|
|
3
3
|
import chalk from "@linxiraos/pi-utils/chalk";
|
|
4
4
|
import { bgAnsi, colorToAnsi, fgAnsi, resolveToHex } from "./color";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type ColorMode,
|
|
7
|
+
isValidThemeColor,
|
|
8
|
+
OPTIONAL_THEME_COLOR_RECORD,
|
|
9
|
+
type ThemeBg,
|
|
10
|
+
type ThemeColor,
|
|
11
|
+
} from "./schema";
|
|
6
12
|
import type { SessionAccentTheme } from "./session-color";
|
|
7
13
|
import {
|
|
8
14
|
type SlashCommandIconName,
|
|
@@ -204,6 +210,14 @@ export class Theme {
|
|
|
204
210
|
this.#fgColors[key] = fgAnsi(value, mode);
|
|
205
211
|
this.#hexFgColors[key] = hex;
|
|
206
212
|
}
|
|
213
|
+
// A theme may omit an optional token, but callers can still name it (a
|
|
214
|
+
// `modelTags.<role>.color` reference is validated against the theme color
|
|
215
|
+
// list). Resolve those to the terminal default so `fg()` stays total while
|
|
216
|
+
// `hasColor()` reports them as unset.
|
|
217
|
+
for (const key of Object.keys(OPTIONAL_THEME_COLOR_RECORD) as ThemeColor[]) {
|
|
218
|
+
this.#fgColors[key] ??= "\x1b[39m";
|
|
219
|
+
this.#hexFgColors[key] ??= "";
|
|
220
|
+
}
|
|
207
221
|
this.#bgColors = {} as Record<ThemeBg, string>;
|
|
208
222
|
this.#hexBgColors = {} as Record<ThemeBg, string>;
|
|
209
223
|
for (const [key, value] of Object.entries(bgColors) as [ThemeBg, string | number][]) {
|
|
@@ -322,6 +336,17 @@ export class Theme {
|
|
|
322
336
|
return this.#sessionAccentInputs;
|
|
323
337
|
}
|
|
324
338
|
|
|
339
|
+
/**
|
|
340
|
+
* True when the theme paints `color` with a real foreground — i.e. the token
|
|
341
|
+
* exists and is not the empty string (terminal default, `\x1b[39m`). Optional
|
|
342
|
+
* tokens absent from the theme JSON are also false. Callers use this to keep
|
|
343
|
+
* terminal-default behavior byte-for-byte when an optional token is unset.
|
|
344
|
+
*/
|
|
345
|
+
hasColor(color: ThemeColor): boolean {
|
|
346
|
+
const ansi = this.#fgColors[color];
|
|
347
|
+
return ansi !== undefined && ansi !== "\x1b[39m";
|
|
348
|
+
}
|
|
349
|
+
|
|
325
350
|
fg(color: ThemeColor, text: string): string {
|
|
326
351
|
const ansi = this.#fgColors[color];
|
|
327
352
|
if (!ansi) throw new Error(`Unknown theme color: ${color}`);
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"colors": {
|
|
35
35
|
"type": "object",
|
|
36
|
-
"description": "Theme color definitions (all required except thinkingMax)",
|
|
36
|
+
"description": "Theme color definitions (all required except thinkingMax and assistantMessageText)",
|
|
37
37
|
"required": [
|
|
38
38
|
"accent",
|
|
39
39
|
"border",
|
|
@@ -143,6 +143,10 @@
|
|
|
143
143
|
"$ref": "#/$defs/colorValue",
|
|
144
144
|
"description": "Default text color (usually empty string)"
|
|
145
145
|
},
|
|
146
|
+
"assistantMessageText": {
|
|
147
|
+
"$ref": "#/$defs/colorValue",
|
|
148
|
+
"description": "Assistant paragraph prose color (optional; empty string or omitted keeps the terminal default foreground)"
|
|
149
|
+
},
|
|
146
150
|
"thinkingText": {
|
|
147
151
|
"$ref": "#/$defs/colorValue",
|
|
148
152
|
"description": "Thinking text color"
|
package/src/theme/theme.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { getAvailableThemes, getAvailableThemesWithPaths, getThemeByName, type T
|
|
|
6
6
|
export { isValidThemeColor, type ThemeBg, type ThemeColor } from "./schema";
|
|
7
7
|
export { getAvailableSymbolPresets, isValidSymbolPreset, type SpinnerType, type SymbolKey, type SymbolPreset, } from "./symbols";
|
|
8
8
|
export { Theme } from "./theme-class";
|
|
9
|
-
export { createHighlightStream, getEditorTheme, getMarkdownTheme, getSelectListTheme, getSettingsListTheme, getSymbolTheme, highlightCode, setMarkdownMermaidRendering, warmHighlighter, } from "./tui-adapters";
|
|
9
|
+
export { createHighlightStream, getEditorTheme, getMarkdownTheme, getMarkdownThemeWithLinkTargets, getSelectListTheme, getSettingsListTheme, getSymbolTheme, highlightCode, setMarkdownMermaidRendering, warmHighlighter, } from "./tui-adapters";
|
|
10
10
|
/** Active theme; imports remain live across initialization, previews, and theme switches. */
|
|
11
11
|
export declare var theme: Theme;
|
|
12
12
|
type ThemeBinding = (value: Theme) => void;
|
package/src/theme/theme.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Terminal, TerminalAppearance } from "../terminal";
|
|
|
5
5
|
import { colorLuma } from "@linxiraos/pi-utils/color";
|
|
6
6
|
import { getCustomThemesDir } from "@linxiraos/pi-utils/dirs";
|
|
7
7
|
import * as logger from "@linxiraos/pi-utils/logger";
|
|
8
|
+
import { terminalMultiplexerSessions } from "../terminal-multiplexer";
|
|
8
9
|
import { setActiveSymbolTheme } from "./active-symbols";
|
|
9
10
|
import { ansi256ToHex, resolveThemeColors, resolveVarRefs } from "./color";
|
|
10
11
|
import {
|
|
@@ -34,6 +35,7 @@ export {
|
|
|
34
35
|
createHighlightStream,
|
|
35
36
|
getEditorTheme,
|
|
36
37
|
getMarkdownTheme,
|
|
38
|
+
getMarkdownThemeWithLinkTargets,
|
|
37
39
|
getSelectListTheme,
|
|
38
40
|
getSettingsListTheme,
|
|
39
41
|
getSymbolTheme,
|
|
@@ -49,10 +51,12 @@ var terminalReportedAppearance: "dark" | "light" | undefined;
|
|
|
49
51
|
var macOSReportedAppearance: "dark" | "light" | undefined;
|
|
50
52
|
|
|
51
53
|
function shouldUseMacOSAppearanceFallback(): boolean {
|
|
52
|
-
//
|
|
53
|
-
// appearance cannot be trusted there. Fall back
|
|
54
|
-
// without letting it override valid terminal
|
|
55
|
-
|
|
54
|
+
// Some multiplexers break OSC 11 passthrough on some platforms (Zellij on
|
|
55
|
+
// macOS), so terminal-derived appearance cannot be trusted there. Fall back
|
|
56
|
+
// to host macOS appearance without letting it override valid terminal
|
|
57
|
+
// signals elsewhere.
|
|
58
|
+
const platform = process.platform;
|
|
59
|
+
return terminalMultiplexerSessions().some(multiplexer => multiplexer.backgroundQueryBrokenOn?.includes(platform));
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
function detectTerminalBackground(): "dark" | "light" {
|
|
@@ -16,6 +16,9 @@ export declare function createHighlightStream(lang?: string, highlightTheme?: Th
|
|
|
16
16
|
/** Warm native syntax grammars off-thread once per process. */
|
|
17
17
|
export declare function warmHighlighter(): Promise<void>;
|
|
18
18
|
export declare function getSymbolTheme(): SymbolTheme;
|
|
19
|
+
/** Only themes assembled here have stable callbacks and private link state. */
|
|
20
|
+
export declare function canCacheMarkdownListItems(candidate: MarkdownTheme): boolean;
|
|
21
|
+
export declare function getMarkdownThemeWithLinkTargets(targets: ReadonlyMap<string, string>): MarkdownTheme;
|
|
19
22
|
export declare function setMarkdownMermaidRendering(enabled: boolean): void;
|
|
20
23
|
export declare function getMarkdownTheme(): MarkdownTheme;
|
|
21
24
|
export declare function getSelectListTheme(): SelectListTheme;
|
|
@@ -183,8 +183,75 @@ export function getSymbolTheme(): SymbolTheme {
|
|
|
183
183
|
|
|
184
184
|
let cachedMarkdownTheme: MarkdownTheme | undefined;
|
|
185
185
|
let cachedMarkdownThemeRef: Theme | undefined;
|
|
186
|
+
const cacheableMarkdownThemes = new WeakMap<
|
|
187
|
+
MarkdownTheme,
|
|
188
|
+
{
|
|
189
|
+
theme: Theme;
|
|
190
|
+
fields: ReadonlyArray<readonly [PropertyKey, unknown]>;
|
|
191
|
+
symbolFields: ReadonlyArray<readonly [PropertyKey, unknown]>;
|
|
192
|
+
symbolValues: string;
|
|
193
|
+
}
|
|
194
|
+
>();
|
|
195
|
+
const linkTargetSnapshots = new WeakMap<ReadonlyMap<string, string>, ReadonlyMap<string, string>>();
|
|
186
196
|
let markdownMermaidRendering = true;
|
|
187
197
|
|
|
198
|
+
function ownFields(object: object): ReadonlyArray<readonly [PropertyKey, unknown]> {
|
|
199
|
+
return Reflect.ownKeys(object).map(key => [key, Object.getOwnPropertyDescriptor(object, key)?.value]);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function hasUnchangedOwnFields(object: object, fields: ReadonlyArray<readonly [PropertyKey, unknown]>): boolean {
|
|
203
|
+
if (Reflect.ownKeys(object).length !== fields.length) return false;
|
|
204
|
+
return fields.every(([key, value]) => {
|
|
205
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
206
|
+
return descriptor !== undefined && "value" in descriptor && descriptor.value === value;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Only themes assembled here have stable callbacks and private link state. */
|
|
211
|
+
export function canCacheMarkdownListItems(candidate: MarkdownTheme): boolean {
|
|
212
|
+
const registered = cacheableMarkdownThemes.get(candidate);
|
|
213
|
+
if (registered === undefined || registered.theme !== theme || candidate.symbols === undefined) return false;
|
|
214
|
+
if (!hasUnchangedOwnFields(candidate, registered.fields)) return false;
|
|
215
|
+
if (!hasUnchangedOwnFields(candidate.symbols, registered.symbolFields)) return false;
|
|
216
|
+
try {
|
|
217
|
+
return JSON.stringify(candidate.symbols) === registered.symbolValues;
|
|
218
|
+
} catch {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function registerCacheableMarkdownTheme(markdownTheme: MarkdownTheme): void {
|
|
224
|
+
if (markdownTheme.symbols === undefined) return;
|
|
225
|
+
cacheableMarkdownThemes.set(markdownTheme, {
|
|
226
|
+
theme,
|
|
227
|
+
fields: ownFields(markdownTheme),
|
|
228
|
+
symbolFields: ownFields(markdownTheme.symbols),
|
|
229
|
+
symbolValues: JSON.stringify(markdownTheme.symbols),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function getMarkdownThemeWithLinkTargets(targets: ReadonlyMap<string, string>): MarkdownTheme {
|
|
234
|
+
const base = getMarkdownTheme();
|
|
235
|
+
let snapshot = linkTargetSnapshots.get(targets);
|
|
236
|
+
let matches = snapshot?.size === targets.size;
|
|
237
|
+
if (matches && snapshot !== undefined) {
|
|
238
|
+
for (const [href, target] of targets) {
|
|
239
|
+
if (snapshot.get(href) !== target) {
|
|
240
|
+
matches = false;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (snapshot === undefined || !matches) {
|
|
246
|
+
snapshot = new Map(targets);
|
|
247
|
+
linkTargetSnapshots.set(targets, snapshot);
|
|
248
|
+
}
|
|
249
|
+
const stableSnapshot = snapshot;
|
|
250
|
+
const linked: MarkdownTheme = { ...base, resolveLink: href => stableSnapshot.get(href) };
|
|
251
|
+
if (canCacheMarkdownListItems(base)) registerCacheableMarkdownTheme(linked);
|
|
252
|
+
return linked;
|
|
253
|
+
}
|
|
254
|
+
|
|
188
255
|
export function setMarkdownMermaidRendering(enabled: boolean): void {
|
|
189
256
|
if (markdownMermaidRendering === enabled) return;
|
|
190
257
|
markdownMermaidRendering = enabled;
|
|
@@ -247,6 +314,7 @@ export function getMarkdownTheme(): MarkdownTheme {
|
|
|
247
314
|
};
|
|
248
315
|
cachedMarkdownTheme = markdownTheme;
|
|
249
316
|
cachedMarkdownThemeRef = theme;
|
|
317
|
+
registerCacheableMarkdownTheme(markdownTheme);
|
|
250
318
|
return markdownTheme;
|
|
251
319
|
}
|
|
252
320
|
|
package/src/tmux.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function isInsideTmux(env?: NodeJS.ProcessEnv): boolean;
|
|
3
|
-
/** Wrap a control sequence in tmux's DCS passthrough envelope. */
|
|
4
|
-
export declare function wrapTmuxPassthrough(payload: string): string;
|
|
1
|
+
export { wrapTmuxPassthrough } from "./multiplexers/tmux";
|
|
5
2
|
/** Pass a control sequence through tmux, leaving direct-terminal output unchanged. */
|
|
6
3
|
export declare function wrapTmuxPassthroughIfNeeded(payload: string, env?: NodeJS.ProcessEnv): string;
|
|
4
|
+
/**
|
|
5
|
+
* Whether the terminal hosting this tmux session draws TSP programs natively:
|
|
6
|
+
* it sets the session user option `@tern-tsp` while it watches the session.
|
|
7
|
+
*/
|
|
8
|
+
export declare function tmuxSessionSpeaksTsp(env?: NodeJS.ProcessEnv): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Stop (`on` false) or restore (`on` true) tmux turning this pane's APC
|
|
11
|
+
* strings into its title, which would otherwise swallow every TSP message.
|
|
12
|
+
*/
|
|
13
|
+
export declare function setTmuxPaneApcTitles(on: boolean, env?: NodeJS.ProcessEnv): void;
|
|
7
14
|
/**
|
|
8
15
|
* Resolve the terminal emulator name recorded for this tmux client.
|
|
9
16
|
*
|
package/src/tmux.ts
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import { $which } from "@linxiraos/pi-utils";
|
|
2
2
|
import { isBunTestRuntime } from "@linxiraos/pi-utils/env";
|
|
3
|
+
import { tmuxMultiplexer, wrapTmuxPassthrough } from "./multiplexers/tmux";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
export function isInsideTmux(env: NodeJS.ProcessEnv = Bun.env): boolean {
|
|
6
|
-
return Boolean(env.TMUX);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/** Wrap a control sequence in tmux's DCS passthrough envelope. */
|
|
10
|
-
export function wrapTmuxPassthrough(payload: string): string {
|
|
11
|
-
return `\x1bPtmux;${payload.replaceAll("\x1b", "\x1b\x1b")}\x1b\\`;
|
|
12
|
-
}
|
|
5
|
+
export { wrapTmuxPassthrough } from "./multiplexers/tmux";
|
|
13
6
|
|
|
14
7
|
/** Pass a control sequence through tmux, leaving direct-terminal output unchanged. */
|
|
15
8
|
export function wrapTmuxPassthroughIfNeeded(payload: string, env: NodeJS.ProcessEnv = Bun.env): string {
|
|
16
|
-
return
|
|
9
|
+
return tmuxMultiplexer.isInside(env) ? wrapTmuxPassthrough(payload) : payload;
|
|
17
10
|
}
|
|
18
11
|
|
|
19
12
|
const CLIENT_TERMTYPE_NAME = /^([A-Za-z][A-Za-z0-9._+-]*)(?=\s|\(|$)/u;
|
|
@@ -38,6 +31,49 @@ function queryTmuxClientTerminalName(env: NodeJS.ProcessEnv): string | null {
|
|
|
38
31
|
}
|
|
39
32
|
}
|
|
40
33
|
|
|
34
|
+
const TMUX_COMMAND_TIMEOUT_MS = 500;
|
|
35
|
+
|
|
36
|
+
/** Run a tmux command against the current server; its stdout, or null on failure. */
|
|
37
|
+
function runTmux(args: string[], env: NodeJS.ProcessEnv): string | null {
|
|
38
|
+
const tmux = $which("tmux", { PATH: env.PATH });
|
|
39
|
+
if (!tmux) return null;
|
|
40
|
+
try {
|
|
41
|
+
const result = Bun.spawnSync([tmux, ...args], {
|
|
42
|
+
env,
|
|
43
|
+
stdout: "pipe",
|
|
44
|
+
stderr: "ignore",
|
|
45
|
+
timeout: TMUX_COMMAND_TIMEOUT_MS,
|
|
46
|
+
killSignal: "SIGKILL",
|
|
47
|
+
});
|
|
48
|
+
return result.exitCode === 0 ? result.stdout.toString() : null;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Whether the terminal hosting this tmux session draws TSP programs natively:
|
|
56
|
+
* it sets the session user option `@tern-tsp` while it watches the session.
|
|
57
|
+
*/
|
|
58
|
+
export function tmuxSessionSpeaksTsp(env: NodeJS.ProcessEnv = Bun.env): boolean {
|
|
59
|
+
return (runTmux(["show-options", "-qv", "@tern-tsp"], env)?.trim() ?? "") !== "";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Stop (`on` false) or restore (`on` true) tmux turning this pane's APC
|
|
64
|
+
* strings into its title, which would otherwise swallow every TSP message.
|
|
65
|
+
*/
|
|
66
|
+
export function setTmuxPaneApcTitles(on: boolean, env: NodeJS.ProcessEnv = Bun.env): void {
|
|
67
|
+
const pane = env.TMUX_PANE;
|
|
68
|
+
if (!pane) return;
|
|
69
|
+
runTmux(
|
|
70
|
+
on
|
|
71
|
+
? ["set-option", "-pu", "-t", pane, "allow-set-title"]
|
|
72
|
+
: ["set-option", "-p", "-t", pane, "allow-set-title", "off"],
|
|
73
|
+
env,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
41
77
|
/**
|
|
42
78
|
* Resolve the terminal emulator name recorded for this tmux client.
|
|
43
79
|
*
|
|
@@ -46,7 +82,7 @@ function queryTmuxClientTerminalName(env: NodeJS.ProcessEnv): string | null {
|
|
|
46
82
|
* query runs once per process and degrades to `null` when unavailable.
|
|
47
83
|
*/
|
|
48
84
|
export function resolveTmuxClientTerminalName(env: NodeJS.ProcessEnv = Bun.env): string | null {
|
|
49
|
-
if (!
|
|
85
|
+
if (!tmuxMultiplexer.isInside(env) || isBunTestRuntime()) return null;
|
|
50
86
|
if (cachedClientTerminalName === undefined) cachedClientTerminalName = queryTmuxClientTerminalName(env);
|
|
51
87
|
return cachedClientTerminalName;
|
|
52
88
|
}
|
package/src/tools/ast-edit.ts
CHANGED
|
@@ -53,13 +53,19 @@ interface AstEditRenderArgs {
|
|
|
53
53
|
const COLLAPSED_CHANGE_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* Flatten
|
|
57
|
-
* by a blank line and carry no tree guides — the
|
|
58
|
-
* so nested `├─ │` gutters would just be
|
|
59
|
-
* least the first group, then fills up
|
|
60
|
-
* rest as `… N more changes`.
|
|
56
|
+
* Flatten change groups (line indices, styled on demand) into frame body
|
|
57
|
+
* lines. Groups are separated by a blank line and carry no tree guides — the
|
|
58
|
+
* frame border is the container, so nested `├─ │` gutters would just be
|
|
59
|
+
* noise. Collapsed mode always shows at least the first group, then fills up
|
|
60
|
+
* to `budget` lines before summarizing the rest as `… N more changes`.
|
|
61
61
|
*/
|
|
62
|
-
function buildChangeBody(
|
|
62
|
+
function buildChangeBody(
|
|
63
|
+
groups: readonly (readonly number[])[],
|
|
64
|
+
styleLine: (index: number) => string,
|
|
65
|
+
expanded: boolean,
|
|
66
|
+
budget: number,
|
|
67
|
+
theme: Theme,
|
|
68
|
+
): string[] {
|
|
63
69
|
const lines: string[] = [];
|
|
64
70
|
let shown = 0;
|
|
65
71
|
for (let i = 0; i < groups.length; i++) {
|
|
@@ -70,7 +76,7 @@ function buildChangeBody(groups: string[][], expanded: boolean, budget: number,
|
|
|
70
76
|
// Always emit the first group; budget only gates subsequent ones.
|
|
71
77
|
if (!expanded && shown > 0 && lines.length + separator + group.length + reserved > budget) break;
|
|
72
78
|
if (separator) lines.push("");
|
|
73
|
-
lines.push(
|
|
79
|
+
for (const index of group) lines.push(styleLine(index));
|
|
74
80
|
shown++;
|
|
75
81
|
}
|
|
76
82
|
const remaining = groups.length - shown;
|
|
@@ -239,29 +245,36 @@ export const astEditToolRenderer = {
|
|
|
239
245
|
// Resolve hyperlinks over the whole output so nested directory headers
|
|
240
246
|
// reconstruct across the blank-line groups the tree list collapses by.
|
|
241
247
|
const contexts = classifyGroupedLines(allLines, details?.cwd ?? details?.searchPath, details?.searchPath);
|
|
242
|
-
|
|
248
|
+
// Style lazily: collapsed bodies show only the first groups.
|
|
249
|
+
const styledLines: (string | undefined)[] = Array.from({ length: allLines.length }, () => undefined);
|
|
250
|
+
const styleLine = (index: number): string => {
|
|
251
|
+
const cached = styledLines[index];
|
|
252
|
+
if (cached !== undefined) return cached;
|
|
243
253
|
const ctx = contexts[index]!;
|
|
244
254
|
// Swap the inner code-frame gutter `│` for a space so it does not nest a
|
|
245
255
|
// second vertical bar inside the frame border.
|
|
246
|
-
const display = replaceTabs(
|
|
256
|
+
const display = replaceTabs(allLines[index]!.replace("│", " "));
|
|
257
|
+
let styled: string;
|
|
247
258
|
if (ctx.kind === "dir") {
|
|
248
|
-
const
|
|
249
|
-
|
|
259
|
+
const accent = uiTheme.fg("accent", display);
|
|
260
|
+
styled = ctx.headerPath ? fileHyperlink(ctx.headerPath, accent) : accent;
|
|
261
|
+
} else if (ctx.kind === "file") {
|
|
262
|
+
const tinted = uiTheme.fg(ctx.depth === 1 ? "accent" : "dim", display);
|
|
263
|
+
styled = ctx.headerPath ? fileHyperlink(ctx.headerPath, tinted) : tinted;
|
|
264
|
+
} else if (display.startsWith("+")) {
|
|
265
|
+
styled = uiTheme.fg("toolDiffAdded", display);
|
|
266
|
+
} else if (display.startsWith("-")) {
|
|
267
|
+
styled = uiTheme.fg("toolDiffRemoved", display);
|
|
268
|
+
} else {
|
|
269
|
+
styled = uiTheme.fg("toolOutput", display);
|
|
250
270
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return uiTheme.fg("toolOutput", display);
|
|
271
|
+
styledLines[index] = styled;
|
|
272
|
+
return styled;
|
|
273
|
+
};
|
|
274
|
+
const changeGroups = groupLineIndicesByBlank(allLines).filter(indices => {
|
|
275
|
+
const first = allLines[indices[0]!]!;
|
|
276
|
+
return !first.startsWith("Safety cap reached") && !first.startsWith("Parse issues:");
|
|
258
277
|
});
|
|
259
|
-
const changeGroups = groupLineIndicesByBlank(allLines)
|
|
260
|
-
.filter(indices => {
|
|
261
|
-
const first = allLines[indices[0]!]!;
|
|
262
|
-
return !first.startsWith("Safety cap reached") && !first.startsWith("Parse issues:");
|
|
263
|
-
})
|
|
264
|
-
.map(indices => indices.map(index => styledLines[index]!));
|
|
265
278
|
|
|
266
279
|
const badge = { label: "proposed", color: "warning" as const };
|
|
267
280
|
const header = renderStatusLine(
|
|
@@ -278,17 +291,28 @@ export const astEditToolRenderer = {
|
|
|
278
291
|
uiTheme.fg("warning", formatParseErrorsCountLabel(details.parseErrors, details.parseErrorsTotal)),
|
|
279
292
|
);
|
|
280
293
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
294
|
+
// The body is spinner-invariant: rebuild it only on expansion/width change.
|
|
295
|
+
let bodyMemo: { expanded: boolean; width: number; lines: readonly string[] } | undefined;
|
|
296
|
+
return framedToolCard(
|
|
297
|
+
uiTheme,
|
|
298
|
+
({ contentWidth }) => {
|
|
299
|
+
const expanded = Boolean(options.expanded);
|
|
300
|
+
if (bodyMemo === undefined || bodyMemo.expanded !== expanded || bodyMemo.width !== contentWidth) {
|
|
301
|
+
const changeLines = buildChangeBody(changeGroups, styleLine, expanded, COLLAPSED_CHANGE_LIMIT, uiTheme);
|
|
302
|
+
const lines = [...changeLines, ...extraLines].map(l => truncateToWidth(l, contentWidth, Ellipsis.Omit));
|
|
303
|
+
while (lines.length > 0 && lines[0].trim() === "") lines.shift();
|
|
304
|
+
bodyMemo = { expanded, width: contentWidth, lines };
|
|
305
|
+
}
|
|
306
|
+
const bodyLines = bodyMemo.lines;
|
|
307
|
+
return {
|
|
308
|
+
header,
|
|
309
|
+
sections: bodyLines.length > 0 ? [{ content: bodyLines }] : [],
|
|
310
|
+
phase: options.isPartial ? "partial" : "success",
|
|
311
|
+
borderColor: "borderMuted",
|
|
312
|
+
};
|
|
313
|
+
},
|
|
314
|
+
{ onInvalidate: () => (bodyMemo = undefined) },
|
|
315
|
+
);
|
|
292
316
|
},
|
|
293
317
|
describeCall(args: AstEditRenderArgs): NativeToolView {
|
|
294
318
|
return {
|