@astralyn/pi 1.4.9 → 1.4.11
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 +69 -0
- package/dist/bundle/chunks/{chunk-BAGHIP5J.js → chunk-NUQMVNKQ.js} +3 -3
- package/dist/bundle/chunks/{chunk-3PKXUKTB.js → chunk-PUU43PDV.js} +121 -136
- package/dist/bundle/chunks/{chunk-F66AY3WR.js → chunk-TH3QDDPD.js} +1 -1
- package/dist/bundle/chunks/{execute-P22VMM2H.js → execute-VLAIP26K.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-VKMBQTR2.js → virtual-modules-63XNTSVN.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 -5
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +14 -7
- package/dist/core/extensions/loader.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 +31 -12
- package/dist/core/tasks/delivery.d.ts.map +1 -1
- package/dist/core/tasks/delivery.js +97 -36
- 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 +7 -11
- package/dist/core/tasks/limits.d.ts.map +1 -1
- package/dist/core/tasks/limits.js +8 -16
- 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 +15 -23
- package/dist/core/tasks/runtime.d.ts.map +1 -1
- package/dist/core/tasks/runtime.js +50 -178
- 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 -6
- package/dist/core/tasks/session.d.ts.map +1 -1
- package/dist/core/tasks/session.js +70 -22
- 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 +64 -93
- 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/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/cached-compaction/summary.d.ts +1 -1
- package/dist/extensions/cached-compaction/summary.d.ts.map +1 -1
- package/dist/extensions/cached-compaction/summary.js +13 -16
- package/dist/extensions/cached-compaction/summary.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/tasks/tool/actions.d.ts +1 -2
- package/dist/extensions/tasks/tool/actions.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/actions.js +12 -80
- package/dist/extensions/tasks/tool/actions.js.map +1 -1
- package/dist/extensions/tasks/tool/constants.d.ts +1 -3
- package/dist/extensions/tasks/tool/constants.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/constants.js +1 -3
- package/dist/extensions/tasks/tool/constants.js.map +1 -1
- package/dist/extensions/tasks/tool/schema.d.ts +2 -3
- package/dist/extensions/tasks/tool/schema.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/schema.js +10 -18
- package/dist/extensions/tasks/tool/schema.js.map +1 -1
- package/dist/extensions/tasks/tool/types.d.ts +1 -17
- 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 +1 -2
- package/dist/extensions/tasks/tool.d.ts.map +1 -1
- package/dist/extensions/tasks/tool.js +5 -6
- 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 +2 -16
- package/dist/extensions/tasks/ui/render.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/render.js +13 -85
- 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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.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/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +38 -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 +2 -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/extensions/agent.md +39 -7
- package/docs/bundled/extensions/statusline.md +1 -1
- package/docs/bundled/tasks.md +28 -37
- package/docs/bundled/tool-presentation.md +1 -1
- package/docs/extensions.md +15 -13
- package/docs/json.md +3 -0
- package/docs/models-configuration.md +1 -1
- package/docs/models.md +7 -85
- package/docs/rpc.md +14 -0
- package/docs/sdk.md +12 -2
- package/docs/session-format.md +11 -5
- package/docs/sessions.md +1 -1
- package/docs/usage.md +2 -0
- package/package.json +3 -4
- package/dist/modes/interactive/tool-view/tool-execution.d.ts +0 -14
- package/dist/modes/interactive/tool-view/tool-execution.d.ts.map +0 -1
- package/dist/modes/interactive/tool-view/tool-execution.js +0 -13
- package/dist/modes/interactive/tool-view/tool-execution.js.map +0 -1
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/exy.png +0 -0
- package/npm-shrinkwrap.json +0 -4207
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The footer row, one builder per panel mode. Context hints sit on the left, the Escape action is
|
|
3
|
+
* pinned to the right edge, and transient feedback temporarily replaces the hints.
|
|
4
|
+
*/
|
|
5
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
6
|
+
const FEEDBACK_GLYPH = { warning: "!", error: "×" };
|
|
7
|
+
/** Cells kept between the left content and the pinned Escape action. */
|
|
8
|
+
const GAP = 3;
|
|
9
|
+
/** Drops the lowest-priority items, the later of equals first, until the rest fit. */
|
|
10
|
+
function dropToFit(items, room, separator) {
|
|
11
|
+
const kept = [...items];
|
|
12
|
+
const width = () => kept.reduce((sum, item) => sum + visibleWidth(item.text), 0) + visibleWidth(separator) * (kept.length - 1);
|
|
13
|
+
while (kept.length > 1 && width() > room) {
|
|
14
|
+
let drop = 0;
|
|
15
|
+
for (const [index, item] of kept.entries())
|
|
16
|
+
if (item.priority >= kept[drop].priority)
|
|
17
|
+
drop = index;
|
|
18
|
+
kept.splice(drop, 1);
|
|
19
|
+
}
|
|
20
|
+
return kept;
|
|
21
|
+
}
|
|
22
|
+
function compose(env, spec) {
|
|
23
|
+
const { width, y, theme } = env;
|
|
24
|
+
const hits = [];
|
|
25
|
+
const escapeWidth = visibleWidth(spec.escape.text);
|
|
26
|
+
const room = Math.max(0, width - escapeWidth - GAP);
|
|
27
|
+
let showEscape = true;
|
|
28
|
+
let left;
|
|
29
|
+
if (spec.feedback) {
|
|
30
|
+
const message = `${FEEDBACK_GLYPH[spec.feedback.tone]} ${spec.feedback.text}`;
|
|
31
|
+
// A long message is worth more than the Escape hint while it lasts.
|
|
32
|
+
showEscape = visibleWidth(message) <= room;
|
|
33
|
+
left = theme.fg(spec.feedback.tone, truncateToWidth(message, showEscape ? room : width, "…"));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
let x = 0;
|
|
37
|
+
const kept = dropToFit(spec.items, room, spec.separator);
|
|
38
|
+
for (const item of kept) {
|
|
39
|
+
if (item.action)
|
|
40
|
+
hits.push({ x, y, width: visibleWidth(item.text), action: item.action });
|
|
41
|
+
x += visibleWidth(item.text) + visibleWidth(spec.separator);
|
|
42
|
+
}
|
|
43
|
+
left = theme.fg(spec.tone, truncateToWidth(kept.map((item) => item.text).join(spec.separator), room, "…"));
|
|
44
|
+
}
|
|
45
|
+
const gap = Math.max(0, width - visibleWidth(left) - (showEscape ? escapeWidth : 0));
|
|
46
|
+
if (showEscape && spec.escape.action)
|
|
47
|
+
hits.push({ x: width - escapeWidth, y, width: escapeWidth, action: spec.escape.action });
|
|
48
|
+
return { line: left + " ".repeat(gap) + (showEscape ? theme.fg(spec.tone, spec.escape.text) : ""), hits };
|
|
49
|
+
}
|
|
50
|
+
const BACK = { kind: "back" };
|
|
51
|
+
export function browseFooter(env, view) {
|
|
52
|
+
const { hint } = env;
|
|
53
|
+
const items = [];
|
|
54
|
+
const add = (text, priority, action) => items.push({ text, priority, action });
|
|
55
|
+
const arrows = `${hint("tui.select.up")} / ${hint("tui.select.down")}`;
|
|
56
|
+
if (view.focus === "list") {
|
|
57
|
+
add(`${arrows} tasks`, 5);
|
|
58
|
+
add(`${hint("tui.select.confirm")} open`, 4, { kind: "open" });
|
|
59
|
+
add(`${hint("app.tasks.search")} find`, 3, { kind: "key", key: "app.tasks.search" });
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
add(`${arrows} scroll`, 5);
|
|
63
|
+
// Without the list beside it, Escape is the way back.
|
|
64
|
+
if (view.split)
|
|
65
|
+
add(`${hint("app.tasks.nextFocus")} tasks`, 6, { kind: "key", key: "app.tasks.nextFocus" });
|
|
66
|
+
add(`${hint("app.tasks.previousTab")} output`, 6, { kind: "tab", tab: "output" });
|
|
67
|
+
add(`${hint("app.tasks.nextTab")} details`, 6, { kind: "tab", tab: "info" });
|
|
68
|
+
}
|
|
69
|
+
if (view.stoppable)
|
|
70
|
+
add(`${hint("app.tasks.kill")} stop`, 2, { kind: "key", key: "app.tasks.kill" });
|
|
71
|
+
if (view.detachable)
|
|
72
|
+
add(`${hint("app.tasks.detachSelected")} background`, 2, { kind: "key", key: "app.tasks.detachSelected" });
|
|
73
|
+
add(`${hint("app.tasks.help")} keys`, 1, { kind: "key", key: "app.tasks.help" });
|
|
74
|
+
const back = !view.split && view.focus === "inspector";
|
|
75
|
+
return compose(env, {
|
|
76
|
+
items,
|
|
77
|
+
separator: " · ",
|
|
78
|
+
escape: { text: `${hint("tui.select.cancel")} ${back ? "back" : "close"}`, priority: 0, action: BACK },
|
|
79
|
+
tone: "dim",
|
|
80
|
+
feedback: env.feedback,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
export function searchFooter(env) {
|
|
84
|
+
const { hint } = env;
|
|
85
|
+
return compose(env, {
|
|
86
|
+
items: [
|
|
87
|
+
{ text: `${hint("tui.select.up")} / ${hint("tui.select.down")} choose`, priority: 2 },
|
|
88
|
+
{ text: `${hint("tui.select.confirm")} locate`, priority: 1 },
|
|
89
|
+
],
|
|
90
|
+
separator: " · ",
|
|
91
|
+
escape: { text: `${hint("tui.select.cancel")} cancel`, priority: 0, action: BACK },
|
|
92
|
+
tone: "dim",
|
|
93
|
+
feedback: env.feedback,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
export function helpFooter(env) {
|
|
97
|
+
const { hint } = env;
|
|
98
|
+
return compose(env, {
|
|
99
|
+
items: [{ text: `${hint("tui.select.up")} / ${hint("tui.select.down")} scroll`, priority: 1 }],
|
|
100
|
+
separator: " · ",
|
|
101
|
+
escape: { text: `${hint("tui.select.cancel")} back`, priority: 0, action: BACK },
|
|
102
|
+
tone: "dim",
|
|
103
|
+
feedback: env.feedback,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The stop question borrows only the footer row, so the task stays in view while confirming;
|
|
108
|
+
* `source` and `title` name the target, and the title gives way first on narrow widths.
|
|
109
|
+
*/
|
|
110
|
+
export function confirmStopFooter(env, target) {
|
|
111
|
+
const { hint, width } = env;
|
|
112
|
+
const confirm = `${hint("app.tasks.confirmStop")} stop`;
|
|
113
|
+
const cancel = `${hint("tui.select.cancel")} cancel`;
|
|
114
|
+
const separator = " ";
|
|
115
|
+
const room = width - visibleWidth(cancel) - GAP - visibleWidth(separator + confirm);
|
|
116
|
+
const bare = `Stop ${target.source}?`;
|
|
117
|
+
const spare = room - visibleWidth(`Stop ${target.source} ""?`);
|
|
118
|
+
const question = spare >= 4 ? `Stop ${target.source} "${truncateToWidth(target.title, spare, "…")}"?` : bare;
|
|
119
|
+
return compose(env, {
|
|
120
|
+
items: [
|
|
121
|
+
{ text: question, priority: 0 },
|
|
122
|
+
{ text: confirm, priority: 0, action: { kind: "key", key: "app.tasks.confirmStop" } },
|
|
123
|
+
],
|
|
124
|
+
separator,
|
|
125
|
+
escape: { text: cancel, priority: 0, action: BACK },
|
|
126
|
+
tone: "warning",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=footer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/footer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The footer row, one builder per panel mode. Context hints sit on the left, the Escape action is\n * pinned to the right edge, and transient feedback temporarily replaces the hints.\n */\nimport { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { Keybinding } from \"../../../core/keybindings.ts\";\nimport type { Theme, ThemeColor } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { PanelFocus, TaskAction, TaskHit } from \"./model.ts\";\n\n/** An outcome the panel does not already show: the action did not do what was asked. */\nexport interface Feedback {\n\ttone: \"warning\" | \"error\";\n\ttext: string;\n}\n\nconst FEEDBACK_GLYPH: Record<Feedback[\"tone\"], string> = { warning: \"!\", error: \"×\" };\n/** Cells kept between the left content and the pinned Escape action. */\nconst GAP = 3;\n\ninterface FooterItem {\n\ttext: string;\n\t/** Higher numbers are dropped first when the row is too narrow. */\n\tpriority: number;\n\taction?: TaskAction;\n}\n\ninterface FooterSpec {\n\titems: FooterItem[];\n\tseparator: string;\n\tescape: FooterItem;\n\ttone: ThemeColor;\n\tfeedback?: Feedback;\n}\n\nexport interface FooterEnv {\n\twidth: number;\n\t/** The footer's screen row. */\n\ty: number;\n\ttheme: Theme;\n\thint: (key: Keybinding) => string;\n\tfeedback?: Feedback;\n}\n\nexport interface Footer {\n\tline: string;\n\thits: TaskHit[];\n}\n\n/** Drops the lowest-priority items, the later of equals first, until the rest fit. */\nfunction dropToFit(items: FooterItem[], room: number, separator: string): FooterItem[] {\n\tconst kept = [...items];\n\tconst width = () =>\n\t\tkept.reduce((sum, item) => sum + visibleWidth(item.text), 0) + visibleWidth(separator) * (kept.length - 1);\n\twhile (kept.length > 1 && width() > room) {\n\t\tlet drop = 0;\n\t\tfor (const [index, item] of kept.entries()) if (item.priority >= kept[drop]!.priority) drop = index;\n\t\tkept.splice(drop, 1);\n\t}\n\treturn kept;\n}\n\nfunction compose(env: FooterEnv, spec: FooterSpec): Footer {\n\tconst { width, y, theme } = env;\n\tconst hits: TaskHit[] = [];\n\tconst escapeWidth = visibleWidth(spec.escape.text);\n\tconst room = Math.max(0, width - escapeWidth - GAP);\n\tlet showEscape = true;\n\tlet left: string;\n\tif (spec.feedback) {\n\t\tconst message = `${FEEDBACK_GLYPH[spec.feedback.tone]} ${spec.feedback.text}`;\n\t\t// A long message is worth more than the Escape hint while it lasts.\n\t\tshowEscape = visibleWidth(message) <= room;\n\t\tleft = theme.fg(spec.feedback.tone, truncateToWidth(message, showEscape ? room : width, \"…\"));\n\t} else {\n\t\tlet x = 0;\n\t\tconst kept = dropToFit(spec.items, room, spec.separator);\n\t\tfor (const item of kept) {\n\t\t\tif (item.action) hits.push({ x, y, width: visibleWidth(item.text), action: item.action });\n\t\t\tx += visibleWidth(item.text) + visibleWidth(spec.separator);\n\t\t}\n\t\tleft = theme.fg(spec.tone, truncateToWidth(kept.map((item) => item.text).join(spec.separator), room, \"…\"));\n\t}\n\tconst gap = Math.max(0, width - visibleWidth(left) - (showEscape ? escapeWidth : 0));\n\tif (showEscape && spec.escape.action)\n\t\thits.push({ x: width - escapeWidth, y, width: escapeWidth, action: spec.escape.action });\n\treturn { line: left + \" \".repeat(gap) + (showEscape ? theme.fg(spec.tone, spec.escape.text) : \"\"), hits };\n}\n\nconst BACK: TaskAction = { kind: \"back\" };\n\nexport function browseFooter(\n\tenv: FooterEnv,\n\tview: { focus: PanelFocus; split: boolean; stoppable: boolean; detachable: boolean },\n): Footer {\n\tconst { hint } = env;\n\tconst items: FooterItem[] = [];\n\tconst add = (text: string, priority: number, action?: TaskAction) => items.push({ text, priority, action });\n\tconst arrows = `${hint(\"tui.select.up\")} / ${hint(\"tui.select.down\")}`;\n\tif (view.focus === \"list\") {\n\t\tadd(`${arrows} tasks`, 5);\n\t\tadd(`${hint(\"tui.select.confirm\")} open`, 4, { kind: \"open\" });\n\t\tadd(`${hint(\"app.tasks.search\")} find`, 3, { kind: \"key\", key: \"app.tasks.search\" });\n\t} else {\n\t\tadd(`${arrows} scroll`, 5);\n\t\t// Without the list beside it, Escape is the way back.\n\t\tif (view.split) add(`${hint(\"app.tasks.nextFocus\")} tasks`, 6, { kind: \"key\", key: \"app.tasks.nextFocus\" });\n\t\tadd(`${hint(\"app.tasks.previousTab\")} output`, 6, { kind: \"tab\", tab: \"output\" });\n\t\tadd(`${hint(\"app.tasks.nextTab\")} details`, 6, { kind: \"tab\", tab: \"info\" });\n\t}\n\tif (view.stoppable) add(`${hint(\"app.tasks.kill\")} stop`, 2, { kind: \"key\", key: \"app.tasks.kill\" });\n\tif (view.detachable)\n\t\tadd(`${hint(\"app.tasks.detachSelected\")} background`, 2, { kind: \"key\", key: \"app.tasks.detachSelected\" });\n\tadd(`${hint(\"app.tasks.help\")} keys`, 1, { kind: \"key\", key: \"app.tasks.help\" });\n\tconst back = !view.split && view.focus === \"inspector\";\n\treturn compose(env, {\n\t\titems,\n\t\tseparator: \" · \",\n\t\tescape: { text: `${hint(\"tui.select.cancel\")} ${back ? \"back\" : \"close\"}`, priority: 0, action: BACK },\n\t\ttone: \"dim\",\n\t\tfeedback: env.feedback,\n\t});\n}\n\nexport function searchFooter(env: FooterEnv): Footer {\n\tconst { hint } = env;\n\treturn compose(env, {\n\t\titems: [\n\t\t\t{ text: `${hint(\"tui.select.up\")} / ${hint(\"tui.select.down\")} choose`, priority: 2 },\n\t\t\t{ text: `${hint(\"tui.select.confirm\")} locate`, priority: 1 },\n\t\t],\n\t\tseparator: \" · \",\n\t\tescape: { text: `${hint(\"tui.select.cancel\")} cancel`, priority: 0, action: BACK },\n\t\ttone: \"dim\",\n\t\tfeedback: env.feedback,\n\t});\n}\n\nexport function helpFooter(env: FooterEnv): Footer {\n\tconst { hint } = env;\n\treturn compose(env, {\n\t\titems: [{ text: `${hint(\"tui.select.up\")} / ${hint(\"tui.select.down\")} scroll`, priority: 1 }],\n\t\tseparator: \" · \",\n\t\tescape: { text: `${hint(\"tui.select.cancel\")} back`, priority: 0, action: BACK },\n\t\ttone: \"dim\",\n\t\tfeedback: env.feedback,\n\t});\n}\n\n/**\n * The stop question borrows only the footer row, so the task stays in view while confirming;\n * `source` and `title` name the target, and the title gives way first on narrow widths.\n */\nexport function confirmStopFooter(env: FooterEnv, target: { source: string; title: string }): Footer {\n\tconst { hint, width } = env;\n\tconst confirm = `${hint(\"app.tasks.confirmStop\")} stop`;\n\tconst cancel = `${hint(\"tui.select.cancel\")} cancel`;\n\tconst separator = \" \";\n\tconst room = width - visibleWidth(cancel) - GAP - visibleWidth(separator + confirm);\n\tconst bare = `Stop ${target.source}?`;\n\tconst spare = room - visibleWidth(`Stop ${target.source} \"\"?`);\n\tconst question = spare >= 4 ? `Stop ${target.source} \"${truncateToWidth(target.title, spare, \"…\")}\"?` : bare;\n\treturn compose(env, {\n\t\titems: [\n\t\t\t{ text: question, priority: 0 },\n\t\t\t{ text: confirm, priority: 0, action: { kind: \"key\", key: \"app.tasks.confirmStop\" } },\n\t\t],\n\t\tseparator,\n\t\tescape: { text: cancel, priority: 0, action: BACK },\n\t\ttone: \"warning\",\n\t});\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Theme } from "../../../modes/interactive/theme/theme.ts";
|
|
2
|
+
export interface Rect {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
}
|
|
8
|
+
/** Columns from a pane's left edge to its title: the corner, one rule cell and a space. */
|
|
9
|
+
export declare const TITLE_INDENT = 3;
|
|
10
|
+
interface FrameOptions {
|
|
11
|
+
width: number;
|
|
12
|
+
theme: Theme;
|
|
13
|
+
/** The focused pane's border is accented. */
|
|
14
|
+
accent: boolean;
|
|
15
|
+
/** Styled title for the top border. */
|
|
16
|
+
title?: string;
|
|
17
|
+
/** Styled text right-aligned in the bottom border, such as a scroll range. */
|
|
18
|
+
status?: string;
|
|
19
|
+
/** Content rows, clipped or padded to the inner width. */
|
|
20
|
+
rows: string[];
|
|
21
|
+
/** Index of the content row drawn as a `├──┤` rule joined to the frame; that row's text is ignored. */
|
|
22
|
+
divider?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface Frame {
|
|
25
|
+
lines: string[];
|
|
26
|
+
/** Where the bottom-border status starts (pane-relative) and how many cells it occupies. */
|
|
27
|
+
status: {
|
|
28
|
+
x: number;
|
|
29
|
+
width: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Pads or clips one content row to exactly `width` cells. */
|
|
33
|
+
export declare const fitCell: (text: string, width: number) => string;
|
|
34
|
+
export declare function framePane(options: FrameOptions): Frame;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/frame.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAEvE,MAAM,WAAW,IAAI;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,2FAA2F;AAC3F,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,UAAU,YAAY;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,6CAA6C;IAC7C,MAAM,EAAE,OAAO,CAAC;IAChB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,uGAAuG;IACvG,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,KAAK;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,4FAA4F;IAC5F,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACrC;AAED,8DAA8D;AAC9D,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,MAA8D,CAAC;AAErH,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CA2BtD"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Bordered pane chrome: the title sits in the top border and a status in the bottom one. */
|
|
2
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
/** Columns from a pane's left edge to its title: the corner, one rule cell and a space. */
|
|
4
|
+
export const TITLE_INDENT = 3;
|
|
5
|
+
/** Pads or clips one content row to exactly `width` cells. */
|
|
6
|
+
export const fitCell = (text, width) => truncateToWidth(text, Math.max(1, width), "…", true);
|
|
7
|
+
export function framePane(options) {
|
|
8
|
+
const { width, theme, rows } = options;
|
|
9
|
+
const border = (text) => theme.fg(options.accent ? "borderAccent" : "borderMuted", text);
|
|
10
|
+
const inner = Math.max(1, width - 2);
|
|
11
|
+
// `┌─ title ──┐` and `└── status ─┘`: three cells of corner, rule and space frame the text.
|
|
12
|
+
const room = Math.max(0, width - 5);
|
|
13
|
+
const title = options.title && room > 0 ? truncateToWidth(options.title, room, "…") : "";
|
|
14
|
+
const status = options.status && room > 0 ? truncateToWidth(options.status, room, "…") : "";
|
|
15
|
+
const top = title
|
|
16
|
+
? border("┌─ ") + title + border(` ${"─".repeat(Math.max(0, width - 5 - visibleWidth(title)))}┐`)
|
|
17
|
+
: border(`┌${"─".repeat(Math.max(0, width - 2))}┐`);
|
|
18
|
+
const statusWidth = visibleWidth(status);
|
|
19
|
+
const bottom = status
|
|
20
|
+
? border(`└${"─".repeat(Math.max(0, width - 5 - statusWidth))} `) + status + border(" ─┘")
|
|
21
|
+
: border(`└${"─".repeat(Math.max(0, width - 2))}┘`);
|
|
22
|
+
return {
|
|
23
|
+
lines: [
|
|
24
|
+
top,
|
|
25
|
+
...rows.map((row, index) => index === options.divider
|
|
26
|
+
? border(`├${"─".repeat(Math.max(0, width - 2))}┤`)
|
|
27
|
+
: border("│") + fitCell(row, inner) + border("│")),
|
|
28
|
+
bottom,
|
|
29
|
+
],
|
|
30
|
+
status: { x: Math.max(0, width - 3 - statusWidth), width: statusWidth },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=frame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/frame.ts"],"names":[],"mappings":"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","sourcesContent":["/** Bordered pane chrome: the title sits in the top border and a status in the bottom one. */\nimport { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\n\nexport interface Rect {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n/** Columns from a pane's left edge to its title: the corner, one rule cell and a space. */\nexport const TITLE_INDENT = 3;\n\ninterface FrameOptions {\n\twidth: number;\n\ttheme: Theme;\n\t/** The focused pane's border is accented. */\n\taccent: boolean;\n\t/** Styled title for the top border. */\n\ttitle?: string;\n\t/** Styled text right-aligned in the bottom border, such as a scroll range. */\n\tstatus?: string;\n\t/** Content rows, clipped or padded to the inner width. */\n\trows: string[];\n\t/** Index of the content row drawn as a `├──┤` rule joined to the frame; that row's text is ignored. */\n\tdivider?: number;\n}\n\nexport interface Frame {\n\tlines: string[];\n\t/** Where the bottom-border status starts (pane-relative) and how many cells it occupies. */\n\tstatus: { x: number; width: number };\n}\n\n/** Pads or clips one content row to exactly `width` cells. */\nexport const fitCell = (text: string, width: number): string => truncateToWidth(text, Math.max(1, width), \"…\", true);\n\nexport function framePane(options: FrameOptions): Frame {\n\tconst { width, theme, rows } = options;\n\tconst border = (text: string) => theme.fg(options.accent ? \"borderAccent\" : \"borderMuted\", text);\n\tconst inner = Math.max(1, width - 2);\n\t// `┌─ title ──┐` and `└── status ─┘`: three cells of corner, rule and space frame the text.\n\tconst room = Math.max(0, width - 5);\n\tconst title = options.title && room > 0 ? truncateToWidth(options.title, room, \"…\") : \"\";\n\tconst status = options.status && room > 0 ? truncateToWidth(options.status, room, \"…\") : \"\";\n\tconst top = title\n\t\t? border(\"┌─ \") + title + border(` ${\"─\".repeat(Math.max(0, width - 5 - visibleWidth(title)))}┐`)\n\t\t: border(`┌${\"─\".repeat(Math.max(0, width - 2))}┐`);\n\tconst statusWidth = visibleWidth(status);\n\tconst bottom = status\n\t\t? border(`└${\"─\".repeat(Math.max(0, width - 5 - statusWidth))} `) + status + border(\" ─┘\")\n\t\t: border(`└${\"─\".repeat(Math.max(0, width - 2))}┘`);\n\treturn {\n\t\tlines: [\n\t\t\ttop,\n\t\t\t...rows.map((row, index) =>\n\t\t\t\tindex === options.divider\n\t\t\t\t\t? border(`├${\"─\".repeat(Math.max(0, width - 2))}┤`)\n\t\t\t\t\t: border(\"│\") + fitCell(row, inner) + border(\"│\"),\n\t\t\t),\n\t\t\tbottom,\n\t\t],\n\t\tstatus: { x: Math.max(0, width - 3 - statusWidth), width: statusWidth },\n\t};\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The `?` controls page: every binding grouped by purpose, with the user's current keys. */
|
|
2
|
+
import type { Keybinding } from "../../../core/keybindings.ts";
|
|
3
|
+
import type { Theme } from "../../../modes/interactive/theme/theme.ts";
|
|
4
|
+
/** Controls grouped by purpose; actions sharing a key, such as list and inspector paging, share a row. */
|
|
5
|
+
export declare function helpRows(hint: (key: Keybinding) => string, theme: Theme): string[];
|
|
6
|
+
//# sourceMappingURL=help.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/help.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAG7F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAEvE,0GAA0G;AAC1G,wBAAgB,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAiDlF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** The `?` controls page: every binding grouped by purpose, with the user's current keys. */
|
|
2
|
+
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
/** Controls grouped by purpose; actions sharing a key, such as list and inspector paging, share a row. */
|
|
4
|
+
export function helpRows(hint, theme) {
|
|
5
|
+
const keys = (...bindings) => [...new Set(bindings.map(hint).filter(Boolean))].join(" / ");
|
|
6
|
+
const listPage = keys("tui.select.pageUp", "tui.select.pageDown");
|
|
7
|
+
const inspectorPage = keys("tui.editor.pageUp", "tui.editor.pageDown");
|
|
8
|
+
const paging = listPage === inspectorPage
|
|
9
|
+
? [[listPage, "Page the list or inspector"]]
|
|
10
|
+
: [
|
|
11
|
+
[listPage, "Page the list"],
|
|
12
|
+
[inspectorPage, "Page the inspector"],
|
|
13
|
+
];
|
|
14
|
+
const sections = [
|
|
15
|
+
[
|
|
16
|
+
"Navigate",
|
|
17
|
+
[
|
|
18
|
+
[keys("tui.select.up", "tui.select.down"), "Select a task, or scroll the inspector"],
|
|
19
|
+
...paging,
|
|
20
|
+
[keys("app.tasks.top", "app.tasks.bottom"), "Top or bottom"],
|
|
21
|
+
[hint("tui.select.confirm"), "Open the selected task"],
|
|
22
|
+
[keys("app.tasks.nextFocus", "app.tasks.previousFocus"), "Switch between list and inspector"],
|
|
23
|
+
],
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
"View",
|
|
27
|
+
[
|
|
28
|
+
[keys("app.tasks.previousTab", "app.tasks.nextTab"), "Output or Details, in the inspector"],
|
|
29
|
+
[hint("app.tasks.follow"), "Follow the latest output, or load the final output"],
|
|
30
|
+
[hint("app.tasks.search"), "Find a task; Enter locates it, Esc restores the selection"],
|
|
31
|
+
],
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
"Act",
|
|
35
|
+
[
|
|
36
|
+
[hint("app.tasks.kill"), `Stop the selected task; ${hint("app.tasks.confirmStop")} confirms`],
|
|
37
|
+
[hint("app.tasks.detachSelected"), "Move the selected foreground task to the background"],
|
|
38
|
+
[hint("app.tasks.detach"), "Move every foreground execution to the background"],
|
|
39
|
+
[
|
|
40
|
+
hint("tui.select.cancel"),
|
|
41
|
+
"Close search, help or confirmation; otherwise go back from the inspector in a narrow terminal, or close the panel",
|
|
42
|
+
],
|
|
43
|
+
],
|
|
44
|
+
],
|
|
45
|
+
];
|
|
46
|
+
const keyWidth = Math.max(...sections.flatMap(([, rows]) => rows.map(([key]) => visibleWidth(key)))) + 2;
|
|
47
|
+
return sections.flatMap(([title, entries], index) => [
|
|
48
|
+
...(index ? [""] : []),
|
|
49
|
+
theme.fg("accent", title),
|
|
50
|
+
...entries.map(([key, label]) => ` ${key}${" ".repeat(keyWidth - visibleWidth(key))}${label}`),
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=help.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/help.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAE7F,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAItD,0GAA0G;AAC1G,MAAM,UAAU,QAAQ,CAAC,IAAiC,EAAE,KAAY;IACvE,MAAM,IAAI,GAAG,CAAC,GAAG,QAAsB,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzG,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC;IAClE,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC;IACvE,MAAM,MAAM,GACX,QAAQ,KAAK,aAAa;QACzB,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,4BAA4B,CAAC,CAAC;QAC5C,CAAC,CAAC;YACA,CAAC,QAAQ,EAAE,eAAe,CAAC;YAC3B,CAAC,aAAa,EAAE,oBAAoB,CAAC;SACrC,CAAC;IACL,MAAM,QAAQ,GAA6C;QAC1D;YACC,UAAU;YACV;gBACC,CAAC,IAAI,CAAC,eAAe,EAAE,iBAAiB,CAAC,EAAE,wCAAwC,CAAC;gBACpF,GAAG,MAAM;gBACT,CAAC,IAAI,CAAC,eAAe,EAAE,kBAAkB,CAAC,EAAE,eAAe,CAAC;gBAC5D,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAE,wBAAwB,CAAC;gBACtD,CAAC,IAAI,CAAC,qBAAqB,EAAE,yBAAyB,CAAC,EAAE,mCAAmC,CAAC;aAC7F;SACD;QACD;YACC,MAAM;YACN;gBACC,CAAC,IAAI,CAAC,uBAAuB,EAAE,mBAAmB,CAAC,EAAE,qCAAqC,CAAC;gBAC3F,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,oDAAoD,CAAC;gBAChF,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,2DAA2D,CAAC;aACvF;SACD;QACD;YACC,KAAK;YACL;gBACC,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,2BAA2B,IAAI,CAAC,uBAAuB,CAAC,WAAW,CAAC;gBAC7F,CAAC,IAAI,CAAC,0BAA0B,CAAC,EAAE,qDAAqD,CAAC;gBACzF,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,mDAAmD,CAAC;gBAC/E;oBACC,IAAI,CAAC,mBAAmB,CAAC;oBACzB,mHAAmH;iBACnH;aACD;SACD;KACD,CAAC;IACF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACzG,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACpD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;QACzB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC;KAC/F,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/** The `?` controls page: every binding grouped by purpose, with the user's current keys. */\n\nimport { visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { Keybinding } from \"../../../core/keybindings.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\n\n/** Controls grouped by purpose; actions sharing a key, such as list and inspector paging, share a row. */\nexport function helpRows(hint: (key: Keybinding) => string, theme: Theme): string[] {\n\tconst keys = (...bindings: Keybinding[]) => [...new Set(bindings.map(hint).filter(Boolean))].join(\" / \");\n\tconst listPage = keys(\"tui.select.pageUp\", \"tui.select.pageDown\");\n\tconst inspectorPage = keys(\"tui.editor.pageUp\", \"tui.editor.pageDown\");\n\tconst paging: Array<[string, string]> =\n\t\tlistPage === inspectorPage\n\t\t\t? [[listPage, \"Page the list or inspector\"]]\n\t\t\t: [\n\t\t\t\t\t[listPage, \"Page the list\"],\n\t\t\t\t\t[inspectorPage, \"Page the inspector\"],\n\t\t\t\t];\n\tconst sections: Array<[string, Array<[string, string]>]> = [\n\t\t[\n\t\t\t\"Navigate\",\n\t\t\t[\n\t\t\t\t[keys(\"tui.select.up\", \"tui.select.down\"), \"Select a task, or scroll the inspector\"],\n\t\t\t\t...paging,\n\t\t\t\t[keys(\"app.tasks.top\", \"app.tasks.bottom\"), \"Top or bottom\"],\n\t\t\t\t[hint(\"tui.select.confirm\"), \"Open the selected task\"],\n\t\t\t\t[keys(\"app.tasks.nextFocus\", \"app.tasks.previousFocus\"), \"Switch between list and inspector\"],\n\t\t\t],\n\t\t],\n\t\t[\n\t\t\t\"View\",\n\t\t\t[\n\t\t\t\t[keys(\"app.tasks.previousTab\", \"app.tasks.nextTab\"), \"Output or Details, in the inspector\"],\n\t\t\t\t[hint(\"app.tasks.follow\"), \"Follow the latest output, or load the final output\"],\n\t\t\t\t[hint(\"app.tasks.search\"), \"Find a task; Enter locates it, Esc restores the selection\"],\n\t\t\t],\n\t\t],\n\t\t[\n\t\t\t\"Act\",\n\t\t\t[\n\t\t\t\t[hint(\"app.tasks.kill\"), `Stop the selected task; ${hint(\"app.tasks.confirmStop\")} confirms`],\n\t\t\t\t[hint(\"app.tasks.detachSelected\"), \"Move the selected foreground task to the background\"],\n\t\t\t\t[hint(\"app.tasks.detach\"), \"Move every foreground execution to the background\"],\n\t\t\t\t[\n\t\t\t\t\thint(\"tui.select.cancel\"),\n\t\t\t\t\t\"Close search, help or confirmation; otherwise go back from the inspector in a narrow terminal, or close the panel\",\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t];\n\tconst keyWidth = Math.max(...sections.flatMap(([, rows]) => rows.map(([key]) => visibleWidth(key)))) + 2;\n\treturn sections.flatMap(([title, entries], index) => [\n\t\t...(index ? [\"\"] : []),\n\t\ttheme.fg(\"accent\", title),\n\t\t...entries.map(([key, label]) => ` ${key}${\" \".repeat(keyWidth - visibleWidth(key))}${label}`),\n\t]);\n}\n"]}
|
|
@@ -4,10 +4,12 @@ import { TaskViewport } from "./viewport.ts";
|
|
|
4
4
|
interface ReadingPosition {
|
|
5
5
|
info: TaskViewport;
|
|
6
6
|
output: TaskViewport;
|
|
7
|
+
/** Whether Output last followed the tail; a change of kind restarts its viewport. */
|
|
8
|
+
tails?: boolean;
|
|
7
9
|
read?: TaskRead;
|
|
8
10
|
}
|
|
9
11
|
interface InspectorOptions {
|
|
10
|
-
host: Pick<TasksContext, "read" | "retain">;
|
|
12
|
+
host: Pick<TasksContext, "read" | "retain" | "presentOutput">;
|
|
11
13
|
theme: Theme;
|
|
12
14
|
requestRender(): void;
|
|
13
15
|
}
|
|
@@ -29,8 +31,7 @@ export declare class TaskInspector {
|
|
|
29
31
|
invalidate(): void;
|
|
30
32
|
refresh(): Promise<void>;
|
|
31
33
|
output(width: number): string[];
|
|
32
|
-
|
|
33
|
-
details(status: string): string[];
|
|
34
|
+
details(width: number): string[];
|
|
34
35
|
dispose(): void;
|
|
35
36
|
}
|
|
36
37
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/inspector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEzF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C,UAAU,eAAe;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,QAAQ,CAAC;CAChB;AACD,UAAU,gBAAgB;IACzB,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"inspector.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/inspector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEzF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C,UAAU,eAAe;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,qFAAqF;IACrF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,QAAQ,CAAC;CAChB;AACD,UAAU,gBAAgB;IACzB,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,GAAG,eAAe,CAAC,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;IACb,aAAa,IAAI,IAAI,CAAC;CACtB;AAOD,8FAA8F;AAC9F,qBAAa,aAAa;IACzB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAChE,OAAO,CAAC,OAAO,CAAC,CAAa;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,iFAAiF;IACjF,OAAO,CAAC,WAAW,CAAC,CAA0E;IAC9F,OAAO,CAAC,WAAW,CAAC,CAAyD;gBACjE,OAAO,EAAE,gBAAgB;IAGrC,IAAI,QAAQ,IAAI,eAAe,CAQ9B;IACD,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI;IAiB5C,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI;IAGrC,UAAU,IAAI,IAAI;IAIZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B9B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAc/B,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAQhC,OAAO,IAAI,IAAI;CAOf"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { isTaskTerminal } from "../../../core/tasks/types.js";
|
|
2
2
|
import { cleanTaskText } from "./text.js";
|
|
3
3
|
import { TaskViewport } from "./viewport.js";
|
|
4
|
-
import { detailLines, outputLines } from "./views.js";
|
|
4
|
+
import { detailLines, outputLines, streamsLog } from "./views.js";
|
|
5
|
+
/** Output follows the tail while it is a log or still live; a settled report reads from its first line. */
|
|
6
|
+
function tailsOutput(task) {
|
|
7
|
+
return task.output === "log" || !isTaskTerminal(task.status);
|
|
8
|
+
}
|
|
5
9
|
/** Owns the selected task's bounded reads. Reading never acknowledges completion delivery. */
|
|
6
10
|
export class TaskInspector {
|
|
7
11
|
constructor(options) {
|
|
@@ -30,6 +34,14 @@ export class TaskInspector {
|
|
|
30
34
|
previous?.();
|
|
31
35
|
}
|
|
32
36
|
this.task = task;
|
|
37
|
+
if (!task)
|
|
38
|
+
return;
|
|
39
|
+
const position = this.position;
|
|
40
|
+
const tails = tailsOutput(task);
|
|
41
|
+
if (position.tails !== tails) {
|
|
42
|
+
position.tails = tails;
|
|
43
|
+
position.output.retarget(tails);
|
|
44
|
+
}
|
|
33
45
|
}
|
|
34
46
|
prune(ids) {
|
|
35
47
|
for (const id of this.positions.keys())
|
|
@@ -42,7 +54,9 @@ export class TaskInspector {
|
|
|
42
54
|
}
|
|
43
55
|
async refresh() {
|
|
44
56
|
const task = this.task;
|
|
45
|
-
|
|
57
|
+
// Only an attached log streams. A terminal file-less log is read once for its expiry diagnostic.
|
|
58
|
+
const terminalFallback = task?.output === "log" && isTaskTerminal(task.status);
|
|
59
|
+
if (this.disposed || !task || (!streamsLog(task) && !terminalFallback) || this.reading)
|
|
46
60
|
return;
|
|
47
61
|
const position = this.position;
|
|
48
62
|
if (position.read && (!position.output.follow || isTaskTerminal(position.read.task.status)))
|
|
@@ -77,20 +91,25 @@ export class TaskInspector {
|
|
|
77
91
|
const task = this.task;
|
|
78
92
|
if (!task)
|
|
79
93
|
return [];
|
|
94
|
+
// A live presentation changes with time, not with the snapshot, so it is never cached.
|
|
95
|
+
const live = isTaskTerminal(task.status)
|
|
96
|
+
? undefined
|
|
97
|
+
: this.options.host.presentOutput(task.id, width, this.options.theme);
|
|
98
|
+
if (live)
|
|
99
|
+
return outputLines(task, undefined, this.options.theme, width, live);
|
|
80
100
|
const read = this.position.read;
|
|
81
101
|
const cache = this.outputCache;
|
|
82
102
|
if (cache?.task !== task || cache.read !== read || cache.width !== width)
|
|
83
103
|
this.outputCache = { task, read, width, lines: outputLines(task, read, this.options.theme, width) };
|
|
84
104
|
return this.outputCache.lines;
|
|
85
105
|
}
|
|
86
|
-
|
|
87
|
-
details(status) {
|
|
106
|
+
details(width) {
|
|
88
107
|
const task = this.task;
|
|
89
108
|
if (!task)
|
|
90
109
|
return [];
|
|
91
110
|
const cache = this.detailCache;
|
|
92
|
-
if (cache?.task !== task || cache.
|
|
93
|
-
this.detailCache = { task,
|
|
111
|
+
if (cache?.task !== task || cache.width !== width)
|
|
112
|
+
this.detailCache = { task, width, lines: detailLines(task, this.options.theme, width) };
|
|
94
113
|
return this.detailCache.lines;
|
|
95
114
|
}
|
|
96
115
|
dispose() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/inspector.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TaskRead, TaskSnapshot, TasksContext } from \"../../../core/tasks/types.ts\";\nimport { isTaskTerminal } from \"../../../core/tasks/types.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport { cleanTaskText } from \"./text.ts\";\nimport { TaskViewport } from \"./viewport.ts\";\nimport { detailLines, outputLines } from \"./views.ts\";\n\ninterface ReadingPosition {\n\tinfo: TaskViewport;\n\toutput: TaskViewport;\n\tread?: TaskRead;\n}\ninterface InspectorOptions {\n\thost: Pick<TasksContext, \"read\" | \"retain\">;\n\ttheme: Theme;\n\trequestRender(): void;\n}\n\n/** Owns the selected task's bounded reads. Reading never acknowledges completion delivery. */\nexport class TaskInspector {\n\ttask?: TaskSnapshot;\n\tprivate readonly options: InspectorOptions;\n\tprivate readonly positions = new Map<string, ReadingPosition>();\n\tprivate release?: () => void;\n\tprivate reading = false;\n\tprivate disposed = false;\n\t/** Snapshots are immutable, so identity tells when the lines must be rebuilt. */\n\tprivate outputCache?: { task: TaskSnapshot; read?: TaskRead; width: number; lines: string[] };\n\tprivate detailCache?: { task: TaskSnapshot; status: string; lines: string[] };\n\tconstructor(options: InspectorOptions) {\n\t\tthis.options = options;\n\t}\n\tget position(): ReadingPosition {\n\t\tconst id = this.task?.id;\n\t\tlet position = id ? this.positions.get(id) : undefined;\n\t\tif (!position) {\n\t\t\tposition = { info: new TaskViewport(), output: new TaskViewport(true) };\n\t\t\tif (id) this.positions.set(id, position);\n\t\t}\n\t\treturn position;\n\t}\n\tselect(task: TaskSnapshot | undefined): void {\n\t\tif (this.disposed) return;\n\t\tif (this.task?.id !== task?.id) {\n\t\t\tconst release = task ? this.options.host.retain(task.id) : undefined;\n\t\t\tconst previous = this.release;\n\t\t\tthis.release = release;\n\t\t\tprevious?.();\n\t\t}\n\t\tthis.task = task;\n\t}\n\tprune(ids: ReadonlySet<string>): void {\n\t\tfor (const id of this.positions.keys()) if (!ids.has(id)) this.positions.delete(id);\n\t}\n\tinvalidate(): void {\n\t\tthis.outputCache = undefined;\n\t\tthis.detailCache = undefined;\n\t}\n\tasync refresh(): Promise<void> {\n\t\tconst task = this.task;\n\t\tif (this.disposed || !task || this.reading) return;\n\t\tconst position = this.position;\n\t\tif (position.read && (!position.output.follow || isTaskTerminal(position.read.task.status))) return;\n\t\tthis.reading = true;\n\t\ttry {\n\t\t\tconst read = await this.options.host.read(task.id, { mode: \"tail\", bytes: 48 * 1024 });\n\t\t\tif (this.disposed || this.task?.id !== task.id || (!position.output.follow && position.read)) return;\n\t\t\tposition.read = read;\n\t\t} catch (error) {\n\t\t\tif (!this.disposed && this.task?.id === task.id && (position.output.follow || !position.read)) {\n\t\t\t\tposition.read = {\n\t\t\t\t\ttask,\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\treadError: `Cannot read output: ${cleanTaskText(String(error))}`,\n\t\t\t\t\ttotalBytes: 0,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t};\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.reading = false;\n\t\t\tif (!this.disposed) this.options.requestRender();\n\t\t\tif (!this.disposed && this.task?.id !== task.id) await this.refresh();\n\t\t}\n\t}\n\toutput(width: number): string[] {\n\t\tconst task = this.task;\n\t\tif (!task) return [];\n\t\tconst read = this.position.read;\n\t\tconst cache = this.outputCache;\n\t\tif (cache?.task !== task || cache.read !== read || cache.width !== width)\n\t\t\tthis.outputCache = { task, read, width, lines: outputLines(task, read, this.options.theme, width) };\n\t\treturn this.outputCache!.lines;\n\t}\n\t/** `status` carries the runtime, so Details rebuild at most once per displayed tick. */\n\tdetails(status: string): string[] {\n\t\tconst task = this.task;\n\t\tif (!task) return [];\n\t\tconst cache = this.detailCache;\n\t\tif (cache?.task !== task || cache.status !== status)\n\t\t\tthis.detailCache = { task, status, lines: detailLines(task, status, this.options.theme) };\n\t\treturn this.detailCache!.lines;\n\t}\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.release?.();\n\t\tthis.release = undefined;\n\t\tthis.positions.clear();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"inspector.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/inspector.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TaskRead, TaskSnapshot, TasksContext } from \"../../../core/tasks/types.ts\";\nimport { isTaskTerminal } from \"../../../core/tasks/types.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport { cleanTaskText } from \"./text.ts\";\nimport { TaskViewport } from \"./viewport.ts\";\nimport { detailLines, outputLines, streamsLog } from \"./views.ts\";\n\ninterface ReadingPosition {\n\tinfo: TaskViewport;\n\toutput: TaskViewport;\n\t/** Whether Output last followed the tail; a change of kind restarts its viewport. */\n\ttails?: boolean;\n\tread?: TaskRead;\n}\ninterface InspectorOptions {\n\thost: Pick<TasksContext, \"read\" | \"retain\" | \"presentOutput\">;\n\ttheme: Theme;\n\trequestRender(): void;\n}\n\n/** Output follows the tail while it is a log or still live; a settled report reads from its first line. */\nfunction tailsOutput(task: TaskSnapshot): boolean {\n\treturn task.output === \"log\" || !isTaskTerminal(task.status);\n}\n\n/** Owns the selected task's bounded reads. Reading never acknowledges completion delivery. */\nexport class TaskInspector {\n\ttask?: TaskSnapshot;\n\tprivate readonly options: InspectorOptions;\n\tprivate readonly positions = new Map<string, ReadingPosition>();\n\tprivate release?: () => void;\n\tprivate reading = false;\n\tprivate disposed = false;\n\t/** Snapshots are immutable, so identity tells when the lines must be rebuilt. */\n\tprivate outputCache?: { task: TaskSnapshot; read?: TaskRead; width: number; lines: string[] };\n\tprivate detailCache?: { task: TaskSnapshot; width: number; lines: string[] };\n\tconstructor(options: InspectorOptions) {\n\t\tthis.options = options;\n\t}\n\tget position(): ReadingPosition {\n\t\tconst id = this.task?.id;\n\t\tlet position = id ? this.positions.get(id) : undefined;\n\t\tif (!position) {\n\t\t\tposition = { info: new TaskViewport(), output: new TaskViewport(true) };\n\t\t\tif (id) this.positions.set(id, position);\n\t\t}\n\t\treturn position;\n\t}\n\tselect(task: TaskSnapshot | undefined): void {\n\t\tif (this.disposed) return;\n\t\tif (this.task?.id !== task?.id) {\n\t\t\tconst release = task ? this.options.host.retain(task.id) : undefined;\n\t\t\tconst previous = this.release;\n\t\t\tthis.release = release;\n\t\t\tprevious?.();\n\t\t}\n\t\tthis.task = task;\n\t\tif (!task) return;\n\t\tconst position = this.position;\n\t\tconst tails = tailsOutput(task);\n\t\tif (position.tails !== tails) {\n\t\t\tposition.tails = tails;\n\t\t\tposition.output.retarget(tails);\n\t\t}\n\t}\n\tprune(ids: ReadonlySet<string>): void {\n\t\tfor (const id of this.positions.keys()) if (!ids.has(id)) this.positions.delete(id);\n\t}\n\tinvalidate(): void {\n\t\tthis.outputCache = undefined;\n\t\tthis.detailCache = undefined;\n\t}\n\tasync refresh(): Promise<void> {\n\t\tconst task = this.task;\n\t\t// Only an attached log streams. A terminal file-less log is read once for its expiry diagnostic.\n\t\tconst terminalFallback = task?.output === \"log\" && isTaskTerminal(task.status);\n\t\tif (this.disposed || !task || (!streamsLog(task) && !terminalFallback) || this.reading) return;\n\t\tconst position = this.position;\n\t\tif (position.read && (!position.output.follow || isTaskTerminal(position.read.task.status))) return;\n\t\tthis.reading = true;\n\t\ttry {\n\t\t\tconst read = await this.options.host.read(task.id, { mode: \"tail\", bytes: 48 * 1024 });\n\t\t\tif (this.disposed || this.task?.id !== task.id || (!position.output.follow && position.read)) return;\n\t\t\tposition.read = read;\n\t\t} catch (error) {\n\t\t\tif (!this.disposed && this.task?.id === task.id && (position.output.follow || !position.read)) {\n\t\t\t\tposition.read = {\n\t\t\t\t\ttask,\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\treadError: `Cannot read output: ${cleanTaskText(String(error))}`,\n\t\t\t\t\ttotalBytes: 0,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t};\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.reading = false;\n\t\t\tif (!this.disposed) this.options.requestRender();\n\t\t\tif (!this.disposed && this.task?.id !== task.id) await this.refresh();\n\t\t}\n\t}\n\toutput(width: number): string[] {\n\t\tconst task = this.task;\n\t\tif (!task) return [];\n\t\t// A live presentation changes with time, not with the snapshot, so it is never cached.\n\t\tconst live = isTaskTerminal(task.status)\n\t\t\t? undefined\n\t\t\t: this.options.host.presentOutput(task.id, width, this.options.theme);\n\t\tif (live) return outputLines(task, undefined, this.options.theme, width, live);\n\t\tconst read = this.position.read;\n\t\tconst cache = this.outputCache;\n\t\tif (cache?.task !== task || cache.read !== read || cache.width !== width)\n\t\t\tthis.outputCache = { task, read, width, lines: outputLines(task, read, this.options.theme, width) };\n\t\treturn this.outputCache!.lines;\n\t}\n\tdetails(width: number): string[] {\n\t\tconst task = this.task;\n\t\tif (!task) return [];\n\t\tconst cache = this.detailCache;\n\t\tif (cache?.task !== task || cache.width !== width)\n\t\t\tthis.detailCache = { task, width, lines: detailLines(task, this.options.theme, width) };\n\t\treturn this.detailCache!.lines;\n\t}\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.release?.();\n\t\tthis.release = undefined;\n\t\tthis.positions.clear();\n\t}\n}\n"]}
|
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { KeybindingsManager } from "../../../core/keybindings.ts";
|
|
2
2
|
import type { Theme } from "../../../modes/interactive/theme/theme.ts";
|
|
3
|
+
import { type Feedback } from "./footer.ts";
|
|
4
|
+
import { type Rect } from "./frame.ts";
|
|
3
5
|
import type { TaskInspector } from "./inspector.ts";
|
|
4
6
|
import type { TaskList } from "./list.ts";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
width: number;
|
|
10
|
-
key?: AppKeybinding;
|
|
11
|
-
taskId?: string;
|
|
12
|
-
tab?: TasksTab;
|
|
13
|
-
cancel?: boolean;
|
|
7
|
+
import type { PanelFocus, PanelMode, TaskHit, TaskSelection } from "./model.ts";
|
|
8
|
+
/** A pane the pointer can act on: wheel scrolls it and a click on it focuses it. */
|
|
9
|
+
export interface PaneRegion extends Rect {
|
|
10
|
+
pane: PanelFocus | "help";
|
|
14
11
|
}
|
|
15
12
|
export interface TasksLayout {
|
|
13
|
+
lines: string[];
|
|
16
14
|
hits: TaskHit[];
|
|
17
|
-
|
|
15
|
+
regions: PaneRegion[];
|
|
16
|
+
/** Both panes are visible; otherwise one pane at a time fills the width. */
|
|
17
|
+
split: boolean;
|
|
18
18
|
listPageSize: number;
|
|
19
|
-
lines: string[];
|
|
20
19
|
/** The terminal is below the supported size; only a full-screen resize notice is shown. */
|
|
21
20
|
tooSmall?: boolean;
|
|
22
21
|
}
|
|
23
|
-
interface LayoutOptions {
|
|
22
|
+
export interface LayoutOptions {
|
|
24
23
|
width: number;
|
|
25
24
|
height: number;
|
|
26
25
|
theme: Theme;
|
|
@@ -30,13 +29,11 @@ interface LayoutOptions {
|
|
|
30
29
|
list: TaskList;
|
|
31
30
|
focus: PanelFocus;
|
|
32
31
|
mode: PanelMode;
|
|
33
|
-
|
|
34
|
-
searchLine: (width: number) => string;
|
|
35
|
-
feedback?: string;
|
|
32
|
+
feedback?: Feedback;
|
|
36
33
|
/** Whether the host would move this task to the background now. */
|
|
37
34
|
canDetach: (id: string) => boolean;
|
|
38
35
|
}
|
|
39
|
-
/**
|
|
36
|
+
/** The list takes 40% of a split terminal, between 34 and 60 columns. */
|
|
37
|
+
export declare function listWidthFor(width: number): number;
|
|
40
38
|
export declare function renderTasksLayout(options: LayoutOptions): TasksLayout;
|
|
41
|
-
export {};
|
|
42
39
|
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/layout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/layout.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAc,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAEnF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,EAGN,KAAK,QAAQ,EAKb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAsB,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAWhF,oFAAoF;AACpF,MAAM,WAAW,UAAW,SAAQ,IAAI;IACvC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;IACjD,SAAS,EAAE,aAAa,CAAC;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,mEAAmE;IACnE,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAuBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,WAAW,CAgDrE"}
|