opencode-courier 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -135
- package/dist/index.js +24 -4
- package/dist/memory/domain.d.ts +119 -0
- package/dist/memory/domain.js +84 -0
- package/dist/memory/events.d.ts +26 -0
- package/dist/memory/events.js +46 -0
- package/dist/memory/forget.d.ts +10 -0
- package/dist/memory/forget.js +20 -0
- package/dist/memory/index.d.ts +10 -0
- package/dist/memory/index.js +11 -0
- package/dist/memory/note.d.ts +37 -0
- package/dist/memory/note.js +29 -0
- package/dist/memory/ports.d.ts +44 -0
- package/dist/memory/ports.js +1 -0
- package/dist/memory/recall.d.ts +27 -0
- package/dist/memory/recall.js +48 -0
- package/dist/memory/record.d.ts +7 -0
- package/dist/memory/record.js +92 -0
- package/dist/memory/rpc-handlers.d.ts +134 -0
- package/dist/memory/rpc-handlers.js +23 -0
- package/dist/memory/rpc.d.ts +227 -0
- package/dist/memory/rpc.js +60 -0
- package/dist/memory/store.d.ts +10 -0
- package/dist/memory/store.js +90 -0
- package/dist/memory/sweep.d.ts +4 -0
- package/dist/memory/sweep.js +12 -0
- package/dist/memory/text.d.ts +27 -0
- package/dist/memory/text.js +95 -0
- package/dist/memory/tools.d.ts +16 -0
- package/dist/memory/tools.js +78 -0
- package/dist/memory/tui.d.ts +73 -0
- package/dist/person/rpc.d.ts +4 -4
- package/dist/platform/json.d.ts +1 -0
- package/dist/platform/json.js +1 -0
- package/dist/platform/version.js +2 -2
- package/dist/rpc.d.ts +1 -0
- package/dist/rpc.js +1 -0
- package/dist/skill.js +10 -3
- package/dist/tree/context.d.ts +2 -2
- package/dist/tree/context.js +4 -3
- package/dist/tree/index.d.ts +6 -5
- package/dist/tree/index.js +5 -4
- package/dist/tree/mascot.d.ts +50 -0
- package/dist/tree/mascot.js +171 -0
- package/dist/tree/panel.d.ts +3 -1
- package/dist/tree/panel.js +17 -3
- package/dist/tree/ports.d.ts +12 -2
- package/dist/tree/roster.d.ts +17 -0
- package/dist/tree/roster.js +24 -0
- package/dist/tree/rpc-handlers.d.ts +21 -18
- package/dist/tree/rpc.d.ts +26 -18
- package/dist/tree/rpc.js +2 -0
- package/dist/tree/status.d.ts +5 -5
- package/dist/tree/subtree.d.ts +2 -2
- package/dist/tree/text.js +2 -1
- package/dist/tree/tui.d.ts +115 -8
- package/dist/tui.js +1269 -222
- package/package.json +4 -4
package/dist/tui.js
CHANGED
|
@@ -2,58 +2,471 @@
|
|
|
2
2
|
// src/tui.tsx
|
|
3
3
|
import { Plugin } from "@opencode/plugin/tui";
|
|
4
4
|
|
|
5
|
-
// src/
|
|
5
|
+
// src/memory/rpc.ts
|
|
6
6
|
import { Rpc } from "@opencode/plugin/rpc";
|
|
7
7
|
import { Schema } from "effect";
|
|
8
8
|
var Count = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
|
|
9
|
-
var
|
|
9
|
+
var PART_STATUSES = ["done", "partial", "blocked", "failed", "interrupted", "stopped", "stalled", "out"];
|
|
10
|
+
var Part = Schema.Struct({
|
|
10
11
|
sessionID: Schema.String,
|
|
11
12
|
title: Schema.String,
|
|
12
|
-
parentID: Schema.String,
|
|
13
13
|
depth: Count,
|
|
14
|
-
|
|
14
|
+
isolated: Schema.Boolean,
|
|
15
|
+
group: Schema.optionalKey(Schema.String),
|
|
16
|
+
status: Schema.Literals(PART_STATUSES),
|
|
17
|
+
duration: Schema.optionalKey(Schema.Number),
|
|
18
|
+
cost: Schema.optionalKey(Schema.Number),
|
|
19
|
+
context: Schema.optionalKey(Schema.Number),
|
|
20
|
+
refused: Schema.optionalKey(Count)
|
|
15
21
|
});
|
|
16
|
-
var
|
|
22
|
+
var Lesson = Schema.Struct({ text: Schema.String, at: Schema.Number, depth: Schema.optionalKey(Count) });
|
|
23
|
+
var Record = Schema.Struct({
|
|
17
24
|
sessionID: Schema.String,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
startedAt: Schema.Number,
|
|
26
|
+
movedAt: Schema.Number,
|
|
27
|
+
title: Schema.String,
|
|
28
|
+
split: Schema.String,
|
|
29
|
+
finished: Schema.Boolean,
|
|
30
|
+
recalled: Schema.optionalKey(Schema.Struct({ at: Schema.optionalKey(Schema.Number), calls: Count })),
|
|
31
|
+
lesson: Schema.optionalKey(Lesson),
|
|
32
|
+
parts: Schema.Array(Part)
|
|
23
33
|
});
|
|
24
|
-
var
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
multiple: Schema.optionalKey(Schema.Boolean)
|
|
34
|
+
var Records = Schema.Struct({
|
|
35
|
+
memory: Schema.Boolean,
|
|
36
|
+
projectID: Schema.String,
|
|
37
|
+
records: Schema.Array(Record)
|
|
29
38
|
});
|
|
30
|
-
var
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
var Forgotten = Schema.Struct({ outcome: Schema.Literals(["record", "lesson", "none"]) });
|
|
40
|
+
var memoryRpc = Rpc.define({
|
|
41
|
+
id: "courier-memory",
|
|
42
|
+
methods: {
|
|
43
|
+
records: {
|
|
44
|
+
input: Schema.toStandardSchemaV1(Schema.Struct({})),
|
|
45
|
+
output: Schema.toStandardSchemaV1(Records)
|
|
46
|
+
},
|
|
47
|
+
forget: {
|
|
48
|
+
input: Schema.toStandardSchemaV1(Schema.Struct({ sessionID: Schema.String, lesson: Schema.optionalKey(Schema.Boolean) })),
|
|
49
|
+
output: Schema.toStandardSchemaV1(Forgotten)
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
events: {}
|
|
35
53
|
});
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
|
|
55
|
+
// src/kernel/protocol.ts
|
|
56
|
+
var STATUSES = ["done", "partial", "blocked", "failed"];
|
|
57
|
+
|
|
58
|
+
// src/memory/domain.ts
|
|
59
|
+
var RETENTION_MS = 90 * 24 * 60 * 60000;
|
|
60
|
+
var PART_STATUSES2 = [...STATUSES, "interrupted", "stopped", "stalled", "out"];
|
|
61
|
+
|
|
62
|
+
// src/memory/text.ts
|
|
63
|
+
function duration(ms) {
|
|
64
|
+
const minutes = Math.round(ms / 60000);
|
|
65
|
+
if (minutes < 1)
|
|
66
|
+
return `${Math.max(0, Math.round(ms / 1000))} s`;
|
|
67
|
+
if (minutes < 60)
|
|
68
|
+
return `${minutes} min`;
|
|
69
|
+
return `${Math.floor(minutes / 60)} h ${minutes % 60} min`;
|
|
70
|
+
}
|
|
71
|
+
var ONE_LINE = "what the next orchestrator in this project should know about running a tree like this one, with the job it comes from and the reason";
|
|
72
|
+
var NOTE_EMPTY = `Not noted: text is empty. Write one line, ${ONE_LINE}, or nothing.`;
|
|
73
|
+
|
|
74
|
+
// src/memory/tui.tsx
|
|
75
|
+
var MEMORY_COMMAND = "courier.memory";
|
|
76
|
+
var MEMORY_USAGE = "/courier memory lists this project's tree records, to read or forget";
|
|
77
|
+
var MEMORY_OFF = "Memory is off in this project: the plugin's memory option is false, so no record of a session tree is kept or listed here.";
|
|
78
|
+
var NO_RECORDS = "No record of a session tree in this project: one is written as each turn in a tree ends, and kept 90 days.";
|
|
79
|
+
var LESSON_LINE_MAX = 160;
|
|
80
|
+
var ROW_TITLE_MAX = 46;
|
|
81
|
+
var TODAY = "Today";
|
|
82
|
+
var LESSON_ROW = 90;
|
|
83
|
+
var pad = (n) => String(n).padStart(2, "0");
|
|
84
|
+
var localDay = (at) => new Date(at).toDateString();
|
|
85
|
+
var day = (at) => {
|
|
86
|
+
const date = new Date(at);
|
|
87
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
88
|
+
};
|
|
89
|
+
var zone = (date) => new Intl.DateTimeFormat("en-US", {
|
|
90
|
+
timeZoneName: "short"
|
|
91
|
+
}).formatToParts(date).find((part) => part.type === "timeZoneName")?.value ?? "local";
|
|
92
|
+
var minute = (at) => {
|
|
93
|
+
const date = new Date(at);
|
|
94
|
+
return `${day(at)} ${pad(date.getHours())}:${pad(date.getMinutes())} ${zone(date)}`;
|
|
95
|
+
};
|
|
96
|
+
var oneLine2 = (text) => text.replace(/\s+/g, " ").trim();
|
|
97
|
+
var clip = (text, max) => text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
|
98
|
+
var count = (n, noun) => `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
99
|
+
var money = (cost) => `$${cost.toFixed(2)}`;
|
|
100
|
+
var nameOf = (record) => clip(oneLine2(record.title) || record.sessionID, 60);
|
|
101
|
+
function partsCounted(parts) {
|
|
102
|
+
if (!parts.length)
|
|
103
|
+
return "no parts";
|
|
104
|
+
const statuses = PART_STATUSES.flatMap((status) => {
|
|
105
|
+
const n = parts.filter((part) => part.status === status).length;
|
|
106
|
+
return n ? [`${n} ${status}`] : [];
|
|
107
|
+
}).join(", ");
|
|
108
|
+
return `${count(parts.length, "part")}: ${statuses}`;
|
|
109
|
+
}
|
|
110
|
+
function costOf(record) {
|
|
111
|
+
return record.parts.some((part) => part.cost !== undefined) ? record.parts.reduce((sum, part) => sum + (part.cost ?? 0), 0) : undefined;
|
|
112
|
+
}
|
|
113
|
+
function recordTitle(record, open) {
|
|
114
|
+
return `${clip(oneLine2(record.title) || record.sessionID, ROW_TITLE_MAX)}${record.sessionID === open ? " (this session)" : ""}`;
|
|
115
|
+
}
|
|
116
|
+
function recordCategory(record, now = Date.now()) {
|
|
117
|
+
const moved = localDay(record.movedAt);
|
|
118
|
+
return moved === localDay(now) ? TODAY : moved;
|
|
119
|
+
}
|
|
120
|
+
function lessonHeading(lesson) {
|
|
121
|
+
const from = lesson.depth === undefined ? "" : `, from depth ${lesson.depth}`;
|
|
122
|
+
return `lesson (${day(lesson.at)}${from})`;
|
|
123
|
+
}
|
|
124
|
+
function recordDescription(record) {
|
|
125
|
+
const cost = costOf(record);
|
|
126
|
+
return [partsCounted(record.parts), duration(record.movedAt - record.startedAt), cost === undefined ? "" : money(cost), record.finished ? "" : "not finished"].filter(Boolean).join(" \xB7 ");
|
|
127
|
+
}
|
|
128
|
+
function recordDetails(record) {
|
|
129
|
+
return record.lesson ? [clip(`${lessonHeading(record.lesson)}: "${oneLine2(record.lesson.text)}"`, LESSON_LINE_MAX)] : [];
|
|
130
|
+
}
|
|
131
|
+
function partLine(part) {
|
|
132
|
+
return [part.status, `depth ${part.depth}`, part.isolated ? "isolated" : "", part.group ? `group ${part.group}` : "", part.duration === undefined ? "" : duration(part.duration), part.cost === undefined ? "" : money(part.cost), part.context === undefined ? "" : `ctx ${Math.round(part.context * 100)}%`, part.refused ? `${count(part.refused, "spawn")} refused` : ""].filter(Boolean).join(" \xB7 ");
|
|
133
|
+
}
|
|
134
|
+
function wrap(text, width) {
|
|
135
|
+
const rows = [];
|
|
136
|
+
let row = "";
|
|
137
|
+
for (const word of oneLine2(text).split(" ").filter(Boolean)) {
|
|
138
|
+
if (row && row.length + 1 + word.length <= width) {
|
|
139
|
+
row = `${row} ${word}`;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (row)
|
|
143
|
+
rows.push(row);
|
|
144
|
+
row = word;
|
|
145
|
+
while (row.length > width) {
|
|
146
|
+
rows.push(row.slice(0, width));
|
|
147
|
+
row = row.slice(width);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (row)
|
|
151
|
+
rows.push(row);
|
|
152
|
+
return rows;
|
|
153
|
+
}
|
|
154
|
+
var lessonLine = (record) => record.lesson ? {
|
|
155
|
+
kind: "lesson",
|
|
156
|
+
row: 0
|
|
157
|
+
} : {
|
|
158
|
+
kind: "fact",
|
|
159
|
+
key: "lesson"
|
|
160
|
+
};
|
|
161
|
+
function nearby(sessionIDs, sessionID, forgotten) {
|
|
162
|
+
const at = sessionIDs.indexOf(sessionID);
|
|
163
|
+
if (at < 0)
|
|
164
|
+
return forgotten ? [] : [sessionID];
|
|
165
|
+
return [...forgotten ? [] : [sessionID], ...sessionIDs.slice(at + 1), ...sessionIDs.slice(0, at).reverse()];
|
|
166
|
+
}
|
|
167
|
+
function detailOptions(record) {
|
|
168
|
+
const fact = (key, title, description) => ({
|
|
169
|
+
title,
|
|
170
|
+
value: {
|
|
171
|
+
kind: "fact",
|
|
172
|
+
key
|
|
173
|
+
},
|
|
174
|
+
category: "Tree",
|
|
175
|
+
...description ? {
|
|
176
|
+
description
|
|
177
|
+
} : {}
|
|
178
|
+
});
|
|
179
|
+
const cost = costOf(record);
|
|
180
|
+
const recalled = record.recalled ? fact("recalled", `Recalled ${count(record.recalled.calls, "time")}`, record.recalled.at === undefined ? "by a part only" : `first by the top ${minute(record.recalled.at)}`) : fact("recalled", "Not recalled");
|
|
181
|
+
const lesson = record.lesson ? [{
|
|
182
|
+
title: lessonHeading(record.lesson),
|
|
183
|
+
value: {
|
|
184
|
+
kind: "lesson",
|
|
185
|
+
row: 0
|
|
186
|
+
},
|
|
187
|
+
category: "Lesson"
|
|
188
|
+
}, ...wrap(record.lesson.text, LESSON_ROW).map((row, at) => ({
|
|
189
|
+
title: row,
|
|
190
|
+
value: {
|
|
191
|
+
kind: "lesson",
|
|
192
|
+
row: at + 1
|
|
193
|
+
},
|
|
194
|
+
category: "Lesson"
|
|
195
|
+
}))] : [{
|
|
196
|
+
title: "No lesson",
|
|
197
|
+
value: {
|
|
198
|
+
kind: "fact",
|
|
199
|
+
key: "lesson"
|
|
200
|
+
},
|
|
201
|
+
category: "Lesson",
|
|
202
|
+
description: "courier_note writes one line per tree"
|
|
203
|
+
}];
|
|
204
|
+
return [fact("top", `Top session ${record.sessionID}`), fact("started", `Started ${minute(record.startedAt)}`, `last moved ${minute(record.movedAt)}, ran ${duration(record.movedAt - record.startedAt)}`), fact("split", clip(oneLine2(record.split), 160)), fact("finished", record.finished ? "Finished" : "Not finished", cost === undefined ? undefined : `${money(cost)} in all`), recalled, ...record.parts.length ? record.parts.map((part) => ({
|
|
205
|
+
title: `${" ".repeat(Math.max(0, part.depth - 1))}${clip(oneLine2(part.title) || part.sessionID, 60)}`,
|
|
206
|
+
value: {
|
|
207
|
+
kind: "part",
|
|
208
|
+
sessionID: part.sessionID
|
|
209
|
+
},
|
|
210
|
+
category: "Parts",
|
|
211
|
+
description: partLine(part)
|
|
212
|
+
})) : [{
|
|
213
|
+
title: "No parts",
|
|
214
|
+
value: {
|
|
215
|
+
kind: "fact",
|
|
216
|
+
key: "parts"
|
|
217
|
+
},
|
|
218
|
+
category: "Parts",
|
|
219
|
+
description: "no session was started in this tree"
|
|
220
|
+
}], ...lesson];
|
|
221
|
+
}
|
|
222
|
+
function forgetMessage(record, lesson) {
|
|
223
|
+
const name = `"${nameOf(record)}" (${day(record.movedAt)}, ${partsCounted(record.parts)})`;
|
|
224
|
+
if (lesson)
|
|
225
|
+
return `Forget the lesson of ${name}? The record keeps its facts; courier_recall no longer lists the lesson: "${clip(oneLine2(record.lesson?.text ?? ""), 120)}".`;
|
|
226
|
+
return `Forget the record of ${name}, top session ${record.sessionID}? It leaves this project's memory and courier_recall${record.lesson ? ", its lesson with it" : ""}; the sessions themselves are untouched.`;
|
|
227
|
+
}
|
|
228
|
+
function forgottenLine(record, outcome) {
|
|
229
|
+
const name = `"${nameOf(record)}"`;
|
|
230
|
+
if (outcome === "record")
|
|
231
|
+
return `Forgot the record of ${name}`;
|
|
232
|
+
if (outcome === "lesson")
|
|
233
|
+
return `Forgot the lesson of ${name}; its facts stay`;
|
|
234
|
+
return `Nothing to forget for ${name}: it was already gone`;
|
|
235
|
+
}
|
|
236
|
+
function addMemoryView(ctx) {
|
|
237
|
+
const memory = ctx.client.rpc(memoryRpc);
|
|
238
|
+
const openSession = () => {
|
|
239
|
+
const route = ctx.ui.router.current();
|
|
240
|
+
return route.type === "session" ? route.sessionID : undefined;
|
|
241
|
+
};
|
|
242
|
+
const where = () => {
|
|
243
|
+
const sessionID = openSession();
|
|
244
|
+
return (sessionID ? ctx.data.session.get(sessionID)?.location : undefined) ?? ctx.location ?? ctx.data.location.default();
|
|
245
|
+
};
|
|
246
|
+
const reason = (error) => {
|
|
247
|
+
if (error instanceof Error)
|
|
248
|
+
return error.message;
|
|
249
|
+
return typeof error === "object" && error && "message" in error ? String(error.message) : String(error);
|
|
250
|
+
};
|
|
251
|
+
const fail = (what) => (error) => ctx.ui.toast.show({
|
|
252
|
+
title: "courier",
|
|
253
|
+
message: `${what}: ${reason(error)}`,
|
|
254
|
+
variant: "error"
|
|
255
|
+
});
|
|
256
|
+
const say = (message) => ctx.ui.toast.show({
|
|
257
|
+
title: "courier",
|
|
258
|
+
message,
|
|
259
|
+
variant: "info"
|
|
260
|
+
});
|
|
261
|
+
function show(options, chosen, forget) {
|
|
262
|
+
let request = () => {
|
|
263
|
+
return;
|
|
264
|
+
};
|
|
265
|
+
const requested = new Promise((resolve) => {
|
|
266
|
+
request = resolve;
|
|
267
|
+
});
|
|
268
|
+
const choosing = ctx.ui.dialog.select({
|
|
269
|
+
...options,
|
|
270
|
+
actions: [{
|
|
271
|
+
bind: "x",
|
|
272
|
+
title: "forget",
|
|
273
|
+
onTrigger: (value) => request(forget(value))
|
|
274
|
+
}]
|
|
275
|
+
});
|
|
276
|
+
ctx.ui.dialog.set({
|
|
277
|
+
size: "xlarge"
|
|
278
|
+
});
|
|
279
|
+
return Promise.race([choosing.then((value) => value === undefined ? {
|
|
280
|
+
kind: "back"
|
|
281
|
+
} : chosen(value)), requested]);
|
|
282
|
+
}
|
|
283
|
+
const showList = (read, current) => {
|
|
284
|
+
const open2 = openSession();
|
|
285
|
+
const now = Date.now();
|
|
286
|
+
return show({
|
|
287
|
+
title: `Memory (${read.records.length})`,
|
|
288
|
+
placeholder: "Search",
|
|
289
|
+
...current ? {
|
|
290
|
+
current
|
|
291
|
+
} : {},
|
|
292
|
+
options: read.records.map((record) => ({
|
|
293
|
+
title: recordTitle(record, open2),
|
|
294
|
+
value: record,
|
|
295
|
+
category: recordCategory(record, now),
|
|
296
|
+
description: recordDescription(record),
|
|
297
|
+
details: recordDetails(record)
|
|
298
|
+
}))
|
|
299
|
+
}, (record) => ({
|
|
300
|
+
kind: "open",
|
|
301
|
+
record
|
|
302
|
+
}), (record) => ({
|
|
303
|
+
kind: "forget",
|
|
304
|
+
record,
|
|
305
|
+
lesson: false
|
|
306
|
+
}));
|
|
307
|
+
};
|
|
308
|
+
const showRecord = (record, current) => show({
|
|
309
|
+
title: `${localDay(record.movedAt)} ${nameOf(record)}`,
|
|
310
|
+
placeholder: "Search",
|
|
311
|
+
options: detailOptions(record),
|
|
312
|
+
...current ? {
|
|
313
|
+
current
|
|
314
|
+
} : {}
|
|
315
|
+
}, () => ({
|
|
316
|
+
kind: "back"
|
|
317
|
+
}), (line) => ({
|
|
318
|
+
kind: "forget",
|
|
319
|
+
record,
|
|
320
|
+
lesson: line.kind === "lesson",
|
|
321
|
+
line
|
|
322
|
+
}));
|
|
323
|
+
async function confirmForget(record, lesson) {
|
|
324
|
+
const confirmed = await ctx.ui.dialog.confirm({
|
|
325
|
+
title: lesson ? "Forget lesson" : "Forget record",
|
|
326
|
+
message: forgetMessage(record, lesson),
|
|
327
|
+
label: {
|
|
328
|
+
confirm: "Forget",
|
|
329
|
+
cancel: "Keep"
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
if (!confirmed)
|
|
333
|
+
return false;
|
|
334
|
+
try {
|
|
335
|
+
const result = await memory.forget({
|
|
336
|
+
sessionID: record.sessionID,
|
|
337
|
+
lesson
|
|
338
|
+
}, {
|
|
339
|
+
location: where()
|
|
340
|
+
});
|
|
341
|
+
ctx.ui.toast.show({
|
|
342
|
+
title: "courier",
|
|
343
|
+
message: forgottenLine(record, result.outcome),
|
|
344
|
+
variant: result.outcome === "none" ? "warning" : "success"
|
|
345
|
+
});
|
|
346
|
+
return true;
|
|
347
|
+
} catch (error) {
|
|
348
|
+
fail("Not forgotten")(error);
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
let opened = false;
|
|
353
|
+
async function open() {
|
|
354
|
+
if (opened)
|
|
355
|
+
return;
|
|
356
|
+
opened = true;
|
|
357
|
+
try {
|
|
358
|
+
let viewing;
|
|
359
|
+
let near = [];
|
|
360
|
+
let line;
|
|
361
|
+
for (;; ) {
|
|
362
|
+
const read = await memory.records({}, {
|
|
363
|
+
location: where()
|
|
364
|
+
}).catch(fail("The records could not be read"));
|
|
365
|
+
if (!read)
|
|
366
|
+
return;
|
|
367
|
+
if (!read.memory)
|
|
368
|
+
return say(MEMORY_OFF);
|
|
369
|
+
if (!read.records.length)
|
|
370
|
+
return say(NO_RECORDS);
|
|
371
|
+
const record = viewing === undefined ? undefined : read.records.find((one) => one.sessionID === viewing);
|
|
372
|
+
viewing = record?.sessionID;
|
|
373
|
+
const listed = read.records.map((one) => one.sessionID);
|
|
374
|
+
const current = near.map((sessionID) => read.records.find((one) => one.sessionID === sessionID)).find((one) => one !== undefined);
|
|
375
|
+
const at = line === "lesson" ? record && lessonLine(record) : line;
|
|
376
|
+
const request = await (record ? showRecord(record, at) : showList(read, current));
|
|
377
|
+
line = undefined;
|
|
378
|
+
if (request.kind === "back") {
|
|
379
|
+
if (!record)
|
|
380
|
+
return;
|
|
381
|
+
near = nearby(listed, record.sessionID, false);
|
|
382
|
+
viewing = undefined;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (request.kind === "open") {
|
|
386
|
+
viewing = request.record.sessionID;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const forgotten = await confirmForget(request.record, request.lesson);
|
|
390
|
+
near = nearby(listed, request.record.sessionID, forgotten && !request.lesson);
|
|
391
|
+
if (record && !forgotten)
|
|
392
|
+
line = request.line;
|
|
393
|
+
else if (request.lesson)
|
|
394
|
+
line = "lesson";
|
|
395
|
+
else
|
|
396
|
+
viewing = undefined;
|
|
397
|
+
}
|
|
398
|
+
} finally {
|
|
399
|
+
opened = false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
ctx.keymap.layer(() => ({
|
|
403
|
+
mode: "global",
|
|
404
|
+
commands: [{
|
|
405
|
+
id: MEMORY_COMMAND,
|
|
406
|
+
title: "Courier: memory",
|
|
407
|
+
description: "The records of this project's session trees, newest first under the day each last moved: enter for one in full, x to forget one or its lesson",
|
|
408
|
+
group: "Courier",
|
|
409
|
+
palette: true,
|
|
410
|
+
run: () => void open()
|
|
411
|
+
}]
|
|
412
|
+
}));
|
|
413
|
+
return {
|
|
414
|
+
open
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// src/person/rpc.ts
|
|
419
|
+
import { Rpc as Rpc2 } from "@opencode/plugin/rpc";
|
|
420
|
+
import { Schema as Schema2 } from "effect";
|
|
421
|
+
var Count2 = Schema2.Int.check(Schema2.isGreaterThanOrEqualTo(0));
|
|
422
|
+
var Session = Schema2.Struct({
|
|
423
|
+
sessionID: Schema2.String,
|
|
424
|
+
title: Schema2.String,
|
|
425
|
+
parentID: Schema2.String,
|
|
426
|
+
depth: Count2,
|
|
427
|
+
group: Schema2.optionalKey(Schema2.String)
|
|
428
|
+
});
|
|
429
|
+
var Permission = Schema2.Struct({
|
|
430
|
+
sessionID: Schema2.String,
|
|
431
|
+
requestID: Schema2.String,
|
|
432
|
+
action: Schema2.String,
|
|
433
|
+
resources: Schema2.Array(Schema2.String),
|
|
434
|
+
save: Schema2.optionalKey(Schema2.Array(Schema2.String)),
|
|
435
|
+
passesOn: Schema2.optionalKey(Schema2.Struct({ sessionID: Schema2.String, requestID: Schema2.String }))
|
|
436
|
+
});
|
|
437
|
+
var Prompt = Schema2.Struct({
|
|
438
|
+
question: Schema2.String,
|
|
439
|
+
header: Schema2.String,
|
|
440
|
+
options: Schema2.Array(Schema2.Struct({ label: Schema2.String, description: Schema2.String })),
|
|
441
|
+
multiple: Schema2.optionalKey(Schema2.Boolean)
|
|
442
|
+
});
|
|
443
|
+
var Question = Schema2.Struct({
|
|
444
|
+
sessionID: Schema2.String,
|
|
445
|
+
requestID: Schema2.String,
|
|
446
|
+
questions: Schema2.Array(Prompt),
|
|
447
|
+
stopped: Schema2.optionalKey(Schema2.Boolean)
|
|
41
448
|
});
|
|
42
|
-
var
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
449
|
+
var Asks = Schema2.Struct({
|
|
450
|
+
top: Schema2.String,
|
|
451
|
+
sessions: Schema2.Array(Session),
|
|
452
|
+
permissions: Schema2.Array(Permission),
|
|
453
|
+
questions: Schema2.Array(Question)
|
|
46
454
|
});
|
|
47
|
-
var
|
|
455
|
+
var Answered = Schema2.Struct({
|
|
456
|
+
answered: Schema2.Boolean,
|
|
457
|
+
by: Schema2.optionalKey(Schema2.Literals(["result", "message"])),
|
|
458
|
+
problem: Schema2.optionalKey(Schema2.String)
|
|
459
|
+
});
|
|
460
|
+
var personRpc = Rpc2.define({
|
|
48
461
|
id: "courier-person",
|
|
49
462
|
methods: {
|
|
50
463
|
asks: {
|
|
51
|
-
input:
|
|
52
|
-
output:
|
|
464
|
+
input: Schema2.toStandardSchemaV1(Schema2.Struct({ sessionID: Schema2.String })),
|
|
465
|
+
output: Schema2.toStandardSchemaV1(Asks)
|
|
53
466
|
},
|
|
54
467
|
answer: {
|
|
55
|
-
input:
|
|
56
|
-
output:
|
|
468
|
+
input: Schema2.toStandardSchemaV1(Schema2.Struct({ sessionID: Schema2.String, requestID: Schema2.String, answers: Schema2.Array(Schema2.Array(Schema2.String)) })),
|
|
469
|
+
output: Schema2.toStandardSchemaV1(Answered)
|
|
57
470
|
}
|
|
58
471
|
},
|
|
59
472
|
events: {}
|
|
@@ -103,7 +516,7 @@ function pathOf(asks, sessionID, topTitle, currentTitle = () => {
|
|
|
103
516
|
titles.unshift(currentTitle(at.sessionID) || at.title);
|
|
104
517
|
return [topTitle, ...titles].join(" \u203A ");
|
|
105
518
|
}
|
|
106
|
-
var
|
|
519
|
+
var clip2 = (text, max = 80) => text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
|
107
520
|
function askTitle(ask) {
|
|
108
521
|
switch (ask.kind) {
|
|
109
522
|
case "permission": {
|
|
@@ -114,17 +527,17 @@ function askTitle(ask) {
|
|
|
114
527
|
} = ask.request;
|
|
115
528
|
const what = resources.length ? `${action} ${resources.join(", ")}` : action;
|
|
116
529
|
if (ask.session.depth > 0)
|
|
117
|
-
return
|
|
118
|
-
return
|
|
530
|
+
return clip2(`Permission: ${what}`);
|
|
531
|
+
return clip2(passesOn ? `Permission (top session): courier_answer once for "${what}"` : `Permission (top session): ${what}`);
|
|
119
532
|
}
|
|
120
533
|
case "question": {
|
|
121
534
|
const [first, ...rest] = ask.question.questions;
|
|
122
535
|
const what = first ? `${first.header}: ${first.question}` : ask.question.requestID;
|
|
123
536
|
const more = rest.length ? ` (+${rest.length} more)` : "";
|
|
124
|
-
return
|
|
537
|
+
return clip2(`Question: ${what}${more}`);
|
|
125
538
|
}
|
|
126
539
|
case "form":
|
|
127
|
-
return
|
|
540
|
+
return clip2(`Form: ${ask.form.title}`);
|
|
128
541
|
}
|
|
129
542
|
}
|
|
130
543
|
function askFooter(ask) {
|
|
@@ -384,12 +797,12 @@ function addAsksInbox(ctx, follows) {
|
|
|
384
797
|
} = ask;
|
|
385
798
|
const answers = [];
|
|
386
799
|
for (const [index, prompt] of question.questions.entries()) {
|
|
387
|
-
const
|
|
800
|
+
const count2 = question.questions.length > 1 ? ` (${index + 1}/${question.questions.length})` : "";
|
|
388
801
|
const choices = prompt.options.map((option) => ({
|
|
389
802
|
...option,
|
|
390
803
|
value: option.label
|
|
391
804
|
}));
|
|
392
|
-
const picked = await choose(`${prompt.header}: ${prompt.question}${
|
|
805
|
+
const picked = await choose(`${prompt.header}: ${prompt.question}${count2}`, choices, {
|
|
393
806
|
multiple: !!prompt.multiple,
|
|
394
807
|
own: true
|
|
395
808
|
});
|
|
@@ -797,76 +1210,224 @@ import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
|
797
1210
|
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
798
1211
|
import { setProp as _$setProp } from "@opentui/solid";
|
|
799
1212
|
import { createElement as _$createElement } from "@opentui/solid";
|
|
800
|
-
import {
|
|
1213
|
+
import { MouseButton, RGBA, Timeline } from "@opentui/core";
|
|
1214
|
+
import { batch, createEffect, createMemo, createResource, createRoot, createSignal, For, Index, on, onCleanup, onMount, Show, untrack } from "solid-js";
|
|
1215
|
+
|
|
1216
|
+
// src/tree/mascot.ts
|
|
1217
|
+
var MASCOT_EVERY = 5;
|
|
1218
|
+
var BLINK_EVERY = 11;
|
|
1219
|
+
var GLYPH = {
|
|
1220
|
+
idle: [["........", "........", "..gggg..", "gggggggg"]],
|
|
1221
|
+
spawn: [
|
|
1222
|
+
["........", "...bb...", "g.bbbb.g", "gggggggg"],
|
|
1223
|
+
[".g....g.", "...bb...", "ggbbbbgg", "gggggggg"]
|
|
1224
|
+
],
|
|
1225
|
+
running: [
|
|
1226
|
+
["..g..g..", "...bb...", ".gbbbbg.", "gggggggg"],
|
|
1227
|
+
[".g....g.", "........", ".gbbbbg.", "gggggggg"]
|
|
1228
|
+
],
|
|
1229
|
+
waiting: [
|
|
1230
|
+
[".bbbbbbw", ".bkbbkbw", ".bbaabbm", "ggggggg."],
|
|
1231
|
+
[".bbbbbbb", ".bkbbkbb", ".bbaabbm", "ggggggg."]
|
|
1232
|
+
],
|
|
1233
|
+
stalled: [
|
|
1234
|
+
["..e.....", ".bbbbbb.", ".bkbbkb.", "gbbaabbg"],
|
|
1235
|
+
["..e.e...", ".bbbbbb.", ".bkbbkb.", "gbbaabbg"],
|
|
1236
|
+
["..e.e.e.", ".bbbbbb.", ".bkbbkb.", "gbbaabbg"]
|
|
1237
|
+
],
|
|
1238
|
+
report: [
|
|
1239
|
+
["......aa", "bbbbbbaa", "bkbbkbm.", "bbaabbm."],
|
|
1240
|
+
["s.....aa", "bbbbbbaa", "bkbbkbm.", "bbaabbm."]
|
|
1241
|
+
],
|
|
1242
|
+
gone: [["........", ".mmmmmm.", ".mkmmkm.", "mmmmmmmm"]]
|
|
1243
|
+
};
|
|
1244
|
+
var OPEN_EYES = new Set(["waiting", "report"]);
|
|
1245
|
+
function glyph(state, frame) {
|
|
1246
|
+
const frames = GLYPH[state];
|
|
1247
|
+
const pixels = frames[(frame ?? 0) % frames.length];
|
|
1248
|
+
if (frame === undefined)
|
|
1249
|
+
return pixels;
|
|
1250
|
+
if (state === "idle" && breathesOut(frame))
|
|
1251
|
+
return [...pixels.slice(0, 2), ".gggggg.", ...pixels.slice(3)];
|
|
1252
|
+
return OPEN_EYES.has(state) && blinks(frame) ? pixels.map((row) => row.replaceAll("k", "b")) : pixels;
|
|
1253
|
+
}
|
|
1254
|
+
var breathesOut = (frame) => Math.floor(frame / 2) % 2 === 1;
|
|
1255
|
+
var blinks = (frame) => frame % BLINK_EVERY === BLINK_EVERY - 1;
|
|
1256
|
+
function cells(pixels) {
|
|
1257
|
+
const lines = [];
|
|
1258
|
+
for (let row = 0;row < pixels.length; row += 2) {
|
|
1259
|
+
const top = pixels[row];
|
|
1260
|
+
const bottom = pixels[row + 1] ?? "";
|
|
1261
|
+
lines.push([...top].map((pixel, column) => cell(token(pixel), token(bottom[column]))));
|
|
1262
|
+
}
|
|
1263
|
+
return lines;
|
|
1264
|
+
}
|
|
1265
|
+
var token = (pixel) => pixel && pixel !== "." ? pixel : undefined;
|
|
1266
|
+
function cell(top, bottom) {
|
|
1267
|
+
if (!top && !bottom)
|
|
1268
|
+
return { char: " " };
|
|
1269
|
+
if (!top)
|
|
1270
|
+
return { char: "\u2584", fg: bottom };
|
|
1271
|
+
if (top === bottom)
|
|
1272
|
+
return { char: "\u2588", fg: top };
|
|
1273
|
+
return bottom ? { char: "\u2580", fg: top, bg: bottom } : { char: "\u2580", fg: top };
|
|
1274
|
+
}
|
|
1275
|
+
var SIZE = 16;
|
|
1276
|
+
var SLUMP = [2, 2, 2, 1, 1, 0, 0];
|
|
1277
|
+
function digby(rise, { top = 6, eyes = "open", slump = false, shift = 0, mound = "g" } = {}) {
|
|
1278
|
+
const grid = Array.from({ length: SIZE }, () => new Array(SIZE).fill("."));
|
|
1279
|
+
const span = (row, width, pixel, skip = (_column) => false, by = 0) => {
|
|
1280
|
+
if (row < 0 || row >= SIZE)
|
|
1281
|
+
return;
|
|
1282
|
+
for (let column = (SIZE - width) / 2;column < (SIZE + width) / 2; column++)
|
|
1283
|
+
if (!skip(column))
|
|
1284
|
+
grid[row][column + by] = pixel;
|
|
1285
|
+
};
|
|
1286
|
+
const head = Math.round(10 - 8 * rise);
|
|
1287
|
+
const lean = (at) => slump ? SLUMP[at] : 0;
|
|
1288
|
+
[4, 6, 8, 8, 8, 6, 4].forEach((width, at) => span(head + at, width, "b", undefined, lean(at)));
|
|
1289
|
+
for (let row = head + 7;row <= 11; row++)
|
|
1290
|
+
span(row, 4, "b");
|
|
1291
|
+
const eyeColumns = eyes === "shut" ? [5, 6, 9, 10] : [6, 9];
|
|
1292
|
+
if (eyes !== "none")
|
|
1293
|
+
span(head + 3, 8, "k", (column) => !eyeColumns.includes(column), lean(3));
|
|
1294
|
+
if (eyes === "wide")
|
|
1295
|
+
span(head + 2, 8, "k", (column) => !eyeColumns.includes(column), lean(2));
|
|
1296
|
+
span(head + 4, 2, "a", undefined, lean(4));
|
|
1297
|
+
span(10, SIZE, ".", (column) => column >= 6 && column <= 9);
|
|
1298
|
+
span(10, top, mound, (column) => column >= 6 && column <= 9, shift);
|
|
1299
|
+
span(11, 10, mound, undefined, shift);
|
|
1300
|
+
span(12, 12, mound, undefined, shift);
|
|
1301
|
+
for (const row of [13, 14, 15])
|
|
1302
|
+
span(row, SIZE, mound);
|
|
1303
|
+
return grid.map((row) => row.join(""));
|
|
1304
|
+
}
|
|
1305
|
+
function paint(pixels, spots) {
|
|
1306
|
+
const grid = pixels.map((row) => [...row]);
|
|
1307
|
+
for (const [row, column, drawn] of spots)
|
|
1308
|
+
[...drawn].forEach((pixel, at) => {
|
|
1309
|
+
if (pixel !== "." && grid[row]?.[column + at] !== undefined)
|
|
1310
|
+
grid[row][column + at] = pixel;
|
|
1311
|
+
});
|
|
1312
|
+
return grid.map((row) => row.join(""));
|
|
1313
|
+
}
|
|
1314
|
+
var post = (row, column, length, pixel) => Array.from({ length }, (_, at) => [row + at, column, pixel]);
|
|
1315
|
+
var headTop = (rise) => Math.round(10 - 8 * rise);
|
|
1316
|
+
var running = (rise, shift, dirt) => paint(digby(rise, { shift }), [[headTop(rise), 7, "aa"], ...dirt]);
|
|
1317
|
+
var peek = (column, up) => [
|
|
1318
|
+
...up ? [[8, column, ".b."], [9, column, "bkb"]] : [[9, column, ".b."]],
|
|
1319
|
+
[10, column, "gbg"],
|
|
1320
|
+
[11, column, "ggg"]
|
|
1321
|
+
];
|
|
1322
|
+
var spawn = (up, dirt) => paint(digby(0.1), [...peek(0, up), ...peek(13, up), [12, 0, "gg"], [12, 14, "gg"], ...dirt]);
|
|
1323
|
+
var sign = (shown) => [...shown ? [[2, 13, "www"], [3, 13, "wkw"], [4, 13, "www"]] : [], ...post(5, 14, 7, "m")];
|
|
1324
|
+
var waiting = (shown) => (open) => paint(digby(1, { eyes: open ? "wide" : "none" }), [[8, 4, "mm"], [8, 10, "mm"], ...sign(shown)]);
|
|
1325
|
+
var stalled = (dots) => () => paint(digby(0.7, { eyes: "shut", slump: true }), [[5, 0, dots]]);
|
|
1326
|
+
var report = (rise, spark) => (open) => paint(digby(rise, { eyes: open ? "open" : "none" }), [
|
|
1327
|
+
[0, 12, "aaaa"],
|
|
1328
|
+
[1, 12, "akka"],
|
|
1329
|
+
[2, 12, "aaaa"],
|
|
1330
|
+
...post(3, 13, 8, "m"),
|
|
1331
|
+
...spark.map((row, at) => [1 + at, 0, row])
|
|
1332
|
+
]);
|
|
1333
|
+
var ART = {
|
|
1334
|
+
spawn: [
|
|
1335
|
+
() => spawn(true, [[3, 1, "m"], [5, 3, "m"], [2, 14, "m"], [5, 12, "m"]]),
|
|
1336
|
+
() => spawn(false, [[1, 0, "m"], [4, 2, "m"], [3, 15, "m"], [6, 13, "m"]])
|
|
1337
|
+
],
|
|
1338
|
+
running: [
|
|
1339
|
+
() => running(0.22, 0, [[5, 2, "m"], [7, 1, "m"], [4, 13, "m"], [6, 14, "m"]]),
|
|
1340
|
+
() => running(0.12, 1, [[3, 1, "m"], [6, 3, "m"], [5, 12, "m"], [3, 15, "m"]])
|
|
1341
|
+
],
|
|
1342
|
+
waiting: [waiting(true), waiting(false)],
|
|
1343
|
+
stalled: [stalled("e.e.e"), stalled("e"), stalled("e.e")],
|
|
1344
|
+
report: [report(1, [".s.", "sss", ".s."]), report(1.25, ["s.s", ".s.", "s.s"]), report(1.1, [".s.", "sss", ".s."])],
|
|
1345
|
+
gone: [() => digby(-0.3, { mound: "m" })]
|
|
1346
|
+
};
|
|
1347
|
+
var Z_LINES = [3, 2, 1, undefined];
|
|
1348
|
+
function idleArt(frame) {
|
|
1349
|
+
const lines = cells(digby(-0.3, { top: frame !== undefined && breathesOut(frame) ? 8 : 6 }));
|
|
1350
|
+
const line = Z_LINES[(frame ?? 0) % Z_LINES.length];
|
|
1351
|
+
if (line === undefined)
|
|
1352
|
+
return lines;
|
|
1353
|
+
return lines.map((cellsOf, at) => at === line ? cellsOf.map((one, column) => column === 13 ? { char: "z", fg: "m" } : one) : cellsOf);
|
|
1354
|
+
}
|
|
1355
|
+
function art(state, frame) {
|
|
1356
|
+
if (state === "idle")
|
|
1357
|
+
return idleArt(frame);
|
|
1358
|
+
const frames = ART[state];
|
|
1359
|
+
return cells(frames[(frame ?? 0) % frames.length](!(frame !== undefined && OPEN_EYES.has(state) && blinks(frame))));
|
|
1360
|
+
}
|
|
801
1361
|
|
|
802
1362
|
// src/tree/rpc.ts
|
|
803
|
-
import { Rpc as
|
|
804
|
-
import { Schema as
|
|
805
|
-
var
|
|
806
|
-
var Group =
|
|
807
|
-
var Summary =
|
|
808
|
-
running:
|
|
809
|
-
waiting:
|
|
810
|
-
idle:
|
|
811
|
-
stalled:
|
|
812
|
-
groups:
|
|
1363
|
+
import { Rpc as Rpc3 } from "@opencode/plugin/rpc";
|
|
1364
|
+
import { Schema as Schema3 } from "effect";
|
|
1365
|
+
var Count3 = Schema3.Int.check(Schema3.isGreaterThanOrEqualTo(0));
|
|
1366
|
+
var Group = Schema3.Struct({ name: Schema3.String, reported: Count3, members: Count3 });
|
|
1367
|
+
var Summary = Schema3.Struct({
|
|
1368
|
+
running: Count3,
|
|
1369
|
+
waiting: Count3,
|
|
1370
|
+
idle: Count3,
|
|
1371
|
+
stalled: Schema3.optionalKey(Count3),
|
|
1372
|
+
groups: Schema3.Array(Group)
|
|
813
1373
|
});
|
|
814
|
-
var Wait =
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1374
|
+
var Wait = Schema3.Union([
|
|
1375
|
+
Schema3.Struct({ type: Schema3.Literal("permission"), action: Schema3.String, resources: Schema3.Array(Schema3.String) }),
|
|
1376
|
+
Schema3.Struct({ type: Schema3.Literal("question"), question: Schema3.String }),
|
|
1377
|
+
Schema3.Struct({ type: Schema3.Literal("form"), title: Schema3.String })
|
|
818
1378
|
]);
|
|
819
|
-
var Row =
|
|
820
|
-
sessionID:
|
|
821
|
-
parentID:
|
|
822
|
-
title:
|
|
823
|
-
depth:
|
|
824
|
-
state:
|
|
825
|
-
stalled:
|
|
826
|
-
outcome:
|
|
827
|
-
stopped:
|
|
828
|
-
report:
|
|
829
|
-
group:
|
|
830
|
-
isolated:
|
|
831
|
-
created:
|
|
832
|
-
idle:
|
|
833
|
-
cost:
|
|
834
|
-
|
|
835
|
-
|
|
1379
|
+
var Row = Schema3.Struct({
|
|
1380
|
+
sessionID: Schema3.String,
|
|
1381
|
+
parentID: Schema3.String,
|
|
1382
|
+
title: Schema3.String,
|
|
1383
|
+
depth: Count3,
|
|
1384
|
+
state: Schema3.Literals(["running", "waiting", "idle", "stalled", "gone"]),
|
|
1385
|
+
stalled: Schema3.optionalKey(Schema3.Number),
|
|
1386
|
+
outcome: Schema3.optionalKey(Schema3.Literals(["succeeded", "failed", "interrupted"])),
|
|
1387
|
+
stopped: Schema3.optionalKey(Schema3.Boolean),
|
|
1388
|
+
report: Schema3.optionalKey(Schema3.Struct({ state: Schema3.Literals(["owed", "reported", "ended"]), status: Schema3.optionalKey(Schema3.String), progressed: Schema3.Boolean })),
|
|
1389
|
+
group: Schema3.optionalKey(Schema3.Struct({ name: Schema3.String, standing: Schema3.String })),
|
|
1390
|
+
isolated: Schema3.Boolean,
|
|
1391
|
+
created: Schema3.Number,
|
|
1392
|
+
idle: Schema3.optionalKey(Schema3.Number),
|
|
1393
|
+
cost: Schema3.optionalKey(Schema3.Number),
|
|
1394
|
+
context: Schema3.optionalKey(Schema3.Number),
|
|
1395
|
+
tool: Schema3.optionalKey(Schema3.Struct({ name: Schema3.String, at: Schema3.Number })),
|
|
1396
|
+
waits: Schema3.Array(Wait)
|
|
836
1397
|
});
|
|
837
|
-
var Tree =
|
|
838
|
-
rows:
|
|
839
|
-
truncated:
|
|
840
|
-
stalled:
|
|
1398
|
+
var Tree = Schema3.Struct({
|
|
1399
|
+
rows: Schema3.Array(Row),
|
|
1400
|
+
truncated: Schema3.optionalKey(Count3),
|
|
1401
|
+
stalled: Schema3.optionalKey(Schema3.Number)
|
|
841
1402
|
});
|
|
842
|
-
var Stopped =
|
|
843
|
-
stopped:
|
|
844
|
-
cancelled:
|
|
1403
|
+
var Stopped = Schema3.Struct({
|
|
1404
|
+
stopped: Schema3.Array(Schema3.Struct({ sessionID: Schema3.String, title: Schema3.String, outcome: Schema3.String })),
|
|
1405
|
+
cancelled: Count3
|
|
845
1406
|
});
|
|
846
|
-
var
|
|
847
|
-
|
|
848
|
-
|
|
1407
|
+
var Forgotten2 = Schema3.Union([
|
|
1408
|
+
Schema3.Struct({ outcome: Schema3.Literals(["forgotten", "removed", "gone"]) }),
|
|
1409
|
+
Schema3.Struct({ outcome: Schema3.Literal("kept"), reason: Schema3.String })
|
|
849
1410
|
]);
|
|
850
|
-
var treeRpc =
|
|
1411
|
+
var treeRpc = Rpc3.define({
|
|
851
1412
|
id: "courier",
|
|
852
1413
|
methods: {
|
|
853
1414
|
summary: {
|
|
854
|
-
input:
|
|
855
|
-
output:
|
|
1415
|
+
input: Schema3.toStandardSchemaV1(Schema3.Struct({ sessionID: Schema3.String })),
|
|
1416
|
+
output: Schema3.toStandardSchemaV1(Summary)
|
|
856
1417
|
},
|
|
857
1418
|
tree: {
|
|
858
|
-
input:
|
|
859
|
-
output:
|
|
1419
|
+
input: Schema3.toStandardSchemaV1(Schema3.Struct({ sessionID: Schema3.String })),
|
|
1420
|
+
output: Schema3.toStandardSchemaV1(Tree)
|
|
860
1421
|
},
|
|
861
1422
|
stop: {
|
|
862
|
-
input:
|
|
863
|
-
output:
|
|
864
|
-
errors: { refused:
|
|
1423
|
+
input: Schema3.toStandardSchemaV1(Schema3.Struct({ sessionID: Schema3.String, by: Schema3.String })),
|
|
1424
|
+
output: Schema3.toStandardSchemaV1(Stopped),
|
|
1425
|
+
errors: { refused: Schema3.toStandardSchemaV1(Schema3.Struct({})) }
|
|
865
1426
|
},
|
|
866
1427
|
cleanup: {
|
|
867
|
-
input:
|
|
868
|
-
output:
|
|
869
|
-
errors: { refused:
|
|
1428
|
+
input: Schema3.toStandardSchemaV1(Schema3.Struct({ sessionID: Schema3.String, by: Schema3.String })),
|
|
1429
|
+
output: Schema3.toStandardSchemaV1(Forgotten2),
|
|
1430
|
+
errors: { refused: Schema3.toStandardSchemaV1(Schema3.Struct({})) }
|
|
870
1431
|
}
|
|
871
1432
|
},
|
|
872
1433
|
events: {}
|
|
@@ -879,6 +1440,176 @@ var SETTLE_MS2 = 250;
|
|
|
879
1440
|
var PANEL_SETTLE_MS = 1000;
|
|
880
1441
|
var PANEL = "courier-tree";
|
|
881
1442
|
var TICK_MS = 1000;
|
|
1443
|
+
var FRAME_MS = 90;
|
|
1444
|
+
var FLASH_MS = 1500;
|
|
1445
|
+
var BAR_MS = 450;
|
|
1446
|
+
function animationsOn(options) {
|
|
1447
|
+
return options?.animations !== false;
|
|
1448
|
+
}
|
|
1449
|
+
function createMotion(enabled) {
|
|
1450
|
+
const [frame, setFrame] = createSignal(0);
|
|
1451
|
+
const [keepers, setKeepers] = createSignal([]);
|
|
1452
|
+
const [moving, setMoving] = createSignal([]);
|
|
1453
|
+
const cadence = createMemo(() => {
|
|
1454
|
+
if (!enabled)
|
|
1455
|
+
return;
|
|
1456
|
+
if (moving().length)
|
|
1457
|
+
return 1;
|
|
1458
|
+
const wanted = keepers().filter((one) => one.when());
|
|
1459
|
+
return wanted.length ? Math.min(...wanted.map((one) => one.every)) : undefined;
|
|
1460
|
+
});
|
|
1461
|
+
createEffect(() => {
|
|
1462
|
+
const every = cadence();
|
|
1463
|
+
if (every === undefined)
|
|
1464
|
+
return;
|
|
1465
|
+
const timer = setInterval(() => batch(() => {
|
|
1466
|
+
const now = untrack(moving);
|
|
1467
|
+
for (const one of now)
|
|
1468
|
+
one.step(FRAME_MS * every);
|
|
1469
|
+
setFrame((count2) => count2 + every);
|
|
1470
|
+
const left = now.filter((one) => !one.done());
|
|
1471
|
+
if (left.length !== now.length)
|
|
1472
|
+
setMoving(left);
|
|
1473
|
+
}), FRAME_MS * every);
|
|
1474
|
+
onCleanup(() => clearInterval(timer));
|
|
1475
|
+
});
|
|
1476
|
+
const tween = (from, to, duration2, ease) => {
|
|
1477
|
+
if (!enabled || from === to)
|
|
1478
|
+
return {
|
|
1479
|
+
value: () => to,
|
|
1480
|
+
done: () => true
|
|
1481
|
+
};
|
|
1482
|
+
const target = {
|
|
1483
|
+
value: from
|
|
1484
|
+
};
|
|
1485
|
+
const timeline = new Timeline({
|
|
1486
|
+
duration: duration2,
|
|
1487
|
+
autoplay: false
|
|
1488
|
+
});
|
|
1489
|
+
timeline.add(target, {
|
|
1490
|
+
duration: duration2,
|
|
1491
|
+
ease,
|
|
1492
|
+
value: to
|
|
1493
|
+
});
|
|
1494
|
+
timeline.play();
|
|
1495
|
+
let elapsed = 0;
|
|
1496
|
+
const one = {
|
|
1497
|
+
value: () => {
|
|
1498
|
+
frame();
|
|
1499
|
+
return target.value;
|
|
1500
|
+
},
|
|
1501
|
+
done: () => {
|
|
1502
|
+
frame();
|
|
1503
|
+
return elapsed >= duration2;
|
|
1504
|
+
},
|
|
1505
|
+
step: (ms) => {
|
|
1506
|
+
elapsed += ms;
|
|
1507
|
+
timeline.update(ms);
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
setMoving((all) => [...all, one]);
|
|
1511
|
+
return one;
|
|
1512
|
+
};
|
|
1513
|
+
return {
|
|
1514
|
+
enabled,
|
|
1515
|
+
frame,
|
|
1516
|
+
animates: (when, every = 1) => setKeepers((all) => [...all, {
|
|
1517
|
+
when,
|
|
1518
|
+
every
|
|
1519
|
+
}]),
|
|
1520
|
+
tween
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
function createFlashes(motion, rows) {
|
|
1524
|
+
const [flashes, setFlashes] = createSignal(new Map);
|
|
1525
|
+
let seen;
|
|
1526
|
+
createEffect(() => {
|
|
1527
|
+
const read = rows();
|
|
1528
|
+
const before = seen;
|
|
1529
|
+
seen = new Map(read.map((row) => [row.sessionID, row.report?.state]));
|
|
1530
|
+
if (!before || !motion.enabled)
|
|
1531
|
+
return;
|
|
1532
|
+
const arrived = read.filter((row) => row.report?.state === "reported" && before.has(row.sessionID) && before.get(row.sessionID) !== "reported");
|
|
1533
|
+
if (!arrived.length)
|
|
1534
|
+
return;
|
|
1535
|
+
const next = new Map([...untrack(flashes)].filter(([, one]) => !untrack(one.done)));
|
|
1536
|
+
for (const row of arrived)
|
|
1537
|
+
next.set(row.sessionID, motion.tween(1, 0, FLASH_MS, "outQuad"));
|
|
1538
|
+
setFlashes(next);
|
|
1539
|
+
});
|
|
1540
|
+
const strength = (sessionID) => {
|
|
1541
|
+
const one = flashes().get(sessionID);
|
|
1542
|
+
return one && !one.done() ? one.value() : 0;
|
|
1543
|
+
};
|
|
1544
|
+
const reported = () => [...flashes().values()].some((one) => !one.done());
|
|
1545
|
+
return {
|
|
1546
|
+
strength,
|
|
1547
|
+
reported
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
function createBarFill(motion, counts) {
|
|
1551
|
+
const target = createMemo(() => filledCells(counts()));
|
|
1552
|
+
const [easing, setEasing] = createSignal();
|
|
1553
|
+
let last;
|
|
1554
|
+
createEffect(() => {
|
|
1555
|
+
const cells2 = target();
|
|
1556
|
+
const shown = counts().started > 0;
|
|
1557
|
+
if (last && cells2 !== last.cells) {
|
|
1558
|
+
const from = untrack(() => easing()?.value()) ?? last.cells;
|
|
1559
|
+
setEasing(last.shown ? motion.tween(from, cells2, BAR_MS, "outQuad") : undefined);
|
|
1560
|
+
}
|
|
1561
|
+
last = {
|
|
1562
|
+
cells: cells2,
|
|
1563
|
+
shown
|
|
1564
|
+
};
|
|
1565
|
+
});
|
|
1566
|
+
return () => easing()?.value() ?? target();
|
|
1567
|
+
}
|
|
1568
|
+
function createSpawns(motion, rows) {
|
|
1569
|
+
const [hold, setHold] = createSignal();
|
|
1570
|
+
let seen;
|
|
1571
|
+
createEffect(() => {
|
|
1572
|
+
const read = rows();
|
|
1573
|
+
if (!read)
|
|
1574
|
+
return;
|
|
1575
|
+
const before = seen;
|
|
1576
|
+
seen = new Set(read.map((row) => row.sessionID));
|
|
1577
|
+
if (before && motion.enabled && read.some((row) => !before.has(row.sessionID)))
|
|
1578
|
+
setHold(motion.tween(1, 0, FLASH_MS, "linear"));
|
|
1579
|
+
});
|
|
1580
|
+
return () => {
|
|
1581
|
+
const one = hold();
|
|
1582
|
+
return one !== undefined && !one.done();
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
function mascotState(rows, {
|
|
1586
|
+
reported = false,
|
|
1587
|
+
spawned = false
|
|
1588
|
+
} = {}) {
|
|
1589
|
+
if (reported)
|
|
1590
|
+
return "report";
|
|
1591
|
+
if (spawned)
|
|
1592
|
+
return "spawn";
|
|
1593
|
+
if (rows.some((row) => row.state === "waiting"))
|
|
1594
|
+
return "waiting";
|
|
1595
|
+
if (rows.some((row) => row.state === "stalled"))
|
|
1596
|
+
return "stalled";
|
|
1597
|
+
if (rows.some((row) => row.state === "running"))
|
|
1598
|
+
return "running";
|
|
1599
|
+
return rows.length && rows.every((row) => row.state === "gone") ? "gone" : "idle";
|
|
1600
|
+
}
|
|
1601
|
+
function countsLine(rows) {
|
|
1602
|
+
const count2 = (state) => rows.filter((row) => row.state === state).length;
|
|
1603
|
+
return [count2("waiting") ? `${count2("waiting")} waiting on you` : "", count2("stalled") ? `${count2("stalled")} stalled` : "", count2("running") ? `${count2("running")} running` : "", count2("idle") ? `${count2("idle")} idle` : ""].filter(Boolean).join(" \xB7 ");
|
|
1604
|
+
}
|
|
1605
|
+
var GLYPH_ROWS = 12;
|
|
1606
|
+
var GLYPH_COLUMNS = 54;
|
|
1607
|
+
var ART_ROWS = 14;
|
|
1608
|
+
var BELOW_ROWS = 10;
|
|
1609
|
+
function blend(tint, rest, amount) {
|
|
1610
|
+
const mix = (a, b) => b + (a - b) * amount;
|
|
1611
|
+
return RGBA.fromValues(mix(tint.r, rest.r), mix(tint.g, rest.g), mix(tint.b, rest.b), mix(tint.a, rest.a));
|
|
1612
|
+
}
|
|
882
1613
|
function summaryLine(summary, maxWidth = Infinity) {
|
|
883
1614
|
const groups = summary.groups.map((group) => `group ${group.name} ${group.reported}/${group.members}`);
|
|
884
1615
|
const parts = [summary.waiting ? `${summary.waiting} waiting on you` : "", summary.stalled ? `${summary.stalled} stalled` : "", summary.running ? `${summary.running} running` : "", ...groups, summary.idle ? `${summary.idle} idle` : ""].filter(Boolean);
|
|
@@ -893,14 +1624,14 @@ function summaryLine(summary, maxWidth = Infinity) {
|
|
|
893
1624
|
return line(kept);
|
|
894
1625
|
return parts[0];
|
|
895
1626
|
}
|
|
896
|
-
function
|
|
1627
|
+
function duration2(ms) {
|
|
897
1628
|
const seconds = Math.max(0, Math.floor(ms / 1000));
|
|
898
|
-
const
|
|
1629
|
+
const pad2 = (value) => String(value).padStart(2, "0");
|
|
899
1630
|
if (seconds < 60)
|
|
900
1631
|
return `${seconds}s`;
|
|
901
1632
|
if (seconds < 3600)
|
|
902
|
-
return `${Math.floor(seconds / 60)}m ${
|
|
903
|
-
return `${Math.floor(seconds / 3600)}h ${
|
|
1633
|
+
return `${Math.floor(seconds / 60)}m ${pad2(seconds % 60)}s`;
|
|
1634
|
+
return `${Math.floor(seconds / 3600)}h ${pad2(Math.floor(seconds / 60) % 60)}m`;
|
|
904
1635
|
}
|
|
905
1636
|
function parts(rows, parentID) {
|
|
906
1637
|
const children = rows.filter((row) => row.parentID === parentID);
|
|
@@ -909,12 +1640,15 @@ function parts(rows, parentID) {
|
|
|
909
1640
|
started: children.length
|
|
910
1641
|
};
|
|
911
1642
|
}
|
|
912
|
-
function
|
|
1643
|
+
function filledCells({
|
|
913
1644
|
reported,
|
|
914
1645
|
started
|
|
915
1646
|
}, width = 10) {
|
|
916
|
-
|
|
917
|
-
|
|
1647
|
+
return started ? Math.round(reported / started * width) : 0;
|
|
1648
|
+
}
|
|
1649
|
+
function bar(counts, width = 10, filled = filledCells(counts, width)) {
|
|
1650
|
+
const cells2 = Math.max(0, Math.min(width, Math.round(filled)));
|
|
1651
|
+
return `[${"\u2588".repeat(cells2)}${"\u2591".repeat(width - cells2)}] ${counts.reported}/${counts.started} reported`;
|
|
918
1652
|
}
|
|
919
1653
|
function waitLine(wait) {
|
|
920
1654
|
if (wait.type === "permission")
|
|
@@ -922,7 +1656,66 @@ function waitLine(wait) {
|
|
|
922
1656
|
return wait.type === "form" ? `shows a form: ${wait.title}` : `asks: ${wait.question}`;
|
|
923
1657
|
}
|
|
924
1658
|
function statusLine(row) {
|
|
925
|
-
return
|
|
1659
|
+
return statusParts(row).map((part) => part.text).join(" \xB7 ");
|
|
1660
|
+
}
|
|
1661
|
+
function statusParts(row) {
|
|
1662
|
+
const parts2 = [{
|
|
1663
|
+
text: stateWord(row),
|
|
1664
|
+
kind: "state"
|
|
1665
|
+
}, {
|
|
1666
|
+
text: reportWords(row.report, row.stopped),
|
|
1667
|
+
kind: "report"
|
|
1668
|
+
}, {
|
|
1669
|
+
text: row.group ? `group ${row.group.name} ${row.group.standing}` : "",
|
|
1670
|
+
kind: "plain"
|
|
1671
|
+
}, {
|
|
1672
|
+
text: row.isolated ? "isolated" : "",
|
|
1673
|
+
kind: "plain"
|
|
1674
|
+
}];
|
|
1675
|
+
return parts2.filter((part) => part.text);
|
|
1676
|
+
}
|
|
1677
|
+
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1678
|
+
function rowMark(row, theme, frame) {
|
|
1679
|
+
const {
|
|
1680
|
+
error,
|
|
1681
|
+
warning,
|
|
1682
|
+
success,
|
|
1683
|
+
info
|
|
1684
|
+
} = theme.text.feedback;
|
|
1685
|
+
if (row.state === "gone")
|
|
1686
|
+
return {
|
|
1687
|
+
mark: " ",
|
|
1688
|
+
color: undefined
|
|
1689
|
+
};
|
|
1690
|
+
if (row.state === "stalled")
|
|
1691
|
+
return {
|
|
1692
|
+
mark: "!",
|
|
1693
|
+
color: error.base
|
|
1694
|
+
};
|
|
1695
|
+
if (row.state === "waiting")
|
|
1696
|
+
return {
|
|
1697
|
+
mark: "\u25CF",
|
|
1698
|
+
color: warning.base
|
|
1699
|
+
};
|
|
1700
|
+
if (row.state === "running")
|
|
1701
|
+
return {
|
|
1702
|
+
mark: frame === undefined ? "\u2022" : SPINNER[frame % SPINNER.length],
|
|
1703
|
+
color: info.base
|
|
1704
|
+
};
|
|
1705
|
+
if (!row.stopped && (row.outcome === "failed" || row.outcome === "interrupted"))
|
|
1706
|
+
return {
|
|
1707
|
+
mark: "\u2717",
|
|
1708
|
+
color: error.base
|
|
1709
|
+
};
|
|
1710
|
+
if (row.report?.state === "reported")
|
|
1711
|
+
return {
|
|
1712
|
+
mark: "\u2713",
|
|
1713
|
+
color: success.base
|
|
1714
|
+
};
|
|
1715
|
+
return {
|
|
1716
|
+
mark: " ",
|
|
1717
|
+
color: undefined
|
|
1718
|
+
};
|
|
926
1719
|
}
|
|
927
1720
|
function stateWord(row) {
|
|
928
1721
|
if (row.state === "waiting")
|
|
@@ -935,21 +1728,23 @@ function stateWord(row) {
|
|
|
935
1728
|
return row.outcome;
|
|
936
1729
|
return row.state;
|
|
937
1730
|
}
|
|
938
|
-
function reportWords(
|
|
939
|
-
if (!
|
|
1731
|
+
function reportWords(report2, stopped) {
|
|
1732
|
+
if (!report2)
|
|
940
1733
|
return "";
|
|
941
|
-
if (
|
|
1734
|
+
if (report2.state === "owed")
|
|
942
1735
|
return "owes a report";
|
|
943
|
-
if (
|
|
1736
|
+
if (report2.state === "ended")
|
|
944
1737
|
return stopped ? "" : "ended without a report";
|
|
945
|
-
return ["reported",
|
|
1738
|
+
return ["reported", report2.status].filter(Boolean).join(" ");
|
|
946
1739
|
}
|
|
947
|
-
var counted = (
|
|
948
|
-
|
|
949
|
-
|
|
1740
|
+
var counted = (count2, noun) => `${count2} ${noun}${count2 === 1 ? "" : "s"}`;
|
|
1741
|
+
var CONTEXT_WARN = 0.8;
|
|
1742
|
+
function activityParts(row, now) {
|
|
1743
|
+
return [`ran ${duration2((row.idle ?? now) - row.created)}`, row.tool ? `last ${row.tool.name} ${duration2(now - row.tool.at)} ago` : "no tool call yet", row.context !== undefined ? `ctx ${Math.round(row.context * 100)}%` : "", row.cost !== undefined ? `$${row.cost.toFixed(2)}` : ""].filter(Boolean);
|
|
950
1744
|
}
|
|
1745
|
+
var activityLine = (row, now) => activityParts(row, now).join(" \xB7 ");
|
|
951
1746
|
function stalledLine(since, now) {
|
|
952
|
-
return `a message waits in its inbox since ${
|
|
1747
|
+
return `a message waits in its inbox since ${duration2(now - since)}, woken again in vain`;
|
|
953
1748
|
}
|
|
954
1749
|
function detailLines(row, rows, now) {
|
|
955
1750
|
const started = parts(rows, row.sessionID);
|
|
@@ -972,7 +1767,7 @@ function courierOf(ctx) {
|
|
|
972
1767
|
var REREAD_MS = 15000;
|
|
973
1768
|
function changes(ctx, types, settle) {
|
|
974
1769
|
const [changed, setChanged] = createSignal(0);
|
|
975
|
-
const reread = setInterval(() => setChanged((
|
|
1770
|
+
const reread = setInterval(() => setChanged((count2) => count2 + 1), REREAD_MS);
|
|
976
1771
|
let settling;
|
|
977
1772
|
const unlisten = ctx.data.listen(({
|
|
978
1773
|
details
|
|
@@ -981,7 +1776,7 @@ function changes(ctx, types, settle) {
|
|
|
981
1776
|
return;
|
|
982
1777
|
settling = setTimeout(() => {
|
|
983
1778
|
settling = undefined;
|
|
984
|
-
setChanged((
|
|
1779
|
+
setChanged((count2) => count2 + 1);
|
|
985
1780
|
}, settle);
|
|
986
1781
|
});
|
|
987
1782
|
return [changed, () => {
|
|
@@ -1107,8 +1902,8 @@ async function confirmStop(ctx, courier, location, by, target) {
|
|
|
1107
1902
|
}
|
|
1108
1903
|
return true;
|
|
1109
1904
|
}
|
|
1110
|
-
function
|
|
1111
|
-
const name = `"${
|
|
1905
|
+
function forgottenLine2(title, result) {
|
|
1906
|
+
const name = `"${oneLine3(title)}"`;
|
|
1112
1907
|
if (result.outcome === "kept")
|
|
1113
1908
|
return `Kept ${name} and its worktree: ${result.reason}`;
|
|
1114
1909
|
if (result.outcome === "removed")
|
|
@@ -1119,7 +1914,7 @@ async function confirmForget(ctx, courier, location, by, target, done) {
|
|
|
1119
1914
|
const what = target.isolated ? "Its worktree is removed unless it has uncommitted changes or commits found nowhere else." : "It leaves this tree and courier_children.";
|
|
1120
1915
|
const confirmed = await ctx.ui.dialog.confirm({
|
|
1121
1916
|
title: "Forget session",
|
|
1122
|
-
message: `Forget "${
|
|
1917
|
+
message: `Forget "${oneLine3(target.title)}" (${target.sessionID})? ${what}`,
|
|
1123
1918
|
label: {
|
|
1124
1919
|
confirm: "Forget",
|
|
1125
1920
|
cancel: "Keep"
|
|
@@ -1136,7 +1931,7 @@ async function confirmForget(ctx, courier, location, by, target, done) {
|
|
|
1136
1931
|
});
|
|
1137
1932
|
ctx.ui.toast.show({
|
|
1138
1933
|
title: "courier",
|
|
1139
|
-
message:
|
|
1934
|
+
message: forgottenLine2(target.title, result),
|
|
1140
1935
|
variant: result.outcome === "kept" ? "warning" : "success"
|
|
1141
1936
|
});
|
|
1142
1937
|
if (result.outcome !== "kept")
|
|
@@ -1152,7 +1947,25 @@ async function confirmForget(ctx, courier, location, by, target, done) {
|
|
|
1152
1947
|
}
|
|
1153
1948
|
var FOCUS_PROMPT = "pane.focus.left";
|
|
1154
1949
|
var SPLIT_WIDTH = 108;
|
|
1155
|
-
var
|
|
1950
|
+
var oneLine3 = (title) => title.replace(/[\r\n]+/g, " ");
|
|
1951
|
+
var digbyColours = (theme) => ({
|
|
1952
|
+
b: theme.text.base,
|
|
1953
|
+
k: theme.background.base,
|
|
1954
|
+
g: theme.background.raised.max,
|
|
1955
|
+
m: theme.text.muted,
|
|
1956
|
+
a: theme.text.action.primary.selected,
|
|
1957
|
+
w: theme.text.feedback.warning.base,
|
|
1958
|
+
e: theme.text.feedback.error.base,
|
|
1959
|
+
s: theme.text.feedback.success.base
|
|
1960
|
+
});
|
|
1961
|
+
function restBackground(theme, {
|
|
1962
|
+
selected,
|
|
1963
|
+
hovered
|
|
1964
|
+
}) {
|
|
1965
|
+
if (selected)
|
|
1966
|
+
return theme.background.raised.high;
|
|
1967
|
+
return hovered ? theme.background.raised.base : undefined;
|
|
1968
|
+
}
|
|
1156
1969
|
function TreePanel(props) {
|
|
1157
1970
|
const {
|
|
1158
1971
|
ctx,
|
|
@@ -1246,13 +2059,25 @@ function TreePanel(props) {
|
|
|
1246
2059
|
if (next)
|
|
1247
2060
|
setSelectedID(next.sessionID);
|
|
1248
2061
|
};
|
|
2062
|
+
const openRow = (row) => ctx.ui.router.navigate({
|
|
2063
|
+
type: "session",
|
|
2064
|
+
sessionID: row.sessionID
|
|
2065
|
+
});
|
|
1249
2066
|
const open = () => {
|
|
1250
2067
|
const row = current();
|
|
1251
2068
|
if (row)
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
2069
|
+
openRow(row);
|
|
2070
|
+
};
|
|
2071
|
+
const [hovered, setHovered] = createSignal();
|
|
2072
|
+
createEffect(() => {
|
|
2073
|
+
const id = hovered();
|
|
2074
|
+
if (id !== undefined && !rows().some((row) => row.sessionID === id))
|
|
2075
|
+
setHovered(undefined);
|
|
2076
|
+
});
|
|
2077
|
+
let pressed;
|
|
2078
|
+
const click = (row) => {
|
|
2079
|
+
setSelectedID(row.sessionID);
|
|
2080
|
+
openRow(row);
|
|
1256
2081
|
};
|
|
1257
2082
|
const stopSelected = () => {
|
|
1258
2083
|
const row = current();
|
|
@@ -1270,7 +2095,7 @@ function TreePanel(props) {
|
|
|
1270
2095
|
if (row.state !== "gone")
|
|
1271
2096
|
return ctx.ui.toast.show({
|
|
1272
2097
|
title: "courier",
|
|
1273
|
-
message: `x forgets only a gone session; "${
|
|
2098
|
+
message: `x forgets only a gone session; "${oneLine3(row.title)}" is ${row.state}.`,
|
|
1274
2099
|
variant: "warning"
|
|
1275
2100
|
});
|
|
1276
2101
|
confirmForget(ctx, courier, location, view.root, {
|
|
@@ -1335,6 +2160,61 @@ function TreePanel(props) {
|
|
|
1335
2160
|
}]
|
|
1336
2161
|
}));
|
|
1337
2162
|
const root = () => parts(rows(), view.root);
|
|
2163
|
+
const motion = createMotion(props.animations);
|
|
2164
|
+
motion.animates(() => rows().some((row) => row.state === "running"));
|
|
2165
|
+
const flashes = createFlashes(motion, rows);
|
|
2166
|
+
const filled = createBarFill(motion, root);
|
|
2167
|
+
const frame = () => motion.enabled ? motion.frame() : undefined;
|
|
2168
|
+
const spawned = createSpawns(motion, () => latest()?.tree?.rows);
|
|
2169
|
+
const [size, setSize] = createSignal({
|
|
2170
|
+
width: 0,
|
|
2171
|
+
height: 0
|
|
2172
|
+
});
|
|
2173
|
+
const height = () => size().height;
|
|
2174
|
+
const empty = () => Boolean(latest()?.tree && !rows().length);
|
|
2175
|
+
const glyphShown = () => height() >= GLYPH_ROWS && size().width >= GLYPH_COLUMNS;
|
|
2176
|
+
const artShown = () => empty() && height() >= ART_ROWS;
|
|
2177
|
+
const [listHeight, setListHeight] = createSignal(0);
|
|
2178
|
+
const treeLines = () => rows().reduce((lines, row) => lines + 1 + detailLines(row, rows(), now()).length, 0);
|
|
2179
|
+
const belowShown = () => rows().length > 0 && listHeight() - treeLines() >= BELOW_ROWS;
|
|
2180
|
+
const headerGlyph = () => glyphShown() && !artShown() && !belowShown();
|
|
2181
|
+
const mascot = createMemo(() => mascotState(rows(), {
|
|
2182
|
+
reported: flashes.reported(),
|
|
2183
|
+
spawned: spawned()
|
|
2184
|
+
}));
|
|
2185
|
+
motion.animates(() => (glyphShown() || artShown() || belowShown()) && mascot() !== "gone", MASCOT_EVERY);
|
|
2186
|
+
const mascotFrame = () => motion.enabled ? Math.floor(motion.frame() / MASCOT_EVERY) : undefined;
|
|
2187
|
+
const header = createMemo(() => cells(glyph(mascot(), mascotFrame())));
|
|
2188
|
+
const art2 = createMemo(() => art(mascot(), mascotFrame()));
|
|
2189
|
+
const tokens = digbyColours(theme);
|
|
2190
|
+
const drawn = (line) => (() => {
|
|
2191
|
+
var _el$4 = _$createElement("text");
|
|
2192
|
+
_$setProp(_el$4, "wrapMode", "none");
|
|
2193
|
+
_$setProp(_el$4, "flexShrink", 0);
|
|
2194
|
+
_$insert(_el$4, _$createComponent(Index, {
|
|
2195
|
+
get each() {
|
|
2196
|
+
return line();
|
|
2197
|
+
},
|
|
2198
|
+
children: (one) => (() => {
|
|
2199
|
+
var _el$5 = _$createElement("span");
|
|
2200
|
+
_$insert(_el$5, () => one().char);
|
|
2201
|
+
_$effect((_$p) => _$setProp(_el$5, "style", {
|
|
2202
|
+
fg: one().fg && tokens[one().fg],
|
|
2203
|
+
bg: one().bg && tokens[one().bg]
|
|
2204
|
+
}, _$p));
|
|
2205
|
+
return _el$5;
|
|
2206
|
+
})()
|
|
2207
|
+
}));
|
|
2208
|
+
return _el$4;
|
|
2209
|
+
})();
|
|
2210
|
+
const rowBackground = (row, chosen) => {
|
|
2211
|
+
const rest = restBackground(theme, {
|
|
2212
|
+
selected: chosen && input.focused,
|
|
2213
|
+
hovered: hovered() === row.sessionID
|
|
2214
|
+
});
|
|
2215
|
+
const strength = flashes.strength(row.sessionID);
|
|
2216
|
+
return strength > 0 ? blend(theme.background.feedback.success.base, rest ?? theme.background.base, strength) : rest;
|
|
2217
|
+
};
|
|
1338
2218
|
const rowColor = (row) => {
|
|
1339
2219
|
if (row.state === "stalled")
|
|
1340
2220
|
return theme.text.feedback.error.base;
|
|
@@ -1342,84 +2222,153 @@ function TreePanel(props) {
|
|
|
1342
2222
|
return theme.text.feedback.warning.base;
|
|
1343
2223
|
return row.state === "gone" ? theme.text.muted : theme.text.base;
|
|
1344
2224
|
};
|
|
2225
|
+
const partColor = (row, kind) => {
|
|
2226
|
+
const marked = rowMark(row, theme);
|
|
2227
|
+
if (kind === "state" && row.state === "running")
|
|
2228
|
+
return marked.color;
|
|
2229
|
+
if (kind === "report" && row.report?.state === "reported")
|
|
2230
|
+
return theme.text.feedback.success.base;
|
|
2231
|
+
return theme.text.muted;
|
|
2232
|
+
};
|
|
1345
2233
|
return (() => {
|
|
1346
|
-
var _el$
|
|
1347
|
-
_$insertNode(_el$
|
|
1348
|
-
_$insertNode(_el$
|
|
1349
|
-
_$insertNode(_el$
|
|
1350
|
-
_$setProp(_el$
|
|
1351
|
-
_$setProp(_el$
|
|
1352
|
-
_$setProp(_el$
|
|
1353
|
-
_$setProp(_el$
|
|
1354
|
-
_$
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
_$
|
|
2234
|
+
var _el$6 = _$createElement("box"), _el$7 = _$createElement("box"), _el$8 = _$createElement("text"), _el$20 = _$createElement("scrollbox"), _el$22 = _$createElement("text");
|
|
2235
|
+
_$insertNode(_el$6, _el$7);
|
|
2236
|
+
_$insertNode(_el$6, _el$20);
|
|
2237
|
+
_$insertNode(_el$6, _el$22);
|
|
2238
|
+
_$setProp(_el$6, "flexDirection", "column");
|
|
2239
|
+
_$setProp(_el$6, "flexGrow", 1);
|
|
2240
|
+
_$setProp(_el$6, "paddingLeft", 1);
|
|
2241
|
+
_$setProp(_el$6, "paddingRight", 1);
|
|
2242
|
+
_$setProp(_el$6, "onSizeChange", function() {
|
|
2243
|
+
setSize({
|
|
2244
|
+
width: this.width,
|
|
2245
|
+
height: this.height
|
|
2246
|
+
});
|
|
2247
|
+
});
|
|
2248
|
+
_$setProp(_el$6, "onMouseDrag", () => pressed = undefined);
|
|
2249
|
+
_$insertNode(_el$7, _el$8);
|
|
2250
|
+
_$setProp(_el$7, "flexDirection", "row");
|
|
2251
|
+
_$setProp(_el$7, "flexShrink", 0);
|
|
2252
|
+
_$setProp(_el$7, "gap", 2);
|
|
2253
|
+
_$insert(_el$7, _$createComponent(Show, {
|
|
2254
|
+
get when() {
|
|
2255
|
+
return headerGlyph();
|
|
2256
|
+
},
|
|
2257
|
+
get children() {
|
|
2258
|
+
return drawn(() => header()[0]);
|
|
2259
|
+
}
|
|
2260
|
+
}), _el$8);
|
|
2261
|
+
_$insertNode(_el$8, _$createTextNode(`courier tree`));
|
|
2262
|
+
_$setProp(_el$8, "wrapMode", "none");
|
|
2263
|
+
_$insert(_el$7, _$createComponent(Show, {
|
|
1361
2264
|
get when() {
|
|
1362
2265
|
return root().started;
|
|
1363
2266
|
},
|
|
1364
2267
|
get children() {
|
|
1365
|
-
var _el$
|
|
1366
|
-
_$setProp(_el$
|
|
1367
|
-
_$insert(_el$
|
|
1368
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1369
|
-
return _el$
|
|
2268
|
+
var _el$0 = _$createElement("text");
|
|
2269
|
+
_$setProp(_el$0, "wrapMode", "none");
|
|
2270
|
+
_$insert(_el$0, () => bar(root(), 10, filled()));
|
|
2271
|
+
_$effect((_$p) => _$setProp(_el$0, "fg", tokens.a, _$p));
|
|
2272
|
+
return _el$0;
|
|
1370
2273
|
}
|
|
1371
2274
|
}), null);
|
|
1372
|
-
_$insert(_el$
|
|
2275
|
+
_$insert(_el$7, _$createComponent(Show, {
|
|
1373
2276
|
get when() {
|
|
1374
2277
|
return latest()?.tree?.truncated;
|
|
1375
2278
|
},
|
|
1376
2279
|
get children() {
|
|
1377
|
-
var _el$
|
|
1378
|
-
_$insertNode(_el$
|
|
1379
|
-
_$setProp(_el$
|
|
1380
|
-
_$insert(_el$
|
|
1381
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1382
|
-
return _el$
|
|
2280
|
+
var _el$1 = _$createElement("text"), _el$10 = _$createTextNode(` older left out`);
|
|
2281
|
+
_$insertNode(_el$1, _el$10);
|
|
2282
|
+
_$setProp(_el$1, "wrapMode", "none");
|
|
2283
|
+
_$insert(_el$1, () => latest()?.tree?.truncated, _el$10);
|
|
2284
|
+
_$effect((_$p) => _$setProp(_el$1, "fg", theme.text.feedback.warning.base, _$p));
|
|
2285
|
+
return _el$1;
|
|
1383
2286
|
}
|
|
1384
2287
|
}), null);
|
|
1385
|
-
_$insert(_el$
|
|
2288
|
+
_$insert(_el$6, _$createComponent(Show, {
|
|
2289
|
+
get when() {
|
|
2290
|
+
return glyphShown();
|
|
2291
|
+
},
|
|
2292
|
+
get children() {
|
|
2293
|
+
var _el$11 = _$createElement("box"), _el$12 = _$createElement("text");
|
|
2294
|
+
_$insertNode(_el$11, _el$12);
|
|
2295
|
+
_$setProp(_el$11, "flexDirection", "row");
|
|
2296
|
+
_$setProp(_el$11, "flexShrink", 0);
|
|
2297
|
+
_$setProp(_el$11, "gap", 2);
|
|
2298
|
+
_$insert(_el$11, _$createComponent(Show, {
|
|
2299
|
+
get when() {
|
|
2300
|
+
return headerGlyph();
|
|
2301
|
+
},
|
|
2302
|
+
get children() {
|
|
2303
|
+
return drawn(() => header()[1]);
|
|
2304
|
+
}
|
|
2305
|
+
}), _el$12);
|
|
2306
|
+
_$setProp(_el$12, "wrapMode", "none");
|
|
2307
|
+
_$insert(_el$12, () => countsLine(rows()));
|
|
2308
|
+
_$effect((_$p) => _$setProp(_el$12, "fg", theme.text.muted, _$p));
|
|
2309
|
+
return _el$11;
|
|
2310
|
+
}
|
|
2311
|
+
}), _el$20);
|
|
2312
|
+
_$insert(_el$6, _$createComponent(Show, {
|
|
1386
2313
|
get when() {
|
|
1387
2314
|
return latest()?.error;
|
|
1388
2315
|
},
|
|
1389
2316
|
get children() {
|
|
1390
|
-
var _el$
|
|
1391
|
-
_$insertNode(_el$
|
|
1392
|
-
_$insert(_el$
|
|
1393
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1394
|
-
return _el$
|
|
2317
|
+
var _el$13 = _$createElement("text"), _el$14 = _$createTextNode(`courier RPC failed: `);
|
|
2318
|
+
_$insertNode(_el$13, _el$14);
|
|
2319
|
+
_$insert(_el$13, () => latest()?.error, null);
|
|
2320
|
+
_$effect((_$p) => _$setProp(_el$13, "fg", theme.text.feedback.error.base, _$p));
|
|
2321
|
+
return _el$13;
|
|
1395
2322
|
}
|
|
1396
|
-
}), _el$
|
|
1397
|
-
_$insert(_el$
|
|
2323
|
+
}), _el$20);
|
|
2324
|
+
_$insert(_el$6, _$createComponent(Show, {
|
|
1398
2325
|
get when() {
|
|
1399
2326
|
return latest()?.tree?.stalled;
|
|
1400
2327
|
},
|
|
1401
2328
|
get children() {
|
|
1402
|
-
var _el$
|
|
1403
|
-
_$insertNode(_el$
|
|
1404
|
-
_$insert(_el$
|
|
1405
|
-
_$effect((_$p) => _$setProp(_el$
|
|
1406
|
-
return _el$
|
|
2329
|
+
var _el$15 = _$createElement("text"), _el$16 = _$createTextNode(`This session is stalled: `);
|
|
2330
|
+
_$insertNode(_el$15, _el$16);
|
|
2331
|
+
_$insert(_el$15, () => stalledLine(latest().tree.stalled, now()), null);
|
|
2332
|
+
_$effect((_$p) => _$setProp(_el$15, "fg", theme.text.feedback.error.base, _$p));
|
|
2333
|
+
return _el$15;
|
|
1407
2334
|
}
|
|
1408
|
-
}), _el$
|
|
1409
|
-
_$insert(_el$
|
|
2335
|
+
}), _el$20);
|
|
2336
|
+
_$insert(_el$6, _$createComponent(Show, {
|
|
1410
2337
|
get when() {
|
|
1411
2338
|
return _$memo(() => !!latest()?.tree)() && !rows().length;
|
|
1412
2339
|
},
|
|
1413
2340
|
get children() {
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
2341
|
+
return [(() => {
|
|
2342
|
+
var _el$17 = _$createElement("text");
|
|
2343
|
+
_$insertNode(_el$17, _$createTextNode(`No session started with courier_spawn under this one.`));
|
|
2344
|
+
_$effect((_$p) => _$setProp(_el$17, "fg", theme.text.muted, _$p));
|
|
2345
|
+
return _el$17;
|
|
2346
|
+
})(), _$createComponent(Show, {
|
|
2347
|
+
get when() {
|
|
2348
|
+
return artShown();
|
|
2349
|
+
},
|
|
2350
|
+
get children() {
|
|
2351
|
+
var _el$19 = _$createElement("box");
|
|
2352
|
+
_$setProp(_el$19, "flexDirection", "column");
|
|
2353
|
+
_$setProp(_el$19, "flexShrink", 0);
|
|
2354
|
+
_$setProp(_el$19, "paddingTop", 1);
|
|
2355
|
+
_$insert(_el$19, _$createComponent(Index, {
|
|
2356
|
+
get each() {
|
|
2357
|
+
return art2();
|
|
2358
|
+
},
|
|
2359
|
+
children: (line) => drawn(line)
|
|
2360
|
+
}));
|
|
2361
|
+
return _el$19;
|
|
2362
|
+
}
|
|
2363
|
+
})];
|
|
1418
2364
|
}
|
|
1419
|
-
}), _el$
|
|
1420
|
-
_$use((value) => scroll = value, _el$
|
|
1421
|
-
_$setProp(_el$
|
|
1422
|
-
_$
|
|
2365
|
+
}), _el$20);
|
|
2366
|
+
_$use((value) => scroll = value, _el$20);
|
|
2367
|
+
_$setProp(_el$20, "flexGrow", 1);
|
|
2368
|
+
_$setProp(_el$20, "onSizeChange", function() {
|
|
2369
|
+
setListHeight(this.height);
|
|
2370
|
+
});
|
|
2371
|
+
_$insert(_el$20, _$createComponent(For, {
|
|
1423
2372
|
get each() {
|
|
1424
2373
|
return rows();
|
|
1425
2374
|
},
|
|
@@ -1427,77 +2376,165 @@ function TreePanel(props) {
|
|
|
1427
2376
|
const chosen = () => index() === selected();
|
|
1428
2377
|
const indent = " ".repeat(row.depth - 1);
|
|
1429
2378
|
return (() => {
|
|
1430
|
-
var _el$
|
|
1431
|
-
_$insertNode(_el$
|
|
1432
|
-
_$setProp(_el$
|
|
1433
|
-
_$setProp(_el$
|
|
1434
|
-
_$
|
|
1435
|
-
_$
|
|
1436
|
-
_$setProp(_el$
|
|
1437
|
-
_$setProp(_el$
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
_$
|
|
1447
|
-
_$
|
|
1448
|
-
_$
|
|
1449
|
-
_$
|
|
2379
|
+
var _el$24 = _$createElement("box"), _el$25 = _$createElement("box"), _el$26 = _$createElement("text"), _el$27 = _$createElement("text"), _el$28 = _$createTextNode(` `), _el$29 = _$createElement("text"), _el$30 = _$createElement("text");
|
|
2380
|
+
_$insertNode(_el$24, _el$25);
|
|
2381
|
+
_$setProp(_el$24, "flexDirection", "column");
|
|
2382
|
+
_$setProp(_el$24, "flexShrink", 0);
|
|
2383
|
+
_$setProp(_el$24, "onMouseOver", () => setHovered(row.sessionID));
|
|
2384
|
+
_$setProp(_el$24, "onMouseOut", () => setHovered((id) => id === row.sessionID ? undefined : id));
|
|
2385
|
+
_$setProp(_el$24, "onMouseDown", (event) => pressed = event.button === MouseButton.LEFT ? row.sessionID : undefined);
|
|
2386
|
+
_$setProp(_el$24, "onMouseUp", (event) => {
|
|
2387
|
+
if (event.button !== MouseButton.LEFT)
|
|
2388
|
+
return;
|
|
2389
|
+
event.stopPropagation();
|
|
2390
|
+
const down = pressed;
|
|
2391
|
+
pressed = undefined;
|
|
2392
|
+
if (down === row.sessionID)
|
|
2393
|
+
click(row);
|
|
2394
|
+
});
|
|
2395
|
+
_$insertNode(_el$25, _el$26);
|
|
2396
|
+
_$insertNode(_el$25, _el$27);
|
|
2397
|
+
_$insertNode(_el$25, _el$29);
|
|
2398
|
+
_$insertNode(_el$25, _el$30);
|
|
2399
|
+
_$setProp(_el$25, "flexDirection", "row");
|
|
2400
|
+
_$setProp(_el$25, "flexShrink", 0);
|
|
2401
|
+
_$setProp(_el$26, "wrapMode", "none");
|
|
2402
|
+
_$setProp(_el$26, "flexShrink", 0);
|
|
2403
|
+
_$insert(_el$26, indent, null);
|
|
2404
|
+
_$insert(_el$26, () => chosen() ? "\u25B8 " : " ", null);
|
|
2405
|
+
_$insertNode(_el$27, _el$28);
|
|
2406
|
+
_$setProp(_el$27, "wrapMode", "none");
|
|
2407
|
+
_$setProp(_el$27, "flexShrink", 0);
|
|
2408
|
+
_$insert(_el$27, () => rowMark(row, theme, frame()).mark, _el$28);
|
|
2409
|
+
_$setProp(_el$29, "wrapMode", "none");
|
|
2410
|
+
_$setProp(_el$29, "flexShrink", 1);
|
|
2411
|
+
_$setProp(_el$29, "minWidth", 0);
|
|
2412
|
+
_$insert(_el$29, () => oneLine3(row.title), null);
|
|
2413
|
+
_$insert(_el$29, () => row.sessionID === input.sessionID ? " (on screen)" : "", null);
|
|
2414
|
+
_$insertNode(_el$30, _$createTextNode(` `));
|
|
2415
|
+
_$setProp(_el$30, "wrapMode", "none");
|
|
2416
|
+
_$setProp(_el$30, "flexShrink", 0);
|
|
2417
|
+
_$insert(_el$25, _$createComponent(For, {
|
|
2418
|
+
get each() {
|
|
2419
|
+
return statusParts(row);
|
|
2420
|
+
},
|
|
2421
|
+
children: (part, at2) => [_$createComponent(Show, {
|
|
2422
|
+
get when() {
|
|
2423
|
+
return at2();
|
|
2424
|
+
},
|
|
2425
|
+
get children() {
|
|
2426
|
+
var _el$32 = _$createElement("text");
|
|
2427
|
+
_$insertNode(_el$32, _$createTextNode(` \xB7 `));
|
|
2428
|
+
_$setProp(_el$32, "wrapMode", "none");
|
|
2429
|
+
_$setProp(_el$32, "flexShrink", 0);
|
|
2430
|
+
_$effect((_$p) => _$setProp(_el$32, "fg", theme.text.muted, _$p));
|
|
2431
|
+
return _el$32;
|
|
2432
|
+
}
|
|
2433
|
+
}), (() => {
|
|
2434
|
+
var _el$34 = _$createElement("text");
|
|
2435
|
+
_$setProp(_el$34, "wrapMode", "none");
|
|
2436
|
+
_$setProp(_el$34, "flexShrink", 0);
|
|
2437
|
+
_$insert(_el$34, () => part.text);
|
|
2438
|
+
_$effect((_$p) => _$setProp(_el$34, "fg", partColor(row, part.kind), _$p));
|
|
2439
|
+
return _el$34;
|
|
2440
|
+
})()]
|
|
2441
|
+
}), null);
|
|
2442
|
+
_$insert(_el$24, _$createComponent(For, {
|
|
1450
2443
|
get each() {
|
|
1451
2444
|
return detailLines(row, rows(), now());
|
|
1452
2445
|
},
|
|
1453
2446
|
children: (line) => (() => {
|
|
1454
|
-
var _el$
|
|
1455
|
-
_$insertNode(_el$
|
|
1456
|
-
_$setProp(_el$
|
|
1457
|
-
_$insert(_el$
|
|
1458
|
-
_$insert(_el$
|
|
1459
|
-
|
|
1460
|
-
|
|
2447
|
+
var _el$35 = _$createElement("text"), _el$36 = _$createTextNode(` `);
|
|
2448
|
+
_$insertNode(_el$35, _el$36);
|
|
2449
|
+
_$setProp(_el$35, "wrapMode", "none");
|
|
2450
|
+
_$insert(_el$35, indent, _el$36);
|
|
2451
|
+
_$insert(_el$35, _$createComponent(Show, {
|
|
2452
|
+
get when() {
|
|
2453
|
+
return _$memo(() => row.context !== undefined)() && Math.round(row.context * 100) > CONTEXT_WARN * 100;
|
|
2454
|
+
},
|
|
2455
|
+
fallback: line,
|
|
2456
|
+
get children() {
|
|
2457
|
+
return _$createComponent(For, {
|
|
2458
|
+
get each() {
|
|
2459
|
+
return line.split(" \xB7 ");
|
|
2460
|
+
},
|
|
2461
|
+
children: (part, at2) => [_$memo(() => at2() ? " \xB7 " : ""), _$memo(() => _$memo(() => part === `ctx ${Math.round(row.context * 100)}%`)() ? (() => {
|
|
2462
|
+
var _el$37 = _$createElement("span");
|
|
2463
|
+
_$insert(_el$37, part);
|
|
2464
|
+
_$effect((_$p) => _$setProp(_el$37, "style", {
|
|
2465
|
+
fg: theme.text.feedback.warning.base
|
|
2466
|
+
}, _$p));
|
|
2467
|
+
return _el$37;
|
|
2468
|
+
})() : part)]
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
}), null);
|
|
2472
|
+
_$effect((_$p) => _$setProp(_el$35, "fg", theme.text.muted, _$p));
|
|
2473
|
+
return _el$35;
|
|
1461
2474
|
})()
|
|
1462
2475
|
}), null);
|
|
1463
2476
|
_$effect((_p$) => {
|
|
1464
|
-
var _v$3 = `courier-row-${index()}`, _v$4 = chosen()
|
|
1465
|
-
_v$3 !== _p$.e && (_p$.e = _$setProp(_el$
|
|
1466
|
-
_v$4 !== _p$.t && (_p$.t = _$setProp(_el$
|
|
1467
|
-
_v$5 !== _p$.a && (_p$.a = _$setProp(_el$
|
|
1468
|
-
_v$6 !== _p$.o && (_p$.o = _$setProp(_el$
|
|
2477
|
+
var _v$3 = `courier-row-${index()}`, _v$4 = rowBackground(row, chosen()), _v$5 = rowColor(row), _v$6 = rowMark(row, theme).color, _v$7 = rowColor(row), _v$8 = theme.text.muted;
|
|
2478
|
+
_v$3 !== _p$.e && (_p$.e = _$setProp(_el$24, "id", _v$3, _p$.e));
|
|
2479
|
+
_v$4 !== _p$.t && (_p$.t = _$setProp(_el$24, "backgroundColor", _v$4, _p$.t));
|
|
2480
|
+
_v$5 !== _p$.a && (_p$.a = _$setProp(_el$26, "fg", _v$5, _p$.a));
|
|
2481
|
+
_v$6 !== _p$.o && (_p$.o = _$setProp(_el$27, "fg", _v$6, _p$.o));
|
|
2482
|
+
_v$7 !== _p$.i && (_p$.i = _$setProp(_el$29, "fg", _v$7, _p$.i));
|
|
2483
|
+
_v$8 !== _p$.n && (_p$.n = _$setProp(_el$30, "fg", _v$8, _p$.n));
|
|
1469
2484
|
return _p$;
|
|
1470
2485
|
}, {
|
|
1471
2486
|
e: undefined,
|
|
1472
2487
|
t: undefined,
|
|
1473
2488
|
a: undefined,
|
|
1474
|
-
o: undefined
|
|
2489
|
+
o: undefined,
|
|
2490
|
+
i: undefined,
|
|
2491
|
+
n: undefined
|
|
1475
2492
|
});
|
|
1476
|
-
return _el$
|
|
2493
|
+
return _el$24;
|
|
1477
2494
|
})();
|
|
1478
2495
|
}
|
|
1479
|
-
}));
|
|
1480
|
-
_$
|
|
1481
|
-
|
|
1482
|
-
|
|
2496
|
+
}), null);
|
|
2497
|
+
_$insert(_el$20, _$createComponent(Show, {
|
|
2498
|
+
get when() {
|
|
2499
|
+
return belowShown();
|
|
2500
|
+
},
|
|
2501
|
+
get children() {
|
|
2502
|
+
var _el$21 = _$createElement("box");
|
|
2503
|
+
_$setProp(_el$21, "flexDirection", "column");
|
|
2504
|
+
_$setProp(_el$21, "flexShrink", 0);
|
|
2505
|
+
_$setProp(_el$21, "paddingTop", 1);
|
|
2506
|
+
_$insert(_el$21, _$createComponent(Index, {
|
|
2507
|
+
get each() {
|
|
2508
|
+
return art2();
|
|
2509
|
+
},
|
|
2510
|
+
children: (line) => drawn(line)
|
|
2511
|
+
}));
|
|
2512
|
+
return _el$21;
|
|
2513
|
+
}
|
|
2514
|
+
}), null);
|
|
2515
|
+
_$insertNode(_el$22, _$createTextNode(`\u2191\u2193 select \xB7 enter open \xB7 s stop \xB7 x forget \xB7 r refresh \xB7 f fullscreen \xB7 q close`));
|
|
2516
|
+
_$setProp(_el$22, "wrapMode", "none");
|
|
2517
|
+
_$setProp(_el$22, "flexShrink", 0);
|
|
1483
2518
|
_$effect((_p$) => {
|
|
1484
|
-
var _v$ =
|
|
1485
|
-
_v$ !== _p$.e && (_p$.e = _$setProp(_el$
|
|
1486
|
-
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$
|
|
2519
|
+
var _v$ = tokens.a, _v$2 = theme.text.muted;
|
|
2520
|
+
_v$ !== _p$.e && (_p$.e = _$setProp(_el$8, "fg", _v$, _p$.e));
|
|
2521
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$22, "fg", _v$2, _p$.t));
|
|
1487
2522
|
return _p$;
|
|
1488
2523
|
}, {
|
|
1489
2524
|
e: undefined,
|
|
1490
2525
|
t: undefined
|
|
1491
2526
|
});
|
|
1492
|
-
return _el$
|
|
2527
|
+
return _el$6;
|
|
1493
2528
|
})();
|
|
1494
2529
|
}
|
|
1495
2530
|
var USAGE = "/courier tree opens the session tree; /courier stop [session] stops a session and those under it";
|
|
1496
|
-
|
|
2531
|
+
var usageWith = (verbs) => [USAGE, ...Object.values(verbs).map((verb) => verb.usage)].join("; ");
|
|
2532
|
+
function addTreePanel(ctx, verbs = {}) {
|
|
1497
2533
|
const {
|
|
1498
2534
|
courier,
|
|
1499
2535
|
location
|
|
1500
2536
|
} = courierOf(ctx);
|
|
2537
|
+
const animations = animationsOn(ctx.options);
|
|
1501
2538
|
const [changed, stopListening] = changes(ctx, PANEL_CHANGES, PANEL_SETTLE_MS);
|
|
1502
2539
|
let following;
|
|
1503
2540
|
let attempt = 0;
|
|
@@ -1531,7 +2568,8 @@ function addTreePanel(ctx) {
|
|
|
1531
2568
|
},
|
|
1532
2569
|
courier,
|
|
1533
2570
|
location,
|
|
1534
|
-
changed
|
|
2571
|
+
changed,
|
|
2572
|
+
animations
|
|
1535
2573
|
});
|
|
1536
2574
|
}
|
|
1537
2575
|
})
|
|
@@ -1557,7 +2595,7 @@ function addTreePanel(ctx) {
|
|
|
1557
2595
|
return;
|
|
1558
2596
|
});
|
|
1559
2597
|
const follow = (from, sessionID) => {
|
|
1560
|
-
const
|
|
2598
|
+
const token2 = ++attempt;
|
|
1561
2599
|
const under = (tree) => tree?.rows.some((row) => row.sessionID === sessionID) === true;
|
|
1562
2600
|
Object.assign(from, {
|
|
1563
2601
|
at: undefined,
|
|
@@ -1568,7 +2606,7 @@ function addTreePanel(ctx) {
|
|
|
1568
2606
|
(async () => {
|
|
1569
2607
|
const inside = await treeOf(from.root);
|
|
1570
2608
|
const own = under(inside) ? undefined : await treeOf(sessionID);
|
|
1571
|
-
if (
|
|
2609
|
+
if (token2 !== attempt || following !== from)
|
|
1572
2610
|
return;
|
|
1573
2611
|
if (under(inside))
|
|
1574
2612
|
show(from, sessionID, from.root, inside);
|
|
@@ -1682,7 +2720,7 @@ function addTreePanel(ctx) {
|
|
|
1682
2720
|
commands: [{
|
|
1683
2721
|
id: "courier.command",
|
|
1684
2722
|
title: "Courier",
|
|
1685
|
-
description:
|
|
2723
|
+
description: usageWith(verbs),
|
|
1686
2724
|
group: "Courier",
|
|
1687
2725
|
slash: {
|
|
1688
2726
|
name: "courier",
|
|
@@ -1694,9 +2732,12 @@ function addTreePanel(ctx) {
|
|
|
1694
2732
|
return openPanel();
|
|
1695
2733
|
if (verb === "stop")
|
|
1696
2734
|
return stopUnder(target);
|
|
2735
|
+
const further = Object.hasOwn(verbs, verb) ? verbs[verb] : undefined;
|
|
2736
|
+
if (further)
|
|
2737
|
+
return further.run();
|
|
1697
2738
|
ctx.ui.toast.show({
|
|
1698
2739
|
title: "courier",
|
|
1699
|
-
message:
|
|
2740
|
+
message: usageWith(verbs),
|
|
1700
2741
|
variant: "warning"
|
|
1701
2742
|
});
|
|
1702
2743
|
}
|
|
@@ -1766,7 +2807,13 @@ var tui_default = Plugin.define({
|
|
|
1766
2807
|
id: "courier.tui",
|
|
1767
2808
|
setup: (ctx) => {
|
|
1768
2809
|
const inbox = addAsksInbox(ctx, CHANGES);
|
|
1769
|
-
const
|
|
2810
|
+
const memory = addMemoryView(ctx);
|
|
2811
|
+
const cleanups = [inbox.cleanup, addTreeStatus(ctx, (sessionID) => void inbox.open(sessionID)), addTreePanel(ctx, {
|
|
2812
|
+
memory: {
|
|
2813
|
+
run: () => void memory.open(),
|
|
2814
|
+
usage: MEMORY_USAGE
|
|
2815
|
+
}
|
|
2816
|
+
}), addTreeAttention(ctx)];
|
|
1770
2817
|
return () => {
|
|
1771
2818
|
for (const cleanup of cleanups.toReversed())
|
|
1772
2819
|
cleanup();
|