@astralyn/pi 1.4.8 → 1.4.10
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 +68 -0
- package/dist/bundle/chunks/{chunk-I2COK45I.js → chunk-6GTUUUCJ.js} +128 -128
- package/dist/bundle/chunks/{chunk-PU2IWWXV.js → chunk-DLVLAQEG.js} +1 -1
- package/dist/bundle/chunks/{chunk-MG5XDTXV.js → chunk-LPF7HQ3S.js} +1 -1
- package/dist/bundle/chunks/{chunk-FODCTZXW.js → chunk-MF3T3ETJ.js} +4 -4
- package/dist/bundle/chunks/{chunk-RO2VH6UZ.js → chunk-R267VV5T.js} +1 -1
- package/dist/bundle/chunks/{chunk-RLNMABLM.js → chunk-RVKT7OUB.js} +1 -1
- package/dist/bundle/chunks/{chunk-5VIOGR76.js → chunk-UCH27AVQ.js} +1 -1
- package/dist/bundle/chunks/{cli-MEL7SGJD.js → cli-MBFCFGI2.js} +1 -1
- package/dist/bundle/chunks/{execute-4CWRNHPO.js → execute-FQKRO4XJ.js} +1 -1
- package/dist/bundle/chunks/{runtime-R3X67XMR.js → runtime-3RPOJS5T.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-BBLD7YLS.js → virtual-modules-6IJ6V2CG.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/agent-session.d.ts +34 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +99 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/isolated-session.d.ts +7 -2
- package/dist/core/isolated-session.d.ts.map +1 -1
- package/dist/core/isolated-session.js +7 -3
- package/dist/core/isolated-session.js.map +1 -1
- package/dist/core/session-manager.d.ts +17 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +29 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tasks/delivery.d.ts +42 -7
- package/dist/core/tasks/delivery.d.ts.map +1 -1
- package/dist/core/tasks/delivery.js +129 -15
- package/dist/core/tasks/delivery.js.map +1 -1
- package/dist/core/tasks/execution.d.ts +27 -14
- package/dist/core/tasks/execution.d.ts.map +1 -1
- package/dist/core/tasks/execution.js +90 -86
- package/dist/core/tasks/execution.js.map +1 -1
- package/dist/core/tasks/limits.d.ts +13 -1
- package/dist/core/tasks/limits.d.ts.map +1 -1
- package/dist/core/tasks/limits.js +14 -1
- package/dist/core/tasks/limits.js.map +1 -1
- package/dist/core/tasks/output.d.ts +9 -1
- package/dist/core/tasks/output.d.ts.map +1 -1
- package/dist/core/tasks/output.js +23 -10
- package/dist/core/tasks/output.js.map +1 -1
- package/dist/core/tasks/presentation.d.ts +1 -1
- package/dist/core/tasks/presentation.d.ts.map +1 -1
- package/dist/core/tasks/presentation.js +6 -4
- package/dist/core/tasks/presentation.js.map +1 -1
- package/dist/core/tasks/record.d.ts +41 -29
- package/dist/core/tasks/record.d.ts.map +1 -1
- package/dist/core/tasks/record.js +82 -37
- package/dist/core/tasks/record.js.map +1 -1
- package/dist/core/tasks/reminder.js +2 -2
- package/dist/core/tasks/reminder.js.map +1 -1
- package/dist/core/tasks/runtime.d.ts +17 -14
- package/dist/core/tasks/runtime.d.ts.map +1 -1
- package/dist/core/tasks/runtime.js +68 -116
- package/dist/core/tasks/runtime.js.map +1 -1
- package/dist/core/tasks/scheduler.d.ts +3 -1
- package/dist/core/tasks/scheduler.d.ts.map +1 -1
- package/dist/core/tasks/scheduler.js +16 -12
- package/dist/core/tasks/scheduler.js.map +1 -1
- package/dist/core/tasks/session.d.ts +24 -3
- package/dist/core/tasks/session.d.ts.map +1 -1
- package/dist/core/tasks/session.js +73 -9
- package/dist/core/tasks/session.js.map +1 -1
- package/dist/core/tasks/store.d.ts +17 -4
- package/dist/core/tasks/store.d.ts.map +1 -1
- package/dist/core/tasks/store.js +45 -48
- package/dist/core/tasks/store.js.map +1 -1
- package/dist/core/tasks/types.d.ts +72 -29
- package/dist/core/tasks/types.d.ts.map +1 -1
- package/dist/core/tasks/types.js +7 -0
- package/dist/core/tasks/types.js.map +1 -1
- package/dist/core/tasks/wait.d.ts +19 -0
- package/dist/core/tasks/wait.d.ts.map +1 -0
- package/dist/core/tasks/wait.js +45 -0
- package/dist/core/tasks/wait.js.map +1 -0
- package/dist/core/tools/shell-execution.d.ts.map +1 -1
- package/dist/core/tools/shell-execution.js +2 -1
- package/dist/core/tools/shell-execution.js.map +1 -1
- package/dist/core/tools/shell-tool.d.ts.map +1 -1
- package/dist/core/tools/shell-tool.js +20 -13
- package/dist/core/tools/shell-tool.js.map +1 -1
- package/dist/extensions/agent/activity-lines.d.ts +10 -0
- package/dist/extensions/agent/activity-lines.d.ts.map +1 -0
- package/dist/extensions/agent/activity-lines.js +76 -0
- package/dist/extensions/agent/activity-lines.js.map +1 -0
- package/dist/extensions/agent/activity.d.ts +25 -4
- package/dist/extensions/agent/activity.d.ts.map +1 -1
- package/dist/extensions/agent/activity.js +76 -21
- package/dist/extensions/agent/activity.js.map +1 -1
- package/dist/extensions/agent/artifacts.d.ts +15 -0
- package/dist/extensions/agent/artifacts.d.ts.map +1 -0
- package/dist/extensions/agent/artifacts.js +44 -0
- package/dist/extensions/agent/artifacts.js.map +1 -0
- package/dist/extensions/agent/progress.d.ts +30 -4
- package/dist/extensions/agent/progress.d.ts.map +1 -1
- package/dist/extensions/agent/progress.js +84 -11
- package/dist/extensions/agent/progress.js.map +1 -1
- package/dist/extensions/agent/render.d.ts.map +1 -1
- package/dist/extensions/agent/render.js +8 -24
- package/dist/extensions/agent/render.js.map +1 -1
- package/dist/extensions/agent/report.d.ts +11 -3
- package/dist/extensions/agent/report.d.ts.map +1 -1
- package/dist/extensions/agent/report.js +32 -7
- package/dist/extensions/agent/report.js.map +1 -1
- package/dist/extensions/agent/tool.d.ts.map +1 -1
- package/dist/extensions/agent/tool.js +20 -5
- package/dist/extensions/agent/tool.js.map +1 -1
- package/dist/extensions/agent/types.d.ts +2 -0
- package/dist/extensions/agent/types.d.ts.map +1 -1
- package/dist/extensions/agent/types.js.map +1 -1
- package/dist/extensions/agent/worker.d.ts +7 -0
- package/dist/extensions/agent/worker.d.ts.map +1 -1
- package/dist/extensions/agent/worker.js +9 -3
- package/dist/extensions/agent/worker.js.map +1 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +2 -0
- package/dist/extensions/index.js.map +1 -1
- package/dist/extensions/search/index.d.ts.map +1 -1
- package/dist/extensions/search/index.js +6 -7
- package/dist/extensions/search/index.js.map +1 -1
- package/dist/extensions/shell-paths/index.d.ts +8 -0
- package/dist/extensions/shell-paths/index.d.ts.map +1 -0
- package/dist/extensions/shell-paths/index.js +74 -0
- package/dist/extensions/shell-paths/index.js.map +1 -0
- package/dist/extensions/tasks/tool/actions.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/actions.js +15 -11
- package/dist/extensions/tasks/tool/actions.js.map +1 -1
- package/dist/extensions/tasks/tool/schema.d.ts +1 -1
- package/dist/extensions/tasks/tool/schema.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/schema.js +5 -5
- package/dist/extensions/tasks/tool/schema.js.map +1 -1
- package/dist/extensions/tasks/tool/types.d.ts +2 -0
- package/dist/extensions/tasks/tool/types.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/types.js.map +1 -1
- package/dist/extensions/tasks/tool.d.ts.map +1 -1
- package/dist/extensions/tasks/tool.js +3 -2
- package/dist/extensions/tasks/tool.js.map +1 -1
- package/dist/extensions/tasks/ui/completion-render.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/completion-render.js +2 -2
- package/dist/extensions/tasks/ui/completion-render.js.map +1 -1
- package/dist/extensions/tasks/ui/footer.d.ts +37 -0
- package/dist/extensions/tasks/ui/footer.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/footer.js +129 -0
- package/dist/extensions/tasks/ui/footer.js.map +1 -0
- package/dist/extensions/tasks/ui/frame.d.ts +36 -0
- package/dist/extensions/tasks/ui/frame.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/frame.js +33 -0
- package/dist/extensions/tasks/ui/frame.js.map +1 -0
- package/dist/extensions/tasks/ui/help.d.ts +6 -0
- package/dist/extensions/tasks/ui/help.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/help.js +53 -0
- package/dist/extensions/tasks/ui/help.js.map +1 -0
- package/dist/extensions/tasks/ui/inspector.d.ts +4 -3
- package/dist/extensions/tasks/ui/inspector.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/inspector.js +25 -6
- package/dist/extensions/tasks/ui/inspector.js.map +1 -1
- package/dist/extensions/tasks/ui/layout.d.ts +15 -18
- package/dist/extensions/tasks/ui/layout.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/layout.js +162 -255
- package/dist/extensions/tasks/ui/layout.js.map +1 -1
- package/dist/extensions/tasks/ui/list.d.ts +3 -1
- package/dist/extensions/tasks/ui/list.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/list.js +19 -14
- package/dist/extensions/tasks/ui/list.js.map +1 -1
- package/dist/extensions/tasks/ui/manager.d.ts +17 -5
- package/dist/extensions/tasks/ui/manager.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/manager.js +147 -90
- package/dist/extensions/tasks/ui/manager.js.map +1 -1
- package/dist/extensions/tasks/ui/model.d.ts +32 -5
- package/dist/extensions/tasks/ui/model.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/model.js +7 -7
- package/dist/extensions/tasks/ui/model.js.map +1 -1
- package/dist/extensions/tasks/ui/panes.d.ts +40 -0
- package/dist/extensions/tasks/ui/panes.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/panes.js +144 -0
- package/dist/extensions/tasks/ui/panes.js.map +1 -0
- package/dist/extensions/tasks/ui/render.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/render.js +11 -7
- package/dist/extensions/tasks/ui/render.js.map +1 -1
- package/dist/extensions/tasks/ui/viewport.d.ts +12 -4
- package/dist/extensions/tasks/ui/viewport.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/viewport.js +23 -7
- package/dist/extensions/tasks/ui/viewport.js.map +1 -1
- package/dist/extensions/tasks/ui/views.d.ts +12 -6
- package/dist/extensions/tasks/ui/views.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/views.js +39 -32
- package/dist/extensions/tasks/ui/views.js.map +1 -1
- package/dist/modes/interactive/chat-viewport.d.ts +5 -2
- package/dist/modes/interactive/chat-viewport.d.ts.map +1 -1
- package/dist/modes/interactive/chat-viewport.js +72 -12
- package/dist/modes/interactive/chat-viewport.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +11 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +43 -17
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/sticky-user-message.d.ts +23 -0
- package/dist/modes/interactive/components/sticky-user-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/sticky-user-message.js +58 -0
- package/dist/modes/interactive/components/sticky-user-message.js.map +1 -0
- package/dist/modes/interactive/components/user-message.d.ts +9 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +51 -10
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +4 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tool-view/chat.d.ts +3 -2
- package/dist/modes/interactive/tool-view/chat.d.ts.map +1 -1
- package/dist/modes/interactive/tool-view/chat.js +3 -3
- package/dist/modes/interactive/tool-view/chat.js.map +1 -1
- package/dist/modes/interactive/transcript-layout.d.ts +23 -0
- package/dist/modes/interactive/transcript-layout.d.ts.map +1 -0
- package/dist/modes/interactive/transcript-layout.js +29 -0
- package/dist/modes/interactive/transcript-layout.js.map +1 -0
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +37 -2
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +11 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +48 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +1 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +2 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/bundled/README.md +1 -0
- package/docs/bundled/extensions/agent.md +39 -7
- package/docs/bundled/extensions/shell-paths.md +17 -0
- package/docs/bundled/tasks.md +25 -21
- package/docs/docs.json +4 -0
- package/docs/extensions.md +14 -10
- package/docs/json.md +3 -0
- package/docs/rpc.md +14 -0
- package/docs/sdk.md +11 -1
- package/docs/session-format.md +10 -4
- package/docs/sessions.md +1 -1
- package/docs/usage.md +3 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
2
2
|
/** Independent scroll state for each region, with a source-line anchor for resize. */
|
|
3
3
|
export class TaskViewport {
|
|
4
|
-
constructor(
|
|
4
|
+
constructor(tails = false) {
|
|
5
5
|
this.scroll = 0;
|
|
6
6
|
this.entries = [];
|
|
7
7
|
this.height = 1;
|
|
8
8
|
this.start = 0;
|
|
9
|
-
this.
|
|
9
|
+
this.tailing = tails;
|
|
10
|
+
this.following = tails;
|
|
11
|
+
}
|
|
12
|
+
/** Whether the view is pinned to the end of the content. */
|
|
13
|
+
get follow() {
|
|
14
|
+
return this.following;
|
|
15
|
+
}
|
|
16
|
+
/** Starts over for content of a different kind: the top, or the tail when it tails. */
|
|
17
|
+
retarget(tails) {
|
|
18
|
+
this.tailing = tails;
|
|
19
|
+
this.following = tails;
|
|
20
|
+
this.scroll = 0;
|
|
21
|
+
this.anchor = undefined;
|
|
10
22
|
}
|
|
11
23
|
layout(lines, width, height) {
|
|
12
24
|
this.height = Math.max(1, height);
|
|
@@ -34,23 +46,27 @@ export class TaskViewport {
|
|
|
34
46
|
offset++;
|
|
35
47
|
}
|
|
36
48
|
}
|
|
37
|
-
this.start = this.
|
|
49
|
+
this.start = this.following ? this.max : Math.min(offset, this.max);
|
|
38
50
|
return this.entries.slice(this.start, this.start + this.height).map((entry) => entry.text);
|
|
39
51
|
}
|
|
40
52
|
get max() {
|
|
41
53
|
return Math.max(0, this.entries.length - this.height);
|
|
42
54
|
}
|
|
43
|
-
move(direction, page
|
|
55
|
+
move(direction, page) {
|
|
44
56
|
this.scroll = Math.max(0, Math.min(this.max, this.start + direction * (page ? this.height : 1)));
|
|
45
57
|
const entry = this.entries[this.scroll];
|
|
46
58
|
this.anchor = entry ? { line: entry.line, column: entry.column } : undefined;
|
|
47
|
-
this.
|
|
59
|
+
this.following = this.tailing && direction > 0 && this.scroll === this.max;
|
|
48
60
|
}
|
|
49
|
-
jump(bottom
|
|
61
|
+
jump(bottom) {
|
|
50
62
|
this.scroll = bottom ? this.max : 0;
|
|
51
63
|
const entry = this.entries[this.scroll];
|
|
52
64
|
this.anchor = entry ? { line: entry.line, column: entry.column } : undefined;
|
|
53
|
-
this.
|
|
65
|
+
this.following = bottom && this.tailing;
|
|
66
|
+
}
|
|
67
|
+
/** Whether the laid-out content is taller than the viewport. */
|
|
68
|
+
get overflows() {
|
|
69
|
+
return this.entries.length > this.height;
|
|
54
70
|
}
|
|
55
71
|
get range() {
|
|
56
72
|
return `${this.entries.length ? this.start + 1 : 0}–${Math.min(this.start + this.height, this.entries.length)}/${this.entries.length}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewport.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/viewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAQxE,sFAAsF;AACtF,MAAM,OAAO,YAAY;
|
|
1
|
+
{"version":3,"file":"viewport.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/viewport.ts"],"names":[],"mappings":"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","sourcesContent":["import { visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\n\ninterface Entry {\n\ttext: string;\n\tline: number;\n\tcolumn: number;\n}\n\n/** Independent scroll state for each region, with a source-line anchor for resize. */\nexport class TaskViewport {\n\t/** Whether the content tails: scrolling to its end resumes following, and it starts following. */\n\tprivate tailing: boolean;\n\tprivate following: boolean;\n\tprivate scroll = 0;\n\tprivate anchor?: { line: number; column: number };\n\tprivate entries: Entry[] = [];\n\tprivate wrapped?: { lines: string[]; width: number };\n\tprivate height = 1;\n\tprivate start = 0;\n\tconstructor(tails = false) {\n\t\tthis.tailing = tails;\n\t\tthis.following = tails;\n\t}\n\t/** Whether the view is pinned to the end of the content. */\n\tget follow(): boolean {\n\t\treturn this.following;\n\t}\n\t/** Starts over for content of a different kind: the top, or the tail when it tails. */\n\tretarget(tails: boolean): void {\n\t\tthis.tailing = tails;\n\t\tthis.following = tails;\n\t\tthis.scroll = 0;\n\t\tthis.anchor = undefined;\n\t}\n\tlayout(lines: string[], width: number, height: number): string[] {\n\t\tthis.height = Math.max(1, height);\n\t\t// Output can be 48 KiB; rewrap only when its lines or the width change, not per animation frame.\n\t\tif (lines !== this.wrapped?.lines || width !== this.wrapped.width) {\n\t\t\tthis.wrapped = { lines, width };\n\t\t\tthis.entries = lines.flatMap((line, index) => {\n\t\t\t\tlet column = 0;\n\t\t\t\treturn wrapTextWithAnsi(line, Math.max(1, width)).map((text) => {\n\t\t\t\t\tconst entry = { text, line: index, column };\n\t\t\t\t\tcolumn += visibleWidth(text);\n\t\t\t\t\treturn entry;\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t\tlet offset = this.scroll;\n\t\tif (this.anchor) {\n\t\t\tconst anchor = this.anchor;\n\t\t\tconst first = this.entries.findIndex((entry) => entry.line === anchor.line);\n\t\t\tif (first >= 0) {\n\t\t\t\toffset = first;\n\t\t\t\twhile (\n\t\t\t\t\toffset + 1 < this.entries.length &&\n\t\t\t\t\tthis.entries[offset + 1]!.line === anchor.line &&\n\t\t\t\t\tthis.entries[offset + 1]!.column <= anchor.column\n\t\t\t\t)\n\t\t\t\t\toffset++;\n\t\t\t}\n\t\t}\n\t\tthis.start = this.following ? this.max : Math.min(offset, this.max);\n\t\treturn this.entries.slice(this.start, this.start + this.height).map((entry) => entry.text);\n\t}\n\tprivate get max(): number {\n\t\treturn Math.max(0, this.entries.length - this.height);\n\t}\n\tmove(direction: number, page: boolean): void {\n\t\tthis.scroll = Math.max(0, Math.min(this.max, this.start + direction * (page ? this.height : 1)));\n\t\tconst entry = this.entries[this.scroll];\n\t\tthis.anchor = entry ? { line: entry.line, column: entry.column } : undefined;\n\t\tthis.following = this.tailing && direction > 0 && this.scroll === this.max;\n\t}\n\tjump(bottom: boolean): void {\n\t\tthis.scroll = bottom ? this.max : 0;\n\t\tconst entry = this.entries[this.scroll];\n\t\tthis.anchor = entry ? { line: entry.line, column: entry.column } : undefined;\n\t\tthis.following = bottom && this.tailing;\n\t}\n\t/** Whether the laid-out content is taller than the viewport. */\n\tget overflows(): boolean {\n\t\treturn this.entries.length > this.height;\n\t}\n\tget range(): string {\n\t\treturn `${this.entries.length ? this.start + 1 : 0}–${Math.min(this.start + this.height, this.entries.length)}/${this.entries.length}`;\n\t}\n}\n"]}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { type TaskRead, type TaskSnapshot } from "../../../core/tasks/types.ts";
|
|
2
2
|
import { type Theme } from "../../../modes/interactive/theme/theme.ts";
|
|
3
3
|
/**
|
|
4
|
-
* Details:
|
|
5
|
-
*
|
|
4
|
+
* Details: identity first, then the executor's input and facts as aligned rows whose values wrap
|
|
5
|
+
* within their own column to `width`, then a multi-line input block. The source, status and
|
|
6
|
+
* runtime are in the inspector header.
|
|
6
7
|
*/
|
|
7
|
-
export declare function detailLines(task: TaskSnapshot,
|
|
8
|
+
export declare function detailLines(task: TaskSnapshot, theme: Theme, width: number): string[];
|
|
9
|
+
/** Whether Output streams from a file; any other task shows what its executor published. */
|
|
10
|
+
export declare function streamsLog(task: TaskSnapshot): boolean;
|
|
8
11
|
/**
|
|
9
|
-
* Output: a log streams from its file through bounded reads
|
|
10
|
-
* published
|
|
12
|
+
* Output: a log streams from its file through bounded reads. Any other task shows what its
|
|
13
|
+
* executor published, current with every snapshot: the human `display` when there is one,
|
|
14
|
+
* otherwise the model-facing result. A running task whose executor renders its own Output passes
|
|
15
|
+
* those `live` lines: they come from trusted extension code, so they are shown as they are, only
|
|
16
|
+
* clipped to the width.
|
|
11
17
|
*/
|
|
12
|
-
export declare function outputLines(task: TaskSnapshot, read: TaskRead | undefined, theme: Theme, width: number): string[];
|
|
18
|
+
export declare function outputLines(task: TaskSnapshot, read: TaskRead | undefined, theme: Theme, width: number, live?: readonly string[]): string[];
|
|
13
19
|
//# sourceMappingURL=views.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/views.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAChG,OAAO,EAAmC,KAAK,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAKxG
|
|
1
|
+
{"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/views.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAChG,OAAO,EAAmC,KAAK,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAKxG;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAiCrF;AAED,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAC1B,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,QAAQ,GAAG,SAAS,EAC1B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GACtB,MAAM,EAAE,CAuBV"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/** The inspector's Details and Output content, computed once per task snapshot and output read. */
|
|
2
|
-
import { Markdown, visibleWidth } from "@earendil-works/pi-tui";
|
|
2
|
+
import { Markdown, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
3
3
|
import { isTaskTerminal } from "../../../core/tasks/types.js";
|
|
4
4
|
import { getMarkdownTheme, highlightCode } from "../../../modes/interactive/theme/theme.js";
|
|
5
5
|
import { cleanTaskText } from "./text.js";
|
|
6
6
|
/**
|
|
7
|
-
* Details:
|
|
8
|
-
*
|
|
7
|
+
* Details: identity first, then the executor's input and facts as aligned rows whose values wrap
|
|
8
|
+
* within their own column to `width`, then a multi-line input block. The source, status and
|
|
9
|
+
* runtime are in the inspector header.
|
|
9
10
|
*/
|
|
10
|
-
export function detailLines(task,
|
|
11
|
+
export function detailLines(task, theme, width) {
|
|
11
12
|
const input = task.input && {
|
|
12
13
|
...task.input,
|
|
13
14
|
label: cleanTaskText(task.input.label),
|
|
@@ -18,61 +19,67 @@ export function detailLines(task, status, theme) {
|
|
|
18
19
|
? highlightCode(input.text, input.language)
|
|
19
20
|
: input.text.split("\n")
|
|
20
21
|
: [];
|
|
21
|
-
const rows = [
|
|
22
|
-
["Source", cleanTaskText(task.label)],
|
|
23
|
-
["Task ID", task.id],
|
|
24
|
-
["Status", status],
|
|
25
|
-
];
|
|
22
|
+
const rows = [["Task ID", task.id]];
|
|
26
23
|
if (task.error)
|
|
27
24
|
rows.push(["Error", cleanTaskText(task.error).replace(/\n/g, " "), "warning"]);
|
|
28
|
-
// Without an input, the title is the only description of the work.
|
|
25
|
+
// Without an input, the title is the only complete description of the work.
|
|
29
26
|
if (!input)
|
|
30
27
|
rows.push(["Title", cleanTaskText(task.title)]);
|
|
31
28
|
else if (inputLines.length <= 1)
|
|
32
29
|
rows.push([input.label, inputLines[0] ?? ""]);
|
|
33
30
|
for (const [label, value] of task.facts ?? [])
|
|
34
31
|
rows.push([cleanTaskText(label), cleanTaskText(value)]);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
// A fact that already names the output file would repeat it.
|
|
33
|
+
const shownAsFact = task.facts?.some(([, value]) => value === task.outputPath);
|
|
34
|
+
if (task.outputPath && !shownAsFact)
|
|
35
|
+
rows.push([task.output === "log" ? "Log" : "Full report", cleanTaskText(task.outputPath)]);
|
|
36
|
+
const labelWidth = Math.max(9, ...rows.map(([label]) => visibleWidth(label))) + 1;
|
|
37
|
+
// Values wrap inside their own column, so continuation lines hang under the first value line.
|
|
38
|
+
const valueWidth = Math.max(1, width - labelWidth);
|
|
39
|
+
const lines = rows.flatMap(([label, value, tone]) => wrapTextWithAnsi(value, valueWidth).map((line, index) => {
|
|
40
|
+
const lead = index ? " ".repeat(labelWidth) : label + " ".repeat(labelWidth - visibleWidth(label));
|
|
41
|
+
return lead + (tone ? theme.fg(tone, line) : line);
|
|
42
|
+
}));
|
|
39
43
|
if (input && inputLines.length > 1)
|
|
40
44
|
lines.push("", theme.fg("accent", input.label), ...inputLines);
|
|
41
45
|
if (input?.truncated)
|
|
42
46
|
lines.push(theme.fg("warning", `[Saved ${input.label.toLowerCase()} truncated.]`));
|
|
43
47
|
return lines;
|
|
44
48
|
}
|
|
49
|
+
/** Whether Output streams from a file; any other task shows what its executor published. */
|
|
50
|
+
export function streamsLog(task) {
|
|
51
|
+
return task.output === "log" && task.outputPath !== undefined;
|
|
52
|
+
}
|
|
45
53
|
/**
|
|
46
|
-
* Output: a log streams from its file through bounded reads
|
|
47
|
-
* published
|
|
54
|
+
* Output: a log streams from its file through bounded reads. Any other task shows what its
|
|
55
|
+
* executor published, current with every snapshot: the human `display` when there is one,
|
|
56
|
+
* otherwise the model-facing result. A running task whose executor renders its own Output passes
|
|
57
|
+
* those `live` lines: they come from trusted extension code, so they are shown as they are, only
|
|
58
|
+
* clipped to the width.
|
|
48
59
|
*/
|
|
49
|
-
export function outputLines(task, read, theme, width) {
|
|
60
|
+
export function outputLines(task, read, theme, width, live) {
|
|
61
|
+
if (live && !isTaskTerminal(task.status))
|
|
62
|
+
return live.map((line) => truncateToWidth(line, Math.max(1, width)));
|
|
50
63
|
const empty = task.status === "completed"
|
|
51
64
|
? "Completed with no output."
|
|
52
65
|
: isTaskTerminal(task.status)
|
|
53
66
|
? "No saved output."
|
|
54
67
|
: "No output yet.";
|
|
55
|
-
const fromLog = task
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const
|
|
68
|
+
const fromLog = streamsLog(task) && read !== undefined && !read.readError;
|
|
69
|
+
const human = task.display?.text ? task.display : undefined;
|
|
70
|
+
const published = human ?? task.result;
|
|
71
|
+
const text = cleanTaskText((fromLog ? read.text : published?.text) || empty);
|
|
72
|
+
// Logs and live progress keep their plain rendering; only a settled report's result is Markdown.
|
|
73
|
+
const markdown = !fromLog && !human && task.output === "report" && task.status === "completed";
|
|
74
|
+
const output = markdown
|
|
59
75
|
? new Markdown(text, 0, 0, getMarkdownTheme(), {
|
|
60
76
|
color: (line) => theme.fg("toolOutput", line),
|
|
61
77
|
}).render(Math.max(1, width))
|
|
62
78
|
: text.split("\n").map((line) => theme.fg("toolOutput", line));
|
|
63
79
|
if (read?.readError)
|
|
64
80
|
output.unshift(theme.fg("warning", "Log unavailable; showing saved result."));
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
output.push("");
|
|
68
|
-
output.push(...cleanTaskText(task.display.text)
|
|
69
|
-
.split("\n")
|
|
70
|
-
.map((line) => theme.fg("toolOutput", line)));
|
|
71
|
-
}
|
|
72
|
-
if (task.display?.truncated)
|
|
73
|
-
output.push(theme.fg("warning", "Additional output truncated."));
|
|
74
|
-
if ((!fromLog || read.readError) && task.result?.truncated)
|
|
75
|
-
output.push(theme.fg("warning", "Saved result truncated."));
|
|
81
|
+
if (!fromLog && published?.truncated)
|
|
82
|
+
output.push(theme.fg("warning", human ? "Output truncated." : "Saved result truncated."));
|
|
76
83
|
return output;
|
|
77
84
|
}
|
|
78
85
|
//# sourceMappingURL=views.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"views.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/views.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"views.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/views.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,EAAE,cAAc,EAAoC,MAAM,8BAA8B,CAAC;AAChG,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAc,MAAM,2CAA2C,CAAC;AACxG,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAI1C;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAkB,EAAE,KAAY,EAAE,KAAa;IAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI;QAC3B,GAAG,IAAI,CAAC,KAAK;QACb,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QACtC,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;KACpC,CAAC;IACF,MAAM,UAAU,GAAG,KAAK;QACvB,CAAC,CAAC,KAAK,CAAC,QAAQ;YACf,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC;YAC3C,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QACzB,CAAC,CAAC,EAAE,CAAC;IACN,MAAM,IAAI,GAAgB,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACjD,IAAI,IAAI,CAAC,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;IAC/F,4EAA4E;IAC5E,IAAI,CAAC,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SACvD,IAAI,UAAU,CAAC,MAAM,IAAI,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/E,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvG,6DAA6D;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/E,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW;QAClC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAClF,8FAA8F;IAC9F,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,UAAU,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CACnD,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACvD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;QACnG,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CAAC,CACF,CAAC;IACF,IAAI,KAAK,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,UAAU,CAAC,CAAC;IACnG,IAAI,KAAK,EAAE,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;IACzG,OAAO,KAAK,CAAC;AACd,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,UAAU,CAAC,IAAkB;IAC5C,OAAO,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC;AAC/D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAC1B,IAAkB,EAClB,IAA0B,EAC1B,KAAY,EACZ,KAAa,EACb,IAAwB;IAExB,IAAI,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/G,MAAM,KAAK,GACV,IAAI,CAAC,MAAM,KAAK,WAAW;QAC1B,CAAC,CAAC,2BAA2B;QAC7B,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,CAAC,CAAC,kBAAkB;YACpB,CAAC,CAAC,gBAAgB,CAAC;IACtB,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;IAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5D,MAAM,SAAS,GAAG,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC;IACvC,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC;IAC7E,iGAAiG;IACjG,MAAM,QAAQ,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC;IAC/F,MAAM,MAAM,GAAG,QAAQ;QACtB,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,gBAAgB,EAAE,EAAE;YAC7C,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC;SAC7C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC;IAChE,IAAI,IAAI,EAAE,SAAS;QAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,wCAAwC,CAAC,CAAC,CAAC;IACnG,IAAI,CAAC,OAAO,IAAI,SAAS,EAAE,SAAS;QACnC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC;IAC3F,OAAO,MAAM,CAAC;AACf,CAAC","sourcesContent":["/** The inspector's Details and Output content, computed once per task snapshot and output read. */\nimport { Markdown, truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport { isTaskTerminal, type TaskRead, type TaskSnapshot } from \"../../../core/tasks/types.ts\";\nimport { getMarkdownTheme, highlightCode, type Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport { cleanTaskText } from \"./text.ts\";\n\ntype DetailRow = [label: string, value: string, tone?: \"warning\"];\n\n/**\n * Details: identity first, then the executor's input and facts as aligned rows whose values wrap\n * within their own column to `width`, then a multi-line input block. The source, status and\n * runtime are in the inspector header.\n */\nexport function detailLines(task: TaskSnapshot, theme: Theme, width: number): string[] {\n\tconst input = task.input && {\n\t\t...task.input,\n\t\tlabel: cleanTaskText(task.input.label),\n\t\ttext: cleanTaskText(task.input.text),\n\t};\n\tconst inputLines = input\n\t\t? input.language\n\t\t\t? highlightCode(input.text, input.language)\n\t\t\t: input.text.split(\"\\n\")\n\t\t: [];\n\tconst rows: DetailRow[] = [[\"Task ID\", task.id]];\n\tif (task.error) rows.push([\"Error\", cleanTaskText(task.error).replace(/\\n/g, \" \"), \"warning\"]);\n\t// Without an input, the title is the only complete description of the work.\n\tif (!input) rows.push([\"Title\", cleanTaskText(task.title)]);\n\telse if (inputLines.length <= 1) rows.push([input.label, inputLines[0] ?? \"\"]);\n\tfor (const [label, value] of task.facts ?? []) rows.push([cleanTaskText(label), cleanTaskText(value)]);\n\t// A fact that already names the output file would repeat it.\n\tconst shownAsFact = task.facts?.some(([, value]) => value === task.outputPath);\n\tif (task.outputPath && !shownAsFact)\n\t\trows.push([task.output === \"log\" ? \"Log\" : \"Full report\", cleanTaskText(task.outputPath)]);\n\tconst labelWidth = Math.max(9, ...rows.map(([label]) => visibleWidth(label))) + 1;\n\t// Values wrap inside their own column, so continuation lines hang under the first value line.\n\tconst valueWidth = Math.max(1, width - labelWidth);\n\tconst lines = rows.flatMap(([label, value, tone]) =>\n\t\twrapTextWithAnsi(value, valueWidth).map((line, index) => {\n\t\t\tconst lead = index ? \" \".repeat(labelWidth) : label + \" \".repeat(labelWidth - visibleWidth(label));\n\t\t\treturn lead + (tone ? theme.fg(tone, line) : line);\n\t\t}),\n\t);\n\tif (input && inputLines.length > 1) lines.push(\"\", theme.fg(\"accent\", input.label), ...inputLines);\n\tif (input?.truncated) lines.push(theme.fg(\"warning\", `[Saved ${input.label.toLowerCase()} truncated.]`));\n\treturn lines;\n}\n\n/** Whether Output streams from a file; any other task shows what its executor published. */\nexport function streamsLog(task: TaskSnapshot): boolean {\n\treturn task.output === \"log\" && task.outputPath !== undefined;\n}\n\n/**\n * Output: a log streams from its file through bounded reads. Any other task shows what its\n * executor published, current with every snapshot: the human `display` when there is one,\n * otherwise the model-facing result. A running task whose executor renders its own Output passes\n * those `live` lines: they come from trusted extension code, so they are shown as they are, only\n * clipped to the width.\n */\nexport function outputLines(\n\ttask: TaskSnapshot,\n\tread: TaskRead | undefined,\n\ttheme: Theme,\n\twidth: number,\n\tlive?: readonly string[],\n): string[] {\n\tif (live && !isTaskTerminal(task.status)) return live.map((line) => truncateToWidth(line, Math.max(1, width)));\n\tconst empty =\n\t\ttask.status === \"completed\"\n\t\t\t? \"Completed with no output.\"\n\t\t\t: isTaskTerminal(task.status)\n\t\t\t\t? \"No saved output.\"\n\t\t\t\t: \"No output yet.\";\n\tconst fromLog = streamsLog(task) && read !== undefined && !read.readError;\n\tconst human = task.display?.text ? task.display : undefined;\n\tconst published = human ?? task.result;\n\tconst text = cleanTaskText((fromLog ? read.text : published?.text) || empty);\n\t// Logs and live progress keep their plain rendering; only a settled report's result is Markdown.\n\tconst markdown = !fromLog && !human && task.output === \"report\" && task.status === \"completed\";\n\tconst output = markdown\n\t\t? new Markdown(text, 0, 0, getMarkdownTheme(), {\n\t\t\t\tcolor: (line) => theme.fg(\"toolOutput\", line),\n\t\t\t}).render(Math.max(1, width))\n\t\t: text.split(\"\\n\").map((line) => theme.fg(\"toolOutput\", line));\n\tif (read?.readError) output.unshift(theme.fg(\"warning\", \"Log unavailable; showing saved result.\"));\n\tif (!fromLog && published?.truncated)\n\t\toutput.push(theme.fg(\"warning\", human ? \"Output truncated.\" : \"Saved result truncated.\"));\n\treturn output;\n}\n"]}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type Component, ScrollView, type ScrollViewScrollbar } from "@earendil-works/pi-tui";
|
|
2
|
+
import type { TranscriptLayoutContainer } from "./transcript-layout.ts";
|
|
2
3
|
export interface ChatViewportOptions {
|
|
3
|
-
readonly document:
|
|
4
|
+
readonly document: TranscriptLayoutContainer;
|
|
5
|
+
readonly chat: TranscriptLayoutContainer;
|
|
4
6
|
readonly pendingMessages: Component;
|
|
5
7
|
readonly status: Component;
|
|
6
8
|
readonly editor: Component;
|
|
@@ -10,11 +12,12 @@ export interface ChatViewportOptions {
|
|
|
10
12
|
readonly scrollbar?: ScrollViewScrollbar;
|
|
11
13
|
readonly scrollbarTrackStyle?: (text: string) => string;
|
|
12
14
|
readonly scrollbarThumbStyle?: (text: string) => string;
|
|
15
|
+
readonly outputPad?: () => number;
|
|
13
16
|
}
|
|
14
17
|
export interface ChatViewport {
|
|
15
18
|
readonly root: Component;
|
|
16
19
|
readonly transcript: ScrollView;
|
|
17
20
|
}
|
|
18
|
-
/** Shared fullscreen transcript and fixed input-dock layout. */
|
|
21
|
+
/** Shared fullscreen transcript, sticky prompt, and fixed input-dock layout. */
|
|
19
22
|
export declare function createChatViewport(options: ChatViewportOptions): ChatViewport;
|
|
20
23
|
//# sourceMappingURL=chat-viewport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-viewport.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/chat-viewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,UAAU,EAAE,KAAK,mBAAmB,
|
|
1
|
+
{"version":3,"file":"chat-viewport.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/chat-viewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,UAAU,EAAE,KAAK,mBAAmB,EAAkB,MAAM,wBAAwB,CAAC;AAO9G,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAExE,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC;IAClC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IACzC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CAChC;AA+ED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAiB7E"}
|
|
@@ -1,14 +1,74 @@
|
|
|
1
|
-
import { ScrollView, VStack } from "@earendil-works/pi-tui";
|
|
2
|
-
|
|
1
|
+
import { ScrollView, Spacer, VStack } from "@earendil-works/pi-tui";
|
|
2
|
+
import { findStickyUserMessage, StickyUserMessageComponent, } from "./components/sticky-user-message.js";
|
|
3
|
+
import { UserMessageComponent } from "./components/user-message.js";
|
|
4
|
+
class TranscriptScrollView extends ScrollView {
|
|
5
|
+
updateLayout(contentHeight, viewportHeight, requestRender) {
|
|
6
|
+
super.updateLayout(contentHeight, viewportHeight, requestRender);
|
|
7
|
+
this.onLayout?.(requestRender);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
class ChatTranscriptView {
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.padding = new Spacer(1);
|
|
13
|
+
this.paddingTop = 1;
|
|
14
|
+
this.document = options.document;
|
|
15
|
+
this.chat = options.chat;
|
|
16
|
+
this.sticky = new StickyUserMessageComponent(options.outputPad ?? (() => 1), (row) => this.scrollView.scrollTo(row));
|
|
17
|
+
const content = new VStack([this.padding, this.document]);
|
|
18
|
+
this.scrollView = new TranscriptScrollView(content, {
|
|
19
|
+
follow: "end",
|
|
20
|
+
primary: true,
|
|
21
|
+
overscroll: "chain",
|
|
22
|
+
scrollbar: options.scrollbar ?? "auto",
|
|
23
|
+
...(options.scrollbarTrackStyle === undefined ? {} : { scrollbarTrackStyle: options.scrollbarTrackStyle }),
|
|
24
|
+
...(options.scrollbarThumbStyle === undefined ? {} : { scrollbarThumbStyle: options.scrollbarThumbStyle }),
|
|
25
|
+
});
|
|
26
|
+
this.scrollView.onLayout = (requestRender) => {
|
|
27
|
+
if (this.updateStickyMessage())
|
|
28
|
+
requestRender();
|
|
29
|
+
};
|
|
30
|
+
this.root = new VStack([
|
|
31
|
+
{ component: this.sticky, shrink: 0, minSize: 0 },
|
|
32
|
+
{ component: this.scrollView, basis: 0, grow: 1, shrink: 1, minSize: 1 },
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
updateStickyMessage() {
|
|
36
|
+
const judgment = this.scrollView.viewportHeight < 2
|
|
37
|
+
? undefined
|
|
38
|
+
: findStickyUserMessage(this.getUserMessageAnchors(), this.scrollView.scrollTop - 1);
|
|
39
|
+
const paddingTop = judgment ? 0 : 1;
|
|
40
|
+
let changed = this.sticky.setJudgment(judgment);
|
|
41
|
+
if (paddingTop !== this.paddingTop) {
|
|
42
|
+
this.paddingTop = paddingTop;
|
|
43
|
+
this.padding.setLines(paddingTop);
|
|
44
|
+
changed = true;
|
|
45
|
+
}
|
|
46
|
+
return changed;
|
|
47
|
+
}
|
|
48
|
+
getUserMessageAnchors() {
|
|
49
|
+
const documentLayout = this.document.getRenderedLayout();
|
|
50
|
+
const chatLayout = this.chat.getRenderedLayout();
|
|
51
|
+
const chatRow = documentLayout?.children.find((child) => child.component === this.chat)?.row;
|
|
52
|
+
if (chatRow === undefined || !chatLayout)
|
|
53
|
+
return [];
|
|
54
|
+
const anchors = [];
|
|
55
|
+
for (const child of chatLayout.children) {
|
|
56
|
+
if (!(child.component instanceof UserMessageComponent))
|
|
57
|
+
continue;
|
|
58
|
+
const summary = child.component.getNavigationSummary();
|
|
59
|
+
if (!summary)
|
|
60
|
+
continue;
|
|
61
|
+
anchors.push({
|
|
62
|
+
row: chatRow + child.row + child.component.getNavigationAnchorOffset(chatLayout.width),
|
|
63
|
+
summary,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return anchors;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Shared fullscreen transcript, sticky prompt, and fixed input-dock layout. */
|
|
3
70
|
export function createChatViewport(options) {
|
|
4
|
-
const
|
|
5
|
-
follow: "end",
|
|
6
|
-
primary: true,
|
|
7
|
-
overscroll: "chain",
|
|
8
|
-
scrollbar: options.scrollbar ?? "auto",
|
|
9
|
-
...(options.scrollbarTrackStyle === undefined ? {} : { scrollbarTrackStyle: options.scrollbarTrackStyle }),
|
|
10
|
-
...(options.scrollbarThumbStyle === undefined ? {} : { scrollbarThumbStyle: options.scrollbarThumbStyle }),
|
|
11
|
-
});
|
|
71
|
+
const transcriptView = new ChatTranscriptView(options);
|
|
12
72
|
const dock = new VStack([
|
|
13
73
|
{ component: options.pendingMessages, shrink: 1, minSize: 0 },
|
|
14
74
|
{ component: options.status, shrink: 1, minSize: 0 },
|
|
@@ -18,9 +78,9 @@ export function createChatViewport(options) {
|
|
|
18
78
|
{ component: options.footer, shrink: 1, minSize: 0 },
|
|
19
79
|
]);
|
|
20
80
|
return {
|
|
21
|
-
transcript,
|
|
81
|
+
transcript: transcriptView.scrollView,
|
|
22
82
|
root: new VStack([
|
|
23
|
-
{ component:
|
|
83
|
+
{ component: transcriptView.root, basis: 0, grow: 1, shrink: 1, minSize: 1 },
|
|
24
84
|
{ component: dock, basis: "auto", grow: 0, shrink: 1, minSize: 1 },
|
|
25
85
|
]),
|
|
26
86
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-viewport.js","sourceRoot":"","sources":["../../../src/modes/interactive/chat-viewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,UAAU,EAA4B,MAAM,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"chat-viewport.js","sourceRoot":"","sources":["../../../src/modes/interactive/chat-viewport.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, ScrollView, type ScrollViewScrollbar, Spacer, VStack } from \"@earendil-works/pi-tui\";\nimport {\n\tfindStickyUserMessage,\n\ttype StickyUserMessageAnchor,\n\tStickyUserMessageComponent,\n} from \"./components/sticky-user-message.ts\";\nimport { UserMessageComponent } from \"./components/user-message.ts\";\nimport type { TranscriptLayoutContainer } from \"./transcript-layout.ts\";\n\nexport interface ChatViewportOptions {\n\treadonly document: TranscriptLayoutContainer;\n\treadonly chat: TranscriptLayoutContainer;\n\treadonly pendingMessages: Component;\n\treadonly status: Component;\n\treadonly editor: Component;\n\treadonly footer: Component;\n\treadonly widgetsAbove?: Component;\n\treadonly widgetsBelow?: Component;\n\treadonly scrollbar?: ScrollViewScrollbar;\n\treadonly scrollbarTrackStyle?: (text: string) => string;\n\treadonly scrollbarThumbStyle?: (text: string) => string;\n\treadonly outputPad?: () => number;\n}\n\nexport interface ChatViewport {\n\treadonly root: Component;\n\treadonly transcript: ScrollView;\n}\n\nclass TranscriptScrollView extends ScrollView {\n\tonLayout: ((requestRender: () => void) => void) | undefined;\n\n\toverride updateLayout(contentHeight: number, viewportHeight: number, requestRender: () => void): void {\n\t\tsuper.updateLayout(contentHeight, viewportHeight, requestRender);\n\t\tthis.onLayout?.(requestRender);\n\t}\n}\n\nclass ChatTranscriptView {\n\treadonly root: VStack;\n\treadonly scrollView: TranscriptScrollView;\n\tprivate readonly document: TranscriptLayoutContainer;\n\tprivate readonly chat: TranscriptLayoutContainer;\n\tprivate readonly padding = new Spacer(1);\n\tprivate readonly sticky: StickyUserMessageComponent;\n\tprivate paddingTop = 1;\n\n\tconstructor(options: ChatViewportOptions) {\n\t\tthis.document = options.document;\n\t\tthis.chat = options.chat;\n\t\tthis.sticky = new StickyUserMessageComponent(options.outputPad ?? (() => 1), (row) =>\n\t\t\tthis.scrollView.scrollTo(row),\n\t\t);\n\t\tconst content = new VStack([this.padding, this.document]);\n\t\tthis.scrollView = new TranscriptScrollView(content, {\n\t\t\tfollow: \"end\",\n\t\t\tprimary: true,\n\t\t\toverscroll: \"chain\",\n\t\t\tscrollbar: options.scrollbar ?? \"auto\",\n\t\t\t...(options.scrollbarTrackStyle === undefined ? {} : { scrollbarTrackStyle: options.scrollbarTrackStyle }),\n\t\t\t...(options.scrollbarThumbStyle === undefined ? {} : { scrollbarThumbStyle: options.scrollbarThumbStyle }),\n\t\t});\n\t\tthis.scrollView.onLayout = (requestRender) => {\n\t\t\tif (this.updateStickyMessage()) requestRender();\n\t\t};\n\t\tthis.root = new VStack([\n\t\t\t{ component: this.sticky, shrink: 0, minSize: 0 },\n\t\t\t{ component: this.scrollView, basis: 0, grow: 1, shrink: 1, minSize: 1 },\n\t\t]);\n\t}\n\n\tprivate updateStickyMessage(): boolean {\n\t\tconst judgment =\n\t\t\tthis.scrollView.viewportHeight < 2\n\t\t\t\t? undefined\n\t\t\t\t: findStickyUserMessage(this.getUserMessageAnchors(), this.scrollView.scrollTop - 1);\n\t\tconst paddingTop = judgment ? 0 : 1;\n\t\tlet changed = this.sticky.setJudgment(judgment);\n\t\tif (paddingTop !== this.paddingTop) {\n\t\t\tthis.paddingTop = paddingTop;\n\t\t\tthis.padding.setLines(paddingTop);\n\t\t\tchanged = true;\n\t\t}\n\t\treturn changed;\n\t}\n\n\tprivate getUserMessageAnchors(): StickyUserMessageAnchor[] {\n\t\tconst documentLayout = this.document.getRenderedLayout();\n\t\tconst chatLayout = this.chat.getRenderedLayout();\n\t\tconst chatRow = documentLayout?.children.find((child) => child.component === this.chat)?.row;\n\t\tif (chatRow === undefined || !chatLayout) return [];\n\n\t\tconst anchors: StickyUserMessageAnchor[] = [];\n\t\tfor (const child of chatLayout.children) {\n\t\t\tif (!(child.component instanceof UserMessageComponent)) continue;\n\t\t\tconst summary = child.component.getNavigationSummary();\n\t\t\tif (!summary) continue;\n\t\t\tanchors.push({\n\t\t\t\trow: chatRow + child.row + child.component.getNavigationAnchorOffset(chatLayout.width),\n\t\t\t\tsummary,\n\t\t\t});\n\t\t}\n\t\treturn anchors;\n\t}\n}\n\n/** Shared fullscreen transcript, sticky prompt, and fixed input-dock layout. */\nexport function createChatViewport(options: ChatViewportOptions): ChatViewport {\n\tconst transcriptView = new ChatTranscriptView(options);\n\tconst dock = new VStack([\n\t\t{ component: options.pendingMessages, shrink: 1, minSize: 0 },\n\t\t{ component: options.status, shrink: 1, minSize: 0 },\n\t\t...(options.widgetsAbove === undefined ? [] : [{ component: options.widgetsAbove, shrink: 1, minSize: 0 }]),\n\t\t{ component: options.editor, shrink: 1, minSize: 3 },\n\t\t...(options.widgetsBelow === undefined ? [] : [{ component: options.widgetsBelow, shrink: 1, minSize: 0 }]),\n\t\t{ component: options.footer, shrink: 1, minSize: 0 },\n\t]);\n\treturn {\n\t\ttranscript: transcriptView.scrollView,\n\t\troot: new VStack([\n\t\t\t{ component: transcriptView.root, basis: 0, grow: 1, shrink: 1, minSize: 1 },\n\t\t\t{ component: dock, basis: \"auto\", grow: 0, shrink: 1, minSize: 1 },\n\t\t]),\n\t};\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Component, Container, type Focusable } from "@earendil-works/pi-tui";
|
|
2
2
|
import { KeybindingsManager } from "../../../core/keybindings.ts";
|
|
3
|
-
import type
|
|
3
|
+
import { type SessionInfo, type SessionListProgress } from "../../../core/session-manager.ts";
|
|
4
4
|
import { type NameFilter, type SortMode } from "./session-selector-search.ts";
|
|
5
5
|
/**
|
|
6
6
|
* Custom session list component with multi-line items and search
|
|
@@ -48,6 +48,16 @@ declare class SessionList implements Component, Focusable {
|
|
|
48
48
|
handleInput(keyData: string): void;
|
|
49
49
|
}
|
|
50
50
|
type SessionsLoader = (onProgress?: SessionListProgress, signal?: AbortSignal) => Promise<SessionInfo[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Delete a session file with everything it owns (its artifacts directory and late-task sidecar),
|
|
53
|
+
* trying the `trash` CLI first, then falling back to permanent removal. Success means the session
|
|
54
|
+
* file is gone; `error` on a successful result names owned files that could not be removed.
|
|
55
|
+
*/
|
|
56
|
+
export declare function deleteSessionFile(sessionPath: string): Promise<{
|
|
57
|
+
ok: boolean;
|
|
58
|
+
method: "trash" | "unlink";
|
|
59
|
+
error?: string;
|
|
60
|
+
}>;
|
|
51
61
|
/**
|
|
52
62
|
* Component that renders a session selector
|
|
53
63
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector.ts"],"names":[],"mappings":"AAIA,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EACT,KAAK,SAAS,EAOd,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"session-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
-
import { unlink } from "node:fs/promises";
|
|
3
|
+
import { rm, unlink } from "node:fs/promises";
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import { Container, getKeybindings, Input, Spacer, Text, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
|
|
6
6
|
import { KeybindingsManager } from "../../../core/keybindings.js";
|
|
7
|
+
import { getSessionArtifactDir, listSessionOwnedPaths, } from "../../../core/session-manager.js";
|
|
7
8
|
import { canonicalizePath as _canonicalizePath } from "../../../utils/paths.js";
|
|
8
9
|
import { theme } from "../theme/theme.js";
|
|
9
10
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
@@ -530,11 +531,15 @@ class SessionList {
|
|
|
530
531
|
}
|
|
531
532
|
}
|
|
532
533
|
/**
|
|
533
|
-
* Delete a session file
|
|
534
|
+
* Delete a session file with everything it owns (its artifacts directory and late-task sidecar),
|
|
535
|
+
* trying the `trash` CLI first, then falling back to permanent removal. Success means the session
|
|
536
|
+
* file is gone; `error` on a successful result names owned files that could not be removed.
|
|
534
537
|
*/
|
|
535
|
-
async function deleteSessionFile(sessionPath) {
|
|
536
|
-
// Try `trash` first (if installed)
|
|
537
|
-
const
|
|
538
|
+
export async function deleteSessionFile(sessionPath) {
|
|
539
|
+
// Try `trash` first (if installed), with every owned path in one invocation
|
|
540
|
+
const owned = listSessionOwnedPaths(sessionPath);
|
|
541
|
+
const targets = owned.length ? owned : [sessionPath];
|
|
542
|
+
const trashArgs = targets.some((path) => path.startsWith("-")) ? ["--", ...targets] : targets;
|
|
538
543
|
const trashResult = spawnSync("trash", trashArgs, { encoding: "utf-8" });
|
|
539
544
|
const getTrashErrorHint = () => {
|
|
540
545
|
const parts = [];
|
|
@@ -549,21 +554,38 @@ async function deleteSessionFile(sessionPath) {
|
|
|
549
554
|
return null;
|
|
550
555
|
return `trash: ${parts.join(" · ").slice(0, 200)}`;
|
|
551
556
|
};
|
|
552
|
-
// If trash reports success, or
|
|
553
|
-
|
|
557
|
+
// If trash reports success, or everything is gone afterwards, treat it as successful
|
|
558
|
+
const remaining = listSessionOwnedPaths(sessionPath);
|
|
559
|
+
if (trashResult.status === 0 || (!existsSync(sessionPath) && remaining.length === 0)) {
|
|
554
560
|
return { ok: true, method: "trash" };
|
|
555
561
|
}
|
|
556
|
-
// Fallback to permanent deletion
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
562
|
+
// Fallback to permanent deletion of whatever is left; the session file goes last
|
|
563
|
+
const artifactDir = getSessionArtifactDir(sessionPath);
|
|
564
|
+
const failures = [];
|
|
565
|
+
const order = remaining.filter((path) => path !== sessionPath);
|
|
566
|
+
if (remaining.includes(sessionPath))
|
|
567
|
+
order.push(sessionPath);
|
|
568
|
+
for (const path of order) {
|
|
569
|
+
try {
|
|
570
|
+
if (path === artifactDir)
|
|
571
|
+
await rm(path, { recursive: true, force: true });
|
|
572
|
+
else
|
|
573
|
+
await unlink(path);
|
|
574
|
+
}
|
|
575
|
+
catch (err) {
|
|
576
|
+
if (err.code === "ENOENT")
|
|
577
|
+
continue;
|
|
578
|
+
failures.push(err instanceof Error ? err.message : String(err));
|
|
579
|
+
}
|
|
560
580
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
const error = trashErrorHint ? `${unlinkError} (${trashErrorHint})` : unlinkError;
|
|
565
|
-
return { ok: false, method: "unlink", error };
|
|
581
|
+
const unlinkError = failures.join("; ");
|
|
582
|
+
if (!existsSync(sessionPath)) {
|
|
583
|
+
return { ok: true, method: "unlink", ...(unlinkError ? { error: unlinkError } : {}) };
|
|
566
584
|
}
|
|
585
|
+
const trashErrorHint = getTrashErrorHint();
|
|
586
|
+
const reason = unlinkError || "Session file could not be removed";
|
|
587
|
+
const error = trashErrorHint ? `${reason} (${trashErrorHint})` : reason;
|
|
588
|
+
return { ok: false, method: "unlink", error };
|
|
567
589
|
}
|
|
568
590
|
/**
|
|
569
591
|
* Component that renders a session selector
|
|
@@ -691,7 +713,11 @@ export class SessionSelectorComponent extends Container {
|
|
|
691
713
|
const showCwd = this.scope === "all";
|
|
692
714
|
this.sessionList.setSessions(sessions, showCwd);
|
|
693
715
|
const msg = result.method === "trash" ? "Session moved to trash" : "Session deleted";
|
|
694
|
-
|
|
716
|
+
if (result.error) {
|
|
717
|
+
this.header.setStatusMessage({ type: "error", message: `${msg}, but cleanup failed: ${result.error}` }, 3000);
|
|
718
|
+
}
|
|
719
|
+
else
|
|
720
|
+
this.header.setStatusMessage({ type: "info", message: msg }, 2000);
|
|
695
721
|
await this.refreshSessionsAfterMutation();
|
|
696
722
|
}
|
|
697
723
|
else {
|