lsd-pi 1.3.6 → 1.3.7
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/dist/cli.js +2 -1
- package/dist/lsd-settings-manager.d.ts +2 -0
- package/dist/lsd-settings-manager.js +5 -0
- package/dist/resource-loader.js +33 -3
- package/dist/resources/extensions/cache-timer/index.js +3 -2
- package/dist/welcome-screen.js +2 -2
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js +35 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +12 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +21 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +16 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +12 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +7 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +86 -28
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +16 -10
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +26 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +14 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +111 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +47 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +137 -6
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +40 -14
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +5 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +70 -25
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +4 -4
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/settings-manager.collapse-tool-calls.test.ts +46 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +18 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +20 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +26 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +14 -4
- package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +105 -28
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +13 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +31 -4
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +119 -13
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +59 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +174 -6
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +50 -14
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +73 -25
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +4 -4
- package/packages/pi-tui/dist/components/editor.js +3 -3
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +3 -3
- package/pkg/dist/modes/interactive/theme/themes.js +4 -4
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/cache-timer/index.ts +3 -2
|
@@ -69,8 +69,8 @@ const dark = {
|
|
|
69
69
|
mdQuoteBorder: "gray",
|
|
70
70
|
mdHr: "gray",
|
|
71
71
|
mdListBullet: "accent",
|
|
72
|
-
toolDiffAdded: "
|
|
73
|
-
toolDiffRemoved: "
|
|
72
|
+
toolDiffAdded: "#4ade80",
|
|
73
|
+
toolDiffRemoved: "#fb7185",
|
|
74
74
|
toolDiffContext: "gray",
|
|
75
75
|
syntaxComment: "#6A9955",
|
|
76
76
|
syntaxKeyword: "#569CD6",
|
|
@@ -157,8 +157,8 @@ const light = {
|
|
|
157
157
|
mdQuoteBorder: "mediumGray",
|
|
158
158
|
mdHr: "mediumGray",
|
|
159
159
|
mdListBullet: "green",
|
|
160
|
-
toolDiffAdded: "
|
|
161
|
-
toolDiffRemoved: "
|
|
160
|
+
toolDiffAdded: "#15803d",
|
|
161
|
+
toolDiffRemoved: "#b91c1c",
|
|
162
162
|
toolDiffContext: "mediumGray",
|
|
163
163
|
syntaxComment: "#008000",
|
|
164
164
|
syntaxKeyword: "#0000FF",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"themes.js","sourceRoot":"","sources":["../../../../src/modes/interactive/theme/themes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,MAAM,IAAI,GAAc;IACvB,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE;QACL,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,SAAS;QAClB,QAAQ,EAAE,SAAS;QACnB,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,SAAS;QACpB,OAAO,EAAE,SAAS;QAClB,UAAU,EAAE,SAAS;QACrB,QAAQ,EAAE,SAAS;QACnB,SAAS,EAAE,SAAS;QACpB,UAAU,EAAE,SAAS;QACrB,SAAS,EAAE,SAAS;QACpB,aAAa,EAAE,SAAS;QACxB,aAAa,EAAE,SAAS;QACxB,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE,SAAS;KACtB;IACD,MAAM,EAAE;QACP,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,MAAM;QACd,YAAY,EAAE,MAAM;QACpB,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE,OAAO;QAChB,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,MAAM;QACb,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,EAAE;QACR,YAAY,EAAE,MAAM;QAEpB,UAAU,EAAE,YAAY;QACxB,aAAa,EAAE,WAAW;QAC1B,eAAe,EAAE,EAAE;QACnB,eAAe,EAAE,aAAa;QAC9B,iBAAiB,EAAE,EAAE;QACrB,kBAAkB,EAAE,SAAS;QAC7B,aAAa,EAAE,eAAe;QAC9B,aAAa,EAAE,eAAe;QAC9B,WAAW,EAAE,aAAa;QAC1B,SAAS,EAAE,EAAE;QACb,UAAU,EAAE,MAAM;QAElB,SAAS,EAAE,SAAS;QACpB,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,SAAS;QACpB,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,OAAO;QACpB,iBAAiB,EAAE,MAAM;QACzB,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,MAAM;QACrB,IAAI,EAAE,MAAM;QACZ,YAAY,EAAE,QAAQ;QAEtB,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"themes.js","sourceRoot":"","sources":["../../../../src/modes/interactive/theme/themes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in theme definitions.\n *\n * Each theme is a self-contained record of color values. Variable references\n * (e.g. \"accent\") are resolved against the `vars` map at load time by the\n * theme engine in theme.ts.\n *\n * To add a new built-in theme, add an entry to `builtinThemes` below.\n */\n\n// Re-use the ThemeJson type from the schema defined in theme.ts.\n// We import only the type to avoid circular runtime dependencies.\nimport type { ThemeJson } from \"./theme.js\";\n\n// ---------------------------------------------------------------------------\n// Dark theme\n// ---------------------------------------------------------------------------\n\nconst dark: ThemeJson = {\n\tname: \"dark\",\n\tvars: {\n\t\tcyan: \"#4a8cf7\",\n\t\tblue: \"#4a8cf7\",\n\t\tgreen: \"#b5bd68\",\n\t\tred: \"#cc6666\",\n\t\tyellow: \"#facc15\",\n\t\tviolet: \"#a78bfa\",\n\t\tgray: \"#bec8d6\",\n\t\tdimGray: \"#8793a3\",\n\t\tdarkGray: \"#505050\",\n\t\taccent: \"#60a5fa\",\n\t\tblueMuted: \"#1e3a8a\",\n\t\tblueLow: \"#2563eb\",\n\t\tblueMedium: \"#4a8cf7\",\n\t\tblueHigh: \"#60a5fa\",\n\t\tblueXhigh: \"#93c5fd\",\n\t\tselectedBg: \"#323640\",\n\t\tuserMsgBg: \"#272727\",\n\t\ttoolPendingBg: \"#1e2230\",\n\t\ttoolSuccessBg: \"#1a2330\",\n\t\ttoolErrorBg: \"#2a1e30\",\n\t\tcustomMsgBg: \"#2d2838\",\n\t},\n\tcolors: {\n\t\taccent: \"accent\",\n\t\tborder: \"blue\",\n\t\tborderAccent: \"cyan\",\n\t\tborderMuted: \"blue\",\n\t\tsuccess: \"green\",\n\t\terror: \"red\",\n\t\twarning: \"yellow\",\n\t\tviolet: \"violet\",\n\t\tmuted: \"gray\",\n\t\tdim: \"dimGray\",\n\t\ttext: \"\",\n\t\tthinkingText: \"gray\",\n\n\t\tselectedBg: \"selectedBg\",\n\t\tuserMessageBg: \"userMsgBg\",\n\t\tuserMessageText: \"\",\n\t\tcustomMessageBg: \"customMsgBg\",\n\t\tcustomMessageText: \"\",\n\t\tcustomMessageLabel: \"#9575cd\",\n\t\ttoolPendingBg: \"toolPendingBg\",\n\t\ttoolSuccessBg: \"toolSuccessBg\",\n\t\ttoolErrorBg: \"toolErrorBg\",\n\t\ttoolTitle: \"\",\n\t\ttoolOutput: \"gray\",\n\n\t\tmdHeading: \"#f0c674\",\n\t\tmdLink: \"#5a8aaa\",\n\t\tmdLinkUrl: \"dimGray\",\n\t\tmdCode: \"accent\",\n\t\tmdCodeBlock: \"green\",\n\t\tmdCodeBlockBorder: \"gray\",\n\t\tmdQuote: \"gray\",\n\t\tmdQuoteBorder: \"gray\",\n\t\tmdHr: \"gray\",\n\t\tmdListBullet: \"accent\",\n\n\t\ttoolDiffAdded: \"#4ade80\",\n\t\ttoolDiffRemoved: \"#fb7185\",\n\t\ttoolDiffContext: \"gray\",\n\n\t\tsyntaxComment: \"#6A9955\",\n\t\tsyntaxKeyword: \"#569CD6\",\n\t\tsyntaxFunction: \"#DCDCAA\",\n\t\tsyntaxVariable: \"#9CDCFE\",\n\t\tsyntaxString: \"#CE9178\",\n\t\tsyntaxNumber: \"#B5CEA8\",\n\t\tsyntaxType: \"#4EC9B0\",\n\t\tsyntaxOperator: \"#D4D4D4\",\n\t\tsyntaxPunctuation: \"#D4D4D4\",\n\n\t\tthinkingOff: \"blueMuted\",\n\t\tthinkingMinimal: \"blueLow\",\n\t\tthinkingLow: \"blueMedium\",\n\t\tthinkingMedium: \"blueHigh\",\n\t\tthinkingHigh: \"blueXhigh\",\n\t\tthinkingXhigh: \"cyan\",\n\n\t\tbashMode: \"accent\",\n\t},\n\texport: {\n\t\tpageBg: \"#18181e\",\n\t\tcardBg: \"#1e1e24\",\n\t\tinfoBg: \"#3c3728\",\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// Light theme\n// ---------------------------------------------------------------------------\n\nconst light: ThemeJson = {\n\tname: \"light\",\n\tvars: {\n\t\tteal: \"#3b82f6\",\n\t\tblue: \"#547da7\",\n\t\tgreen: \"#588458\",\n\t\tred: \"#aa5555\",\n\t\tyellow: \"#eab308\",\n\t\twarning: \"#7a5a00\",\n\t\tviolet: \"#8b5cf6\",\n\t\tmediumGray: \"#6c6c6c\",\n\t\tdimGray: \"#767676\",\n\t\tlightGray: \"#b0b0b0\",\n\t\tblueMuted: \"#6b8fb8\",\n\t\tblueLow: \"#547da7\",\n\t\tblueMedium: \"#3b82f6\",\n\t\tblueHigh: \"#2563eb\",\n\t\tblueXhigh: \"#1d4ed8\",\n\t\tselectedBg: \"#d0d0e0\",\n\t\tuserMsgBg: \"#e8e8e8\",\n\t\ttoolPendingBg: \"#e8eaf0\",\n\t\ttoolSuccessBg: \"#e8f0f0\",\n\t\ttoolErrorBg: \"#f0e8ee\",\n\t\tcustomMsgBg: \"#ede7f6\",\n\t},\n\tcolors: {\n\t\taccent: \"teal\",\n\t\tborder: \"blue\",\n\t\tborderAccent: \"teal\",\n\t\tborderMuted: \"lightGray\",\n\t\tsuccess: \"green\",\n\t\terror: \"red\",\n\t\twarning: \"warning\",\n\t\tviolet: \"violet\",\n\t\tmuted: \"mediumGray\",\n\t\tdim: \"dimGray\",\n\t\ttext: \"\",\n\t\tthinkingText: \"mediumGray\",\n\n\t\tselectedBg: \"selectedBg\",\n\t\tuserMessageBg: \"userMsgBg\",\n\t\tuserMessageText: \"\",\n\t\tcustomMessageBg: \"customMsgBg\",\n\t\tcustomMessageText: \"\",\n\t\tcustomMessageLabel: \"#7e57c2\",\n\t\ttoolPendingBg: \"toolPendingBg\",\n\t\ttoolSuccessBg: \"toolSuccessBg\",\n\t\ttoolErrorBg: \"toolErrorBg\",\n\t\ttoolTitle: \"\",\n\t\ttoolOutput: \"mediumGray\",\n\n\t\tmdHeading: \"yellow\",\n\t\tmdLink: \"blue\",\n\t\tmdLinkUrl: \"dimGray\",\n\t\tmdCode: \"teal\",\n\t\tmdCodeBlock: \"green\",\n\t\tmdCodeBlockBorder: \"mediumGray\",\n\t\tmdQuote: \"mediumGray\",\n\t\tmdQuoteBorder: \"mediumGray\",\n\t\tmdHr: \"mediumGray\",\n\t\tmdListBullet: \"green\",\n\n\t\ttoolDiffAdded: \"#15803d\",\n\t\ttoolDiffRemoved: \"#b91c1c\",\n\t\ttoolDiffContext: \"mediumGray\",\n\n\t\tsyntaxComment: \"#008000\",\n\t\tsyntaxKeyword: \"#0000FF\",\n\t\tsyntaxFunction: \"#795E26\",\n\t\tsyntaxVariable: \"#001080\",\n\t\tsyntaxString: \"#A31515\",\n\t\tsyntaxNumber: \"#098658\",\n\t\tsyntaxType: \"#267F99\",\n\t\tsyntaxOperator: \"#000000\",\n\t\tsyntaxPunctuation: \"#000000\",\n\n\t\tthinkingOff: \"blueMuted\",\n\t\tthinkingMinimal: \"blueLow\",\n\t\tthinkingLow: \"blueMedium\",\n\t\tthinkingMedium: \"teal\",\n\t\tthinkingHigh: \"blueHigh\",\n\t\tthinkingXhigh: \"blueXhigh\",\n\n\t\tbashMode: \"accent\",\n\t},\n\texport: {\n\t\tpageBg: \"#f8f8f8\",\n\t\tcardBg: \"#ffffff\",\n\t\tinfoBg: \"#fffae6\",\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// Export\n// ---------------------------------------------------------------------------\n\nexport const builtinThemes: Record<string, ThemeJson> = { dark, light };\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { describe, it } from "node:test";
|
|
6
|
+
|
|
7
|
+
import { SettingsManager } from "./settings-manager.js";
|
|
8
|
+
|
|
9
|
+
describe("SettingsManager collapseToolCalls", () => {
|
|
10
|
+
it("defaults collapseToolCalls to false", () => {
|
|
11
|
+
const manager = SettingsManager.inMemory();
|
|
12
|
+
assert.equal(manager.getCollapseToolCalls(), false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("persists collapseToolCalls changes to settings.json", async () => {
|
|
16
|
+
const root = mkdtempSync(join(tmpdir(), "pi-collapse-tool-calls-settings-"));
|
|
17
|
+
const cwd = join(root, "project");
|
|
18
|
+
const agentDir = join(root, "agent");
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const manager = SettingsManager.create(cwd, agentDir);
|
|
22
|
+
assert.equal(manager.getCollapseToolCalls(), false);
|
|
23
|
+
|
|
24
|
+
manager.setCollapseToolCalls(true);
|
|
25
|
+
await manager.flush();
|
|
26
|
+
|
|
27
|
+
const rawEnabled = JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf8")) as {
|
|
28
|
+
collapseToolCalls?: boolean;
|
|
29
|
+
};
|
|
30
|
+
assert.equal(rawEnabled.collapseToolCalls, true);
|
|
31
|
+
|
|
32
|
+
const reloaded = SettingsManager.create(cwd, agentDir);
|
|
33
|
+
assert.equal(reloaded.getCollapseToolCalls(), true);
|
|
34
|
+
|
|
35
|
+
reloaded.setCollapseToolCalls(false);
|
|
36
|
+
await reloaded.flush();
|
|
37
|
+
|
|
38
|
+
const rawDisabled = JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf8")) as {
|
|
39
|
+
collapseToolCalls?: boolean;
|
|
40
|
+
};
|
|
41
|
+
assert.equal(rawDisabled.collapseToolCalls, false);
|
|
42
|
+
} finally {
|
|
43
|
+
rmSync(root, { recursive: true, force: true });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -161,6 +161,7 @@ export interface Settings {
|
|
|
161
161
|
codexRotate?: boolean; // Enable the bundled codex-rotate extension (default: false)
|
|
162
162
|
fastMode?: boolean; // Enable OpenAI/Codex fast tier routing (priority service tier where supported)
|
|
163
163
|
cacheTimer?: boolean; // Show elapsed time since last response in the footer (default: true)
|
|
164
|
+
verboseFooter?: boolean; // Show extra footer usage stats like input/output/cache tokens (default: false)
|
|
164
165
|
pinLastPrompt?: boolean; // Pin last sent prompt above the editor as a reminder (default: false)
|
|
165
166
|
doubleEscapeAction?: "fork" | "tree" | "none"; // Action for double-escape with empty editor (default: "tree")
|
|
166
167
|
treeFilterMode?: "default" | "no-tools" | "user-only" | "labeled-only" | "all"; // Default filter when opening /tree
|
|
@@ -181,6 +182,7 @@ export interface Settings {
|
|
|
181
182
|
editMode?: "standard" | "hashline"; // Edit tool mode: "standard" (text match) or "hashline" (LINE#ID anchors). Default: "standard"
|
|
182
183
|
timestampFormat?: "date-time-iso" | "date-time-us"; // Timestamp display format for messages. Default: "date-time-iso"
|
|
183
184
|
toolOutputMode?: "minimal" | "normal"; // Collapsed tool rendering mode. "minimal" hides previews until expanded.
|
|
185
|
+
collapseToolCalls?: boolean; // default: false — group low-priority tool calls into collapsed summary lines
|
|
184
186
|
lspAutoInstall?: boolean; // default: false — whether to auto-install missing language servers during onboarding
|
|
185
187
|
lspInstalledServers?: string[]; // list of server names installed via the onboarding wizard
|
|
186
188
|
rtk?: boolean; // default: false — enable RTK shell-command compression (requires restart)
|
|
@@ -1051,6 +1053,14 @@ export class SettingsManager {
|
|
|
1051
1053
|
this.setGlobalSetting("toolOutputMode", mode);
|
|
1052
1054
|
}
|
|
1053
1055
|
|
|
1056
|
+
getCollapseToolCalls(): boolean {
|
|
1057
|
+
return this.settings.collapseToolCalls ?? false;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
setCollapseToolCalls(enabled: boolean): void {
|
|
1061
|
+
this.setGlobalSetting("collapseToolCalls", enabled);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1054
1064
|
getPackages(): PackageSource[] {
|
|
1055
1065
|
return [...(this.settings.packages ?? [])];
|
|
1056
1066
|
}
|
|
@@ -1205,6 +1215,14 @@ export class SettingsManager {
|
|
|
1205
1215
|
this.setGlobalSetting("cacheTimer", enabled);
|
|
1206
1216
|
}
|
|
1207
1217
|
|
|
1218
|
+
getVerboseFooter(): boolean {
|
|
1219
|
+
return this.settings.verboseFooter ?? false;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
setVerboseFooter(enabled: boolean): void {
|
|
1223
|
+
this.setGlobalSetting("verboseFooter", enabled);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1208
1226
|
getPinLastPrompt(): boolean {
|
|
1209
1227
|
return this.settings.pinLastPrompt ?? false;
|
|
1210
1228
|
}
|
|
@@ -6,6 +6,7 @@ import { describe, it } from "node:test";
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
computeEditDiff,
|
|
9
|
+
computeWriteDiff,
|
|
9
10
|
fuzzyFindText,
|
|
10
11
|
generateDiffString,
|
|
11
12
|
normalizeForFuzzyMatch,
|
|
@@ -60,6 +61,25 @@ describe("edit-diff", () => {
|
|
|
60
61
|
assert.match(result.diff, /CHANGED/);
|
|
61
62
|
});
|
|
62
63
|
|
|
64
|
+
it("computes diffs for write preview against existing files", async (t) => {
|
|
65
|
+
const dir = mkdtempSync(join(tmpdir(), "write-diff-test-"));
|
|
66
|
+
t.after(() => {
|
|
67
|
+
rmSync(dir, { recursive: true, force: true });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const file = join(dir, "sample.ts");
|
|
71
|
+
writeFileSync(file, "const title = \"Hello\";\n", "utf-8");
|
|
72
|
+
|
|
73
|
+
const result = await computeWriteDiff(file, "const title = \"Hi\";\n", dir);
|
|
74
|
+
|
|
75
|
+
assert.ok(!("error" in result), "expected a diff result");
|
|
76
|
+
if (!("error" in result)) {
|
|
77
|
+
assert.equal(result.firstChangedLine, 1);
|
|
78
|
+
assert.match(result.diff, /-1 const title = "Hello";/);
|
|
79
|
+
assert.match(result.diff, /\+1 const title = "Hi";/);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
63
83
|
it("computes diffs for preview without native helpers", async (t) => {
|
|
64
84
|
const dir = mkdtempSync(join(tmpdir(), "edit-diff-test-"));
|
|
65
85
|
t.after(() => {
|
|
@@ -329,6 +329,32 @@ function buildLineDiffLinear(oldLines: string[], newLines: string[]): LineDiffOp
|
|
|
329
329
|
return ops;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Compute the diff for a write operation without applying it.
|
|
334
|
+
* Used for preview rendering in the TUI before the tool executes.
|
|
335
|
+
*/
|
|
336
|
+
export async function computeWriteDiff(
|
|
337
|
+
path: string,
|
|
338
|
+
newContent: string,
|
|
339
|
+
cwd: string,
|
|
340
|
+
): Promise<EditDiffResult | EditDiffError> {
|
|
341
|
+
const absolutePath = resolveToCwd(path, cwd);
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
let oldContent = "";
|
|
345
|
+
try {
|
|
346
|
+
await access(absolutePath, constants.R_OK);
|
|
347
|
+
oldContent = await readFile(absolutePath, "utf-8");
|
|
348
|
+
} catch {
|
|
349
|
+
oldContent = "";
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return generateDiffString(normalizeToLF(stripBom(oldContent).text), normalizeToLF(newContent));
|
|
353
|
+
} catch (err) {
|
|
354
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
332
358
|
/**
|
|
333
359
|
* Compute the diff for an edit operation without applying it.
|
|
334
360
|
* Used for preview rendering in the TUI before the tool executes.
|
|
@@ -8,18 +8,28 @@ import { initTheme } from "../../theme/theme.js";
|
|
|
8
8
|
initTheme("dark");
|
|
9
9
|
|
|
10
10
|
describe("ToolSummaryLine", () => {
|
|
11
|
-
it("
|
|
11
|
+
it("renders action-based summaries for grouped identical tools", () => {
|
|
12
12
|
const summary = new ToolSummaryLine();
|
|
13
13
|
summary.addTool("read", 600);
|
|
14
|
-
summary.addTool("lsp", 250);
|
|
15
14
|
summary.addTool("read", 150);
|
|
16
15
|
|
|
17
16
|
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
18
|
-
assert.match(rendered, /^ ●
|
|
19
|
-
assert.ok(rendered.includes("
|
|
17
|
+
assert.match(rendered, /^ ● /);
|
|
18
|
+
assert.ok(rendered.includes("reading 2 files · 0.8s"));
|
|
19
|
+
assert.equal(summary.canGroupWith("read"), true);
|
|
20
|
+
assert.equal(summary.canGroupWith("find"), false);
|
|
21
|
+
assert.equal(rendered.includes("collapsed tools"), false);
|
|
20
22
|
assert.equal(rendered.includes("⎯"), false);
|
|
21
23
|
});
|
|
22
24
|
|
|
25
|
+
it("keeps fallback format for unknown tools", () => {
|
|
26
|
+
const summary = new ToolSummaryLine();
|
|
27
|
+
summary.addTool("custom_tool", 100);
|
|
28
|
+
|
|
29
|
+
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
30
|
+
assert.ok(rendered.includes("custom_tool · 0.1s"));
|
|
31
|
+
});
|
|
32
|
+
|
|
23
33
|
it("renders nothing when empty or hidden", () => {
|
|
24
34
|
const summary = new ToolSummaryLine();
|
|
25
35
|
assert.deepEqual(summary.render(80), []);
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import { visibleWidth, wrapTextWithAnsi } from "@gsd/pi-tui";
|
|
1
2
|
import * as Diff from "diff";
|
|
3
|
+
import chalk from "chalk";
|
|
2
4
|
import { theme } from "../theme/theme.js";
|
|
3
5
|
|
|
6
|
+
const DIFF_BG = {
|
|
7
|
+
addedLine: "#0f2f1a",
|
|
8
|
+
removedLine: "#3a1116",
|
|
9
|
+
addedToken: "#1b5e20",
|
|
10
|
+
removedToken: "#7f1d1d",
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
4
13
|
/**
|
|
5
14
|
* Parse diff line to extract prefix, line number, and content.
|
|
6
15
|
* Format: "+123 content" or "-123 content" or " 123 content" or " ..."
|
|
@@ -18,10 +27,52 @@ function replaceTabs(text: string): string {
|
|
|
18
27
|
return text.replace(/\t/g, " ");
|
|
19
28
|
}
|
|
20
29
|
|
|
30
|
+
function formatLineNum(raw: string, width: number): string {
|
|
31
|
+
const trimmed = raw.trim();
|
|
32
|
+
if (!trimmed) return "".padStart(width, " ");
|
|
33
|
+
return trimmed.padStart(width, " ");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function padToWidth(text: string, width: number): string {
|
|
37
|
+
if (!Number.isFinite(width) || width > 10_000) {
|
|
38
|
+
return text;
|
|
39
|
+
}
|
|
40
|
+
const paddingNeeded = Math.max(0, width - visibleWidth(text));
|
|
41
|
+
return text + " ".repeat(paddingNeeded);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function styleRemovedPrefix(prefix: string): string {
|
|
45
|
+
return chalk.hex("#fb7185")(prefix);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function styleAddedPrefix(prefix: string): string {
|
|
49
|
+
return chalk.hex("#4ade80")(prefix);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function styleRemovedToken(value: string): string {
|
|
53
|
+
return chalk.bgHex(DIFF_BG.removedToken).whiteBright(value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function styleAddedToken(value: string): string {
|
|
57
|
+
return chalk.bgHex(DIFF_BG.addedToken).whiteBright(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function styleAddedLine(text: string, width: number): string {
|
|
61
|
+
const prefix = text.startsWith("+") ? styleAddedPrefix("+") : "";
|
|
62
|
+
const rest = prefix ? text.slice(1) : text;
|
|
63
|
+
return chalk.bgHex(DIFF_BG.addedLine).whiteBright(padToWidth(`${prefix}${rest}`, width));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function styleRemovedLine(text: string, width: number): string {
|
|
67
|
+
const prefix = text.startsWith("-") ? styleRemovedPrefix("-") : "";
|
|
68
|
+
const rest = prefix ? text.slice(1) : text;
|
|
69
|
+
return chalk.bgHex(DIFF_BG.removedLine).whiteBright(padToWidth(`${prefix}${rest}`, width));
|
|
70
|
+
}
|
|
71
|
+
|
|
21
72
|
/**
|
|
22
|
-
* Compute word-level diff and render
|
|
73
|
+
* Compute word-level diff and render changed tokens with subtle emphasis.
|
|
23
74
|
* Uses diffWords which groups whitespace with adjacent words for cleaner highlighting.
|
|
24
|
-
* Strips leading whitespace from
|
|
75
|
+
* Strips leading whitespace from highlighted token spans.
|
|
25
76
|
*/
|
|
26
77
|
function renderIntraLineDiff(oldContent: string, newContent: string): { removedLine: string; addedLine: string } {
|
|
27
78
|
const wordDiff = Diff.diffWords(oldContent, newContent);
|
|
@@ -34,7 +85,6 @@ function renderIntraLineDiff(oldContent: string, newContent: string): { removedL
|
|
|
34
85
|
for (const part of wordDiff) {
|
|
35
86
|
if (part.removed) {
|
|
36
87
|
let value = part.value;
|
|
37
|
-
// Strip leading whitespace from the first removed part
|
|
38
88
|
if (isFirstRemoved) {
|
|
39
89
|
const leadingWs = value.match(/^(\s*)/)?.[1] || "";
|
|
40
90
|
value = value.slice(leadingWs.length);
|
|
@@ -42,11 +92,10 @@ function renderIntraLineDiff(oldContent: string, newContent: string): { removedL
|
|
|
42
92
|
isFirstRemoved = false;
|
|
43
93
|
}
|
|
44
94
|
if (value) {
|
|
45
|
-
removedLine +=
|
|
95
|
+
removedLine += styleRemovedToken(value);
|
|
46
96
|
}
|
|
47
97
|
} else if (part.added) {
|
|
48
98
|
let value = part.value;
|
|
49
|
-
// Strip leading whitespace from the first added part
|
|
50
99
|
if (isFirstAdded) {
|
|
51
100
|
const leadingWs = value.match(/^(\s*)/)?.[1] || "";
|
|
52
101
|
value = value.slice(leadingWs.length);
|
|
@@ -54,7 +103,7 @@ function renderIntraLineDiff(oldContent: string, newContent: string): { removedL
|
|
|
54
103
|
isFirstAdded = false;
|
|
55
104
|
}
|
|
56
105
|
if (value) {
|
|
57
|
-
addedLine +=
|
|
106
|
+
addedLine += styleAddedToken(value);
|
|
58
107
|
}
|
|
59
108
|
} else {
|
|
60
109
|
removedLine += part.value;
|
|
@@ -70,29 +119,52 @@ export interface RenderDiffOptions {
|
|
|
70
119
|
filePath?: string;
|
|
71
120
|
}
|
|
72
121
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
122
|
+
function renderStyledLine(
|
|
123
|
+
text: string,
|
|
124
|
+
width: number,
|
|
125
|
+
kind: "context" | "added" | "removed",
|
|
126
|
+
): string[] {
|
|
127
|
+
const wrapWidth = Math.max(1, width);
|
|
128
|
+
const wrapped = wrapTextWithAnsi(text, wrapWidth);
|
|
129
|
+
if (kind === "added") {
|
|
130
|
+
return wrapped.map((line) => styleAddedLine(line, width));
|
|
131
|
+
}
|
|
132
|
+
if (kind === "removed") {
|
|
133
|
+
return wrapped.map((line) => styleRemovedLine(line, width));
|
|
134
|
+
}
|
|
135
|
+
return wrapped.map((line) => padToWidth(theme.fg("toolDiffContext", line), width));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function renderDiffLines(diffText: string, width: number, _options: RenderDiffOptions = {}): string[] {
|
|
80
139
|
const lines = diffText.split("\n");
|
|
81
140
|
const result: string[] = [];
|
|
82
141
|
|
|
142
|
+
const parsedLines = lines.map(parseDiffLine).filter((p): p is { prefix: string; lineNum: string; content: string } => !!p);
|
|
143
|
+
const lineNumWidth = Math.max(
|
|
144
|
+
1,
|
|
145
|
+
...parsedLines
|
|
146
|
+
.map((p) => p.lineNum.trim())
|
|
147
|
+
.filter(Boolean)
|
|
148
|
+
.map((n) => n.length),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const formatLine = (prefix: "+" | "-" | " ", lineNum: string, content: string): string => {
|
|
152
|
+
const num = formatLineNum(lineNum, lineNumWidth);
|
|
153
|
+
return `${prefix}${num} ${replaceTabs(content)}`;
|
|
154
|
+
};
|
|
155
|
+
|
|
83
156
|
let i = 0;
|
|
84
157
|
while (i < lines.length) {
|
|
85
158
|
const line = lines[i];
|
|
86
159
|
const parsed = parseDiffLine(line);
|
|
87
160
|
|
|
88
161
|
if (!parsed) {
|
|
89
|
-
result.push(
|
|
162
|
+
result.push(...renderStyledLine(line, width, "context"));
|
|
90
163
|
i++;
|
|
91
164
|
continue;
|
|
92
165
|
}
|
|
93
166
|
|
|
94
167
|
if (parsed.prefix === "-") {
|
|
95
|
-
// Collect consecutive removed lines
|
|
96
168
|
const removedLines: { lineNum: string; content: string }[] = [];
|
|
97
169
|
while (i < lines.length) {
|
|
98
170
|
const p = parseDiffLine(lines[i]);
|
|
@@ -101,7 +173,6 @@ export function renderDiff(diffText: string, _options: RenderDiffOptions = {}):
|
|
|
101
173
|
i++;
|
|
102
174
|
}
|
|
103
175
|
|
|
104
|
-
// Collect consecutive added lines
|
|
105
176
|
const addedLines: { lineNum: string; content: string }[] = [];
|
|
106
177
|
while (i < lines.length) {
|
|
107
178
|
const p = parseDiffLine(lines[i]);
|
|
@@ -110,8 +181,6 @@ export function renderDiff(diffText: string, _options: RenderDiffOptions = {}):
|
|
|
110
181
|
i++;
|
|
111
182
|
}
|
|
112
183
|
|
|
113
|
-
// Only do intra-line diffing when there's exactly one removed and one added line
|
|
114
|
-
// (indicating a single line modification). Otherwise, show lines as-is.
|
|
115
184
|
if (removedLines.length === 1 && addedLines.length === 1) {
|
|
116
185
|
const removed = removedLines[0];
|
|
117
186
|
const added = addedLines[0];
|
|
@@ -121,27 +190,35 @@ export function renderDiff(diffText: string, _options: RenderDiffOptions = {}):
|
|
|
121
190
|
replaceTabs(added.content),
|
|
122
191
|
);
|
|
123
192
|
|
|
124
|
-
result.push(
|
|
125
|
-
result.push(
|
|
193
|
+
result.push(...renderStyledLine(formatLine("-", removed.lineNum, removedLine), width, "removed"));
|
|
194
|
+
result.push(...renderStyledLine(formatLine("+", added.lineNum, addedLine), width, "added"));
|
|
126
195
|
} else {
|
|
127
|
-
// Show all removed lines first, then all added lines
|
|
128
196
|
for (const removed of removedLines) {
|
|
129
|
-
result.push(
|
|
197
|
+
result.push(...renderStyledLine(formatLine("-", removed.lineNum, removed.content), width, "removed"));
|
|
130
198
|
}
|
|
131
199
|
for (const added of addedLines) {
|
|
132
|
-
result.push(
|
|
200
|
+
result.push(...renderStyledLine(formatLine("+", added.lineNum, added.content), width, "added"));
|
|
133
201
|
}
|
|
134
202
|
}
|
|
135
203
|
} else if (parsed.prefix === "+") {
|
|
136
|
-
|
|
137
|
-
result.push(theme.fg("toolDiffAdded", `+${parsed.lineNum} ${replaceTabs(parsed.content)}`));
|
|
204
|
+
result.push(...renderStyledLine(formatLine("+", parsed.lineNum, parsed.content), width, "added"));
|
|
138
205
|
i++;
|
|
139
206
|
} else {
|
|
140
|
-
|
|
141
|
-
result.push(theme.fg("toolDiffContext", ` ${parsed.lineNum} ${replaceTabs(parsed.content)}`));
|
|
207
|
+
result.push(...renderStyledLine(formatLine(" ", parsed.lineNum, parsed.content), width, "context"));
|
|
142
208
|
i++;
|
|
143
209
|
}
|
|
144
210
|
}
|
|
145
211
|
|
|
146
|
-
return result
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Render a diff string with Claude-like colored lines.
|
|
217
|
+
* - Context lines: muted gray
|
|
218
|
+
* - Removed lines: red text on subtle red background
|
|
219
|
+
* - Added lines: green text on subtle green background
|
|
220
|
+
* - Changed tokens: slightly stronger background + bold
|
|
221
|
+
*/
|
|
222
|
+
export function renderDiff(diffText: string, options: RenderDiffOptions = {}): string {
|
|
223
|
+
return renderDiffLines(diffText, Number.MAX_SAFE_INTEGER, options).join("\n");
|
|
147
224
|
}
|
|
@@ -47,6 +47,7 @@ export class FooterComponent implements Component {
|
|
|
47
47
|
private autoCompactEnabled = true;
|
|
48
48
|
private permissionMode: PermissionMode = "danger-full-access";
|
|
49
49
|
private notificationSoundEnabled = false;
|
|
50
|
+
private verboseFooterEnabled = false;
|
|
50
51
|
|
|
51
52
|
constructor(
|
|
52
53
|
private session: AgentSession,
|
|
@@ -65,6 +66,10 @@ export class FooterComponent implements Component {
|
|
|
65
66
|
this.notificationSoundEnabled = enabled;
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
setVerboseFooterEnabled(enabled: boolean): void {
|
|
70
|
+
this.verboseFooterEnabled = enabled;
|
|
71
|
+
}
|
|
72
|
+
|
|
68
73
|
/**
|
|
69
74
|
* No-op: git branch caching now handled by provider.
|
|
70
75
|
* Kept for compatibility with existing call sites in interactive-mode.
|
|
@@ -125,7 +130,7 @@ export class FooterComponent implements Component {
|
|
|
125
130
|
const extensionStatuses = this.footerData.getExtensionStatuses();
|
|
126
131
|
const cacheTimerStatusRaw = extensionStatuses.get("cache-timer");
|
|
127
132
|
const cacheTimerStatus = cacheTimerStatusRaw ? sanitizeStatusText(cacheTimerStatusRaw) : "";
|
|
128
|
-
const hotkeysHints = ["
|
|
133
|
+
const hotkeysHints = ["/hotkeys"];
|
|
129
134
|
const firstLineMinPadding = 2;
|
|
130
135
|
const firstLineRightParts = cacheTimerStatus ? [cacheTimerStatus] : [];
|
|
131
136
|
if (this.notificationSoundEnabled) {
|
|
@@ -155,10 +160,12 @@ export class FooterComponent implements Component {
|
|
|
155
160
|
|
|
156
161
|
// Build stats line
|
|
157
162
|
const statsParts = [];
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
if (this.verboseFooterEnabled) {
|
|
164
|
+
if (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);
|
|
165
|
+
if (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);
|
|
166
|
+
if (totalCacheRead) statsParts.push(`Cache ${formatTokens(totalCacheRead)}`);
|
|
167
|
+
if (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);
|
|
168
|
+
}
|
|
162
169
|
|
|
163
170
|
// Show cost with "(sub)" indicator if using OAuth subscription
|
|
164
171
|
const usingSubscription = displayModel ? this.session.modelRegistry.isUsingOAuth(displayModel) : false;
|
|
@@ -168,7 +175,7 @@ export class FooterComponent implements Component {
|
|
|
168
175
|
}
|
|
169
176
|
|
|
170
177
|
// Per-prompt cost annotation (opt-in via show_token_cost preference, #1515)
|
|
171
|
-
if (process.env.GSD_SHOW_TOKEN_COST === "1") {
|
|
178
|
+
if (this.verboseFooterEnabled && process.env.GSD_SHOW_TOKEN_COST === "1") {
|
|
172
179
|
const lastTurnCost = this.session.getLastTurnCost();
|
|
173
180
|
if (lastTurnCost > 0) {
|
|
174
181
|
statsParts.push(`(last: ${formatPromptCost(lastTurnCost)})`);
|
|
@@ -43,6 +43,7 @@ export interface SettingsConfig {
|
|
|
43
43
|
codexRotate: boolean;
|
|
44
44
|
fastMode: boolean;
|
|
45
45
|
cacheTimer: boolean;
|
|
46
|
+
verboseFooter: boolean;
|
|
46
47
|
pinLastPrompt: boolean;
|
|
47
48
|
steeringMode: "all" | "one-at-a-time";
|
|
48
49
|
followUpMode: "all" | "one-at-a-time";
|
|
@@ -67,6 +68,7 @@ export interface SettingsConfig {
|
|
|
67
68
|
clearOnShrink: boolean;
|
|
68
69
|
timestampFormat: "date-time-iso" | "date-time-us";
|
|
69
70
|
toolOutputMode: "minimal" | "normal";
|
|
71
|
+
collapseToolCalls: boolean;
|
|
70
72
|
rtk: boolean;
|
|
71
73
|
editorScheme: "auto" | "vscode" | "cursor" | "zed" | "jetbrains" | "sublime" | "file";
|
|
72
74
|
autoDream: boolean;
|
|
@@ -110,6 +112,7 @@ export interface SettingsCallbacks {
|
|
|
110
112
|
onCodexRotateChange: (enabled: boolean) => void;
|
|
111
113
|
onFastModeChange: (enabled: boolean) => void;
|
|
112
114
|
onCacheTimerChange: (enabled: boolean) => void;
|
|
115
|
+
onVerboseFooterChange: (enabled: boolean) => void;
|
|
113
116
|
onPinLastPromptChange: (enabled: boolean) => void;
|
|
114
117
|
onSteeringModeChange: (mode: "all" | "one-at-a-time") => void;
|
|
115
118
|
onFollowUpModeChange: (mode: "all" | "one-at-a-time") => void;
|
|
@@ -132,6 +135,7 @@ export interface SettingsCallbacks {
|
|
|
132
135
|
onClearOnShrinkChange: (enabled: boolean) => void;
|
|
133
136
|
onTimestampFormatChange: (format: "date-time-iso" | "date-time-us") => void;
|
|
134
137
|
onToolOutputModeChange: (mode: "minimal" | "normal") => void;
|
|
138
|
+
onCollapseToolCallsChange: (enabled: boolean) => void;
|
|
135
139
|
onRtkChange: (enabled: boolean) => void;
|
|
136
140
|
onEditorSchemeChange: (scheme: "auto" | "vscode" | "cursor" | "zed" | "jetbrains" | "sublime" | "file") => void;
|
|
137
141
|
onCancel: () => void;
|
|
@@ -318,6 +322,13 @@ export class SettingsSelectorComponent extends Container {
|
|
|
318
322
|
currentValue: config.hideThinkingBlock ? "true" : "false",
|
|
319
323
|
values: ["true", "false"],
|
|
320
324
|
},
|
|
325
|
+
{
|
|
326
|
+
id: "collapse-tool-calls",
|
|
327
|
+
label: "Collapse tool calls",
|
|
328
|
+
description: "Group low-priority tool calls into collapsed summary lines",
|
|
329
|
+
currentValue: config.collapseToolCalls ? "true" : "false",
|
|
330
|
+
values: ["true", "false"],
|
|
331
|
+
},
|
|
321
332
|
{
|
|
322
333
|
id: "tool-output-mode",
|
|
323
334
|
label: "Tool output mode",
|
|
@@ -537,9 +548,19 @@ export class SettingsSelectorComponent extends Container {
|
|
|
537
548
|
values: ["true", "false"],
|
|
538
549
|
});
|
|
539
550
|
|
|
540
|
-
//
|
|
551
|
+
// Verbose footer toggle (insert after cache-timer)
|
|
541
552
|
const cacheTimerIdx = items.findIndex((item) => item.id === "cache-timer");
|
|
542
553
|
items.splice(cacheTimerIdx + 1, 0, {
|
|
554
|
+
id: "verbose-footer",
|
|
555
|
+
label: "Verbose footer",
|
|
556
|
+
description: "Show extra footer stats like input, output, and cache tokens",
|
|
557
|
+
currentValue: config.verboseFooter ? "true" : "false",
|
|
558
|
+
values: ["true", "false"],
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// Pin last prompt toggle (insert after verbose-footer)
|
|
562
|
+
const verboseFooterIdx = items.findIndex((item) => item.id === "verbose-footer");
|
|
563
|
+
items.splice(verboseFooterIdx + 1, 0, {
|
|
543
564
|
id: "pin-last-prompt",
|
|
544
565
|
label: "Pin last prompt",
|
|
545
566
|
description: "Show your last sent message above the editor so you remember the topic",
|
|
@@ -587,9 +608,9 @@ export class SettingsSelectorComponent extends Container {
|
|
|
587
608
|
values: ["true", "false"],
|
|
588
609
|
});
|
|
589
610
|
|
|
590
|
-
// RTK toggle (insert after
|
|
591
|
-
const
|
|
592
|
-
items.splice(
|
|
611
|
+
// RTK toggle (insert after verbose-footer)
|
|
612
|
+
const verboseFooterIndex = items.findIndex((item) => item.id === "verbose-footer");
|
|
613
|
+
items.splice(verboseFooterIndex + 1, 0, {
|
|
593
614
|
id: "rtk",
|
|
594
615
|
label: "RTK",
|
|
595
616
|
description: "Enable RTK shell-command compression (requires restart)",
|
|
@@ -733,6 +754,9 @@ export class SettingsSelectorComponent extends Container {
|
|
|
733
754
|
case "cache-timer":
|
|
734
755
|
callbacks.onCacheTimerChange(newValue === "true");
|
|
735
756
|
break;
|
|
757
|
+
case "verbose-footer":
|
|
758
|
+
callbacks.onVerboseFooterChange(newValue === "true");
|
|
759
|
+
break;
|
|
736
760
|
case "pin-last-prompt":
|
|
737
761
|
callbacks.onPinLastPromptChange(newValue === "true");
|
|
738
762
|
break;
|
|
@@ -769,6 +793,9 @@ export class SettingsSelectorComponent extends Container {
|
|
|
769
793
|
case "hide-thinking":
|
|
770
794
|
callbacks.onHideThinkingBlockChange(newValue === "true");
|
|
771
795
|
break;
|
|
796
|
+
case "collapse-tool-calls":
|
|
797
|
+
callbacks.onCollapseToolCallsChange(newValue === "true");
|
|
798
|
+
break;
|
|
772
799
|
case "tool-output-mode":
|
|
773
800
|
callbacks.onToolOutputModeChange(newValue as "minimal" | "normal");
|
|
774
801
|
break;
|