@oh-my-pi/pi-coding-agent 16.1.13 → 16.1.15
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 +48 -0
- package/dist/cli.js +5615 -2833
- package/dist/types/advisor/runtime.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +64 -14
- package/dist/types/eval/__tests__/julia-prelude.test.d.ts +1 -0
- package/dist/types/eval/backend-helpers.d.ts +23 -0
- package/dist/types/eval/executor-base.d.ts +118 -0
- package/dist/types/eval/index.d.ts +2 -0
- package/dist/types/eval/jl/executor.d.ts +44 -0
- package/dist/types/eval/jl/index.d.ts +11 -0
- package/dist/types/eval/jl/kernel.d.ts +28 -0
- package/dist/types/eval/jl/prelude.d.ts +1 -0
- package/dist/types/eval/jl/runtime.d.ts +22 -0
- package/dist/types/eval/kernel-base.d.ts +105 -0
- package/dist/types/eval/py/kernel.d.ts +3 -61
- package/dist/types/eval/rb/executor.d.ts +77 -0
- package/dist/types/eval/rb/index.d.ts +11 -0
- package/dist/types/eval/rb/kernel.d.ts +31 -0
- package/dist/types/eval/rb/prelude.d.ts +1 -0
- package/dist/types/eval/rb/runtime.d.ts +23 -0
- package/dist/types/eval/runtime-env.d.ts +24 -0
- package/dist/types/eval/types.d.ts +3 -3
- package/dist/types/export/share.d.ts +8 -1
- package/dist/types/extensibility/extensions/runner.d.ts +2 -15
- package/dist/types/extensibility/hooks/loader.d.ts +1 -25
- package/dist/types/extensibility/session-handler-types.d.ts +23 -0
- package/dist/types/jsonrpc/message-framing.d.ts +35 -0
- package/dist/types/mcp/transports/stdio.d.ts +12 -1
- package/dist/types/mnemopi/embed-client.d.ts +7 -24
- package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +11 -0
- package/dist/types/modes/components/selector-helpers.d.ts +53 -0
- package/dist/types/modes/components/status-line/context-thresholds.d.ts +4 -3
- package/dist/types/modes/components/status-line/types.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +0 -2
- package/dist/types/modes/theme/theme.d.ts +8 -1
- package/dist/types/modes/types.d.ts +0 -2
- package/dist/types/modes/utils/copy-targets.d.ts +1 -1
- package/dist/types/modes/utils/interactive-context-helpers.d.ts +14 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +54 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/secrets/obfuscator.d.ts +3 -3
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/stt/asr-client.d.ts +3 -29
- package/dist/types/subprocess/worker-client.d.ts +149 -0
- package/dist/types/subprocess/worker-runtime.d.ts +107 -0
- package/dist/types/tiny/title-client.d.ts +14 -34
- package/dist/types/tools/eval-backends.d.ts +6 -3
- package/dist/types/tools/eval-render.d.ts +6 -5
- package/dist/types/tools/eval.d.ts +13 -15
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/tts/tts-client.d.ts +3 -28
- package/dist/types/tui/code-cell.d.ts +7 -0
- package/dist/types/utils/file-display-mode.d.ts +1 -1
- package/dist/types/utils/shell-snapshot.d.ts +10 -0
- package/dist/types/web/parallel.d.ts +6 -0
- package/dist/types/web/search/providers/perplexity.d.ts +17 -3
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +114 -0
- package/src/advisor/runtime.ts +129 -1
- package/src/config/model-registry.ts +12 -4
- package/src/config/settings-schema.ts +74 -18
- package/src/config/settings.ts +5 -0
- package/src/dap/client.ts +13 -107
- package/src/eval/__tests__/julia-prelude.test.ts +77 -0
- package/src/eval/backend-helpers.ts +48 -0
- package/src/eval/executor-base.ts +425 -0
- package/src/eval/index.ts +2 -0
- package/src/eval/jl/executor.ts +540 -0
- package/src/eval/jl/index.ts +54 -0
- package/src/eval/jl/kernel.ts +235 -0
- package/src/eval/jl/prelude.jl +930 -0
- package/src/eval/jl/prelude.ts +3 -0
- package/src/eval/jl/runner.jl +634 -0
- package/src/eval/jl/runtime.ts +118 -0
- package/src/eval/js/index.ts +3 -14
- package/src/eval/kernel-base.ts +569 -0
- package/src/eval/py/executor.ts +43 -252
- package/src/eval/py/index.ts +9 -20
- package/src/eval/py/kernel.ts +29 -544
- package/src/eval/rb/executor.ts +504 -0
- package/src/eval/rb/index.ts +54 -0
- package/src/eval/rb/kernel.ts +230 -0
- package/src/eval/rb/prelude.rb +721 -0
- package/src/eval/rb/prelude.ts +3 -0
- package/src/eval/rb/runner.rb +474 -0
- package/src/eval/rb/runtime.ts +132 -0
- package/src/eval/runtime-env.ts +104 -0
- package/src/eval/types.ts +3 -3
- package/src/exec/bash-executor.ts +44 -0
- package/src/export/share.ts +51 -28
- package/src/extensibility/extensions/runner.ts +4 -11
- package/src/extensibility/hooks/loader.ts +3 -21
- package/src/extensibility/session-handler-types.ts +21 -0
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/jsonrpc/message-framing.ts +142 -0
- package/src/lsp/client.ts +13 -109
- package/src/mcp/transports/stdio.ts +20 -4
- package/src/mnemopi/embed-client.ts +43 -198
- package/src/modes/components/agent-dashboard.ts +17 -40
- package/src/modes/components/chat-transcript-builder.ts +18 -102
- package/src/modes/components/custom-editor.test.ts +22 -0
- package/src/modes/components/custom-editor.ts +29 -1
- package/src/modes/components/extensions/extension-dashboard.ts +4 -13
- package/src/modes/components/extensions/extension-list.ts +16 -44
- package/src/modes/components/footer.ts +4 -3
- package/src/modes/components/history-search.ts +4 -16
- package/src/modes/components/selector-helpers.ts +129 -0
- package/src/modes/components/settings-selector.ts +7 -5
- package/src/modes/components/status-line/component.ts +5 -1
- package/src/modes/components/status-line/context-thresholds.ts +11 -3
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/status-line/types.ts +1 -0
- package/src/modes/components/tree-selector.ts +13 -18
- package/src/modes/controllers/command-controller.ts +1 -0
- package/src/modes/controllers/event-controller.ts +3 -9
- package/src/modes/controllers/input-controller.ts +32 -54
- package/src/modes/interactive-mode.ts +5 -7
- package/src/modes/theme/theme.ts +35 -0
- package/src/modes/types.ts +0 -2
- package/src/modes/utils/copy-targets.ts +3 -2
- package/src/modes/utils/interactive-context-helpers.ts +27 -0
- package/src/modes/utils/transcript-render-helpers.ts +157 -0
- package/src/modes/utils/ui-helpers.ts +21 -126
- package/src/prompts/system/system-prompt.md +5 -5
- package/src/prompts/tools/bash.md +2 -3
- package/src/prompts/tools/eval.md +6 -4
- package/src/prompts/tools/find.md +0 -4
- package/src/prompts/tools/read.md +1 -2
- package/src/prompts/tools/replace.md +1 -1
- package/src/sdk.ts +13 -7
- package/src/secrets/obfuscator.ts +3 -9
- package/src/session/agent-session.ts +42 -9
- package/src/slash-commands/builtin-registry.ts +2 -1
- package/src/stt/asr-client.ts +35 -215
- package/src/stt/asr-worker.ts +29 -181
- package/src/subprocess/worker-client.ts +297 -0
- package/src/subprocess/worker-runtime.ts +277 -0
- package/src/task/executor.ts +4 -4
- package/src/tiny/title-client.ts +53 -219
- package/src/tiny/worker.ts +29 -180
- package/src/tools/eval-backends.ts +10 -3
- package/src/tools/eval-render.ts +17 -8
- package/src/tools/eval.ts +187 -22
- package/src/tools/index.ts +51 -28
- package/src/tts/tts-client.ts +38 -206
- package/src/tts/tts-worker.ts +23 -97
- package/src/tui/code-cell.ts +12 -1
- package/src/utils/file-display-mode.ts +2 -3
- package/src/utils/shell-snapshot.ts +63 -1
- package/src/web/parallel.ts +43 -42
- package/src/web/search/providers/parallel.ts +10 -99
- package/src/web/search/providers/perplexity.ts +18 -6
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared scaffolding for the TUI selector/list/dashboard components: viewport
|
|
3
|
+
* windowing, scrollbar-aware row widths, ScrollView rendering, selection
|
|
4
|
+
* clamping, search-character classification, tab-cycling keys, and full-screen
|
|
5
|
+
* padding. Behaviour is identical to the per-component copies these helpers
|
|
6
|
+
* replace.
|
|
7
|
+
*/
|
|
8
|
+
import { extractPrintableText, matchesKey, ScrollView } from "@oh-my-pi/pi-tui";
|
|
9
|
+
import { theme } from "../theme/theme";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Render `rows` through a {@link ScrollView} with the shared list theme (muted
|
|
13
|
+
* track / accent thumb) and an "auto" scrollbar, positioned at `scrollOffset`.
|
|
14
|
+
* Returns the rendered lines for the caller to append to its output.
|
|
15
|
+
*/
|
|
16
|
+
export function renderScrollableList(
|
|
17
|
+
rows: readonly string[],
|
|
18
|
+
options: { width: number; totalRows: number; scrollOffset: number },
|
|
19
|
+
): readonly string[] {
|
|
20
|
+
const sv = new ScrollView(rows, {
|
|
21
|
+
height: rows.length,
|
|
22
|
+
scrollbar: "auto",
|
|
23
|
+
totalRows: options.totalRows,
|
|
24
|
+
theme: { track: t => theme.fg("muted", t), thumb: t => theme.fg("accent", t) },
|
|
25
|
+
});
|
|
26
|
+
sv.setScrollOffset(options.scrollOffset);
|
|
27
|
+
return sv.render(options.width);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Center a viewport window of `maxVisible` rows on `selectedIndex` within a
|
|
32
|
+
* list of `total` rows, clamped to valid bounds. Used by the selection-centered
|
|
33
|
+
* list panes (history search, tree selector).
|
|
34
|
+
*/
|
|
35
|
+
export function centeredWindow(
|
|
36
|
+
selectedIndex: number,
|
|
37
|
+
total: number,
|
|
38
|
+
maxVisible: number,
|
|
39
|
+
): { startIndex: number; endIndex: number } {
|
|
40
|
+
const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), total - maxVisible));
|
|
41
|
+
const endIndex = Math.min(startIndex + maxVisible, total);
|
|
42
|
+
return { startIndex, endIndex };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Width available for row content, reserving the rightmost column for the
|
|
47
|
+
* scrollbar when the list overflows its visible window.
|
|
48
|
+
*/
|
|
49
|
+
export function contentRowWidth(width: number, total: number, maxVisible: number): number {
|
|
50
|
+
const overflow = total > maxVisible;
|
|
51
|
+
return Math.max(0, width - (overflow ? 1 : 0));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Clamp `selectedIndex` into `[0, total)` and nudge `scrollOffset` so the
|
|
56
|
+
* selection stays within the visible window of `maxVisible` rows. Returns the
|
|
57
|
+
* adjusted pair; on an empty list both reset to 0.
|
|
58
|
+
*/
|
|
59
|
+
export function clampSelection(
|
|
60
|
+
selectedIndex: number,
|
|
61
|
+
scrollOffset: number,
|
|
62
|
+
total: number,
|
|
63
|
+
maxVisible: number,
|
|
64
|
+
): { selectedIndex: number; scrollOffset: number } {
|
|
65
|
+
if (total === 0) {
|
|
66
|
+
return { selectedIndex: 0, scrollOffset: 0 };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const selected = Math.max(0, Math.min(selectedIndex, total - 1));
|
|
70
|
+
|
|
71
|
+
let scroll = scrollOffset;
|
|
72
|
+
if (selected < scroll) {
|
|
73
|
+
scroll = selected;
|
|
74
|
+
} else if (selected >= scroll + maxVisible) {
|
|
75
|
+
scroll = selected - maxVisible + 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { selectedIndex: selected, scrollOffset: scroll };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Classify a key event for search-query text entry. Returns the single
|
|
83
|
+
* printable character to append to the query, or `null` when the key is not a
|
|
84
|
+
* searchable character: non-printable, multi-byte, or a reserved `j`/`k`
|
|
85
|
+
* navigation key.
|
|
86
|
+
*/
|
|
87
|
+
export function searchableChar(data: string): string | null {
|
|
88
|
+
const printableText = extractPrintableText(data);
|
|
89
|
+
if (printableText && printableText.length === 1) {
|
|
90
|
+
const printableCharCode = printableText.charCodeAt(0);
|
|
91
|
+
if (printableCharCode > 32 && printableCharCode < 127) {
|
|
92
|
+
if (printableText === "j" || printableText === "k") {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return printableText;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Handle the shared tab-cycling keys: Tab/Right advance to the next tab,
|
|
103
|
+
* Shift+Tab/Left to the previous. Invokes `switchTab` with the direction and
|
|
104
|
+
* returns true when the key was consumed.
|
|
105
|
+
*/
|
|
106
|
+
export function handleTabSwitchKey(data: string, switchTab: (direction: 1 | -1) => void): boolean {
|
|
107
|
+
if (matchesKey(data, "tab") || matchesKey(data, "right")) {
|
|
108
|
+
switchTab(1);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (matchesKey(data, "shift+tab") || matchesKey(data, "left")) {
|
|
112
|
+
switchTab(-1);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Pad `lines` with blank rows up to `rows` so a full-screen overlay covers the
|
|
120
|
+
* viewport instead of letting the transcript peek through below it. Copies
|
|
121
|
+
* before padding — the source array may be component-owned and must not be
|
|
122
|
+
* mutated.
|
|
123
|
+
*/
|
|
124
|
+
export function padLinesToHeight(lines: readonly string[], rows: number): readonly string[] {
|
|
125
|
+
if (lines.length >= rows) return lines;
|
|
126
|
+
const padded = lines.slice();
|
|
127
|
+
while (padded.length < rows) padded.push("");
|
|
128
|
+
return padded;
|
|
129
|
+
}
|
|
@@ -473,15 +473,17 @@ export class SettingsSelectorComponent implements Component {
|
|
|
473
473
|
return true;
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
-
if (event.wheel !== null) {
|
|
477
|
-
list?.handleWheel(event.wheel);
|
|
478
|
-
return true;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
476
|
const tabLine = event.row - this.#tabRowStart;
|
|
482
477
|
const overTabs = tabLine >= 0 && tabLine < this.#tabRowCount;
|
|
483
478
|
const overContent = contentLine >= 0 && contentLine < this.#contentRowCount;
|
|
484
479
|
|
|
480
|
+
if (event.wheel !== null) {
|
|
481
|
+
if (overContent) {
|
|
482
|
+
list?.handleWheelAt(event.wheel, contentLine, innerCol);
|
|
483
|
+
}
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
|
|
485
487
|
if (event.motion) {
|
|
486
488
|
const hovered = overTabs ? this.#tabBar.tabAt(tabLine, innerCol) : undefined;
|
|
487
489
|
this.#tabBar.setHoverTab(hovered && !hovered.muted ? hovered.id : null);
|
|
@@ -727,10 +727,12 @@ export class StatusLineComponent implements Component {
|
|
|
727
727
|
|
|
728
728
|
let contextWindow = state.model?.contextWindow ?? this.session.model?.contextWindow ?? 0;
|
|
729
729
|
let contextPercent: number | null = 0;
|
|
730
|
+
let contextTokens = 0;
|
|
730
731
|
if (includeContext) {
|
|
731
732
|
const breakdown = this.getCachedContextBreakdown();
|
|
733
|
+
contextTokens = breakdown.usedTokens;
|
|
732
734
|
contextWindow = breakdown.contextWindow || contextWindow;
|
|
733
|
-
contextPercent = contextWindow > 0 ? (breakdown.usedTokens / contextWindow) * 100 :
|
|
735
|
+
contextPercent = contextWindow > 0 ? (breakdown.usedTokens / contextWindow) * 100 : null;
|
|
734
736
|
}
|
|
735
737
|
|
|
736
738
|
// Collab guest: context comes from the host's state frames — the local
|
|
@@ -738,6 +740,7 @@ export class StatusLineComponent implements Component {
|
|
|
738
740
|
const collabState = this.#collabStatus?.stateOverride;
|
|
739
741
|
if (collabState?.contextUsage) {
|
|
740
742
|
contextWindow = collabState.contextUsage.contextWindow || contextWindow;
|
|
743
|
+
contextTokens = collabState.contextUsage.tokens ?? contextTokens;
|
|
741
744
|
contextPercent = collabState.contextUsage.percent ?? contextPercent;
|
|
742
745
|
}
|
|
743
746
|
|
|
@@ -756,6 +759,7 @@ export class StatusLineComponent implements Component {
|
|
|
756
759
|
collab: this.#collabStatus,
|
|
757
760
|
usageStats,
|
|
758
761
|
contextPercent,
|
|
762
|
+
contextTokens,
|
|
759
763
|
contextWindow,
|
|
760
764
|
autoCompactEnabled: this.#autoCompactEnabled,
|
|
761
765
|
subagentCount: this.#subagentCount,
|
|
@@ -56,10 +56,18 @@ export function getContextUsageLevel(contextPercent: number, contextWindow: numb
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* Format context usage as `<percent>%/<window>`
|
|
60
|
-
*
|
|
59
|
+
* Format context usage as `<percent>%/<window>` when the model window is known.
|
|
60
|
+
* Unknown windows render as `<tokens>/?`, because `0.0%/0` suggests a real
|
|
61
|
+
* empty context instead of missing provider metadata.
|
|
61
62
|
*/
|
|
62
|
-
export function formatContextUsage(
|
|
63
|
+
export function formatContextUsage(
|
|
64
|
+
contextPercent: number | null | undefined,
|
|
65
|
+
contextWindow: number,
|
|
66
|
+
usedTokens?: number,
|
|
67
|
+
): string {
|
|
68
|
+
if (!Number.isFinite(contextWindow) || contextWindow <= 0) {
|
|
69
|
+
return `${formatNumber(usedTokens ?? 0)}/?`;
|
|
70
|
+
}
|
|
63
71
|
const pct = contextPercent === null || contextPercent === undefined ? "?" : `${contextPercent.toFixed(1)}%`;
|
|
64
72
|
return `${pct}/${formatNumber(contextWindow)}`;
|
|
65
73
|
}
|
|
@@ -375,7 +375,7 @@ const contextPctSegment: StatusLineSegment = {
|
|
|
375
375
|
const window = ctx.contextWindow;
|
|
376
376
|
|
|
377
377
|
const autoIcon = ctx.autoCompactEnabled && theme.icon.auto ? ` ${theme.icon.auto}` : "";
|
|
378
|
-
const text = `${formatContextUsage(pct, window)}${autoIcon}`;
|
|
378
|
+
const text = `${formatContextUsage(pct, window, ctx.contextTokens)}${autoIcon}`;
|
|
379
379
|
|
|
380
380
|
const color = getContextUsageThemeColor(getContextUsageLevel(pct ?? 0, window));
|
|
381
381
|
const content = withIcon(theme.icon.context, theme.fg(color, text));
|
|
@@ -73,6 +73,7 @@ export interface SegmentContext {
|
|
|
73
73
|
};
|
|
74
74
|
/** Context usage percent, or null when unknown (e.g. right after compaction). */
|
|
75
75
|
contextPercent: number | null;
|
|
76
|
+
contextTokens: number;
|
|
76
77
|
contextWindow: number;
|
|
77
78
|
autoCompactEnabled: boolean;
|
|
78
79
|
subagentCount: number;
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
fuzzyMatch,
|
|
7
7
|
Input,
|
|
8
8
|
matchesKey,
|
|
9
|
-
ScrollView,
|
|
10
9
|
Spacer,
|
|
11
10
|
Text,
|
|
12
11
|
TruncatedText,
|
|
@@ -20,6 +19,7 @@ import { shortenPath } from "../../tools/render-utils";
|
|
|
20
19
|
import { toPathList } from "../../tools/search";
|
|
21
20
|
import { canonicalizeMessage } from "../../utils/thinking-display";
|
|
22
21
|
import { DynamicBorder } from "./dynamic-border";
|
|
22
|
+
import { centeredWindow, contentRowWidth, renderScrollableList } from "./selector-helpers";
|
|
23
23
|
|
|
24
24
|
/** Gutter info: position (displayIndent where connector was) and whether to show │ */
|
|
25
25
|
interface GutterInfo {
|
|
@@ -475,14 +475,11 @@ class TreeList implements Component {
|
|
|
475
475
|
return lines;
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
-
const startIndex =
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.#filteredNodes.length - this.maxVisibleLines,
|
|
483
|
-
),
|
|
478
|
+
const { startIndex, endIndex } = centeredWindow(
|
|
479
|
+
this.#selectedIndex,
|
|
480
|
+
this.#filteredNodes.length,
|
|
481
|
+
this.maxVisibleLines,
|
|
484
482
|
);
|
|
485
|
-
const endIndex = Math.min(startIndex + this.maxVisibleLines, this.#filteredNodes.length);
|
|
486
483
|
|
|
487
484
|
// Cap the per-row gutter prefix so a content budget is always preserved.
|
|
488
485
|
// Each indent level renders as 3 cells; deep branching would otherwise eat the
|
|
@@ -494,8 +491,7 @@ class TreeList implements Component {
|
|
|
494
491
|
const contentReserve = Math.max(MIN_CONTENT_COLS, Math.floor(width / 2));
|
|
495
492
|
const maxIndentLevels = Math.max(1, Math.floor((width - contentReserve - OVERHEAD_COLS) / 3));
|
|
496
493
|
|
|
497
|
-
const
|
|
498
|
-
const rowWidth = Math.max(0, width - (overflow ? 1 : 0));
|
|
494
|
+
const rowWidth = contentRowWidth(width, this.#filteredNodes.length, this.maxVisibleLines);
|
|
499
495
|
const rows: string[] = [];
|
|
500
496
|
|
|
501
497
|
for (let i = startIndex; i < endIndex; i++) {
|
|
@@ -584,14 +580,13 @@ class TreeList implements Component {
|
|
|
584
580
|
rows.push(truncateToWidth(line, rowWidth));
|
|
585
581
|
}
|
|
586
582
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
lines.push(...sv.render(width));
|
|
583
|
+
lines.push(
|
|
584
|
+
...renderScrollableList(rows, {
|
|
585
|
+
width,
|
|
586
|
+
totalRows: this.#filteredNodes.length,
|
|
587
|
+
scrollOffset: startIndex,
|
|
588
|
+
}),
|
|
589
|
+
);
|
|
595
590
|
|
|
596
591
|
const filterLabel = this.#getFilterLabel();
|
|
597
592
|
if (filterLabel) {
|
|
@@ -217,6 +217,7 @@ export class CommandController {
|
|
|
217
217
|
try {
|
|
218
218
|
const result = await shareSession(this.ctx.session.sessionManager, {
|
|
219
219
|
serverUrl: this.ctx.settings.get("share.serverUrl"),
|
|
220
|
+
store: this.ctx.settings.get("share.store"),
|
|
220
221
|
state: this.ctx.session.state,
|
|
221
222
|
obfuscator: this.ctx.settings.get("share.redactSecrets") ? this.ctx.session.obfuscator : undefined,
|
|
222
223
|
});
|
|
@@ -5,7 +5,7 @@ import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
|
|
|
5
5
|
import { extractTextContent } from "../../commit/utils";
|
|
6
6
|
import { settings } from "../../config/settings";
|
|
7
7
|
import { getFileSnapshotStore } from "../../edit/file-snapshot-store";
|
|
8
|
-
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
8
|
+
import type { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
9
9
|
import { detectCacheInvalidation } from "../../modes/components/cache-invalidation-marker";
|
|
10
10
|
import {
|
|
11
11
|
ReadToolGroupComponent,
|
|
@@ -25,6 +25,7 @@ import type { ResolveToolDetails } from "../../tools/resolve";
|
|
|
25
25
|
import { vocalizer } from "../../tts/vocalizer";
|
|
26
26
|
import { canonicalizeMessage } from "../../utils/thinking-display";
|
|
27
27
|
import { interruptHint } from "../shared";
|
|
28
|
+
import { createAssistantMessageComponent } from "../utils/interactive-context-helpers";
|
|
28
29
|
import { StreamingRevealController } from "./streaming-reveal";
|
|
29
30
|
import { ToolArgsRevealController } from "./tool-args-reveal";
|
|
30
31
|
|
|
@@ -332,14 +333,7 @@ export class EventController {
|
|
|
332
333
|
this.ctx.ui.requestRender();
|
|
333
334
|
} else if (event.message.role === "assistant") {
|
|
334
335
|
this.#lastVisibleBlockCount = 0;
|
|
335
|
-
this.ctx.streamingComponent =
|
|
336
|
-
undefined,
|
|
337
|
-
this.ctx.hideThinkingBlock,
|
|
338
|
-
() => this.ctx.ui.requestRender(),
|
|
339
|
-
this.ctx.viewSession.extensionRunner?.getAssistantThinkingRenderers(),
|
|
340
|
-
this.ctx.ui.imageBudget,
|
|
341
|
-
this.ctx.proseOnlyThinking,
|
|
342
|
-
);
|
|
336
|
+
this.ctx.streamingComponent = createAssistantMessageComponent(this.ctx);
|
|
343
337
|
this.ctx.streamingMessage = event.message;
|
|
344
338
|
this.ctx.chatContainer.addChild(this.ctx.streamingComponent);
|
|
345
339
|
this.#streamingReveal.begin(this.ctx.streamingComponent, this.ctx.streamingMessage);
|
|
@@ -530,10 +530,7 @@ export class InputController {
|
|
|
530
530
|
// model continues the prior intent rather than second-guessing the interrupt.
|
|
531
531
|
if (text === "." || text === "c") {
|
|
532
532
|
if (this.ctx.onInputCallback) {
|
|
533
|
-
this.ctx.editor.
|
|
534
|
-
this.ctx.pendingImages = [];
|
|
535
|
-
this.ctx.pendingImageLinks = [];
|
|
536
|
-
this.ctx.editor.imageLinks = undefined;
|
|
533
|
+
this.ctx.editor.clearDraft();
|
|
537
534
|
this.ctx.onInputCallback({
|
|
538
535
|
text: manualContinuePrompt,
|
|
539
536
|
cancelled: false,
|
|
@@ -546,16 +543,14 @@ export class InputController {
|
|
|
546
543
|
}
|
|
547
544
|
|
|
548
545
|
const runner = this.ctx.session.extensionRunner;
|
|
549
|
-
let inputImages = this.ctx.pendingImages.length > 0 ? [...this.ctx.pendingImages] : undefined;
|
|
550
|
-
let inputImageLinks =
|
|
546
|
+
let inputImages = this.ctx.editor.pendingImages.length > 0 ? [...this.ctx.editor.pendingImages] : undefined;
|
|
547
|
+
let inputImageLinks =
|
|
548
|
+
this.ctx.editor.pendingImageLinks.length > 0 ? [...this.ctx.editor.pendingImageLinks] : undefined;
|
|
551
549
|
|
|
552
550
|
if (runner?.hasHandlers("input")) {
|
|
553
551
|
const result = await runner.emitInput(text, inputImages, "interactive");
|
|
554
552
|
if (result?.handled) {
|
|
555
|
-
this.ctx.editor.
|
|
556
|
-
this.ctx.pendingImages = [];
|
|
557
|
-
this.ctx.pendingImageLinks = [];
|
|
558
|
-
this.ctx.editor.imageLinks = undefined;
|
|
553
|
+
this.ctx.editor.clearDraft();
|
|
559
554
|
return;
|
|
560
555
|
}
|
|
561
556
|
if (result?.text !== undefined) {
|
|
@@ -603,12 +598,8 @@ export class InputController {
|
|
|
603
598
|
this.ctx.showStatus("This collab link is read-only — prompting is disabled");
|
|
604
599
|
return;
|
|
605
600
|
}
|
|
606
|
-
this.ctx.editor.addToHistory(text);
|
|
607
|
-
this.ctx.editor.setText("");
|
|
608
|
-
this.ctx.editor.imageLinks = undefined;
|
|
609
601
|
const images = inputImages && inputImages.length > 0 ? [...inputImages] : undefined;
|
|
610
|
-
this.ctx.
|
|
611
|
-
this.ctx.pendingImageLinks = [];
|
|
602
|
+
this.ctx.editor.clearDraft(text);
|
|
612
603
|
// No local render: the prompt comes back from the host as a
|
|
613
604
|
// collab-prompt event/entry and renders with the author badge.
|
|
614
605
|
this.ctx.collabGuest.sendPrompt(text, images);
|
|
@@ -680,8 +671,8 @@ export class InputController {
|
|
|
680
671
|
this.ctx.editor.setText("");
|
|
681
672
|
this.ctx.editor.imageLinks = undefined;
|
|
682
673
|
const images = inputImages && inputImages.length > 0 ? [...inputImages] : undefined;
|
|
683
|
-
this.ctx.pendingImages = [];
|
|
684
|
-
this.ctx.pendingImageLinks = [];
|
|
674
|
+
this.ctx.editor.pendingImages = [];
|
|
675
|
+
this.ctx.editor.pendingImageLinks = [];
|
|
685
676
|
// Record the signature so the queued message's eventual delivery
|
|
686
677
|
// (a user-role `message_start` event) leaves any draft the user has
|
|
687
678
|
// typed since queuing intact. Same protection as #783, applied to
|
|
@@ -744,8 +735,8 @@ export class InputController {
|
|
|
744
735
|
// Include any pending images from clipboard paste
|
|
745
736
|
this.ctx.editor.imageLinks = undefined;
|
|
746
737
|
const images = inputImages && inputImages.length > 0 ? [...inputImages] : undefined;
|
|
747
|
-
this.ctx.pendingImages = [];
|
|
748
|
-
this.ctx.pendingImageLinks = [];
|
|
738
|
+
this.ctx.editor.pendingImages = [];
|
|
739
|
+
this.ctx.editor.pendingImageLinks = [];
|
|
749
740
|
|
|
750
741
|
// Render user message immediately, then let session events catch up.
|
|
751
742
|
// Tag the submission as "steer": this is a normal Enter the controller
|
|
@@ -771,8 +762,8 @@ export class InputController {
|
|
|
771
762
|
// semantics instead of throwing AgentBusyError.
|
|
772
763
|
this.ctx.editor.imageLinks = undefined;
|
|
773
764
|
const images = inputImages && inputImages.length > 0 ? [...inputImages] : undefined;
|
|
774
|
-
this.ctx.pendingImages = [];
|
|
775
|
-
this.ctx.pendingImageLinks = [];
|
|
765
|
+
this.ctx.editor.pendingImages = [];
|
|
766
|
+
this.ctx.editor.pendingImageLinks = [];
|
|
776
767
|
try {
|
|
777
768
|
await this.ctx.withLocalSubmission(
|
|
778
769
|
text,
|
|
@@ -787,9 +778,11 @@ export class InputController {
|
|
|
787
778
|
// extension command).
|
|
788
779
|
this.ctx.editor.setText(text);
|
|
789
780
|
if (images && images.length > 0) {
|
|
790
|
-
this.ctx.pendingImages = [...images];
|
|
791
|
-
this.ctx.pendingImageLinks = inputImageLinks
|
|
792
|
-
|
|
781
|
+
this.ctx.editor.pendingImages = [...images];
|
|
782
|
+
this.ctx.editor.pendingImageLinks = inputImageLinks
|
|
783
|
+
? [...inputImageLinks]
|
|
784
|
+
: images.map(() => undefined);
|
|
785
|
+
this.ctx.editor.imageLinks = this.ctx.editor.pendingImageLinks;
|
|
793
786
|
}
|
|
794
787
|
this.ctx.showError(error instanceof Error ? error.message : String(error));
|
|
795
788
|
}
|
|
@@ -816,12 +809,8 @@ export class InputController {
|
|
|
816
809
|
this.ctx.showStatus("Commands run in the main session — press ←← to return first");
|
|
817
810
|
return; // editor text not cleared: Editor does not auto-clear on submit
|
|
818
811
|
}
|
|
819
|
-
const images = this.ctx.pendingImages.length > 0 ? [...this.ctx.pendingImages] : undefined;
|
|
820
|
-
this.ctx.editor.
|
|
821
|
-
this.ctx.editor.setText("");
|
|
822
|
-
this.ctx.editor.imageLinks = undefined;
|
|
823
|
-
this.ctx.pendingImages = [];
|
|
824
|
-
this.ctx.pendingImageLinks = [];
|
|
812
|
+
const images = this.ctx.editor.pendingImages.length > 0 ? [...this.ctx.editor.pendingImages] : undefined;
|
|
813
|
+
this.ctx.editor.clearDraft(text);
|
|
825
814
|
try {
|
|
826
815
|
// prompt() handles idle (new turn) and streaming (queues per streamingBehavior).
|
|
827
816
|
await this.ctx.withLocalSubmission(text, () => target.prompt(text, { streamingBehavior, images }), {
|
|
@@ -989,10 +978,7 @@ export class InputController {
|
|
|
989
978
|
}
|
|
990
979
|
const didRetry = await this.ctx.viewSession.retry();
|
|
991
980
|
if (didRetry) {
|
|
992
|
-
this.ctx.editor.
|
|
993
|
-
this.ctx.pendingImages = [];
|
|
994
|
-
this.ctx.pendingImageLinks = [];
|
|
995
|
-
this.ctx.editor.imageLinks = undefined;
|
|
981
|
+
this.ctx.editor.clearDraft();
|
|
996
982
|
} else {
|
|
997
983
|
this.ctx.showStatus("Nothing to retry");
|
|
998
984
|
}
|
|
@@ -1016,7 +1002,7 @@ export class InputController {
|
|
|
1016
1002
|
// the queued entry is later re-parsed into a skill invocation is a
|
|
1017
1003
|
// separate concern owned by the compaction-resume path.
|
|
1018
1004
|
if (this.ctx.session.isCompacting) {
|
|
1019
|
-
const images = this.ctx.pendingImages.length > 0 ? [...this.ctx.pendingImages] : undefined;
|
|
1005
|
+
const images = this.ctx.editor.pendingImages.length > 0 ? [...this.ctx.editor.pendingImages] : undefined;
|
|
1020
1006
|
this.ctx.queueCompactionMessage(text, "followUp", images);
|
|
1021
1007
|
return;
|
|
1022
1008
|
}
|
|
@@ -1040,14 +1026,10 @@ export class InputController {
|
|
|
1040
1026
|
|
|
1041
1027
|
// Forward any pending clipboard-pasted images alongside the queued text;
|
|
1042
1028
|
// otherwise the follow-up would drop the image (mirrors the Enter/steer path).
|
|
1043
|
-
const images = this.ctx.pendingImages.length > 0 ? [...this.ctx.pendingImages] : undefined;
|
|
1029
|
+
const images = this.ctx.editor.pendingImages.length > 0 ? [...this.ctx.editor.pendingImages] : undefined;
|
|
1044
1030
|
|
|
1045
1031
|
if (this.ctx.session.isStreaming) {
|
|
1046
|
-
this.ctx.editor.
|
|
1047
|
-
this.ctx.editor.setText("");
|
|
1048
|
-
this.ctx.editor.imageLinks = undefined;
|
|
1049
|
-
this.ctx.pendingImages = [];
|
|
1050
|
-
this.ctx.pendingImageLinks = [];
|
|
1032
|
+
this.ctx.editor.clearDraft(text);
|
|
1051
1033
|
await this.ctx.withLocalSubmission(
|
|
1052
1034
|
text,
|
|
1053
1035
|
() => this.ctx.session.prompt(text, { streamingBehavior: "followUp", images }),
|
|
@@ -1059,11 +1041,7 @@ export class InputController {
|
|
|
1059
1041
|
}
|
|
1060
1042
|
|
|
1061
1043
|
// Not streaming — just submit normally
|
|
1062
|
-
this.ctx.editor.
|
|
1063
|
-
this.ctx.editor.setText("");
|
|
1064
|
-
this.ctx.editor.imageLinks = undefined;
|
|
1065
|
-
this.ctx.pendingImages = [];
|
|
1066
|
-
this.ctx.pendingImageLinks = [];
|
|
1044
|
+
this.ctx.editor.clearDraft(text);
|
|
1067
1045
|
await this.ctx.withLocalSubmission(text, () => this.ctx.session.prompt(text, { images }), {
|
|
1068
1046
|
imageCount: images?.length ?? 0,
|
|
1069
1047
|
});
|
|
@@ -1108,7 +1086,7 @@ export class InputController {
|
|
|
1108
1086
|
let queuedText: string;
|
|
1109
1087
|
if (queuedImages.length > 0) {
|
|
1110
1088
|
const parts: string[] = [];
|
|
1111
|
-
let imageOffset = this.ctx.pendingImages.length;
|
|
1089
|
+
let imageOffset = this.ctx.editor.pendingImages.length;
|
|
1112
1090
|
for (const entry of allQueued) {
|
|
1113
1091
|
parts.push(shiftImageMarkers(entry.text, imageOffset));
|
|
1114
1092
|
if (entry.images && entry.images.length > 0) imageOffset += entry.images.length;
|
|
@@ -1124,9 +1102,9 @@ export class InputController {
|
|
|
1124
1102
|
// re-materialized lazily; the restored text already carries the
|
|
1125
1103
|
// renumbered `[Image #N, WxH]` markers).
|
|
1126
1104
|
if (queuedImages.length > 0) {
|
|
1127
|
-
this.ctx.pendingImages.push(...queuedImages);
|
|
1128
|
-
this.ctx.pendingImageLinks.push(...queuedImages.map(() => undefined));
|
|
1129
|
-
this.ctx.editor.imageLinks = this.ctx.pendingImageLinks;
|
|
1105
|
+
this.ctx.editor.pendingImages.push(...queuedImages);
|
|
1106
|
+
this.ctx.editor.pendingImageLinks.push(...queuedImages.map(() => undefined));
|
|
1107
|
+
this.ctx.editor.imageLinks = this.ctx.editor.pendingImageLinks;
|
|
1130
1108
|
}
|
|
1131
1109
|
this.ctx.updatePendingMessagesDisplay();
|
|
1132
1110
|
if (options?.abort) {
|
|
@@ -1148,14 +1126,14 @@ export class InputController {
|
|
|
1148
1126
|
this.ctx.sessionManager.putBlob.bind(this.ctx.sessionManager),
|
|
1149
1127
|
)
|
|
1150
1128
|
)?.[0];
|
|
1151
|
-
this.ctx.pendingImages.push({
|
|
1129
|
+
this.ctx.editor.pendingImages.push({
|
|
1152
1130
|
type: "image",
|
|
1153
1131
|
data: imageData.data,
|
|
1154
1132
|
mimeType: imageData.mimeType,
|
|
1155
1133
|
});
|
|
1156
|
-
this.ctx.pendingImageLinks.push(imageLink);
|
|
1157
|
-
this.ctx.editor.imageLinks = this.ctx.pendingImageLinks;
|
|
1158
|
-
const imageNum = this.ctx.pendingImages.length;
|
|
1134
|
+
this.ctx.editor.pendingImageLinks.push(imageLink);
|
|
1135
|
+
this.ctx.editor.imageLinks = this.ctx.editor.pendingImageLinks;
|
|
1136
|
+
const imageNum = this.ctx.editor.pendingImages.length;
|
|
1159
1137
|
const dims = await this.#imageDimensions(imageData);
|
|
1160
1138
|
const label = dims ? `[Image #${imageNum}, ${dims.width}x${dims.height}]` : `[Image #${imageNum}]`;
|
|
1161
1139
|
this.ctx.editor.insertText(`${label} `);
|
|
@@ -408,8 +408,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
408
408
|
todoPhases: TodoPhase[] = [];
|
|
409
409
|
hideThinkingBlock = false;
|
|
410
410
|
proseOnlyThinking = true;
|
|
411
|
-
pendingImages: ImageContent[] = [];
|
|
412
|
-
pendingImageLinks: (string | undefined)[] = [];
|
|
413
411
|
compactionQueuedMessages: CompactionQueuedMessage[] = [];
|
|
414
412
|
pendingTools = new Map<string, ToolExecutionHandle>();
|
|
415
413
|
pendingBashComponents: BashExecutionComponent[] = [];
|
|
@@ -1094,7 +1092,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1094
1092
|
if (this.#goalSuppressNextContinuation) return;
|
|
1095
1093
|
if (this.#pendingSubmittedInput) return;
|
|
1096
1094
|
if (this.editor.getText().trim().length > 0) return;
|
|
1097
|
-
if ((this.pendingImages?.length ?? 0) > 0) return;
|
|
1095
|
+
if ((this.editor.pendingImages?.length ?? 0) > 0) return;
|
|
1098
1096
|
const state = this.session.getGoalModeState();
|
|
1099
1097
|
if (!state?.enabled || state.goal.status !== "active") return;
|
|
1100
1098
|
const prompt = this.session.goalRuntime.buildContinuationPrompt();
|
|
@@ -1113,7 +1111,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1113
1111
|
if (this.#isAutoSubmitBlocked()) return;
|
|
1114
1112
|
if (this.#pendingSubmittedInput) return;
|
|
1115
1113
|
if (this.editor.getText().trim().length > 0) return;
|
|
1116
|
-
if ((this.pendingImages?.length ?? 0) > 0) return;
|
|
1114
|
+
if ((this.editor.pendingImages?.length ?? 0) > 0) return;
|
|
1117
1115
|
const latestState = this.session.getGoalModeState();
|
|
1118
1116
|
if (!latestState?.enabled || latestState.goal.status !== "active") return;
|
|
1119
1117
|
this.#goalContinuationTurnInFlight = true;
|
|
@@ -1338,9 +1336,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1338
1336
|
this.#stopLoadingAnimation(true);
|
|
1339
1337
|
}
|
|
1340
1338
|
if (!submission.customType) {
|
|
1341
|
-
this.pendingImages = submission.images ? [...submission.images] : [];
|
|
1342
|
-
this.pendingImageLinks = submission.imageLinks ? [...submission.imageLinks] : [];
|
|
1343
|
-
this.editor.imageLinks = this.pendingImageLinks;
|
|
1339
|
+
this.editor.pendingImages = submission.images ? [...submission.images] : [];
|
|
1340
|
+
this.editor.pendingImageLinks = submission.imageLinks ? [...submission.imageLinks] : [];
|
|
1341
|
+
this.editor.imageLinks = this.editor.pendingImageLinks;
|
|
1344
1342
|
this.rebuildChatFromMessages();
|
|
1345
1343
|
this.editor.setText(submission.text);
|
|
1346
1344
|
}
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -169,6 +169,7 @@ export type SymbolKey =
|
|
|
169
169
|
| "lang.cpp"
|
|
170
170
|
| "lang.csharp"
|
|
171
171
|
| "lang.ruby"
|
|
172
|
+
| "lang.julia"
|
|
172
173
|
| "lang.php"
|
|
173
174
|
| "lang.swift"
|
|
174
175
|
| "lang.kotlin"
|
|
@@ -369,6 +370,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
369
370
|
"lang.cpp": "➕",
|
|
370
371
|
"lang.csharp": "♯",
|
|
371
372
|
"lang.ruby": "💎",
|
|
373
|
+
"lang.julia": "Ⓙ",
|
|
372
374
|
"lang.php": "🐘",
|
|
373
375
|
"lang.swift": "🕊",
|
|
374
376
|
"lang.kotlin": "🅺",
|
|
@@ -673,6 +675,7 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
673
675
|
"lang.cpp": "\u{E61D}",
|
|
674
676
|
"lang.csharp": "\u{E7BC}",
|
|
675
677
|
"lang.ruby": "\u{E791}",
|
|
678
|
+
"lang.julia": "\u{E624}",
|
|
676
679
|
"lang.php": "\u{E608}",
|
|
677
680
|
"lang.swift": "\u{E755}",
|
|
678
681
|
"lang.kotlin": "\u{E634}",
|
|
@@ -870,6 +873,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
870
873
|
"lang.cpp": "cpp",
|
|
871
874
|
"lang.csharp": "cs",
|
|
872
875
|
"lang.ruby": "rb",
|
|
876
|
+
"lang.julia": "jl",
|
|
873
877
|
"lang.php": "php",
|
|
874
878
|
"lang.swift": "swift",
|
|
875
879
|
"lang.kotlin": "kt",
|
|
@@ -1335,6 +1339,8 @@ const langMap: Record<string, SymbolKey> = {
|
|
|
1335
1339
|
cs: "lang.csharp",
|
|
1336
1340
|
ruby: "lang.ruby",
|
|
1337
1341
|
rb: "lang.ruby",
|
|
1342
|
+
julia: "lang.julia",
|
|
1343
|
+
jl: "lang.julia",
|
|
1338
1344
|
php: "lang.php",
|
|
1339
1345
|
swift: "lang.swift",
|
|
1340
1346
|
kotlin: "lang.kotlin",
|
|
@@ -1406,6 +1412,20 @@ const langMap: Record<string, SymbolKey> = {
|
|
|
1406
1412
|
bin: "lang.binary",
|
|
1407
1413
|
};
|
|
1408
1414
|
|
|
1415
|
+
/**
|
|
1416
|
+
* Brand colors for language icons, keyed by the resolved `lang.*` SymbolKey.
|
|
1417
|
+
* Used by {@link Theme.getLangIconStyled} so eval-kernel cell headers tint each
|
|
1418
|
+
* language with its recognizable hue (JS yellow, Ruby red, Julia purple, Python
|
|
1419
|
+
* blue) instead of a flat muted gray. Applied as truecolor/256 per the active
|
|
1420
|
+
* color mode; languages without an entry fall back to the muted theme color.
|
|
1421
|
+
*/
|
|
1422
|
+
const LANG_BRAND_COLORS: Partial<Record<SymbolKey, string>> = {
|
|
1423
|
+
"lang.javascript": "#f7df1e",
|
|
1424
|
+
"lang.python": "#3776ab",
|
|
1425
|
+
"lang.ruby": "#cc342d",
|
|
1426
|
+
"lang.julia": "#9558b2",
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1409
1429
|
/**
|
|
1410
1430
|
* Resolve a theme color value (hex string or 256-color index) to a CSS hex string.
|
|
1411
1431
|
* Empty string represents the default terminal color.
|
|
@@ -1874,6 +1894,21 @@ export class Theme {
|
|
|
1874
1894
|
const key = langMap[normalized];
|
|
1875
1895
|
return key ? this.#symbols[key] : this.#symbols["lang.default"];
|
|
1876
1896
|
}
|
|
1897
|
+
|
|
1898
|
+
/**
|
|
1899
|
+
* Language icon tinted with the language's brand color (see
|
|
1900
|
+
* {@link LANG_BRAND_COLORS}). Falls back to the muted theme color for
|
|
1901
|
+
* languages without a brand entry, and returns the bare (possibly empty)
|
|
1902
|
+
* icon when the active symbol preset has none.
|
|
1903
|
+
*/
|
|
1904
|
+
getLangIconStyled(lang: string | undefined): string {
|
|
1905
|
+
const icon = this.getLangIcon(lang);
|
|
1906
|
+
if (!icon) return icon;
|
|
1907
|
+
const key = lang ? langMap[lang.toLowerCase()] : undefined;
|
|
1908
|
+
const hex = key ? LANG_BRAND_COLORS[key] : undefined;
|
|
1909
|
+
if (!hex) return this.fg("muted", icon);
|
|
1910
|
+
return `${colorToAnsi(hex, this.mode)}${icon}\x1b[39m`;
|
|
1911
|
+
}
|
|
1877
1912
|
}
|
|
1878
1913
|
|
|
1879
1914
|
// ============================================================================
|
package/src/modes/types.ts
CHANGED
|
@@ -160,8 +160,6 @@ export interface InteractiveModeContext {
|
|
|
160
160
|
planModePlanFilePath?: string;
|
|
161
161
|
hideThinkingBlock: boolean;
|
|
162
162
|
proseOnlyThinking: boolean;
|
|
163
|
-
pendingImages: ImageContent[];
|
|
164
|
-
pendingImageLinks: (string | undefined)[];
|
|
165
163
|
compactionQueuedMessages: CompactionQueuedMessage[];
|
|
166
164
|
pendingTools: Map<string, ToolExecutionHandle>;
|
|
167
165
|
pendingBashComponents: BashExecutionComponent[];
|