@bojackduy/opencode-learn 0.1.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/dist/tui.js ADDED
@@ -0,0 +1,1528 @@
1
+ // @bun
2
+ // plugins/learn-tui.tsx
3
+ import { use as _$use } from "@opentui/solid";
4
+ import { createComponent as _$createComponent } from "@opentui/solid";
5
+ import { effect as _$effect } from "@opentui/solid";
6
+ import { createTextNode as _$createTextNode } from "@opentui/solid";
7
+ import { insertNode as _$insertNode } from "@opentui/solid";
8
+ import { insert as _$insert } from "@opentui/solid";
9
+ import { memo as _$memo } from "@opentui/solid";
10
+ import { setProp as _$setProp } from "@opentui/solid";
11
+ import { createElement as _$createElement } from "@opentui/solid";
12
+ import { createSignal, For, Show, createEffect } from "solid-js";
13
+ import { useKeyboard, useTerminalDimensions } from "@opentui/solid";
14
+ import * as fs from "fs";
15
+ import * as path from "path";
16
+ import { watch } from "fs";
17
+ import { tmpdir } from "os";
18
+ var PENDING_DIR = ".opencode/learn-pending";
19
+ var TUI_LOG = path.join(tmpdir(), "learn-tui.log");
20
+ function tlog(...a) {
21
+ try {
22
+ fs.appendFileSync(TUI_LOG, `[${new Date().toISOString()}] ${a.map((x) => typeof x === "string" ? x : JSON.stringify(x)).join(" ")}
23
+ `);
24
+ } catch {}
25
+ }
26
+ function ensureDir(dir) {
27
+ try {
28
+ fs.mkdirSync(dir, {
29
+ recursive: true
30
+ });
31
+ } catch {}
32
+ }
33
+ function prevent(e) {
34
+ try {
35
+ e.preventDefault?.();
36
+ e.stopPropagation?.();
37
+ } catch {}
38
+ }
39
+ function QuizDialog(props) {
40
+ const theme = () => props.api.theme.current;
41
+ const dims = useTerminalDimensions();
42
+ const popupWidth = () => Math.max(68, Math.min(dims().width - 4, 92));
43
+ const options = () => props.request.options;
44
+ const correctSet = new Set(props.request.correctIndices);
45
+ const isMulti = () => !!props.request.multiSelect;
46
+ const dontKnowIdx = () => options().length;
47
+ const submitIdx = () => isMulti() ? options().length + 1 : -1;
48
+ const [focused, setFocused] = createSignal("options");
49
+ const [optionIndex, setOptionIndex] = createSignal(0);
50
+ const [phase, setPhase] = createSignal("select");
51
+ const [note, setNote] = createSignal("");
52
+ const [dontKnow, setDontKnow] = createSignal(false);
53
+ const [selected, setSelected] = createSignal(new Map);
54
+ const [feedback, setFeedback] = createSignal(null);
55
+ let noteInputEl;
56
+ createEffect(() => {
57
+ if (focused() === "note" && noteInputEl) {
58
+ try {
59
+ noteInputEl.focus();
60
+ } catch {}
61
+ }
62
+ });
63
+ const toggleOption = (idx) => {
64
+ const opt = options()[idx];
65
+ if (!opt)
66
+ return;
67
+ const map = new Map(selected());
68
+ const key = `opt:${idx}`;
69
+ if (dontKnow())
70
+ setDontKnow(false);
71
+ if (map.has(key))
72
+ map.delete(key);
73
+ else
74
+ map.set(key, {
75
+ label: opt.label,
76
+ value: opt.value,
77
+ index: idx + 1
78
+ });
79
+ setSelected(map);
80
+ };
81
+ const handleDontKnow = () => {
82
+ const willBe = !dontKnow();
83
+ setDontKnow(willBe);
84
+ if (willBe)
85
+ setSelected(new Map);
86
+ else
87
+ setSelected(new Map);
88
+ if (!isMulti() && willBe)
89
+ setTimeout(() => submitSelect(), 0);
90
+ };
91
+ const submitSelect = () => {
92
+ const selMap = selected();
93
+ if (!isMulti() && selMap.size === 0 && !dontKnow())
94
+ return;
95
+ if (isMulti() && selMap.size === 0 && !dontKnow())
96
+ return;
97
+ if (dontKnow()) {
98
+ setFeedback({
99
+ correct: false,
100
+ selectedIndices: []
101
+ });
102
+ setPhase("feedback");
103
+ return;
104
+ }
105
+ const selectedIndices = Array.from(selMap.values()).map((v) => v.index);
106
+ const correct = selectedIndices.length === props.request.correctIndices.length && selectedIndices.every((i) => correctSet.has(i)) && props.request.correctIndices.every((i) => selectedIndices.includes(i));
107
+ setFeedback({
108
+ correct,
109
+ selectedIndices
110
+ });
111
+ setPhase("feedback");
112
+ };
113
+ const confirmFeedback = () => {
114
+ const sel = Array.from(selected().values());
115
+ props.onSubmit({
116
+ answers: dontKnow() ? [] : sel,
117
+ dontKnow: dontKnow(),
118
+ note: note().trim() || undefined
119
+ });
120
+ };
121
+ useKeyboard((evt) => {
122
+ const key = evt.name || evt.sequence || evt.raw || "";
123
+ const seq = evt.sequence || "";
124
+ if (phase() === "feedback") {
125
+ if (key === "enter" || seq === "\r" || key === "escape" || key === "esc") {
126
+ prevent(evt);
127
+ confirmFeedback();
128
+ }
129
+ return;
130
+ }
131
+ if (focused() === "note") {
132
+ if (key === "tab" || seq === "\t") {
133
+ prevent(evt);
134
+ setFocused("options");
135
+ return;
136
+ }
137
+ if (key === "escape" || key === "esc") {
138
+ prevent(evt);
139
+ setFocused("options");
140
+ return;
141
+ }
142
+ if (key === "enter" && (evt.ctrl || evt.meta)) {
143
+ prevent(evt);
144
+ setFocused("options");
145
+ return;
146
+ }
147
+ return;
148
+ }
149
+ if (key === "up" || key === "k" || seq === "\x1B[A") {
150
+ prevent(evt);
151
+ setOptionIndex((i) => Math.max(0, i - 1));
152
+ return;
153
+ }
154
+ if (key === "down" || key === "j" || seq === "\x1B[B") {
155
+ prevent(evt);
156
+ setOptionIndex((i) => Math.min(isMulti() ? submitIdx() : dontKnowIdx(), i + 1));
157
+ return;
158
+ }
159
+ if (key === "tab" || seq === "\t") {
160
+ prevent(evt);
161
+ setFocused("note");
162
+ return;
163
+ }
164
+ if (key === "escape" || key === "esc") {
165
+ prevent(evt);
166
+ props.onCancel();
167
+ return;
168
+ }
169
+ if (key === "space" || seq === " ") {
170
+ prevent(evt);
171
+ const idx = optionIndex();
172
+ if (idx === dontKnowIdx())
173
+ handleDontKnow();
174
+ else {
175
+ if (isMulti())
176
+ toggleOption(idx);
177
+ else {
178
+ const opt = options()[idx];
179
+ if (opt) {
180
+ setSelected(new Map([[`opt:${idx}`, {
181
+ label: opt.label,
182
+ value: opt.value,
183
+ index: idx + 1
184
+ }]]));
185
+ setDontKnow(false);
186
+ submitSelect();
187
+ }
188
+ }
189
+ }
190
+ return;
191
+ }
192
+ if (key === "enter" || seq === "\r") {
193
+ prevent(evt);
194
+ const idx = optionIndex();
195
+ if (idx === dontKnowIdx())
196
+ handleDontKnow();
197
+ else if (isMulti())
198
+ submitSelect();
199
+ else {
200
+ const opt = options()[idx];
201
+ if (opt) {
202
+ setSelected(new Map([[`opt:${idx}`, {
203
+ label: opt.label,
204
+ value: opt.value,
205
+ index: idx + 1
206
+ }]]));
207
+ setDontKnow(false);
208
+ submitSelect();
209
+ }
210
+ }
211
+ return;
212
+ }
213
+ if (seq === "ctrl+j" || key === "enter" && evt.ctrl) {
214
+ prevent(evt);
215
+ submitSelect();
216
+ return;
217
+ }
218
+ });
219
+ return (() => {
220
+ var _el$ = _$createElement("box"), _el$2 = _$createElement("box"), _el$3 = _$createElement("text"), _el$4 = _$createElement("text"), _el$6 = _$createElement("box"), _el$7 = _$createElement("text");
221
+ _$insertNode(_el$, _el$2);
222
+ _$insertNode(_el$, _el$6);
223
+ _$setProp(_el$, "flexDirection", "column");
224
+ _$setProp(_el$, "border", true);
225
+ _$setProp(_el$, "padding", 1);
226
+ _$setProp(_el$, "gap", 1);
227
+ _$insertNode(_el$2, _el$3);
228
+ _$insertNode(_el$2, _el$4);
229
+ _$setProp(_el$2, "flexDirection", "row");
230
+ _$setProp(_el$2, "justifyContent", "space-between");
231
+ _$setProp(_el$2, "alignItems", "center");
232
+ _$setProp(_el$2, "paddingLeft", 1);
233
+ _$setProp(_el$2, "paddingRight", 1);
234
+ _$setProp(_el$2, "height", 1);
235
+ _$setProp(_el$3, "bold", true);
236
+ _$insert(_el$3, (() => {
237
+ var _c$ = _$memo(() => phase() === "feedback");
238
+ return () => _c$() ? _$memo(() => !!feedback()?.correct)() ? "\u2713 CORRECT" : dontKnow() ? "\u25CB I DON'T KNOW" : "\u2717 INCORRECT" : isMulti() ? "\u2611 QUIZ \xB7 MULTI-SELECT" : "\u25CF QUIZ \xB7 SINGLE";
239
+ })());
240
+ _$insertNode(_el$4, _$createTextNode(`learn`));
241
+ _$setProp(_el$4, "dim", true);
242
+ _$insertNode(_el$6, _el$7);
243
+ _$setProp(_el$6, "flexDirection", "column");
244
+ _$setProp(_el$6, "gap", 1);
245
+ _$setProp(_el$6, "paddingLeft", 1);
246
+ _$setProp(_el$6, "paddingRight", 1);
247
+ _$setProp(_el$6, "paddingTop", 1);
248
+ _$setProp(_el$7, "bold", true);
249
+ _$setProp(_el$7, "wrapMode", "wrap");
250
+ _$insert(_el$7, () => props.request.question);
251
+ _$insert(_el$6, _$createComponent(Show, {
252
+ get when() {
253
+ return props.request.details;
254
+ },
255
+ get children() {
256
+ var _el$8 = _$createElement("text");
257
+ _$setProp(_el$8, "wrapMode", "wrap");
258
+ _$insert(_el$8, () => props.request.details);
259
+ _$effect((_$p) => _$setProp(_el$8, "fg", theme().textMuted, _$p));
260
+ return _el$8;
261
+ }
262
+ }), null);
263
+ _$insert(_el$, _$createComponent(Show, {
264
+ get when() {
265
+ return phase() === "select";
266
+ },
267
+ get children() {
268
+ var _el$9 = _$createElement("box"), _el$10 = _$createElement("box"), _el$11 = _$createElement("box"), _el$12 = _$createElement("text"), _el$13 = _$createElement("box"), _el$14 = _$createElement("text"), _el$15 = _$createElement("box"), _el$16 = _$createElement("text"), _el$18 = _$createElement("box"), _el$19 = _$createElement("box"), _el$20 = _$createElement("text"), _el$24 = _$createElement("box"), _el$26 = _$createElement("box"), _el$27 = _$createElement("text");
269
+ _$insertNode(_el$9, _el$10);
270
+ _$insertNode(_el$9, _el$18);
271
+ _$insertNode(_el$9, _el$26);
272
+ _$setProp(_el$9, "flexDirection", "column");
273
+ _$setProp(_el$9, "gap", 0);
274
+ _$setProp(_el$9, "padding", 1);
275
+ _$setProp(_el$9, "border", true);
276
+ _$insert(_el$9, _$createComponent(For, {
277
+ get each() {
278
+ return options();
279
+ },
280
+ children: (opt, i) => {
281
+ const idx = i();
282
+ const isFocused = () => focused() === "options" && optionIndex() === idx;
283
+ const isSelected = () => selected().has(`opt:${idx}`);
284
+ return (() => {
285
+ var _el$51 = _$createElement("box"), _el$52 = _$createElement("box"), _el$53 = _$createElement("text"), _el$54 = _$createElement("box"), _el$55 = _$createElement("text"), _el$56 = _$createElement("box"), _el$57 = _$createElement("text"), _el$58 = _$createTextNode(`. `);
286
+ _$insertNode(_el$51, _el$52);
287
+ _$insertNode(_el$51, _el$54);
288
+ _$insertNode(_el$51, _el$56);
289
+ _$setProp(_el$51, "flexDirection", "row");
290
+ _$setProp(_el$51, "alignItems", "flexStart");
291
+ _$setProp(_el$51, "gap", 1);
292
+ _$setProp(_el$51, "paddingLeft", 1);
293
+ _$setProp(_el$51, "paddingRight", 1);
294
+ _$insertNode(_el$52, _el$53);
295
+ _$setProp(_el$52, "width", 2);
296
+ _$setProp(_el$52, "alignItems", "center");
297
+ _$insert(_el$53, () => isFocused() ? "\u25B8" : " ");
298
+ _$insertNode(_el$54, _el$55);
299
+ _$setProp(_el$54, "width", 2);
300
+ _$setProp(_el$54, "alignItems", "center");
301
+ _$insert(_el$55, (() => {
302
+ var _c$2 = _$memo(() => !!isMulti());
303
+ return () => _c$2() ? isSelected() ? "\u2611" : "\u2610" : isSelected() ? "\u2B22" : "\u25CB";
304
+ })());
305
+ _$insertNode(_el$56, _el$57);
306
+ _$setProp(_el$56, "flexGrow", 1);
307
+ _$insertNode(_el$57, _el$58);
308
+ _$setProp(_el$57, "wrapMode", "wrap");
309
+ _$insert(_el$57, idx + 1, _el$58);
310
+ _$insert(_el$57, () => opt.label, null);
311
+ _$effect((_p$) => {
312
+ var _v$29 = isFocused() ? theme().backgroundElement : undefined, _v$30 = isFocused() ? theme().accent : theme().textMuted, _v$31 = isMulti() ? isSelected() ? theme().success : theme().textMuted : isSelected() ? theme().accent : theme().textMuted, _v$32 = isSelected() ? theme().text : theme().textMuted, _v$33 = isFocused();
313
+ _v$29 !== _p$.e && (_p$.e = _$setProp(_el$51, "backgroundColor", _v$29, _p$.e));
314
+ _v$30 !== _p$.t && (_p$.t = _$setProp(_el$53, "fg", _v$30, _p$.t));
315
+ _v$31 !== _p$.a && (_p$.a = _$setProp(_el$55, "fg", _v$31, _p$.a));
316
+ _v$32 !== _p$.o && (_p$.o = _$setProp(_el$57, "fg", _v$32, _p$.o));
317
+ _v$33 !== _p$.i && (_p$.i = _$setProp(_el$57, "bold", _v$33, _p$.i));
318
+ return _p$;
319
+ }, {
320
+ e: undefined,
321
+ t: undefined,
322
+ a: undefined,
323
+ o: undefined,
324
+ i: undefined
325
+ });
326
+ return _el$51;
327
+ })();
328
+ }
329
+ }), _el$10);
330
+ _$insert(_el$9, _$createComponent(Show, {
331
+ get when() {
332
+ return options().length > 0;
333
+ },
334
+ get children() {
335
+ var _el$0 = _$createElement("box"), _el$1 = _$createElement("text");
336
+ _$insertNode(_el$0, _el$1);
337
+ _$setProp(_el$0, "height", 1);
338
+ _$insert(_el$1, () => "\u2500".repeat(Math.max(20, popupWidth() - 8)));
339
+ _$effect((_$p) => _$setProp(_el$1, "fg", theme().borderSubtle, _$p));
340
+ return _el$0;
341
+ }
342
+ }), _el$10);
343
+ _$insertNode(_el$10, _el$11);
344
+ _$insertNode(_el$10, _el$13);
345
+ _$insertNode(_el$10, _el$15);
346
+ _$setProp(_el$10, "flexDirection", "row");
347
+ _$setProp(_el$10, "alignItems", "flexStart");
348
+ _$setProp(_el$10, "gap", 1);
349
+ _$setProp(_el$10, "paddingLeft", 1);
350
+ _$setProp(_el$10, "paddingRight", 1);
351
+ _$insertNode(_el$11, _el$12);
352
+ _$setProp(_el$11, "width", 2);
353
+ _$setProp(_el$11, "alignItems", "center");
354
+ _$insert(_el$12, () => focused() === "options" && optionIndex() === dontKnowIdx() ? "\u25B8" : " ");
355
+ _$insertNode(_el$13, _el$14);
356
+ _$setProp(_el$13, "width", 2);
357
+ _$setProp(_el$13, "alignItems", "center");
358
+ _$insert(_el$14, () => dontKnow() ? "\u2611" : "\u2610");
359
+ _$insertNode(_el$15, _el$16);
360
+ _$setProp(_el$15, "flexGrow", 1);
361
+ _$insertNode(_el$16, _$createTextNode(`I don't know \u2014 genuine gap, not a guess`));
362
+ _$setProp(_el$16, "italic", true);
363
+ _$setProp(_el$16, "wrapMode", "wrap");
364
+ _$insertNode(_el$18, _el$19);
365
+ _$insertNode(_el$18, _el$24);
366
+ _$setProp(_el$18, "flexDirection", "column");
367
+ _$setProp(_el$18, "gap", 0);
368
+ _$setProp(_el$18, "paddingTop", 1);
369
+ _$insertNode(_el$19, _el$20);
370
+ _$setProp(_el$19, "flexDirection", "row");
371
+ _$setProp(_el$19, "alignItems", "center");
372
+ _$setProp(_el$19, "gap", 1);
373
+ _$insertNode(_el$20, _$createTextNode(`\u270E Note (optional)`));
374
+ _$insert(_el$19, _$createComponent(Show, {
375
+ get when() {
376
+ return focused() === "note";
377
+ },
378
+ get children() {
379
+ var _el$22 = _$createElement("text");
380
+ _$insertNode(_el$22, _$createTextNode(`\u25CF editing`));
381
+ _$effect((_$p) => _$setProp(_el$22, "fg", theme().accent, _$p));
382
+ return _el$22;
383
+ }
384
+ }), null);
385
+ _$setProp(_el$24, "border", true);
386
+ _$setProp(_el$24, "paddingLeft", 1);
387
+ _$setProp(_el$24, "paddingRight", 1);
388
+ _$insert(_el$24, _$createComponent(Show, {
389
+ get when() {
390
+ return focused() === "note";
391
+ },
392
+ get fallback() {
393
+ return (() => {
394
+ var _el$59 = _$createElement("text");
395
+ _$setProp(_el$59, "wrapMode", "wrap");
396
+ _$insert(_el$59, () => note() || "Tab to edit \xB7 share what you were thinking");
397
+ _$effect((_$p) => _$setProp(_el$59, "fg", note() ? theme().text : theme().textMuted, _$p));
398
+ return _el$59;
399
+ })();
400
+ },
401
+ get children() {
402
+ var _el$25 = _$createElement("input");
403
+ _$use((el) => noteInputEl = el, _el$25);
404
+ _$setProp(_el$25, "onInput", (value) => setNote(typeof value === "string" ? value : value?.target?.value ?? value?.value ?? String(value ?? "")));
405
+ _$setProp(_el$25, "onSubmit", () => setFocused("options"));
406
+ _$setProp(_el$25, "placeholder", "what was on your mind?");
407
+ _$effect((_$p) => _$setProp(_el$25, "value", note(), _$p));
408
+ return _el$25;
409
+ }
410
+ }));
411
+ _$insertNode(_el$26, _el$27);
412
+ _$setProp(_el$26, "flexDirection", "row");
413
+ _$setProp(_el$26, "justifyContent", "space-between");
414
+ _$setProp(_el$26, "paddingTop", 1);
415
+ _$insertNode(_el$27, _$createTextNode(`\u2191\u2193/j k \xB7 Space toggle \xB7 \u2193 to Submit \u2192 Enter \xB7 Tab note \xB7 Esc cancel`));
416
+ _$insert(_el$26, _$createComponent(Show, {
417
+ get when() {
418
+ return isMulti();
419
+ },
420
+ get children() {
421
+ var _el$29 = _$createElement("text"), _el$30 = _$createTextNode(` selected `);
422
+ _$insertNode(_el$29, _el$30);
423
+ _$insert(_el$29, () => selected().size, _el$30);
424
+ _$insert(_el$29, () => dontKnow() ? "\xB7 I don't know" : "", null);
425
+ _$effect((_$p) => _$setProp(_el$29, "fg", selected().size > 0 || dontKnow() ? theme().success : theme().warning, _$p));
426
+ return _el$29;
427
+ }
428
+ }), null);
429
+ _$insert(_el$9, _$createComponent(Show, {
430
+ get when() {
431
+ return isMulti();
432
+ },
433
+ get children() {
434
+ var _el$31 = _$createElement("box"), _el$32 = _$createElement("box"), _el$33 = _$createElement("text"), _el$34 = _$createElement("text");
435
+ _$insertNode(_el$31, _el$32);
436
+ _$setProp(_el$31, "justifyContent", "center");
437
+ _$setProp(_el$31, "paddingTop", 1);
438
+ _$insertNode(_el$32, _el$33);
439
+ _$insertNode(_el$32, _el$34);
440
+ _$setProp(_el$32, "flexDirection", "row");
441
+ _$setProp(_el$32, "alignItems", "center");
442
+ _$setProp(_el$32, "gap", 1);
443
+ _$setProp(_el$32, "border", true);
444
+ _$setProp(_el$32, "paddingLeft", 2);
445
+ _$setProp(_el$32, "paddingRight", 2);
446
+ _$insert(_el$33, () => focused() === "options" && optionIndex() === submitIdx() ? "\u25B8" : " ");
447
+ _$insertNode(_el$34, _$createTextNode(`\u21B3 Submit`));
448
+ _$setProp(_el$34, "bold", true);
449
+ _$effect((_p$) => {
450
+ var _v$ = focused() === "options" && optionIndex() === submitIdx() ? theme().accent : selected().size > 0 || dontKnow() ? theme().success : theme().borderSubtle, _v$2 = focused() === "options" && optionIndex() === submitIdx() ? theme().backgroundElement : selected().size > 0 || dontKnow() ? theme().success : theme().background, _v$3 = focused() === "options" && optionIndex() === submitIdx() ? theme().accent : selected().size > 0 || dontKnow() ? theme().background : theme().textMuted, _v$4 = focused() === "options" && optionIndex() === submitIdx() ? theme().accent : selected().size > 0 || dontKnow() ? theme().background : theme().textMuted;
451
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$32, "borderColor", _v$, _p$.e));
452
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$32, "backgroundColor", _v$2, _p$.t));
453
+ _v$3 !== _p$.a && (_p$.a = _$setProp(_el$33, "fg", _v$3, _p$.a));
454
+ _v$4 !== _p$.o && (_p$.o = _$setProp(_el$34, "fg", _v$4, _p$.o));
455
+ return _p$;
456
+ }, {
457
+ e: undefined,
458
+ t: undefined,
459
+ a: undefined,
460
+ o: undefined
461
+ });
462
+ return _el$31;
463
+ }
464
+ }), null);
465
+ _$effect((_p$) => {
466
+ var _v$5 = theme().borderSubtle, _v$6 = theme().background, _v$7 = focused() === "options" && optionIndex() === dontKnowIdx() ? theme().backgroundElement : undefined, _v$8 = focused() === "options" && optionIndex() === dontKnowIdx() ? theme().accent : theme().textMuted, _v$9 = dontKnow() ? theme().warning : theme().textMuted, _v$0 = dontKnow() ? theme().warning : theme().textMuted, _v$1 = focused() === "note" ? theme().accent : theme().textMuted, _v$10 = focused() === "note", _v$11 = focused() === "note" ? theme().accent : theme().borderSubtle, _v$12 = theme().backgroundElement, _v$13 = theme().textMuted;
467
+ _v$5 !== _p$.e && (_p$.e = _$setProp(_el$9, "borderColor", _v$5, _p$.e));
468
+ _v$6 !== _p$.t && (_p$.t = _$setProp(_el$9, "backgroundColor", _v$6, _p$.t));
469
+ _v$7 !== _p$.a && (_p$.a = _$setProp(_el$10, "backgroundColor", _v$7, _p$.a));
470
+ _v$8 !== _p$.o && (_p$.o = _$setProp(_el$12, "fg", _v$8, _p$.o));
471
+ _v$9 !== _p$.i && (_p$.i = _$setProp(_el$14, "fg", _v$9, _p$.i));
472
+ _v$0 !== _p$.n && (_p$.n = _$setProp(_el$16, "fg", _v$0, _p$.n));
473
+ _v$1 !== _p$.s && (_p$.s = _$setProp(_el$20, "fg", _v$1, _p$.s));
474
+ _v$10 !== _p$.h && (_p$.h = _$setProp(_el$20, "bold", _v$10, _p$.h));
475
+ _v$11 !== _p$.r && (_p$.r = _$setProp(_el$24, "borderColor", _v$11, _p$.r));
476
+ _v$12 !== _p$.d && (_p$.d = _$setProp(_el$24, "backgroundColor", _v$12, _p$.d));
477
+ _v$13 !== _p$.l && (_p$.l = _$setProp(_el$27, "fg", _v$13, _p$.l));
478
+ return _p$;
479
+ }, {
480
+ e: undefined,
481
+ t: undefined,
482
+ a: undefined,
483
+ o: undefined,
484
+ i: undefined,
485
+ n: undefined,
486
+ s: undefined,
487
+ h: undefined,
488
+ r: undefined,
489
+ d: undefined,
490
+ l: undefined
491
+ });
492
+ return _el$9;
493
+ }
494
+ }), null);
495
+ _$insert(_el$, _$createComponent(Show, {
496
+ get when() {
497
+ return phase() === "feedback";
498
+ },
499
+ get children() {
500
+ var _el$36 = _$createElement("box"), _el$37 = _$createElement("box"), _el$38 = _$createElement("text"), _el$42 = _$createElement("text"), _el$43 = _$createTextNode(`Correct: `), _el$46 = _$createElement("box"), _el$47 = _$createElement("text"), _el$48 = _$createElement("box"), _el$49 = _$createElement("text");
501
+ _$insertNode(_el$36, _el$37);
502
+ _$insertNode(_el$36, _el$42);
503
+ _$insertNode(_el$36, _el$46);
504
+ _$insertNode(_el$36, _el$48);
505
+ _$setProp(_el$36, "flexDirection", "column");
506
+ _$setProp(_el$36, "gap", 1);
507
+ _$setProp(_el$36, "padding", 1);
508
+ _$setProp(_el$36, "border", true);
509
+ _$insert(_el$36, _$createComponent(For, {
510
+ get each() {
511
+ return options();
512
+ },
513
+ children: (opt, i) => {
514
+ const idx = i() + 1;
515
+ const isSelected = () => feedback()?.selectedIndices.includes(idx) ?? false;
516
+ const isCorrect = () => correctSet.has(idx);
517
+ let icon = " ";
518
+ let fg = theme().textMuted;
519
+ let bg = undefined;
520
+ if (dontKnow()) {
521
+ icon = isCorrect() ? "\u2713" : " ";
522
+ fg = isCorrect() ? theme().background : theme().textMuted;
523
+ bg = isCorrect() ? theme().success : undefined;
524
+ } else if (isSelected() && isCorrect()) {
525
+ icon = "\u2713";
526
+ fg = theme().background;
527
+ bg = theme().success;
528
+ } else if (isSelected() && !isCorrect()) {
529
+ icon = "\u2717";
530
+ fg = theme().background;
531
+ bg = theme().error;
532
+ } else if (!isSelected() && isCorrect()) {
533
+ icon = "\u25CB";
534
+ fg = theme().background;
535
+ bg = theme().warning;
536
+ }
537
+ return (() => {
538
+ var _el$60 = _$createElement("box"), _el$61 = _$createElement("box"), _el$62 = _$createElement("text"), _el$63 = _$createElement("box"), _el$64 = _$createElement("text"), _el$65 = _$createTextNode(`. `);
539
+ _$insertNode(_el$60, _el$61);
540
+ _$insertNode(_el$60, _el$63);
541
+ _$setProp(_el$60, "flexDirection", "row");
542
+ _$setProp(_el$60, "alignItems", "flexStart");
543
+ _$setProp(_el$60, "gap", 1);
544
+ _$setProp(_el$60, "paddingLeft", 1);
545
+ _$setProp(_el$60, "backgroundColor", bg);
546
+ _$insertNode(_el$61, _el$62);
547
+ _$setProp(_el$61, "width", 2);
548
+ _$setProp(_el$61, "alignItems", "center");
549
+ _$setProp(_el$62, "fg", fg);
550
+ _$setProp(_el$62, "bold", true);
551
+ _$insert(_el$62, icon);
552
+ _$insertNode(_el$63, _el$64);
553
+ _$setProp(_el$63, "flexGrow", 1);
554
+ _$insertNode(_el$64, _el$65);
555
+ _$setProp(_el$64, "fg", fg);
556
+ _$setProp(_el$64, "wrapMode", "wrap");
557
+ _$insert(_el$64, idx, _el$65);
558
+ _$insert(_el$64, () => opt.label, null);
559
+ return _el$60;
560
+ })();
561
+ }
562
+ }), _el$37);
563
+ _$insertNode(_el$37, _el$38);
564
+ _$setProp(_el$37, "height", 1);
565
+ _$insert(_el$38, () => "\u2500".repeat(Math.max(20, popupWidth() - 12)));
566
+ _$insert(_el$36, _$createComponent(Show, {
567
+ get when() {
568
+ return dontKnow();
569
+ },
570
+ get children() {
571
+ var _el$39 = _$createElement("text");
572
+ _$insertNode(_el$39, _$createTextNode(`\u25CF You said: I don't know \u2014 genuine gap`));
573
+ _$effect((_$p) => _$setProp(_el$39, "fg", theme().warning, _$p));
574
+ return _el$39;
575
+ }
576
+ }), _el$42);
577
+ _$insert(_el$36, _$createComponent(Show, {
578
+ get when() {
579
+ return !dontKnow();
580
+ },
581
+ get children() {
582
+ var _el$41 = _$createElement("text");
583
+ _$setProp(_el$41, "bold", true);
584
+ _$insert(_el$41, () => feedback()?.correct ? "\u2713 Correct! Well located." : "\u2717 Incorrect \u2014 nice try, let's fix the edge.");
585
+ _$effect((_$p) => _$setProp(_el$41, "fg", feedback()?.correct ? theme().success : theme().error, _$p));
586
+ return _el$41;
587
+ }
588
+ }), _el$42);
589
+ _$insertNode(_el$42, _el$43);
590
+ _$insert(_el$42, () => props.request.correctIndices.map((i) => `${i}. ${options()[i - 1]?.label}`).join(", "), null);
591
+ _$insert(_el$36, _$createComponent(Show, {
592
+ get when() {
593
+ return note();
594
+ },
595
+ get children() {
596
+ var _el$44 = _$createElement("text"), _el$45 = _$createTextNode(`Your note: `);
597
+ _$insertNode(_el$44, _el$45);
598
+ _$insert(_el$44, note, null);
599
+ _$effect((_$p) => _$setProp(_el$44, "fg", theme().textMuted, _$p));
600
+ return _el$44;
601
+ }
602
+ }), _el$46);
603
+ _$insertNode(_el$46, _el$47);
604
+ _$setProp(_el$46, "border", true);
605
+ _$setProp(_el$46, "padding", 1);
606
+ _$setProp(_el$47, "wrapMode", "wrap");
607
+ _$insert(_el$47, () => props.request.explanation);
608
+ _$insertNode(_el$48, _el$49);
609
+ _$setProp(_el$48, "justifyContent", "center");
610
+ _$setProp(_el$48, "paddingTop", 1);
611
+ _$insertNode(_el$49, _$createTextNode(`\u21B5 Enter / Esc to continue \u2192 next probe`));
612
+ _$effect((_p$) => {
613
+ var _v$14 = feedback()?.correct ? theme().success : theme().error, _v$15 = theme().background, _v$16 = theme().borderSubtle, _v$17 = theme().textMuted, _v$18 = theme().borderSubtle, _v$19 = theme().backgroundPanel, _v$20 = theme().text, _v$21 = theme().textMuted;
614
+ _v$14 !== _p$.e && (_p$.e = _$setProp(_el$36, "borderColor", _v$14, _p$.e));
615
+ _v$15 !== _p$.t && (_p$.t = _$setProp(_el$36, "backgroundColor", _v$15, _p$.t));
616
+ _v$16 !== _p$.a && (_p$.a = _$setProp(_el$38, "fg", _v$16, _p$.a));
617
+ _v$17 !== _p$.o && (_p$.o = _$setProp(_el$42, "fg", _v$17, _p$.o));
618
+ _v$18 !== _p$.i && (_p$.i = _$setProp(_el$46, "borderColor", _v$18, _p$.i));
619
+ _v$19 !== _p$.n && (_p$.n = _$setProp(_el$46, "backgroundColor", _v$19, _p$.n));
620
+ _v$20 !== _p$.s && (_p$.s = _$setProp(_el$47, "fg", _v$20, _p$.s));
621
+ _v$21 !== _p$.h && (_p$.h = _$setProp(_el$49, "fg", _v$21, _p$.h));
622
+ return _p$;
623
+ }, {
624
+ e: undefined,
625
+ t: undefined,
626
+ a: undefined,
627
+ o: undefined,
628
+ i: undefined,
629
+ n: undefined,
630
+ s: undefined,
631
+ h: undefined
632
+ });
633
+ return _el$36;
634
+ }
635
+ }), null);
636
+ _$effect((_p$) => {
637
+ var _v$22 = popupWidth(), _v$23 = phase() === "feedback" ? feedback()?.correct ? theme().success : theme().error : theme().accent, _v$24 = theme().backgroundPanel, _v$25 = phase() === "feedback" ? feedback()?.correct ? theme().success : theme().error : theme().accent, _v$26 = theme().background, _v$27 = theme().background, _v$28 = theme().text;
638
+ _v$22 !== _p$.e && (_p$.e = _$setProp(_el$, "width", _v$22, _p$.e));
639
+ _v$23 !== _p$.t && (_p$.t = _$setProp(_el$, "borderColor", _v$23, _p$.t));
640
+ _v$24 !== _p$.a && (_p$.a = _$setProp(_el$, "backgroundColor", _v$24, _p$.a));
641
+ _v$25 !== _p$.o && (_p$.o = _$setProp(_el$2, "backgroundColor", _v$25, _p$.o));
642
+ _v$26 !== _p$.i && (_p$.i = _$setProp(_el$3, "fg", _v$26, _p$.i));
643
+ _v$27 !== _p$.n && (_p$.n = _$setProp(_el$4, "fg", _v$27, _p$.n));
644
+ _v$28 !== _p$.s && (_p$.s = _$setProp(_el$7, "fg", _v$28, _p$.s));
645
+ return _p$;
646
+ }, {
647
+ e: undefined,
648
+ t: undefined,
649
+ a: undefined,
650
+ o: undefined,
651
+ i: undefined,
652
+ n: undefined,
653
+ s: undefined
654
+ });
655
+ return _el$;
656
+ })();
657
+ }
658
+ function QuizBatchDialog(props) {
659
+ const theme = () => props.api.theme.current;
660
+ const dims = useTerminalDimensions();
661
+ const popupWidth = () => Math.max(68, Math.min(dims().width - 4, 96));
662
+ const [idx, setIdx] = createSignal(0);
663
+ if (!props.request.quizzes || props.request.quizzes.length === 0) {
664
+ tlog("QuizBatchDialog empty quizzes, cancelling", props.request.id);
665
+ setTimeout(() => props.onCancel(), 0);
666
+ return null;
667
+ }
668
+ const cur = () => props.request.quizzes[idx()] ?? props.request.quizzes[0];
669
+ const [phase, setPhase] = createSignal("select");
670
+ const [feedback, setFeedback] = createSignal(null);
671
+ const [dontKnow, setDontKnow] = createSignal(false);
672
+ const [selected, setSelected] = createSignal(new Map);
673
+ const [note, setNote] = createSignal("");
674
+ const [focused, setFocused] = createSignal("options");
675
+ const [optionIndex, setOptionIndex] = createSignal(0);
676
+ const [results, setResults] = createSignal([]);
677
+ const isMulti = () => !!cur().multiSelect;
678
+ const dontKnowIdx = () => cur().options.length;
679
+ const submitIdx = () => isMulti() ? cur().options.length + 1 : -1;
680
+ let noteEl;
681
+ createEffect(() => {
682
+ if (focused() === "note" && noteEl)
683
+ try {
684
+ noteEl.focus();
685
+ } catch (e) {
686
+ tlog("note focus failed", String(e));
687
+ }
688
+ });
689
+ const toggle = (i) => {
690
+ try {
691
+ const o = cur().options[i];
692
+ if (!o)
693
+ return;
694
+ const m = new Map(selected());
695
+ const k = `opt:${i}`;
696
+ if (dontKnow())
697
+ setDontKnow(false);
698
+ if (m.has(k))
699
+ m.delete(k);
700
+ else
701
+ m.set(k, {
702
+ label: o.label,
703
+ value: o.value,
704
+ index: i + 1
705
+ });
706
+ setSelected(m);
707
+ } catch (e) {
708
+ tlog("toggle failed", String(e));
709
+ }
710
+ };
711
+ const goNext = () => {
712
+ try {
713
+ const sel = Array.from(selected().values());
714
+ const dk = dontKnow();
715
+ const correctSet = new Set(cur().correctIndices);
716
+ const si = sel.map((a) => a.index);
717
+ const ok = !dk && si.length === cur().correctIndices.length && si.every((i) => correctSet.has(i));
718
+ const entry = {
719
+ answers: dk ? [] : sel,
720
+ dontKnow: dk,
721
+ note: note().trim() || undefined,
722
+ correct: ok
723
+ };
724
+ const nextResults = [...results(), entry];
725
+ tlog("QuizBatchDialog goNext", idx(), ok, JSON.stringify(entry).slice(0, 200));
726
+ setResults(nextResults);
727
+ if (idx() + 1 < props.request.quizzes.length) {
728
+ setIdx((i) => i + 1);
729
+ setSelected(new Map);
730
+ setDontKnow(false);
731
+ setNote("");
732
+ setOptionIndex(0);
733
+ setFocused("options");
734
+ setPhase("select");
735
+ setFeedback(null);
736
+ } else {
737
+ tlog("QuizBatchDialog done, submitting", nextResults.length);
738
+ props.onSubmit({
739
+ results: nextResults
740
+ });
741
+ }
742
+ } catch (e) {
743
+ tlog("goNext failed", String(e));
744
+ props.onCancel();
745
+ }
746
+ };
747
+ const submitSelect = () => {
748
+ try {
749
+ const m = selected();
750
+ if (!isMulti() && m.size === 0 && !dontKnow())
751
+ return;
752
+ if (isMulti() && m.size === 0 && !dontKnow())
753
+ return;
754
+ const sel = Array.from(m.values());
755
+ const dk = dontKnow();
756
+ const correctSet = new Set(cur().correctIndices);
757
+ const si = sel.map((a) => a.index);
758
+ const ok = !dk && si.length === cur().correctIndices.length && si.every((i) => correctSet.has(i));
759
+ tlog("QuizBatchDialog submitSelect", idx(), sel.length, dk, ok);
760
+ setFeedback({
761
+ correct: ok,
762
+ selectedIndices: si
763
+ });
764
+ setPhase("feedback");
765
+ } catch (e) {
766
+ tlog("submitSelect failed", String(e));
767
+ }
768
+ };
769
+ useKeyboard((evt) => {
770
+ try {
771
+ const k = evt.name || evt.sequence || evt.raw || "";
772
+ const seq = evt.sequence || "";
773
+ if (phase() === "feedback") {
774
+ if (k === "enter" || seq === "\r" || k === "escape" || k === "esc") {
775
+ prevent(evt);
776
+ goNext();
777
+ }
778
+ return;
779
+ }
780
+ if (focused() === "note") {
781
+ if (k === "tab" || seq === "\t") {
782
+ prevent(evt);
783
+ setFocused("options");
784
+ return;
785
+ }
786
+ if (k === "escape") {
787
+ prevent(evt);
788
+ setFocused("options");
789
+ return;
790
+ }
791
+ return;
792
+ }
793
+ if (k === "up" || k === "k" || seq === "\x1B[A") {
794
+ prevent(evt);
795
+ setOptionIndex((i) => Math.max(0, i - 1));
796
+ return;
797
+ }
798
+ if (k === "down" || k === "j" || seq === "\x1B[B") {
799
+ prevent(evt);
800
+ setOptionIndex((i) => Math.min(isMulti() ? submitIdx() : dontKnowIdx(), i + 1));
801
+ return;
802
+ }
803
+ if (k === "tab" || seq === "\t") {
804
+ prevent(evt);
805
+ setFocused("note");
806
+ return;
807
+ }
808
+ if (k === "escape" || k === "esc") {
809
+ prevent(evt);
810
+ props.onCancel();
811
+ return;
812
+ }
813
+ if (k === "space" || seq === " ") {
814
+ prevent(evt);
815
+ const i = optionIndex();
816
+ if (i === dontKnowIdx()) {
817
+ const willBe = !dontKnow();
818
+ setDontKnow(willBe);
819
+ if (willBe)
820
+ setSelected(new Map);
821
+ } else if (isMulti() && i === submitIdx())
822
+ submitSelect();
823
+ else if (isMulti())
824
+ toggle(i);
825
+ else {
826
+ const o = cur().options[i];
827
+ if (o) {
828
+ setSelected(new Map([[`opt:${i}`, {
829
+ label: o.label,
830
+ value: o.value,
831
+ index: i + 1
832
+ }]]));
833
+ setDontKnow(false);
834
+ submitSelect();
835
+ }
836
+ }
837
+ return;
838
+ }
839
+ if (k === "enter" || seq === "\r") {
840
+ prevent(evt);
841
+ const i = optionIndex();
842
+ if (i === dontKnowIdx()) {
843
+ const willBe = !dontKnow();
844
+ setDontKnow(willBe);
845
+ if (willBe)
846
+ setSelected(new Map);
847
+ } else if (isMulti())
848
+ submitSelect();
849
+ else {
850
+ const o = cur().options[i];
851
+ if (o) {
852
+ setSelected(new Map([[`opt:${i}`, {
853
+ label: o.label,
854
+ value: o.value,
855
+ index: i + 1
856
+ }]]));
857
+ setDontKnow(false);
858
+ submitSelect();
859
+ }
860
+ }
861
+ return;
862
+ }
863
+ if (seq === "ctrl+j" || k === "enter" && evt.ctrl) {
864
+ prevent(evt);
865
+ submitSelect();
866
+ return;
867
+ }
868
+ } catch (e) {
869
+ tlog("useKeyboard batch failed", String(e));
870
+ }
871
+ });
872
+ return (() => {
873
+ var _el$66 = _$createElement("box"), _el$67 = _$createElement("box"), _el$68 = _$createElement("text"), _el$69 = _$createTextNode(` decks.quiz batch `), _el$70 = _$createTextNode(`/`), _el$71 = _$createTextNode(` `), _el$72 = _$createElement("text"), _el$74 = _$createElement("text");
874
+ _$insertNode(_el$66, _el$67);
875
+ _$insertNode(_el$66, _el$74);
876
+ _$setProp(_el$66, "flexDirection", "column");
877
+ _$setProp(_el$66, "border", true);
878
+ _$setProp(_el$66, "padding", 1);
879
+ _$setProp(_el$66, "gap", 1);
880
+ _$insertNode(_el$67, _el$68);
881
+ _$insertNode(_el$67, _el$72);
882
+ _$setProp(_el$67, "flexDirection", "row");
883
+ _$setProp(_el$67, "justifyContent", "space-between");
884
+ _$setProp(_el$67, "paddingLeft", 1);
885
+ _$setProp(_el$67, "paddingRight", 1);
886
+ _$setProp(_el$67, "height", 1);
887
+ _$insertNode(_el$68, _el$69);
888
+ _$insertNode(_el$68, _el$70);
889
+ _$insertNode(_el$68, _el$71);
890
+ _$setProp(_el$68, "bold", true);
891
+ _$insert(_el$68, () => idx() + 1, _el$70);
892
+ _$insert(_el$68, () => props.request.quizzes.length, _el$71);
893
+ _$insert(_el$68, (() => {
894
+ var _c$3 = _$memo(() => phase() === "feedback");
895
+ return () => _c$3() ? feedback()?.correct ? "\u2713" : "\u2717" : "";
896
+ })(), null);
897
+ _$insertNode(_el$72, _$createTextNode(`learn`));
898
+ _$setProp(_el$72, "dim", true);
899
+ _$setProp(_el$74, "bold", true);
900
+ _$setProp(_el$74, "wrapMode", "wrap");
901
+ _$insert(_el$74, () => cur().question);
902
+ _$insert(_el$66, _$createComponent(Show, {
903
+ get when() {
904
+ return cur().details;
905
+ },
906
+ get children() {
907
+ var _el$75 = _$createElement("text");
908
+ _$setProp(_el$75, "wrapMode", "wrap");
909
+ _$insert(_el$75, () => cur().details);
910
+ _$effect((_$p) => _$setProp(_el$75, "fg", theme().textMuted, _$p));
911
+ return _el$75;
912
+ }
913
+ }), null);
914
+ _$insert(_el$66, _$createComponent(Show, {
915
+ get when() {
916
+ return phase() === "select";
917
+ },
918
+ get children() {
919
+ var _el$76 = _$createElement("box"), _el$77 = _$createElement("box"), _el$78 = _$createElement("text"), _el$79 = _$createElement("box"), _el$80 = _$createElement("box"), _el$81 = _$createElement("text"), _el$82 = _$createElement("box"), _el$83 = _$createElement("text"), _el$84 = _$createElement("box"), _el$85 = _$createElement("text"), _el$87 = _$createElement("box"), _el$88 = _$createElement("text"), _el$90 = _$createElement("box"), _el$92 = _$createElement("box"), _el$93 = _$createElement("text"), _el$95 = _$createElement("text"), _el$96 = _$createTextNode(`/`);
920
+ _$insertNode(_el$76, _el$77);
921
+ _$insertNode(_el$76, _el$79);
922
+ _$insertNode(_el$76, _el$87);
923
+ _$insertNode(_el$76, _el$92);
924
+ _$setProp(_el$76, "flexDirection", "column");
925
+ _$setProp(_el$76, "gap", 0);
926
+ _$setProp(_el$76, "padding", 1);
927
+ _$setProp(_el$76, "border", true);
928
+ _$insert(_el$76, _$createComponent(For, {
929
+ get each() {
930
+ return cur().options;
931
+ },
932
+ children: (opt, i) => {
933
+ const id = i();
934
+ const foc = () => focused() === "options" && optionIndex() === id;
935
+ const sel = () => selected().has(`opt:${id}`);
936
+ return (() => {
937
+ var _el$113 = _$createElement("box"), _el$114 = _$createElement("box"), _el$115 = _$createElement("text"), _el$116 = _$createElement("box"), _el$117 = _$createElement("text"), _el$118 = _$createElement("box"), _el$119 = _$createElement("text"), _el$120 = _$createTextNode(`. `);
938
+ _$insertNode(_el$113, _el$114);
939
+ _$insertNode(_el$113, _el$116);
940
+ _$insertNode(_el$113, _el$118);
941
+ _$setProp(_el$113, "flexDirection", "row");
942
+ _$setProp(_el$113, "alignItems", "flexStart");
943
+ _$setProp(_el$113, "gap", 1);
944
+ _$setProp(_el$113, "paddingLeft", 1);
945
+ _$insertNode(_el$114, _el$115);
946
+ _$setProp(_el$114, "width", 2);
947
+ _$insert(_el$115, () => foc() ? "\u25B8" : " ");
948
+ _$insertNode(_el$116, _el$117);
949
+ _$setProp(_el$116, "width", 2);
950
+ _$insert(_el$117, (() => {
951
+ var _c$4 = _$memo(() => !!isMulti());
952
+ return () => _c$4() ? sel() ? "\u2611" : "\u2610" : sel() ? "\u2B22" : "\u25CB";
953
+ })());
954
+ _$insertNode(_el$118, _el$119);
955
+ _$setProp(_el$118, "flexGrow", 1);
956
+ _$insertNode(_el$119, _el$120);
957
+ _$setProp(_el$119, "wrapMode", "wrap");
958
+ _$insert(_el$119, id + 1, _el$120);
959
+ _$insert(_el$119, () => opt.label, null);
960
+ _$effect((_p$) => {
961
+ var _v$64 = foc() ? theme().backgroundElement : undefined, _v$65 = foc() ? theme().accent : theme().textMuted, _v$66 = isMulti() ? sel() ? theme().success : theme().textMuted : sel() ? theme().accent : theme().textMuted, _v$67 = sel() ? theme().text : theme().textMuted, _v$68 = foc();
962
+ _v$64 !== _p$.e && (_p$.e = _$setProp(_el$113, "backgroundColor", _v$64, _p$.e));
963
+ _v$65 !== _p$.t && (_p$.t = _$setProp(_el$115, "fg", _v$65, _p$.t));
964
+ _v$66 !== _p$.a && (_p$.a = _$setProp(_el$117, "fg", _v$66, _p$.a));
965
+ _v$67 !== _p$.o && (_p$.o = _$setProp(_el$119, "fg", _v$67, _p$.o));
966
+ _v$68 !== _p$.i && (_p$.i = _$setProp(_el$119, "bold", _v$68, _p$.i));
967
+ return _p$;
968
+ }, {
969
+ e: undefined,
970
+ t: undefined,
971
+ a: undefined,
972
+ o: undefined,
973
+ i: undefined
974
+ });
975
+ return _el$113;
976
+ })();
977
+ }
978
+ }), _el$77);
979
+ _$insertNode(_el$77, _el$78);
980
+ _$setProp(_el$77, "height", 1);
981
+ _$insert(_el$78, () => "\u2500".repeat(Math.max(20, popupWidth() - 8)));
982
+ _$insertNode(_el$79, _el$80);
983
+ _$insertNode(_el$79, _el$82);
984
+ _$insertNode(_el$79, _el$84);
985
+ _$setProp(_el$79, "flexDirection", "row");
986
+ _$setProp(_el$79, "gap", 1);
987
+ _$setProp(_el$79, "paddingLeft", 1);
988
+ _$insertNode(_el$80, _el$81);
989
+ _$setProp(_el$80, "width", 2);
990
+ _$insert(_el$81, () => focused() === "options" && optionIndex() === dontKnowIdx() ? "\u25B8" : " ");
991
+ _$insertNode(_el$82, _el$83);
992
+ _$setProp(_el$82, "width", 2);
993
+ _$insert(_el$83, () => dontKnow() ? "\u2611" : "\u2610");
994
+ _$insertNode(_el$84, _el$85);
995
+ _$setProp(_el$84, "flexGrow", 1);
996
+ _$insertNode(_el$85, _$createTextNode(`I don't know`));
997
+ _$setProp(_el$85, "italic", true);
998
+ _$insertNode(_el$87, _el$88);
999
+ _$insertNode(_el$87, _el$90);
1000
+ _$setProp(_el$87, "flexDirection", "column");
1001
+ _$setProp(_el$87, "paddingTop", 1);
1002
+ _$insertNode(_el$88, _$createTextNode(`\u270E Note`));
1003
+ _$setProp(_el$90, "border", true);
1004
+ _$setProp(_el$90, "paddingLeft", 1);
1005
+ _$setProp(_el$90, "paddingRight", 1);
1006
+ _$insert(_el$90, _$createComponent(Show, {
1007
+ get when() {
1008
+ return focused() === "note";
1009
+ },
1010
+ get fallback() {
1011
+ return (() => {
1012
+ var _el$121 = _$createElement("text");
1013
+ _$insert(_el$121, () => note() || "Tab to edit");
1014
+ _$effect((_$p) => _$setProp(_el$121, "fg", theme().textMuted, _$p));
1015
+ return _el$121;
1016
+ })();
1017
+ },
1018
+ get children() {
1019
+ var _el$91 = _$createElement("input");
1020
+ _$use((el) => noteEl = el, _el$91);
1021
+ _$setProp(_el$91, "onInput", (v) => setNote(typeof v === "string" ? v : v?.target?.value ?? ""));
1022
+ _$setProp(_el$91, "onSubmit", () => setFocused("options"));
1023
+ _$setProp(_el$91, "placeholder", "note");
1024
+ _$effect((_$p) => _$setProp(_el$91, "value", note(), _$p));
1025
+ return _el$91;
1026
+ }
1027
+ }));
1028
+ _$insertNode(_el$92, _el$93);
1029
+ _$insertNode(_el$92, _el$95);
1030
+ _$setProp(_el$92, "flexDirection", "row");
1031
+ _$setProp(_el$92, "justifyContent", "space-between");
1032
+ _$setProp(_el$92, "paddingTop", 1);
1033
+ _$insertNode(_el$93, _$createTextNode(`Space toggle \xB7 \u2193 to Submit \u2192 Enter \xB7 Tab note \xB7 Ctrl+Enter submit`));
1034
+ _$insertNode(_el$95, _el$96);
1035
+ _$insert(_el$95, () => idx() + 1, _el$96);
1036
+ _$insert(_el$95, () => props.request.quizzes.length, null);
1037
+ _$insert(_el$76, _$createComponent(Show, {
1038
+ get when() {
1039
+ return isMulti();
1040
+ },
1041
+ get children() {
1042
+ var _el$97 = _$createElement("box"), _el$98 = _$createElement("box"), _el$99 = _$createElement("text"), _el$100 = _$createElement("text");
1043
+ _$insertNode(_el$97, _el$98);
1044
+ _$setProp(_el$97, "justifyContent", "center");
1045
+ _$setProp(_el$97, "paddingTop", 1);
1046
+ _$insertNode(_el$98, _el$99);
1047
+ _$insertNode(_el$98, _el$100);
1048
+ _$setProp(_el$98, "flexDirection", "row");
1049
+ _$setProp(_el$98, "gap", 1);
1050
+ _$setProp(_el$98, "border", true);
1051
+ _$setProp(_el$98, "paddingLeft", 2);
1052
+ _$setProp(_el$98, "paddingRight", 2);
1053
+ _$insert(_el$99, () => focused() === "options" && optionIndex() === submitIdx() ? "\u25B8" : " ");
1054
+ _$insertNode(_el$100, _$createTextNode(`\u21B3 Submit`));
1055
+ _$setProp(_el$100, "bold", true);
1056
+ _$effect((_p$) => {
1057
+ var _v$34 = focused() === "options" && optionIndex() === submitIdx() ? theme().accent : theme().borderSubtle, _v$35 = focused() === "options" && optionIndex() === submitIdx() ? theme().backgroundElement : theme().background, _v$36 = focused() === "options" && optionIndex() === submitIdx() ? theme().accent : theme().textMuted;
1058
+ _v$34 !== _p$.e && (_p$.e = _$setProp(_el$98, "borderColor", _v$34, _p$.e));
1059
+ _v$35 !== _p$.t && (_p$.t = _$setProp(_el$98, "backgroundColor", _v$35, _p$.t));
1060
+ _v$36 !== _p$.a && (_p$.a = _$setProp(_el$99, "fg", _v$36, _p$.a));
1061
+ return _p$;
1062
+ }, {
1063
+ e: undefined,
1064
+ t: undefined,
1065
+ a: undefined
1066
+ });
1067
+ return _el$97;
1068
+ }
1069
+ }), null);
1070
+ _$effect((_p$) => {
1071
+ var _v$37 = theme().borderSubtle, _v$38 = theme().background, _v$39 = theme().borderSubtle, _v$40 = focused() === "options" && optionIndex() === dontKnowIdx() ? theme().backgroundElement : undefined, _v$41 = focused() === "options" && optionIndex() === dontKnowIdx() ? theme().accent : theme().textMuted, _v$42 = dontKnow() ? theme().warning : theme().textMuted, _v$43 = dontKnow() ? theme().warning : theme().textMuted, _v$44 = focused() === "note" ? theme().accent : theme().textMuted, _v$45 = focused() === "note" ? theme().accent : theme().borderSubtle, _v$46 = theme().backgroundElement, _v$47 = theme().textMuted, _v$48 = theme().textMuted;
1072
+ _v$37 !== _p$.e && (_p$.e = _$setProp(_el$76, "borderColor", _v$37, _p$.e));
1073
+ _v$38 !== _p$.t && (_p$.t = _$setProp(_el$76, "backgroundColor", _v$38, _p$.t));
1074
+ _v$39 !== _p$.a && (_p$.a = _$setProp(_el$78, "fg", _v$39, _p$.a));
1075
+ _v$40 !== _p$.o && (_p$.o = _$setProp(_el$79, "backgroundColor", _v$40, _p$.o));
1076
+ _v$41 !== _p$.i && (_p$.i = _$setProp(_el$81, "fg", _v$41, _p$.i));
1077
+ _v$42 !== _p$.n && (_p$.n = _$setProp(_el$83, "fg", _v$42, _p$.n));
1078
+ _v$43 !== _p$.s && (_p$.s = _$setProp(_el$85, "fg", _v$43, _p$.s));
1079
+ _v$44 !== _p$.h && (_p$.h = _$setProp(_el$88, "fg", _v$44, _p$.h));
1080
+ _v$45 !== _p$.r && (_p$.r = _$setProp(_el$90, "borderColor", _v$45, _p$.r));
1081
+ _v$46 !== _p$.d && (_p$.d = _$setProp(_el$90, "backgroundColor", _v$46, _p$.d));
1082
+ _v$47 !== _p$.l && (_p$.l = _$setProp(_el$93, "fg", _v$47, _p$.l));
1083
+ _v$48 !== _p$.u && (_p$.u = _$setProp(_el$95, "fg", _v$48, _p$.u));
1084
+ return _p$;
1085
+ }, {
1086
+ e: undefined,
1087
+ t: undefined,
1088
+ a: undefined,
1089
+ o: undefined,
1090
+ i: undefined,
1091
+ n: undefined,
1092
+ s: undefined,
1093
+ h: undefined,
1094
+ r: undefined,
1095
+ d: undefined,
1096
+ l: undefined,
1097
+ u: undefined
1098
+ });
1099
+ return _el$76;
1100
+ }
1101
+ }), null);
1102
+ _$insert(_el$66, _$createComponent(Show, {
1103
+ get when() {
1104
+ return phase() === "feedback";
1105
+ },
1106
+ get children() {
1107
+ var _el$102 = _$createElement("box"), _el$103 = _$createElement("text"), _el$104 = _$createElement("text"), _el$105 = _$createTextNode(`Correct: `), _el$106 = _$createElement("box"), _el$107 = _$createElement("text"), _el$108 = _$createElement("box"), _el$109 = _$createElement("text"), _el$110 = _$createTextNode(`Enter \u2192 next (`), _el$111 = _$createTextNode(`/`), _el$112 = _$createTextNode(`)`);
1108
+ _$insertNode(_el$102, _el$103);
1109
+ _$insertNode(_el$102, _el$104);
1110
+ _$insertNode(_el$102, _el$106);
1111
+ _$insertNode(_el$102, _el$108);
1112
+ _$setProp(_el$102, "flexDirection", "column");
1113
+ _$setProp(_el$102, "gap", 1);
1114
+ _$setProp(_el$102, "padding", 1);
1115
+ _$setProp(_el$102, "border", true);
1116
+ _$insert(_el$102, _$createComponent(For, {
1117
+ get each() {
1118
+ return cur().options;
1119
+ },
1120
+ children: (opt, i) => {
1121
+ const id = i() + 1;
1122
+ const sel = () => feedback()?.selectedIndices.includes(id) ?? false;
1123
+ const ok = () => new Set(cur().correctIndices).has(id);
1124
+ let ic = " ";
1125
+ let fg = theme().textMuted;
1126
+ let bg = undefined;
1127
+ if (dontKnow()) {
1128
+ ic = ok() ? "\u2713" : " ";
1129
+ fg = ok() ? theme().background : theme().textMuted;
1130
+ bg = ok() ? theme().success : undefined;
1131
+ } else if (sel() && ok()) {
1132
+ ic = "\u2713";
1133
+ fg = theme().background;
1134
+ bg = theme().success;
1135
+ } else if (sel() && !ok()) {
1136
+ ic = "\u2717";
1137
+ fg = theme().background;
1138
+ bg = theme().error;
1139
+ } else if (!sel() && ok()) {
1140
+ ic = "\u25CB";
1141
+ fg = theme().background;
1142
+ bg = theme().warning;
1143
+ }
1144
+ return (() => {
1145
+ var _el$122 = _$createElement("box"), _el$123 = _$createElement("box"), _el$124 = _$createElement("text"), _el$125 = _$createElement("box"), _el$126 = _$createElement("text"), _el$127 = _$createTextNode(`. `);
1146
+ _$insertNode(_el$122, _el$123);
1147
+ _$insertNode(_el$122, _el$125);
1148
+ _$setProp(_el$122, "flexDirection", "row");
1149
+ _$setProp(_el$122, "gap", 1);
1150
+ _$setProp(_el$122, "paddingLeft", 1);
1151
+ _$setProp(_el$122, "backgroundColor", bg);
1152
+ _$insertNode(_el$123, _el$124);
1153
+ _$setProp(_el$123, "width", 2);
1154
+ _$setProp(_el$124, "fg", fg);
1155
+ _$setProp(_el$124, "bold", true);
1156
+ _$insert(_el$124, ic);
1157
+ _$insertNode(_el$125, _el$126);
1158
+ _$setProp(_el$125, "flexGrow", 1);
1159
+ _$insertNode(_el$126, _el$127);
1160
+ _$setProp(_el$126, "fg", fg);
1161
+ _$setProp(_el$126, "wrapMode", "wrap");
1162
+ _$insert(_el$126, id, _el$127);
1163
+ _$insert(_el$126, () => opt.label, null);
1164
+ return _el$122;
1165
+ })();
1166
+ }
1167
+ }), _el$103);
1168
+ _$setProp(_el$103, "bold", true);
1169
+ _$insert(_el$103, () => feedback()?.correct ? "\u2713 Correct" : "\u2717 Incorrect");
1170
+ _$insertNode(_el$104, _el$105);
1171
+ _$insert(_el$104, () => cur().correctIndices.map((i) => `${i}. ${cur().options[i - 1]?.label}`).join(", "), null);
1172
+ _$insertNode(_el$106, _el$107);
1173
+ _$setProp(_el$106, "border", true);
1174
+ _$setProp(_el$106, "padding", 1);
1175
+ _$setProp(_el$107, "wrapMode", "wrap");
1176
+ _$insert(_el$107, () => cur().explanation);
1177
+ _$insertNode(_el$108, _el$109);
1178
+ _$setProp(_el$108, "justifyContent", "center");
1179
+ _$insertNode(_el$109, _el$110);
1180
+ _$insertNode(_el$109, _el$111);
1181
+ _$insertNode(_el$109, _el$112);
1182
+ _$insert(_el$109, () => idx() + 1, _el$111);
1183
+ _$insert(_el$109, () => props.request.quizzes.length, _el$112);
1184
+ _$effect((_p$) => {
1185
+ var _v$49 = feedback()?.correct ? theme().success : theme().error, _v$50 = theme().background, _v$51 = feedback()?.correct ? theme().success : theme().error, _v$52 = theme().textMuted, _v$53 = theme().borderSubtle, _v$54 = theme().backgroundPanel, _v$55 = theme().text, _v$56 = theme().textMuted;
1186
+ _v$49 !== _p$.e && (_p$.e = _$setProp(_el$102, "borderColor", _v$49, _p$.e));
1187
+ _v$50 !== _p$.t && (_p$.t = _$setProp(_el$102, "backgroundColor", _v$50, _p$.t));
1188
+ _v$51 !== _p$.a && (_p$.a = _$setProp(_el$103, "fg", _v$51, _p$.a));
1189
+ _v$52 !== _p$.o && (_p$.o = _$setProp(_el$104, "fg", _v$52, _p$.o));
1190
+ _v$53 !== _p$.i && (_p$.i = _$setProp(_el$106, "borderColor", _v$53, _p$.i));
1191
+ _v$54 !== _p$.n && (_p$.n = _$setProp(_el$106, "backgroundColor", _v$54, _p$.n));
1192
+ _v$55 !== _p$.s && (_p$.s = _$setProp(_el$107, "fg", _v$55, _p$.s));
1193
+ _v$56 !== _p$.h && (_p$.h = _$setProp(_el$109, "fg", _v$56, _p$.h));
1194
+ return _p$;
1195
+ }, {
1196
+ e: undefined,
1197
+ t: undefined,
1198
+ a: undefined,
1199
+ o: undefined,
1200
+ i: undefined,
1201
+ n: undefined,
1202
+ s: undefined,
1203
+ h: undefined
1204
+ });
1205
+ return _el$102;
1206
+ }
1207
+ }), null);
1208
+ _$effect((_p$) => {
1209
+ var _v$57 = popupWidth(), _v$58 = phase() === "feedback" ? feedback()?.correct ? theme().success : theme().error : theme().accent, _v$59 = theme().backgroundPanel, _v$60 = theme().accent, _v$61 = theme().background, _v$62 = theme().background, _v$63 = theme().text;
1210
+ _v$57 !== _p$.e && (_p$.e = _$setProp(_el$66, "width", _v$57, _p$.e));
1211
+ _v$58 !== _p$.t && (_p$.t = _$setProp(_el$66, "borderColor", _v$58, _p$.t));
1212
+ _v$59 !== _p$.a && (_p$.a = _$setProp(_el$66, "backgroundColor", _v$59, _p$.a));
1213
+ _v$60 !== _p$.o && (_p$.o = _$setProp(_el$67, "backgroundColor", _v$60, _p$.o));
1214
+ _v$61 !== _p$.i && (_p$.i = _$setProp(_el$68, "fg", _v$61, _p$.i));
1215
+ _v$62 !== _p$.n && (_p$.n = _$setProp(_el$72, "fg", _v$62, _p$.n));
1216
+ _v$63 !== _p$.s && (_p$.s = _$setProp(_el$74, "fg", _v$63, _p$.s));
1217
+ return _p$;
1218
+ }, {
1219
+ e: undefined,
1220
+ t: undefined,
1221
+ a: undefined,
1222
+ o: undefined,
1223
+ i: undefined,
1224
+ n: undefined,
1225
+ s: undefined
1226
+ });
1227
+ return _el$66;
1228
+ })();
1229
+ }
1230
+ var tui = async (api) => {
1231
+ const dir = api.state.path.directory || api.state.path.worktree || process.cwd();
1232
+ const pendingDir = path.join(dir, PENDING_DIR);
1233
+ ensureDir(pendingDir);
1234
+ const heartbeatPath = path.join(pendingDir, ".tui-alive");
1235
+ try {
1236
+ fs.writeFileSync(heartbeatPath, String(Date.now()), "utf8");
1237
+ } catch {}
1238
+ const hbTimer = setInterval(() => {
1239
+ try {
1240
+ fs.writeFileSync(heartbeatPath, String(Date.now()), "utf8");
1241
+ } catch {}
1242
+ }, 2000);
1243
+ api.lifecycle.onDispose(() => clearInterval(hbTimer));
1244
+ const currentBySession = new Map;
1245
+ let watcher;
1246
+ let pollTimer;
1247
+ const getCurrentSessionID = () => {
1248
+ try {
1249
+ const cur = api.route?.current;
1250
+ if (cur?.name === "session" && cur?.params?.sessionID)
1251
+ return cur.params.sessionID;
1252
+ if (cur?.params?.id)
1253
+ return cur.params.id;
1254
+ } catch {}
1255
+ return null;
1256
+ };
1257
+ const processPending = () => {
1258
+ const curSid = getCurrentSessionID();
1259
+ if (!curSid)
1260
+ return;
1261
+ let current = currentBySession.get(curSid);
1262
+ if (current)
1263
+ return;
1264
+ if (api.ui.dialog.open)
1265
+ return;
1266
+ let files = [];
1267
+ try {
1268
+ files = fs.readdirSync(pendingDir).filter((f) => f.endsWith(".json") && !f.startsWith("response-") && !f.startsWith(".")).sort();
1269
+ } catch {
1270
+ return;
1271
+ }
1272
+ const matching = files.map((f) => {
1273
+ try {
1274
+ const j = JSON.parse(fs.readFileSync(path.join(pendingDir, f), "utf8"));
1275
+ return {
1276
+ f,
1277
+ j
1278
+ };
1279
+ } catch {
1280
+ return null;
1281
+ }
1282
+ }).filter(Boolean);
1283
+ const pick = matching.find((x) => x.j.sessionID === curSid) || matching.find((x) => !x.j.sessionID);
1284
+ if (!pick)
1285
+ return;
1286
+ const file = pick.f;
1287
+ const full = path.join(pendingDir, file);
1288
+ let data = null;
1289
+ try {
1290
+ data = JSON.parse(fs.readFileSync(full, "utf8"));
1291
+ } catch {
1292
+ try {
1293
+ fs.unlinkSync(full);
1294
+ } catch {}
1295
+ return;
1296
+ }
1297
+ if (!data || !data.id) {
1298
+ try {
1299
+ fs.unlinkSync(full);
1300
+ } catch {}
1301
+ return;
1302
+ }
1303
+ try {
1304
+ const cur = api.route?.current;
1305
+ const curSid2 = cur?.params?.sessionID || cur?.sessionID;
1306
+ if (curSid2 && data.sessionID && data.sessionID !== curSid2) {
1307
+ const anyState = api.state;
1308
+ const exists = anyState.session?.get ? anyState.session.get(data.sessionID) : undefined;
1309
+ if (!exists)
1310
+ data.sessionID = curSid2;
1311
+ }
1312
+ } catch {}
1313
+ current = {
1314
+ id: data.id,
1315
+ type: data.type
1316
+ };
1317
+ currentBySession.set(curSid, current);
1318
+ const done = async (result) => {
1319
+ const respPath = path.join(pendingDir, `response-${data.id}.json`);
1320
+ try {
1321
+ fs.writeFileSync(respPath, JSON.stringify({
1322
+ id: data.id,
1323
+ type: data.type,
1324
+ result,
1325
+ sessionID: data.sessionID,
1326
+ at: Date.now()
1327
+ }), "utf8");
1328
+ } catch {}
1329
+ try {
1330
+ const sessionID = data.sessionID;
1331
+ let injectText = "";
1332
+ if (data.type === "quiz") {
1333
+ const qp = data;
1334
+ const r = result;
1335
+ const dontKnow = !!r.dontKnow;
1336
+ const correctSet = new Set(qp.correctIndices);
1337
+ const selectedIndices = (r.answers || []).map((a) => a.index);
1338
+ const selectedStr = dontKnow ? "I don't know" : (r.answers || []).map((a) => `${a.index}. ${a.label}`).join(", ") || "(none)";
1339
+ const correctStr = qp.correctIndices.map((i) => `${i}. ${qp.options[i - 1]?.label}`).join(", ");
1340
+ const correct = dontKnow ? false : selectedIndices.length === qp.correctIndices.length && selectedIndices.every((i) => correctSet.has(i)) && qp.correctIndices.every((i) => selectedIndices.includes(i));
1341
+ injectText = dontKnow ? `[quiz answer] You selected "I don't know" for: "${qp.question}" \u2014 genuine gap. Correct: ${correctStr}. Explanation: ${qp.explanation}${r.note ? ` Note: ${r.note}` : ""}` : `[quiz answer] Question: "${qp.question}" \u2014 You selected: ${selectedStr} \u2014 ${correct ? "Correct \u2713" : "Incorrect \u2717"}. Correct: ${correctStr}. Explanation: ${qp.explanation}${r.note ? ` Note: ${r.note}` : ""}`;
1342
+ } else if (data.type === "quiz_batch") {
1343
+ const batch = data;
1344
+ const results = result.results ?? [];
1345
+ const lines = batch.quizzes.map((qq, i) => {
1346
+ const x = results[i] || {};
1347
+ const cs = (qq.correctIndices || []).map((idx) => `${idx}. ${qq.options[idx - 1]?.label}`).join(", ");
1348
+ const sel = x?.dontKnow ? "I don't know" : (x?.answers || []).map((a) => `${a.index}. ${a.label}`).join(", ") || "(none)";
1349
+ const ok = x?.correct ? "CORRECT" : x?.dontKnow ? "GAP" : "INCORRECT";
1350
+ return `Q${i + 1}: "${qq.question}" -> ${sel} = ${ok}. Correct: ${cs}`;
1351
+ }).join(`
1352
+ `);
1353
+ injectText = `[quiz_batch answered] ${batch.quizzes.length} quizzes
1354
+ ` + lines;
1355
+ }
1356
+ const anyClient = api.client;
1357
+ const sidToUse = "";
1358
+ if (sidToUse && injectText) {
1359
+ try {
1360
+ api.ui.toast({
1361
+ message: `inject ${sidToUse.slice(0, 6)}`,
1362
+ variant: "info",
1363
+ duration: 1200
1364
+ });
1365
+ } catch {}
1366
+ try {
1367
+ if (anyClient.session?.prompt) {
1368
+ try {
1369
+ await anyClient.session.prompt({
1370
+ path: {
1371
+ sessionID: sidToUse
1372
+ },
1373
+ body: {
1374
+ prompt: {
1375
+ text: injectText
1376
+ }
1377
+ }
1378
+ });
1379
+ } catch {
1380
+ await anyClient.session.prompt({
1381
+ path: {
1382
+ sessionID: sidToUse
1383
+ },
1384
+ body: {
1385
+ parts: [{
1386
+ type: "text",
1387
+ text: injectText
1388
+ }]
1389
+ }
1390
+ });
1391
+ }
1392
+ } else if (anyClient.tui?.submitPrompt) {
1393
+ await anyClient.tui.submitPrompt({
1394
+ text: injectText
1395
+ });
1396
+ }
1397
+ try {
1398
+ const base = api.serverUrl || "http://127.0.0.1:4096";
1399
+ await fetch(`${String(base).replace(/\/$/, "")}/api/session/${sidToUse}/prompt`, {
1400
+ method: "POST",
1401
+ headers: {
1402
+ "Content-Type": "application/json"
1403
+ },
1404
+ body: JSON.stringify({
1405
+ prompt: {
1406
+ text: injectText
1407
+ }
1408
+ })
1409
+ });
1410
+ } catch {}
1411
+ } catch (e) {
1412
+ try {
1413
+ console.error("learn-tui inject failed", e);
1414
+ } catch {}
1415
+ }
1416
+ }
1417
+ } catch {}
1418
+ try {
1419
+ fs.unlinkSync(full);
1420
+ } catch {}
1421
+ api.ui.dialog.clear();
1422
+ currentBySession.delete(curSid);
1423
+ setTimeout(processPending, 150);
1424
+ };
1425
+ const cancel = async () => {
1426
+ const respPath = path.join(pendingDir, `response-${data.id}.json`);
1427
+ try {
1428
+ fs.writeFileSync(respPath, JSON.stringify({
1429
+ id: data.id,
1430
+ type: data.type,
1431
+ cancelled: true,
1432
+ sessionID: data.sessionID,
1433
+ at: Date.now()
1434
+ }), "utf8");
1435
+ } catch {}
1436
+ try {
1437
+ const sid = data.sessionID;
1438
+ if (sid) {
1439
+ const anyClient = api.client;
1440
+ const injectText = data.type === "quiz_batch" ? `[quiz_batch cancelled] ${data.quizzes.length} quizzes \u2014 user cancelled` : `[quiz cancelled] Question: "${data.question}" \u2014 user cancelled`;
1441
+ try {
1442
+ if (anyClient.session?.prompt) {
1443
+ try {
1444
+ await anyClient.session.prompt({
1445
+ path: {
1446
+ sessionID: sid
1447
+ },
1448
+ body: {
1449
+ prompt: {
1450
+ text: injectText
1451
+ }
1452
+ }
1453
+ });
1454
+ } catch {
1455
+ await anyClient.session.prompt({
1456
+ path: {
1457
+ sessionID: sid
1458
+ },
1459
+ body: {
1460
+ parts: [{
1461
+ type: "text",
1462
+ text: injectText
1463
+ }]
1464
+ }
1465
+ });
1466
+ }
1467
+ }
1468
+ } catch {}
1469
+ }
1470
+ } catch {}
1471
+ try {
1472
+ fs.unlinkSync(full);
1473
+ } catch {}
1474
+ api.ui.dialog.clear();
1475
+ currentBySession.delete(curSid);
1476
+ setTimeout(processPending, 150);
1477
+ };
1478
+ if (data.type === "quiz") {
1479
+ tlog("processPending quiz", data.id);
1480
+ api.ui.dialog.replace(() => _$createComponent(QuizDialog, {
1481
+ api,
1482
+ request: data,
1483
+ onSubmit: done,
1484
+ onCancel: cancel
1485
+ }));
1486
+ } else if (data.type === "quiz_batch") {
1487
+ tlog("processPending quiz_batch", data.id, data.quizzes.length);
1488
+ api.ui.dialog.replace(() => _$createComponent(QuizBatchDialog, {
1489
+ api,
1490
+ request: data,
1491
+ onSubmit: done,
1492
+ onCancel: cancel
1493
+ }));
1494
+ } else {
1495
+ tlog("processPending unknown", data.type, data.id);
1496
+ try {
1497
+ fs.unlinkSync(full);
1498
+ } catch {}
1499
+ currentBySession.delete(curSid);
1500
+ return;
1501
+ }
1502
+ try {
1503
+ api.ui.dialog.setSize("large");
1504
+ } catch {}
1505
+ };
1506
+ try {
1507
+ watcher = watch(pendingDir, () => setTimeout(processPending, 50));
1508
+ api.lifecycle.onDispose(() => watcher?.close());
1509
+ } catch {}
1510
+ pollTimer = setInterval(processPending, 700);
1511
+ api.lifecycle.onDispose(() => clearInterval(pollTimer));
1512
+ const off = api.event.on("session.status", () => setTimeout(processPending, 100));
1513
+ api.lifecycle.onDispose(off);
1514
+ setTimeout(processPending, 300);
1515
+ api.ui.toast({
1516
+ message: "learn TUI ready \u2014 beautiful quiz + question",
1517
+ variant: "info",
1518
+ duration: 2200
1519
+ });
1520
+ };
1521
+ var learn_tui_default = {
1522
+ id: "learn-tui",
1523
+ tui
1524
+ };
1525
+ export {
1526
+ tui,
1527
+ learn_tui_default as default
1528
+ };