privateer-agent 0.15.4 → 0.15.6
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 +11 -0
- package/bin/boot-bundle.d.mts +2 -0
- package/bin/boot-bundle.mjs +44 -0
- package/bin/fresh-supervisor.d.mts +5 -1
- package/bin/fresh-supervisor.mjs +47 -10
- package/bin/privateer-launch.mjs +27 -3
- package/bin/privateer-subagent.mjs +5 -2
- package/bin/privateer-verify.mjs +12 -0
- package/bin/session-recovery.d.mts +53 -0
- package/bin/session-recovery.mjs +240 -0
- package/bin/session-seal-preload.mjs +17 -0
- package/bin/session-seal.d.mts +25 -0
- package/bin/session-seal.mjs +242 -0
- package/dist-boot/boot.mjs +3 -0
- package/dist-boot/c-27JKSMWL.mjs +1460 -0
- package/dist-boot/c-2BMRFV24.mjs +269 -0
- package/dist-boot/c-2CTF6JA3.mjs +166 -0
- package/dist-boot/c-2G3BBHRJ.mjs +84 -0
- package/dist-boot/c-2IDLKXX6.mjs +250 -0
- package/dist-boot/c-2NJD5VYM.mjs +8 -0
- package/dist-boot/c-2S5EEQPW.mjs +1602 -0
- package/dist-boot/c-2WRDCUVI.mjs +25 -0
- package/dist-boot/c-2YN3M256.mjs +14 -0
- package/dist-boot/c-37GM3GMU.mjs +187 -0
- package/dist-boot/c-3STKRSVO.mjs +2480 -0
- package/dist-boot/c-3TPV6TQQ.mjs +2320 -0
- package/dist-boot/c-3UVHLQDJ.mjs +225 -0
- package/dist-boot/c-42PUUZYC.mjs +697 -0
- package/dist-boot/c-4D5UMWVW.mjs +683 -0
- package/dist-boot/c-4ETZRMTA.mjs +46 -0
- package/dist-boot/c-4QT25BD5.mjs +1482 -0
- package/dist-boot/c-4WVR32AP.mjs +2579 -0
- package/dist-boot/c-5EMM567G.mjs +152 -0
- package/dist-boot/c-5FA4CV63.mjs +145 -0
- package/dist-boot/c-5GV3X3RB.mjs +14 -0
- package/dist-boot/c-5JCEPRIY.mjs +52 -0
- package/dist-boot/c-5NDKIIWT.mjs +16 -0
- package/dist-boot/c-5QL3AR26.mjs +4665 -0
- package/dist-boot/c-5XY6MFKN.mjs +5142 -0
- package/dist-boot/c-65UZIZC7.mjs +2897 -0
- package/dist-boot/c-6FUXKHOK.mjs +1969 -0
- package/dist-boot/c-6JHWQE4V.mjs +18242 -0
- package/dist-boot/c-6LOBR4I6.mjs +5280 -0
- package/dist-boot/c-6QHOZXKQ.mjs +14394 -0
- package/dist-boot/c-6ZO2SADQ.mjs +178 -0
- package/dist-boot/c-6ZORVVZP.mjs +1449 -0
- package/dist-boot/c-76QH4UCP.mjs +2441 -0
- package/dist-boot/c-77GO5L5Y.mjs +20982 -0
- package/dist-boot/c-7M23XFTN.mjs +5052 -0
- package/dist-boot/c-7TC367E2.mjs +32 -0
- package/dist-boot/c-7UOG7POX.mjs +1524 -0
- package/dist-boot/c-7VHP3FLN.mjs +376 -0
- package/dist-boot/c-A73JHZP2.mjs +13207 -0
- package/dist-boot/c-ABXULAFL.mjs +71 -0
- package/dist-boot/c-ANDD2AQO.mjs +74 -0
- package/dist-boot/c-AOY77AZL.mjs +1430 -0
- package/dist-boot/c-AXEPZ22I.mjs +108 -0
- package/dist-boot/c-B2Y7VNHU.mjs +427 -0
- package/dist-boot/c-B7VJUH73.mjs +77 -0
- package/dist-boot/c-BFW6CDVL.mjs +24 -0
- package/dist-boot/c-BHNIF2AR.mjs +188 -0
- package/dist-boot/c-BKF2O6BL.mjs +23 -0
- package/dist-boot/c-BOZXFTZN.mjs +8 -0
- package/dist-boot/c-BRNYJWZL.mjs +28 -0
- package/dist-boot/c-C3N4Z2HO.mjs +93 -0
- package/dist-boot/c-C4IV7ZFG.mjs +717 -0
- package/dist-boot/c-C5NK6UYV.mjs +793 -0
- package/dist-boot/c-C67644MB.mjs +1659 -0
- package/dist-boot/c-C6S2YB53.mjs +620 -0
- package/dist-boot/c-CDBEJSBF.mjs +37 -0
- package/dist-boot/c-CG47HBKU.mjs +435 -0
- package/dist-boot/c-CKCTWCT5.mjs +51 -0
- package/dist-boot/c-CN77DCGF.mjs +60 -0
- package/dist-boot/c-CRBXNKFO.mjs +1795 -0
- package/dist-boot/c-CRIHASQ6.mjs +973 -0
- package/dist-boot/c-CU4NGAUO.mjs +3786 -0
- package/dist-boot/c-CWRFLGQU.mjs +1883 -0
- package/dist-boot/c-CYQB2USO.mjs +32 -0
- package/dist-boot/c-DEE67DJO.mjs +379 -0
- package/dist-boot/c-DIFDBYOP.mjs +1258 -0
- package/dist-boot/c-DOSGVXDZ.mjs +34879 -0
- package/dist-boot/c-DR7XQI32.mjs +381 -0
- package/dist-boot/c-DTGPSB46.mjs +14666 -0
- package/dist-boot/c-DTHDNA4P.mjs +320 -0
- package/dist-boot/c-E4Z3Y5B5.mjs +3864 -0
- package/dist-boot/c-E66Z2TI7.mjs +203 -0
- package/dist-boot/c-EBU6RLRV.mjs +1527 -0
- package/dist-boot/c-EBZZNGSF.mjs +432 -0
- package/dist-boot/c-EC3LJ2KF.mjs +1032 -0
- package/dist-boot/c-EQBKXWNX.mjs +15829 -0
- package/dist-boot/c-EUEPIW4S.mjs +574 -0
- package/dist-boot/c-EX4YVWAT.mjs +378 -0
- package/dist-boot/c-EYZGSAJU.mjs +98 -0
- package/dist-boot/c-F75VRKY4.mjs +172 -0
- package/dist-boot/c-FH4NXKHV.mjs +177 -0
- package/dist-boot/c-FHNUCTEM.mjs +140 -0
- package/dist-boot/c-FJ5T42DS.mjs +5349 -0
- package/dist-boot/c-FPCNRL7G.mjs +1728 -0
- package/dist-boot/c-G4DLN7Q4.mjs +8 -0
- package/dist-boot/c-G56GYQFZ.mjs +2507 -0
- package/dist-boot/c-GLIOERDQ.mjs +1200 -0
- package/dist-boot/c-GMJ5HUBM.mjs +14 -0
- package/dist-boot/c-H3HSGMAQ.mjs +461 -0
- package/dist-boot/c-HIKTO6P7.mjs +275 -0
- package/dist-boot/c-I2MCJ34O.mjs +464 -0
- package/dist-boot/c-I3C6LBW4.mjs +765 -0
- package/dist-boot/c-I54OKQHW.mjs +387 -0
- package/dist-boot/c-IFSP3R4D.mjs +80 -0
- package/dist-boot/c-IZK77VB7.mjs +171 -0
- package/dist-boot/c-J3Y25UGE.mjs +54 -0
- package/dist-boot/c-J5IESS7W.mjs +27750 -0
- package/dist-boot/c-JJWZBY7L.mjs +254 -0
- package/dist-boot/c-JKTFXTGF.mjs +463 -0
- package/dist-boot/c-JN57AXQ4.mjs +77 -0
- package/dist-boot/c-KFIO6LUW.mjs +1096 -0
- package/dist-boot/c-KIEMEFWW.mjs +478 -0
- package/dist-boot/c-KIVXWBYD.mjs +28 -0
- package/dist-boot/c-KP6CRIP4.mjs +580 -0
- package/dist-boot/c-L26VOJX4.mjs +11558 -0
- package/dist-boot/c-L5GHWG5G.mjs +5988 -0
- package/dist-boot/c-M7NSQAKK.mjs +353 -0
- package/dist-boot/c-MCEWOPBW.mjs +56 -0
- package/dist-boot/c-MGPIEG7I.mjs +20 -0
- package/dist-boot/c-NREXJV3M.mjs +393 -0
- package/dist-boot/c-O5PP6CTX.mjs +162 -0
- package/dist-boot/c-OBQDHSXM.mjs +1774 -0
- package/dist-boot/c-OLQHHDD4.mjs +21 -0
- package/dist-boot/c-OXW4JQQH.mjs +319 -0
- package/dist-boot/c-P3D7ZUWN.mjs +1288 -0
- package/dist-boot/c-P4PQZVV5.mjs +948 -0
- package/dist-boot/c-P63T3XL2.mjs +26373 -0
- package/dist-boot/c-P6N5DQGZ.mjs +194 -0
- package/dist-boot/c-P7MLKQFC.mjs +58 -0
- package/dist-boot/c-PB3PZJL5.mjs +1276 -0
- package/dist-boot/c-PTN4S6G2.mjs +1259 -0
- package/dist-boot/c-PYCE5J7U.mjs +1083 -0
- package/dist-boot/c-Q4HYCRLB.mjs +19410 -0
- package/dist-boot/c-QUXMETGG.mjs +68 -0
- package/dist-boot/c-RDMJZCOX.mjs +356 -0
- package/dist-boot/c-ROGJK76M.mjs +17 -0
- package/dist-boot/c-RQJJ26T4.mjs +708 -0
- package/dist-boot/c-RSKCUPJF.mjs +7559 -0
- package/dist-boot/c-RVJU4O6P.mjs +37 -0
- package/dist-boot/c-S2RVHR3M.mjs +165 -0
- package/dist-boot/c-SKMK3PEE.mjs +91120 -0
- package/dist-boot/c-TI7SM4EG.mjs +20 -0
- package/dist-boot/c-TILJ6GXD.mjs +1235 -0
- package/dist-boot/c-TT6BCUJD.mjs +114 -0
- package/dist-boot/c-U4ITOXAN.mjs +2107 -0
- package/dist-boot/c-UGERXVO2.mjs +7120 -0
- package/dist-boot/c-UPBH5AXY.mjs +1363 -0
- package/dist-boot/c-US2PUNZL.mjs +597 -0
- package/dist-boot/c-UU4WZPPE.mjs +20 -0
- package/dist-boot/c-UWVIYPQS.mjs +39 -0
- package/dist-boot/c-V4RHQBPQ.mjs +17683 -0
- package/dist-boot/c-V5MRVK47.mjs +351 -0
- package/dist-boot/c-V6CL266R.mjs +1755 -0
- package/dist-boot/c-VJTMFKYE.mjs +14975 -0
- package/dist-boot/c-VJTRUKIB.mjs +773 -0
- package/dist-boot/c-W4IFSHPW.mjs +43 -0
- package/dist-boot/c-WETGB2CF.mjs +113 -0
- package/dist-boot/c-WO3QSZUU.mjs +1337 -0
- package/dist-boot/c-WQLH73TY.mjs +242 -0
- package/dist-boot/c-XL735JW5.mjs +500 -0
- package/dist-boot/c-Y7HADL5B.mjs +1107 -0
- package/dist-boot/c-YGQHSVSK.mjs +41 -0
- package/dist-boot/c-YMP2AMEB.mjs +10350 -0
- package/dist-boot/c-YRDO2WEF.mjs +12012 -0
- package/dist-boot/c-YUSTRATJ.mjs +8 -0
- package/dist-boot/c-Z2262R22.mjs +2697 -0
- package/dist-boot/c-Z4ALQMJC.mjs +709 -0
- package/dist-boot/c-Z5ZUQVUY.mjs +62 -0
- package/dist-boot/c-ZDZUKXF7.mjs +874 -0
- package/dist-boot/c-ZJYLGIC3.mjs +32 -0
- package/dist-boot/c-ZNA5CDMI.mjs +77 -0
- package/dist-boot/c-ZSO6DI3M.mjs +204 -0
- package/dist-boot/c-ZWUVHKMK.mjs +100 -0
- package/dist-boot/c-ZY4MESEW.mjs +832 -0
- package/dist-boot/c-ZZ5REI4L.mjs +329 -0
- package/dist-boot/cli.mjs +51 -0
- package/dist-boot/ext-anthropic-attribution.mjs +207 -0
- package/dist-boot/ext-pi-background-tasks.mjs +7234 -0
- package/dist-boot/ext-pi-subagents.mjs +37 -0
- package/dist-boot/ext-privateer-account.mjs +4426 -0
- package/dist-boot/ext-privateer-brand.mjs +5274 -0
- package/dist-boot/ext-privateer-codemode.mjs +37 -0
- package/dist-boot/ext-privateer-computer.mjs +626 -0
- package/dist-boot/ext-privateer-connect.mjs +1862 -0
- package/dist-boot/ext-privateer-context.mjs +323 -0
- package/dist-boot/ext-privateer-desktop.mjs +92 -0
- package/dist-boot/ext-privateer-final-audit.mjs +48 -0
- package/dist-boot/ext-privateer-fresh.mjs +81 -0
- package/dist-boot/ext-privateer-gate.mjs +5959 -0
- package/dist-boot/ext-privateer-hints.mjs +236 -0
- package/dist-boot/ext-privateer-media.mjs +2626 -0
- package/dist-boot/ext-privateer-models.mjs +3397 -0
- package/dist-boot/ext-privateer-posture.mjs +3006 -0
- package/dist-boot/ext-privateer-privacy.mjs +4578 -0
- package/dist-boot/ext-privateer-recovery.mjs +287 -0
- package/dist-boot/ext-privateer-speak.mjs +4754 -0
- package/dist-boot/ext-privateer-terminals.mjs +725 -0
- package/dist-boot/ext-privateer-tools.mjs +528 -0
- package/dist-boot/ext-privateer-truncation.mjs +74 -0
- package/dist-boot/ext-privateer-update.mjs +210 -0
- package/dist-boot/ext-privateer-vision.mjs +148 -0
- package/dist-boot/ext-privateer-web.mjs +543 -0
- package/dist-boot/ext-rpiv-ask-user-question.mjs +710 -0
- package/dist-boot/manifest.json +9 -0
- package/extensions/privateer-brand.ts +3 -1
- package/extensions/privateer-context.ts +4 -2
- package/extensions/privateer-final-audit.ts +17 -0
- package/extensions/privateer-gate.ts +55 -9
- package/extensions/privateer-recovery.ts +175 -0
- package/extensions/privateer-web.ts +4 -4
- package/package.json +3 -1
- package/patches/@earendil-works+pi-coding-agent+1.3.0.patch +199 -2
- package/patches/pi-background-tasks+2.6.10.patch +91 -7
- package/src/cli/chat.ts +7 -1
- package/src/config/moatManifest.json +2 -0
- package/src/config/whatsNew.json +4 -4
- package/src/context.ts +33 -5
- package/src/engine/finalAudit.ts +38 -0
- package/src/engine/stallWatchdog.ts +8 -1
- package/src/providers/account.ts +34 -3
- package/src/remote/attentionPush.ts +40 -0
- package/src/remote/liveTaskSession.ts +7 -1
- package/src/remote/relayClient.ts +38 -4
- package/src/remote/remoteBridge.ts +17 -0
- package/src/remote/transcriptSnapshot.ts +104 -0
|
@@ -0,0 +1,2507 @@
|
|
|
1
|
+
import{createRequire as __pvCR}from"node:module";import{fileURLToPath as __pvF}from"node:url";import{dirname as __pvD}from"node:path";const require=__pvCR(import.meta.url);const __pvUrl=(r)=>new URL(r,import.meta.url).href;const __pvFile=(r)=>__pvF(new URL(r,import.meta.url));
|
|
2
|
+
import {
|
|
3
|
+
COLLAPSE_KEY_OFF,
|
|
4
|
+
DEFAULT_COLLAPSE_KEY,
|
|
5
|
+
ROW_INTENT_META,
|
|
6
|
+
displayLabel,
|
|
7
|
+
formatAnswerScalar,
|
|
8
|
+
formatKeySpecForDisplay,
|
|
9
|
+
t
|
|
10
|
+
} from "./c-2IDLKXX6.mjs";
|
|
11
|
+
import "./c-B2Y7VNHU.mjs";
|
|
12
|
+
import {
|
|
13
|
+
DynamicBorder
|
|
14
|
+
} from "./c-SKMK3PEE.mjs";
|
|
15
|
+
import "./c-I54OKQHW.mjs";
|
|
16
|
+
import "./c-UPBH5AXY.mjs";
|
|
17
|
+
import "./c-7UOG7POX.mjs";
|
|
18
|
+
import "./c-E4Z3Y5B5.mjs";
|
|
19
|
+
import "./c-6ZO2SADQ.mjs";
|
|
20
|
+
import "./c-JKTFXTGF.mjs";
|
|
21
|
+
import {
|
|
22
|
+
getMarkdownTheme
|
|
23
|
+
} from "./c-CRBXNKFO.mjs";
|
|
24
|
+
import "./c-KP6CRIP4.mjs";
|
|
25
|
+
import "./c-RSKCUPJF.mjs";
|
|
26
|
+
import "./c-PTN4S6G2.mjs";
|
|
27
|
+
import "./c-P6N5DQGZ.mjs";
|
|
28
|
+
import "./c-6LOBR4I6.mjs";
|
|
29
|
+
import "./c-3STKRSVO.mjs";
|
|
30
|
+
import "./c-YRDO2WEF.mjs";
|
|
31
|
+
import "./c-BHNIF2AR.mjs";
|
|
32
|
+
import "./c-ABXULAFL.mjs";
|
|
33
|
+
import "./c-2G3BBHRJ.mjs";
|
|
34
|
+
import "./c-O5PP6CTX.mjs";
|
|
35
|
+
import "./c-CDBEJSBF.mjs";
|
|
36
|
+
import "./c-DEE67DJO.mjs";
|
|
37
|
+
import "./c-WO3QSZUU.mjs";
|
|
38
|
+
import "./c-S2RVHR3M.mjs";
|
|
39
|
+
import {
|
|
40
|
+
CURSOR_MARKER,
|
|
41
|
+
Container,
|
|
42
|
+
Editor,
|
|
43
|
+
Key,
|
|
44
|
+
Markdown,
|
|
45
|
+
Spacer,
|
|
46
|
+
Text,
|
|
47
|
+
matchesKey,
|
|
48
|
+
truncateToWidth,
|
|
49
|
+
visibleWidth,
|
|
50
|
+
wrapTextWithAnsi
|
|
51
|
+
} from "./c-EQBKXWNX.mjs";
|
|
52
|
+
import "./c-RQJJ26T4.mjs";
|
|
53
|
+
import "./c-L5GHWG5G.mjs";
|
|
54
|
+
import {
|
|
55
|
+
__name
|
|
56
|
+
} from "./c-CN77DCGF.mjs";
|
|
57
|
+
|
|
58
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/scroll-window.ts?ext=rpiv-ask-user-question
|
|
59
|
+
var OVERFLOW_UP = "\u2191";
|
|
60
|
+
var OVERFLOW_DOWN = "\u2193";
|
|
61
|
+
var OVERFLOW_BOTH = "\u2195";
|
|
62
|
+
function computeScrollStart(bodyRange, rowsBeforeBody, availableMiddle, middleRows) {
|
|
63
|
+
if (!bodyRange) return 0;
|
|
64
|
+
const focusedRowInMiddle = rowsBeforeBody + bodyRange[0];
|
|
65
|
+
const focusedHeight = bodyRange[1] - bodyRange[0];
|
|
66
|
+
const idealStart = focusedRowInMiddle - Math.floor(Math.max(0, availableMiddle - focusedHeight) / 2);
|
|
67
|
+
return Math.max(0, Math.min(idealStart, middleRows - availableMiddle));
|
|
68
|
+
}
|
|
69
|
+
__name(computeScrollStart, "computeScrollStart");
|
|
70
|
+
function decorateOverflow(scrollableMiddle, hasUp, hasDown, theme, protectedRows) {
|
|
71
|
+
const drawable = /* @__PURE__ */ __name((row) => !protectedRows || row < protectedRows[0] || row >= protectedRows[1], "drawable");
|
|
72
|
+
if (hasUp && hasDown && scrollableMiddle.length === 1) {
|
|
73
|
+
if (drawable(0)) scrollableMiddle[0] = theme.fg("dim", OVERFLOW_BOTH);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (hasUp && scrollableMiddle.length > 0 && drawable(0)) {
|
|
77
|
+
scrollableMiddle[0] = theme.fg("dim", OVERFLOW_UP);
|
|
78
|
+
}
|
|
79
|
+
const last = scrollableMiddle.length - 1;
|
|
80
|
+
if (hasDown && last > 0 && drawable(last)) {
|
|
81
|
+
scrollableMiddle[last] = theme.fg("dim", OVERFLOW_DOWN);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
__name(decorateOverflow, "decorateOverflow");
|
|
85
|
+
function windowLines(lines, rows, focus, theme) {
|
|
86
|
+
if (lines.length <= rows) return { lines: [...lines], focus };
|
|
87
|
+
const start = computeScrollStart(focus, 0, rows, lines.length);
|
|
88
|
+
const visible = lines.slice(start, start + rows);
|
|
89
|
+
const inWindow = focus && [focus[0] - start, focus[1] - start];
|
|
90
|
+
decorateOverflow(visible, start > 0, start + rows < lines.length, theme, inWindow);
|
|
91
|
+
return { lines: visible, focus: inWindow };
|
|
92
|
+
}
|
|
93
|
+
__name(windowLines, "windowLines");
|
|
94
|
+
|
|
95
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/derivations.ts?ext=rpiv-ask-user-question
|
|
96
|
+
function selectConfirmedIndicator(questions, currentTab, answers, items) {
|
|
97
|
+
const q = questions[currentTab];
|
|
98
|
+
if (!q || q.multiSelect === true) return void 0;
|
|
99
|
+
const prior = answers.get(currentTab);
|
|
100
|
+
if (!prior) return void 0;
|
|
101
|
+
if (prior.kind === "custom") {
|
|
102
|
+
const otherIndex = items.findIndex((it) => it.kind === "other");
|
|
103
|
+
if (otherIndex < 0) return void 0;
|
|
104
|
+
return { index: otherIndex, labelOverride: prior.answer ?? "" };
|
|
105
|
+
}
|
|
106
|
+
if (prior.kind !== "option" || typeof prior.answer !== "string") return void 0;
|
|
107
|
+
const index = items.findIndex((it) => it.kind === "option" && it.label === prior.answer);
|
|
108
|
+
if (index < 0) return void 0;
|
|
109
|
+
return { index };
|
|
110
|
+
}
|
|
111
|
+
__name(selectConfirmedIndicator, "selectConfirmedIndicator");
|
|
112
|
+
function selectActivePreviewPaneIndex(currentTab, totalQuestions) {
|
|
113
|
+
if (totalQuestions <= 0) return 0;
|
|
114
|
+
return Math.min(currentTab, totalQuestions - 1);
|
|
115
|
+
}
|
|
116
|
+
__name(selectActivePreviewPaneIndex, "selectActivePreviewPaneIndex");
|
|
117
|
+
function selectSavedNote(notesByTab, answers, tab) {
|
|
118
|
+
const note = notesByTab.get(tab) ?? answers.get(tab)?.notes;
|
|
119
|
+
return note && note.length > 0 ? note : void 0;
|
|
120
|
+
}
|
|
121
|
+
__name(selectSavedNote, "selectSavedNote");
|
|
122
|
+
|
|
123
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-copy.ts?ext=rpiv-ask-user-question
|
|
124
|
+
var HINT_PART_ENTER = "Enter to select";
|
|
125
|
+
var HINT_PART_NAV = "\u2191/\u2193 to navigate";
|
|
126
|
+
var HINT_PART_NEW_LINE = "Shift+Enter for newline";
|
|
127
|
+
var HINT_PART_CLEAR = "Ctrl+U to clear";
|
|
128
|
+
var HINT_PART_TOGGLE = "Space to toggle";
|
|
129
|
+
var HINT_PART_NOTES = "n to add notes";
|
|
130
|
+
var HINT_PART_TAB = "Tab to switch questions";
|
|
131
|
+
var HINT_PART_CANCEL = "Esc to cancel";
|
|
132
|
+
var KEY_PLACEHOLDER = "{key}";
|
|
133
|
+
var HINT_PART_COLLAPSE_TEMPLATE = `${KEY_PLACEHOLDER} to collapse`;
|
|
134
|
+
var HINT_PART_EXPAND_TEMPLATE = `${KEY_PLACEHOLDER} to expand`;
|
|
135
|
+
var HINT_PART_COLLAPSE = HINT_PART_COLLAPSE_TEMPLATE.replace(
|
|
136
|
+
KEY_PLACEHOLDER,
|
|
137
|
+
formatKeySpecForDisplay(DEFAULT_COLLAPSE_KEY)
|
|
138
|
+
);
|
|
139
|
+
var HINT_SINGLE = [HINT_PART_ENTER, HINT_PART_NAV, HINT_PART_NOTES, HINT_PART_CANCEL].join(" \xB7 ");
|
|
140
|
+
var HINT_MULTI = [HINT_PART_ENTER, HINT_PART_NAV, HINT_PART_NOTES, HINT_PART_TAB, HINT_PART_CANCEL].join(
|
|
141
|
+
" \xB7 "
|
|
142
|
+
);
|
|
143
|
+
var COLLAPSED_HINT_TEMPLATE = [HINT_PART_EXPAND_TEMPLATE, HINT_PART_CANCEL].join(" \xB7 ");
|
|
144
|
+
var REVIEW_HEADING = "Review your answers";
|
|
145
|
+
var READY_PROMPT = "Ready to submit your answers?";
|
|
146
|
+
var INCOMPLETE_WARNING_PREFIX = "\u26A0 Answer remaining questions before submitting:";
|
|
147
|
+
|
|
148
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts?ext=rpiv-ask-user-question
|
|
149
|
+
var NOTES_HEADER = "Notes:";
|
|
150
|
+
var GLOBAL_NOTES_HEADER = "Global note:";
|
|
151
|
+
var REVIEW_GLOBAL_HINT = "n to add a note";
|
|
152
|
+
var REVIEW_NOTE_LABEL = "Note";
|
|
153
|
+
var SAVED_NOTE_LABEL = "Note for this answer:";
|
|
154
|
+
var OneLineClippedText = class {
|
|
155
|
+
constructor(text, paddingLeft = 0) {
|
|
156
|
+
this.text = text;
|
|
157
|
+
this.paddingLeft = paddingLeft;
|
|
158
|
+
}
|
|
159
|
+
text;
|
|
160
|
+
paddingLeft;
|
|
161
|
+
static {
|
|
162
|
+
__name(this, "OneLineClippedText");
|
|
163
|
+
}
|
|
164
|
+
render(width) {
|
|
165
|
+
const pad = " ".repeat(this.paddingLeft);
|
|
166
|
+
const avail = Math.max(0, width - this.paddingLeft);
|
|
167
|
+
return [pad + truncateToWidth(this.text, avail, "\u2026", false)];
|
|
168
|
+
}
|
|
169
|
+
invalidate() {
|
|
170
|
+
}
|
|
171
|
+
handleInput(_data) {
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var QuestionTabStrategy = class {
|
|
175
|
+
constructor(config) {
|
|
176
|
+
this.config = config;
|
|
177
|
+
}
|
|
178
|
+
config;
|
|
179
|
+
static {
|
|
180
|
+
__name(this, "QuestionTabStrategy");
|
|
181
|
+
}
|
|
182
|
+
/** Spacer(1) + OneLineClippedText(hint, 1) = 2 rendered rows. */
|
|
183
|
+
footerRowCount = 2;
|
|
184
|
+
headingRows(state) {
|
|
185
|
+
const out = [];
|
|
186
|
+
const question = this.config.questions[state.currentTab];
|
|
187
|
+
if (!this.config.isMulti && question?.header && question.header.length > 0) {
|
|
188
|
+
out.push(new Text(this.config.theme.bg("selectedBg", ` ${question.header} `), 1, 0));
|
|
189
|
+
out.push(new Spacer(1));
|
|
190
|
+
}
|
|
191
|
+
if (question) {
|
|
192
|
+
out.push(new Text(this.config.theme.bold(question.question), 1, 0));
|
|
193
|
+
out.push(new Spacer(1));
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
bodyComponent(state) {
|
|
198
|
+
const question = this.config.questions[state.currentTab];
|
|
199
|
+
const mso = this.config.tabsByIndex[state.currentTab]?.multiSelect;
|
|
200
|
+
if (question?.multiSelect === true && mso) return mso;
|
|
201
|
+
return this.config.getPreviewPane();
|
|
202
|
+
}
|
|
203
|
+
bodyHeight(width, _state) {
|
|
204
|
+
return this.config.getCurrentBodyHeight(width);
|
|
205
|
+
}
|
|
206
|
+
midRows(state) {
|
|
207
|
+
if (state.notesVisible) {
|
|
208
|
+
return [
|
|
209
|
+
new Text(this.config.theme.fg("muted", t("notes.header", NOTES_HEADER)), 1, 0),
|
|
210
|
+
this.config.notesInput,
|
|
211
|
+
new Spacer(1)
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
const note = selectSavedNote(state.notesByTab, state.answers, state.currentTab);
|
|
215
|
+
if (note === void 0) return [];
|
|
216
|
+
const [firstLine = "", ...rest] = note.split("\n");
|
|
217
|
+
const text = `${t("notes.saved_label", SAVED_NOTE_LABEL)} ${firstLine}${rest.length > 0 ? " \u2026" : ""}`;
|
|
218
|
+
return [new OneLineClippedText(this.config.theme.fg("dim", text), 1)];
|
|
219
|
+
}
|
|
220
|
+
footerRows(state) {
|
|
221
|
+
const question = this.config.questions[state.currentTab];
|
|
222
|
+
return [
|
|
223
|
+
new Spacer(1),
|
|
224
|
+
new OneLineClippedText(
|
|
225
|
+
this.config.theme.fg("dim", buildHintText(question, this.config.isMulti, state, this.config.collapseKey)),
|
|
226
|
+
1
|
|
227
|
+
)
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
focusedItemRowRange(width, state) {
|
|
231
|
+
const question = this.config.questions[state.currentTab];
|
|
232
|
+
const mso = this.config.tabsByIndex[state.currentTab]?.multiSelect;
|
|
233
|
+
if (question?.multiSelect === true && mso) return mso.focusedItemRowRange(width);
|
|
234
|
+
return this.config.getPreviewPane().focusedItemRowRange(width);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
var SubmitTabStrategy = class {
|
|
238
|
+
constructor(config) {
|
|
239
|
+
this.config = config;
|
|
240
|
+
}
|
|
241
|
+
config;
|
|
242
|
+
static {
|
|
243
|
+
__name(this, "SubmitTabStrategy");
|
|
244
|
+
}
|
|
245
|
+
/** Spacer(1) + Text(prompt, 1) + submitPicker(2) + OneLineClippedText(hint, 1) = 5 rendered rows. Fallback path lands at 5 via 2 Spacer(1)s in the picker slot. */
|
|
246
|
+
footerRowCount = 5;
|
|
247
|
+
headingRows(_state) {
|
|
248
|
+
return [
|
|
249
|
+
new Text(this.config.theme.bold(this.config.theme.fg("accent", t("review.heading", REVIEW_HEADING))), 1, 0),
|
|
250
|
+
new Spacer(1)
|
|
251
|
+
];
|
|
252
|
+
}
|
|
253
|
+
bodyComponent(state) {
|
|
254
|
+
const c = new Container();
|
|
255
|
+
for (let i = 0; i < this.config.questions.length; i++) {
|
|
256
|
+
const q = this.config.questions[i];
|
|
257
|
+
const a = state.answers.get(i);
|
|
258
|
+
if (!a) continue;
|
|
259
|
+
const label = q.header && q.header.length > 0 ? q.header : `Q${i + 1}`;
|
|
260
|
+
const answerText = formatAnswerScalar(a, "summary");
|
|
261
|
+
c.addChild(new Text(this.config.theme.fg("muted", ` \u25CF ${label}`), 1, 0));
|
|
262
|
+
c.addChild(
|
|
263
|
+
new Text(` ${this.config.theme.fg("muted", "\u2192")} ${this.config.theme.fg("text", answerText)}`, 1, 0)
|
|
264
|
+
);
|
|
265
|
+
if (a.notes && a.notes.length > 0) {
|
|
266
|
+
c.addChild(
|
|
267
|
+
new Text(
|
|
268
|
+
this.config.theme.fg("dim", ` ${t("notes.saved_label", SAVED_NOTE_LABEL)} ${a.notes}`),
|
|
269
|
+
1,
|
|
270
|
+
0
|
|
271
|
+
)
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const globalNote = state.notesByTab.get(this.config.questions.length);
|
|
276
|
+
if (!state.notesVisible && globalNote && globalNote.length > 0) {
|
|
277
|
+
c.addChild(new Text(this.config.theme.fg("muted", ` \u25CF ${t("review.note_label", REVIEW_NOTE_LABEL)}`), 1, 0));
|
|
278
|
+
c.addChild(
|
|
279
|
+
new Text(` ${this.config.theme.fg("muted", "\u2192")} ${this.config.theme.fg("text", globalNote)}`, 1, 0)
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
return c;
|
|
283
|
+
}
|
|
284
|
+
bodyHeight(width, state) {
|
|
285
|
+
return this.bodyComponent(state).render(width).length;
|
|
286
|
+
}
|
|
287
|
+
midRows(state) {
|
|
288
|
+
if (!state.notesVisible) return [];
|
|
289
|
+
return [
|
|
290
|
+
new Text(this.config.theme.fg("muted", t("notes.global_header", GLOBAL_NOTES_HEADER)), 1, 0),
|
|
291
|
+
this.config.notesInput,
|
|
292
|
+
new Spacer(1)
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
footerRows(state) {
|
|
296
|
+
const missing = [];
|
|
297
|
+
for (let i = 0; i < this.config.questions.length; i++) {
|
|
298
|
+
const q = this.config.questions[i];
|
|
299
|
+
if (!state.answers.has(i)) {
|
|
300
|
+
missing.push(q.header && q.header.length > 0 ? q.header : `Q${i + 1}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const promptText = missing.length === 0 ? this.config.theme.fg("muted", t("review.ready", READY_PROMPT)) : this.config.theme.fg(
|
|
304
|
+
"warning",
|
|
305
|
+
`${t("review.incomplete", INCOMPLETE_WARNING_PREFIX)} ${missing.join(", ")}`
|
|
306
|
+
);
|
|
307
|
+
const out = [new Spacer(1), new OneLineClippedText(promptText, 1)];
|
|
308
|
+
if (this.config.submitPicker) {
|
|
309
|
+
out.push(this.config.submitPicker);
|
|
310
|
+
} else {
|
|
311
|
+
out.push(new Spacer(1));
|
|
312
|
+
out.push(new Spacer(1));
|
|
313
|
+
}
|
|
314
|
+
out.push(new OneLineClippedText(this.config.theme.fg("dim", buildSubmitHintText(state)), 1));
|
|
315
|
+
return out;
|
|
316
|
+
}
|
|
317
|
+
focusedItemRowRange(_width, _state) {
|
|
318
|
+
return void 0;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
function buildHintText(question, isMulti, state, collapseKey) {
|
|
322
|
+
const parts = [t("hint.enter", HINT_PART_ENTER), t("hint.navigate", HINT_PART_NAV)];
|
|
323
|
+
if (question?.multiSelect === true) parts.push(t("hint.toggle", HINT_PART_TOGGLE));
|
|
324
|
+
if (question && !state.notesVisible && !state.inputMode) parts.push(t("hint.notes", HINT_PART_NOTES));
|
|
325
|
+
if (isMulti) parts.push(t("hint.tab", HINT_PART_TAB));
|
|
326
|
+
parts.push(t("hint.cancel", HINT_PART_CANCEL));
|
|
327
|
+
if (collapseKey !== COLLAPSE_KEY_OFF) {
|
|
328
|
+
parts.push(
|
|
329
|
+
t("hint.collapse", HINT_PART_COLLAPSE_TEMPLATE).replace(KEY_PLACEHOLDER, formatKeySpecForDisplay(collapseKey))
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
if (state.notesVisible || state.inputMode) parts.push(t("hint.newline", HINT_PART_NEW_LINE));
|
|
333
|
+
if (state.inputMode) parts.push(t("hint.clear", HINT_PART_CLEAR));
|
|
334
|
+
return parts.join(" \xB7 ");
|
|
335
|
+
}
|
|
336
|
+
__name(buildHintText, "buildHintText");
|
|
337
|
+
function buildSubmitHintText(state) {
|
|
338
|
+
const parts = [t("hint.enter", HINT_PART_ENTER), t("hint.navigate", HINT_PART_NAV)];
|
|
339
|
+
if (!state.notesVisible) parts.push(t("review.global_hint", REVIEW_GLOBAL_HINT));
|
|
340
|
+
parts.push(t("hint.cancel", HINT_PART_CANCEL));
|
|
341
|
+
if (state.notesVisible) parts.push(t("hint.newline", HINT_PART_NEW_LINE));
|
|
342
|
+
return parts.join(" \xB7 ");
|
|
343
|
+
}
|
|
344
|
+
__name(buildSubmitHintText, "buildSubmitHintText");
|
|
345
|
+
|
|
346
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts?ext=rpiv-ask-user-question
|
|
347
|
+
var MIN_SCROLL_ROWS_WITH_PINNED_HEADING = 3;
|
|
348
|
+
function renderFitsTerminal(natural, spacerRows) {
|
|
349
|
+
return spacerRows > 0 ? [...natural, ...Array(spacerRows).fill("")] : natural;
|
|
350
|
+
}
|
|
351
|
+
__name(renderFitsTerminal, "renderFitsTerminal");
|
|
352
|
+
function renderChromeOnly(natural, topFixed, bottomFixed, termRows) {
|
|
353
|
+
const chromeOnly = [...natural.slice(0, topFixed), ...natural.slice(natural.length - bottomFixed)];
|
|
354
|
+
return chromeOnly.length > termRows ? chromeOnly.slice(0, termRows) : chromeOnly;
|
|
355
|
+
}
|
|
356
|
+
__name(renderChromeOnly, "renderChromeOnly");
|
|
357
|
+
var DialogView = class {
|
|
358
|
+
static {
|
|
359
|
+
__name(this, "DialogView");
|
|
360
|
+
}
|
|
361
|
+
liveProps;
|
|
362
|
+
config;
|
|
363
|
+
questionStrategy;
|
|
364
|
+
submitStrategy;
|
|
365
|
+
maxFooterRowCount;
|
|
366
|
+
constructor(config, initialProps) {
|
|
367
|
+
this.config = config;
|
|
368
|
+
this.liveProps = initialProps;
|
|
369
|
+
this.questionStrategy = new QuestionTabStrategy({
|
|
370
|
+
theme: config.theme,
|
|
371
|
+
questions: config.questions,
|
|
372
|
+
getPreviewPane: /* @__PURE__ */ __name(() => this.liveProps.activePreviewPane, "getPreviewPane"),
|
|
373
|
+
tabsByIndex: config.tabsByIndex,
|
|
374
|
+
notesInput: config.notesInput,
|
|
375
|
+
isMulti: config.isMulti,
|
|
376
|
+
getCurrentBodyHeight: config.getCurrentBodyHeight,
|
|
377
|
+
collapseKey: config.collapseKey
|
|
378
|
+
});
|
|
379
|
+
this.submitStrategy = config.isMulti ? new SubmitTabStrategy({
|
|
380
|
+
theme: config.theme,
|
|
381
|
+
questions: config.questions,
|
|
382
|
+
submitPicker: config.submitPicker,
|
|
383
|
+
notesInput: config.notesInput
|
|
384
|
+
}) : void 0;
|
|
385
|
+
this.maxFooterRowCount = Math.max(this.questionStrategy.footerRowCount, this.submitStrategy?.footerRowCount ?? 0);
|
|
386
|
+
}
|
|
387
|
+
setProps(props) {
|
|
388
|
+
this.liveProps = props;
|
|
389
|
+
}
|
|
390
|
+
handleInput(_data) {
|
|
391
|
+
}
|
|
392
|
+
// Invalidation is driven by `QuestionnairePropsAdapter.invalidate()`, which
|
|
393
|
+
// owns the full set of renderables (binding registries + extras like
|
|
394
|
+
// `notesInput`). DialogView has no cached layout of its own.
|
|
395
|
+
invalidate() {
|
|
396
|
+
}
|
|
397
|
+
render(width) {
|
|
398
|
+
const state = this.liveProps.state;
|
|
399
|
+
const onSubmit = this.config.isMulti && state.currentTab === this.config.questions.length;
|
|
400
|
+
const strategy = onSubmit && this.submitStrategy ? this.submitStrategy : this.questionStrategy;
|
|
401
|
+
const headingRowCache = strategy.headingRows(state);
|
|
402
|
+
const headingHeight = headingRowCache.reduce((rows, c) => rows + c.render(width).length, 0);
|
|
403
|
+
const topFixed = 1 + (this.config.isMulti && this.config.tabBar ? 2 : 0) + 1;
|
|
404
|
+
const bottomFixed = 1 + strategy.footerRowCount;
|
|
405
|
+
const termRows = this.config.getTerminalRows();
|
|
406
|
+
this.allotBodyRows(
|
|
407
|
+
onSubmit,
|
|
408
|
+
termRows - topFixed - headingHeight - bottomFixed - this.rowsAroundBody(strategy, width)
|
|
409
|
+
);
|
|
410
|
+
const natural = this.buildContainerFromStrategy(strategy, headingRowCache).render(width);
|
|
411
|
+
const restingMidRows = state.notesVisible ? 0 : this.rowsAroundBody(strategy, width) - 1;
|
|
412
|
+
const spacerRows = Math.max(
|
|
413
|
+
0,
|
|
414
|
+
this.config.getBodyHeight(width) + this.maxFooterRowCount - strategy.bodyHeight(width, state) - strategy.footerRowCount - restingMidRows
|
|
415
|
+
);
|
|
416
|
+
if (natural.length <= termRows) {
|
|
417
|
+
return renderFitsTerminal(natural, Math.min(spacerRows, termRows - natural.length));
|
|
418
|
+
}
|
|
419
|
+
const pinHeading = termRows - topFixed - headingHeight - bottomFixed >= MIN_SCROLL_ROWS_WITH_PINNED_HEADING;
|
|
420
|
+
const top = pinHeading ? topFixed + headingHeight : topFixed;
|
|
421
|
+
const middleRows = natural.length - top - bottomFixed;
|
|
422
|
+
const availableMiddle = Math.max(0, termRows - top - bottomFixed);
|
|
423
|
+
if (availableMiddle === 0) {
|
|
424
|
+
return renderChromeOnly(natural, top, bottomFixed, termRows);
|
|
425
|
+
}
|
|
426
|
+
const windowRows = Math.min(availableMiddle, middleRows);
|
|
427
|
+
const rowsBeforeBody = pinHeading ? 0 : headingHeight;
|
|
428
|
+
const focusedRange = strategy.focusedItemRowRange(width, state);
|
|
429
|
+
const scrollStart = computeScrollStart(focusedRange, rowsBeforeBody, windowRows, middleRows);
|
|
430
|
+
const scrollableMiddle = natural.slice(top + scrollStart, top + scrollStart + windowRows);
|
|
431
|
+
const focusedInWindow = focusedRange && [
|
|
432
|
+
rowsBeforeBody + focusedRange[0] - scrollStart,
|
|
433
|
+
rowsBeforeBody + focusedRange[1] - scrollStart
|
|
434
|
+
];
|
|
435
|
+
decorateOverflow(
|
|
436
|
+
scrollableMiddle,
|
|
437
|
+
scrollStart > 0,
|
|
438
|
+
scrollStart + windowRows < middleRows,
|
|
439
|
+
this.config.theme,
|
|
440
|
+
focusedInWindow
|
|
441
|
+
);
|
|
442
|
+
const result = [...natural.slice(0, top), ...scrollableMiddle, ...natural.slice(natural.length - bottomFixed)];
|
|
443
|
+
return result.length > termRows ? result.slice(0, termRows) : result;
|
|
444
|
+
}
|
|
445
|
+
/** Rows between the body and the bottom border: the body's trailing Spacer(1) plus any mid rows (e.g. the notes editor). */
|
|
446
|
+
rowsAroundBody(strategy, width) {
|
|
447
|
+
return 1 + strategy.midRows(this.liveProps.state).reduce((rows, c) => rows + c.render(width).length, 0);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Hand the active question's preview pane the rows its body may use this frame, so a body
|
|
451
|
+
* taller than the space fits itself — options scroll in their column, the preview box
|
|
452
|
+
* shrinks but closes — instead of the scroll window below slicing through it (#210).
|
|
453
|
+
* Other tabs' panes are cleared so a stale allowance never skews the cross-tab height.
|
|
454
|
+
* Below `MIN_SCROLL_ROWS_WITH_PINNED_HEADING` the dialog's own scroll window takes over.
|
|
455
|
+
*/
|
|
456
|
+
allotBodyRows(onSubmit, bodyRows) {
|
|
457
|
+
const active = onSubmit || bodyRows < MIN_SCROLL_ROWS_WITH_PINNED_HEADING ? void 0 : this.liveProps.activePreviewPane;
|
|
458
|
+
for (const tab of this.config.tabsByIndex) {
|
|
459
|
+
tab.preview?.setRowAllowance?.(tab.preview === active ? bodyRows : void 0);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
buildContainerFromStrategy(strategy, headingRowCache) {
|
|
463
|
+
const { theme, isMulti, tabBar } = this.config;
|
|
464
|
+
const state = this.liveProps.state;
|
|
465
|
+
const container = new Container();
|
|
466
|
+
const border = /* @__PURE__ */ __name(() => new DynamicBorder((s) => theme.fg("accent", s)), "border");
|
|
467
|
+
container.addChild(border());
|
|
468
|
+
if (isMulti && tabBar) container.addChild(tabBar);
|
|
469
|
+
container.addChild(new Spacer(1));
|
|
470
|
+
for (const c of headingRowCache) container.addChild(c);
|
|
471
|
+
container.addChild(strategy.bodyComponent(state));
|
|
472
|
+
container.addChild(new Spacer(1));
|
|
473
|
+
for (const c of strategy.midRows(state)) container.addChild(c);
|
|
474
|
+
container.addChild(border());
|
|
475
|
+
for (const c of strategy.footerRows(state)) container.addChild(c);
|
|
476
|
+
return container;
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/component-binding.ts?ext=rpiv-ask-user-question
|
|
481
|
+
function globalBinding(spec) {
|
|
482
|
+
return {
|
|
483
|
+
apply: /* @__PURE__ */ __name((state, ctx) => spec.component.setProps(spec.select(state, ctx)), "apply"),
|
|
484
|
+
invalidate: /* @__PURE__ */ __name(() => spec.component.invalidate(), "invalidate")
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
__name(globalBinding, "globalBinding");
|
|
488
|
+
function perTabBinding(spec) {
|
|
489
|
+
return {
|
|
490
|
+
apply: /* @__PURE__ */ __name((state, ctx) => {
|
|
491
|
+
if (spec.predicate && !spec.predicate(state, ctx)) return;
|
|
492
|
+
spec.resolve(ctx.tab)?.setProps(spec.select(state, ctx));
|
|
493
|
+
}, "apply")
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
__name(perTabBinding, "perTabBinding");
|
|
497
|
+
|
|
498
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/inline-input.ts?ext=rpiv-ask-user-question
|
|
499
|
+
var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
500
|
+
function resolveCursorOffset(buffer, requested) {
|
|
501
|
+
if (requested !== void 0 && requested >= 0 && requested <= buffer.length) return requested;
|
|
502
|
+
return buffer.length;
|
|
503
|
+
}
|
|
504
|
+
__name(resolveCursorOffset, "resolveCursorOffset");
|
|
505
|
+
function buildCursorRaw(buffer, offset) {
|
|
506
|
+
const before = buffer.slice(0, offset);
|
|
507
|
+
const [firstGrapheme] = graphemeSegmenter.segment(buffer.slice(offset));
|
|
508
|
+
const rawAt = firstGrapheme ? firstGrapheme.segment : "";
|
|
509
|
+
const cursorAtLineEnd = rawAt === "\n";
|
|
510
|
+
const atCursor = rawAt === "" || rawAt === " " || cursorAtLineEnd ? "\xA0" : rawAt;
|
|
511
|
+
const after = buffer.slice(offset + (cursorAtLineEnd ? 0 : rawAt.length));
|
|
512
|
+
return `${before}${CURSOR_MARKER}\x1B[7m${atCursor}\x1B[27m${after}`;
|
|
513
|
+
}
|
|
514
|
+
__name(buildCursorRaw, "buildCursorRaw");
|
|
515
|
+
function renderInlineInputRow(opts) {
|
|
516
|
+
const { buffer, cursorOffset, rowPrefix, continuationPrefix, contentWidth, selectedText } = opts;
|
|
517
|
+
const raw = buildCursorRaw(buffer, resolveCursorOffset(buffer, cursorOffset));
|
|
518
|
+
return wrapTextWithAnsi(raw, contentWidth).map((segment, index) => {
|
|
519
|
+
const prefix = index === 0 ? rowPrefix : continuationPrefix;
|
|
520
|
+
return selectedText(`${prefix}${segment}`);
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
__name(renderInlineInputRow, "renderInlineInputRow");
|
|
524
|
+
|
|
525
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts?ext=rpiv-ask-user-question
|
|
526
|
+
var ACTIVE_POINTER = "\u276F ";
|
|
527
|
+
var INACTIVE_POINTER = " ";
|
|
528
|
+
var CHECKED = "[\u2714]";
|
|
529
|
+
var UNCHECKED = "[ ]";
|
|
530
|
+
var NUMBER_SEPARATOR = ". ";
|
|
531
|
+
var BOX_LABEL_GAP = " ";
|
|
532
|
+
var CONTINUATION_INDENT = " ";
|
|
533
|
+
var MULTI_SUBMIT_LABEL = "Submit";
|
|
534
|
+
var MultiSelectView = class {
|
|
535
|
+
constructor(theme, question) {
|
|
536
|
+
this.theme = theme;
|
|
537
|
+
this.question = question;
|
|
538
|
+
this.props = {
|
|
539
|
+
rows: [],
|
|
540
|
+
other: { active: false, checked: false, inputMode: false, inputBuffer: "", inputCursorOffset: void 0 },
|
|
541
|
+
nextActive: false,
|
|
542
|
+
nextLabel: displayLabel("next")
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
theme;
|
|
546
|
+
question;
|
|
547
|
+
static {
|
|
548
|
+
__name(this, "MultiSelectView");
|
|
549
|
+
}
|
|
550
|
+
props;
|
|
551
|
+
cachedLayout;
|
|
552
|
+
setProps(props) {
|
|
553
|
+
this.props = props;
|
|
554
|
+
this.cachedLayout = void 0;
|
|
555
|
+
}
|
|
556
|
+
handleInput(_data) {
|
|
557
|
+
}
|
|
558
|
+
invalidate() {
|
|
559
|
+
this.cachedLayout = void 0;
|
|
560
|
+
}
|
|
561
|
+
render(width) {
|
|
562
|
+
return this.layout(width).lines;
|
|
563
|
+
}
|
|
564
|
+
focusedItemRowRange(width) {
|
|
565
|
+
return this.layout(width).focusedRange;
|
|
566
|
+
}
|
|
567
|
+
naturalHeight(width) {
|
|
568
|
+
return this.layout(width).lines.length;
|
|
569
|
+
}
|
|
570
|
+
layout(width) {
|
|
571
|
+
if (this.cachedLayout?.width === width) return this.cachedLayout.value;
|
|
572
|
+
const build = { lines: [], focusedRange: [0, 0] };
|
|
573
|
+
const contentWidth = Math.max(1, width - this.prefixVisibleWidth());
|
|
574
|
+
const numberWidth = String(Math.max(1, this.question.options.length + 1)).length;
|
|
575
|
+
this.appendOptionRows(build, width, contentWidth, numberWidth);
|
|
576
|
+
const otherStart = build.lines.length;
|
|
577
|
+
build.lines.push(...this.renderOtherRow(contentWidth, numberWidth));
|
|
578
|
+
if (this.props.other.active) build.focusedRange = [otherStart, build.lines.length];
|
|
579
|
+
this.appendNextRow(build, width);
|
|
580
|
+
const value = { lines: build.lines, focusedRange: build.focusedRange };
|
|
581
|
+
this.cachedLayout = { width, value };
|
|
582
|
+
return value;
|
|
583
|
+
}
|
|
584
|
+
appendOptionRows(build, width, contentWidth, numberWidth) {
|
|
585
|
+
for (let i = 0; i < this.question.options.length; i++) {
|
|
586
|
+
const opt = this.question.options[i];
|
|
587
|
+
const row = this.props.rows[i];
|
|
588
|
+
if (!opt || !row) continue;
|
|
589
|
+
const start = build.lines.length;
|
|
590
|
+
const pointer = row.active ? this.theme.fg("accent", ACTIVE_POINTER) : INACTIVE_POINTER;
|
|
591
|
+
const box = row.checked ? this.theme.fg("accent", CHECKED) : this.theme.fg("muted", UNCHECKED);
|
|
592
|
+
const label = truncateToWidth(opt.label, contentWidth, "\u2026");
|
|
593
|
+
const styledLabel = row.active ? this.theme.fg("accent", this.theme.bold(label)) : label;
|
|
594
|
+
const number = String(i + 1).padStart(numberWidth, " ");
|
|
595
|
+
build.lines.push(
|
|
596
|
+
truncateToWidth(`${pointer}${number}${NUMBER_SEPARATOR}${box}${BOX_LABEL_GAP}${styledLabel}`, width, "")
|
|
597
|
+
);
|
|
598
|
+
if (opt.description) {
|
|
599
|
+
for (const segment of wrapTextWithAnsi(opt.description, contentWidth)) {
|
|
600
|
+
build.lines.push(CONTINUATION_INDENT + this.theme.fg("muted", segment));
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (row.active) build.focusedRange = [start, build.lines.length];
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
appendNextRow(build, width) {
|
|
607
|
+
const nextStart = build.lines.length;
|
|
608
|
+
const nextPointer = this.props.nextActive ? this.theme.fg("accent", ACTIVE_POINTER) : INACTIVE_POINTER;
|
|
609
|
+
const nextLabel = this.props.nextActive ? this.theme.fg("accent", this.theme.bold(this.props.nextLabel)) : this.props.nextLabel;
|
|
610
|
+
build.lines.push(truncateToWidth(`${nextPointer}${nextLabel}`, width, ""));
|
|
611
|
+
if (this.props.nextActive) build.focusedRange = [nextStart, build.lines.length];
|
|
612
|
+
}
|
|
613
|
+
renderOtherRow(contentWidth, numberWidth) {
|
|
614
|
+
const other = this.props.other;
|
|
615
|
+
const pointer = other.active ? this.theme.fg("accent", ACTIVE_POINTER) : INACTIVE_POINTER;
|
|
616
|
+
const box = other.checked ? this.theme.fg("accent", CHECKED) : this.theme.fg("muted", UNCHECKED);
|
|
617
|
+
const number = String(this.question.options.length + 1).padStart(numberWidth, " ");
|
|
618
|
+
const rowPrefix = `${pointer}${number}${NUMBER_SEPARATOR}${box}${BOX_LABEL_GAP}`;
|
|
619
|
+
const continuationPrefix = " ".repeat(visibleWidth(rowPrefix));
|
|
620
|
+
const selectedText = /* @__PURE__ */ __name((text) => this.theme.fg("accent", this.theme.bold(text)), "selectedText");
|
|
621
|
+
if (other.active && other.inputMode) {
|
|
622
|
+
return renderInlineInputRow({
|
|
623
|
+
buffer: other.inputBuffer,
|
|
624
|
+
cursorOffset: other.inputCursorOffset,
|
|
625
|
+
rowPrefix,
|
|
626
|
+
continuationPrefix,
|
|
627
|
+
contentWidth,
|
|
628
|
+
selectedText
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return wrapTextWithAnsi(other.inputBuffer || displayLabel("other"), contentWidth).map((segment, index) => {
|
|
632
|
+
const line = `${index === 0 ? rowPrefix : continuationPrefix}${segment}`;
|
|
633
|
+
return other.active ? selectedText(line) : line;
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
prefixVisibleWidth() {
|
|
637
|
+
const numberWidth = String(Math.max(1, this.question.options.length + 1)).length;
|
|
638
|
+
return visibleWidth(INACTIVE_POINTER) + numberWidth + visibleWidth(`${NUMBER_SEPARATOR}${UNCHECKED}${BOX_LABEL_GAP}`);
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts?ext=rpiv-ask-user-question
|
|
643
|
+
var WrappingSelect = class _WrappingSelect {
|
|
644
|
+
static {
|
|
645
|
+
__name(this, "WrappingSelect");
|
|
646
|
+
}
|
|
647
|
+
static ACTIVE_POINTER = "\u276F ";
|
|
648
|
+
static INACTIVE_POINTER = " ";
|
|
649
|
+
static NUMBER_SEPARATOR = ". ";
|
|
650
|
+
static CONFIRMED_MARK = " \u2714";
|
|
651
|
+
static MIN_CONTENT_WIDTH = 1;
|
|
652
|
+
items;
|
|
653
|
+
maxVisible;
|
|
654
|
+
theme;
|
|
655
|
+
numberStartOffset;
|
|
656
|
+
totalItemsForNumbering;
|
|
657
|
+
selectedIndex = 0;
|
|
658
|
+
focused = true;
|
|
659
|
+
inputBuffer = "";
|
|
660
|
+
inputCursorOffset = void 0;
|
|
661
|
+
/**
|
|
662
|
+
* Index of the row that was previously confirmed for this list (e.g. the user's prior
|
|
663
|
+
* answer when re-entering a multi-question tab). Renders `<label> ✔` in the active-row
|
|
664
|
+
* styling but WITHOUT the `❯` pointer — pointer is reserved for the live cursor. When
|
|
665
|
+
* `selectedIndex === confirmedIndex && focused`, the active rendering wins (no double-mark).
|
|
666
|
+
*/
|
|
667
|
+
confirmedIndex = void 0;
|
|
668
|
+
/**
|
|
669
|
+
* When set together with `confirmedIndex`, replaces the row's static label at render time.
|
|
670
|
+
* Used for the `kind: "other"` sentinel — its label is "Type something." but if the user's
|
|
671
|
+
* prior answer was custom text, we render that text instead (e.g. `4. Hello ✔`).
|
|
672
|
+
*/
|
|
673
|
+
confirmedLabelOverride = void 0;
|
|
674
|
+
constructor(items, maxVisible, theme, options = {}) {
|
|
675
|
+
this.items = items;
|
|
676
|
+
this.maxVisible = Math.max(1, maxVisible);
|
|
677
|
+
this.theme = theme;
|
|
678
|
+
this.numberStartOffset = options.numberStartOffset ?? 0;
|
|
679
|
+
this.totalItemsForNumbering = options.totalItemsForNumbering ?? items.length;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Update the numbering offset + total padding width without rebuilding the component.
|
|
683
|
+
* Lets the host realign the number column when the underlying item set changes.
|
|
684
|
+
*/
|
|
685
|
+
setNumbering(numberStartOffset, totalItemsForNumbering) {
|
|
686
|
+
this.numberStartOffset = numberStartOffset;
|
|
687
|
+
this.totalItemsForNumbering = Math.max(1, totalItemsForNumbering);
|
|
688
|
+
}
|
|
689
|
+
setSelectedIndex(index) {
|
|
690
|
+
this.selectedIndex = Math.max(0, Math.min(index, this.items.length - 1));
|
|
691
|
+
}
|
|
692
|
+
setFocused(focused) {
|
|
693
|
+
this.focused = focused;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Mark a previously-confirmed row. Pass `undefined` to clear. `labelOverride` replaces
|
|
697
|
+
* the row's static `item.label` at render time — used for the `kind: "other"` sentinel so
|
|
698
|
+
* the row reads `Hello ✔` instead of `Type something. ✔` when the prior answer was custom
|
|
699
|
+
* text.
|
|
700
|
+
*/
|
|
701
|
+
setConfirmedIndex(index, labelOverride) {
|
|
702
|
+
if (index === void 0) {
|
|
703
|
+
this.confirmedIndex = void 0;
|
|
704
|
+
this.confirmedLabelOverride = void 0;
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
this.confirmedIndex = Math.max(0, Math.min(index, this.items.length - 1));
|
|
708
|
+
this.confirmedLabelOverride = labelOverride;
|
|
709
|
+
}
|
|
710
|
+
setInputBuffer(text) {
|
|
711
|
+
this.inputBuffer = text;
|
|
712
|
+
}
|
|
713
|
+
/** Set the cursor offset for the inline input row. `undefined` → end-of-buffer fallback. */
|
|
714
|
+
setInputCursorOffset(offset) {
|
|
715
|
+
this.inputCursorOffset = offset;
|
|
716
|
+
}
|
|
717
|
+
/** Intentionally empty — input is routed at the container level. */
|
|
718
|
+
handleInput(_data) {
|
|
719
|
+
}
|
|
720
|
+
invalidate() {
|
|
721
|
+
}
|
|
722
|
+
render(width) {
|
|
723
|
+
if (this.items.length === 0) return [];
|
|
724
|
+
const { startIndex, endIndex } = this.computeVisibleWindow();
|
|
725
|
+
const numberWidth = String(Math.max(1, this.totalItemsForNumbering)).length;
|
|
726
|
+
const lines = [];
|
|
727
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
728
|
+
const item = this.items[i];
|
|
729
|
+
if (!item) continue;
|
|
730
|
+
const isActive = i === this.selectedIndex && this.focused;
|
|
731
|
+
lines.push(...this.renderItem(item, i, isActive, width, numberWidth));
|
|
732
|
+
}
|
|
733
|
+
if (this.hasItemsOutsideWindow(startIndex, endIndex)) {
|
|
734
|
+
lines.push(this.theme.scrollInfo(` (${this.selectedIndex + 1}/${this.items.length})`));
|
|
735
|
+
}
|
|
736
|
+
return lines;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Returns the [startRow, endRow) range of the focused (selected) item within
|
|
740
|
+
* the output of `render(width)`. Computed by iterating the visible window and
|
|
741
|
+
* summing per-item row counts — O(maxVisible) per call.
|
|
742
|
+
*/
|
|
743
|
+
focusedItemRowRange(width) {
|
|
744
|
+
if (this.items.length === 0) return [0, 0];
|
|
745
|
+
const { startIndex, endIndex } = this.computeVisibleWindow();
|
|
746
|
+
const numberWidth = String(Math.max(1, this.totalItemsForNumbering)).length;
|
|
747
|
+
let row = 0;
|
|
748
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
749
|
+
const item = this.items[i];
|
|
750
|
+
if (!item) continue;
|
|
751
|
+
const isActive = i === this.selectedIndex && this.focused;
|
|
752
|
+
const itemRowCount = this.computeItemRowCount(item, i, isActive, width, numberWidth);
|
|
753
|
+
if (i === this.selectedIndex) {
|
|
754
|
+
return [row, row + itemRowCount];
|
|
755
|
+
}
|
|
756
|
+
row += itemRowCount;
|
|
757
|
+
}
|
|
758
|
+
return [0, 1];
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Per-item row count. Delegates to `renderItem().length` so `renderItem` remains
|
|
762
|
+
* the single source of truth for per-item row math — eliminates the prior shadow-copy
|
|
763
|
+
* that risked silent miscounts when new `kind` values branch in `renderItem` but not here.
|
|
764
|
+
*/
|
|
765
|
+
computeItemRowCount(item, index, isActive, width, numberWidth) {
|
|
766
|
+
return this.renderItem(item, index, isActive, width, numberWidth).length;
|
|
767
|
+
}
|
|
768
|
+
computeVisibleWindow() {
|
|
769
|
+
const half = Math.floor(this.maxVisible / 2);
|
|
770
|
+
const startIndex = Math.max(0, Math.min(this.selectedIndex - half, this.items.length - this.maxVisible));
|
|
771
|
+
const endIndex = Math.min(startIndex + this.maxVisible, this.items.length);
|
|
772
|
+
return { startIndex, endIndex };
|
|
773
|
+
}
|
|
774
|
+
hasItemsOutsideWindow(startIndex, endIndex) {
|
|
775
|
+
return startIndex > 0 || endIndex < this.items.length;
|
|
776
|
+
}
|
|
777
|
+
renderItem(item, index, isActive, width, numberWidth) {
|
|
778
|
+
const rowPrefix = this.buildRowPrefix(index, isActive, numberWidth);
|
|
779
|
+
const continuationPrefix = " ".repeat(visibleWidth(rowPrefix));
|
|
780
|
+
const contentWidth = Math.max(_WrappingSelect.MIN_CONTENT_WIDTH, width - visibleWidth(rowPrefix));
|
|
781
|
+
if (this.shouldRenderAsInlineInput(item, isActive)) {
|
|
782
|
+
return this.renderInlineInputRow(rowPrefix, continuationPrefix, contentWidth);
|
|
783
|
+
}
|
|
784
|
+
const { label, isConfirmed } = this.deriveConfirmedState(item, index);
|
|
785
|
+
const applySelectedStyle = isActive || isConfirmed;
|
|
786
|
+
return [
|
|
787
|
+
...this.renderLabelBlock(label, rowPrefix, continuationPrefix, contentWidth, applySelectedStyle),
|
|
788
|
+
...this.renderDescriptionBlock(item.description, continuationPrefix, contentWidth)
|
|
789
|
+
];
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Derive the confirmed-rendering facts for one row: the final label (custom draft
|
|
793
|
+
* or confirmed-override aware) and whether the ✔ mark applies.
|
|
794
|
+
*/
|
|
795
|
+
deriveConfirmedState(item, index) {
|
|
796
|
+
const customDraft = item.kind === "other" ? this.inputBuffer : void 0;
|
|
797
|
+
const customDraftDiffersFromConfirmed = item.kind === "other" && customDraft !== "" && index === this.confirmedIndex && customDraft !== (this.confirmedLabelOverride ?? "");
|
|
798
|
+
const isConfirmed = index === this.confirmedIndex && !customDraftDiffersFromConfirmed;
|
|
799
|
+
const baseLabel = customDraft ? customDraft : item.label;
|
|
800
|
+
const label = isConfirmed ? `${this.confirmedLabelOverride ?? baseLabel}${_WrappingSelect.CONFIRMED_MARK}` : baseLabel;
|
|
801
|
+
return { label, isConfirmed };
|
|
802
|
+
}
|
|
803
|
+
buildRowPrefix(index, isActive, numberWidth) {
|
|
804
|
+
const pointer = isActive ? _WrappingSelect.ACTIVE_POINTER : _WrappingSelect.INACTIVE_POINTER;
|
|
805
|
+
const displayNumber = this.numberStartOffset + index + 1;
|
|
806
|
+
const paddedNumber = String(displayNumber).padStart(numberWidth, " ");
|
|
807
|
+
return `${pointer}${paddedNumber}${_WrappingSelect.NUMBER_SEPARATOR}`;
|
|
808
|
+
}
|
|
809
|
+
shouldRenderAsInlineInput(item, isActive) {
|
|
810
|
+
return item.kind === "other" && isActive;
|
|
811
|
+
}
|
|
812
|
+
/** Render the inline editor across logical and visually wrapped lines. */
|
|
813
|
+
renderInlineInputRow(rowPrefix, continuationPrefix, contentWidth) {
|
|
814
|
+
return renderInlineInputRow({
|
|
815
|
+
buffer: this.inputBuffer,
|
|
816
|
+
cursorOffset: this.inputCursorOffset,
|
|
817
|
+
rowPrefix,
|
|
818
|
+
continuationPrefix,
|
|
819
|
+
contentWidth,
|
|
820
|
+
selectedText: this.theme.selectedText
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
renderLabelBlock(label, rowPrefix, continuationPrefix, contentWidth, applySelectedStyle) {
|
|
824
|
+
const wrapped = wrapTextWithAnsi(label, contentWidth);
|
|
825
|
+
return wrapped.map((segment, index) => {
|
|
826
|
+
const prefix = index === 0 ? rowPrefix : continuationPrefix;
|
|
827
|
+
const line = `${prefix}${segment}`;
|
|
828
|
+
return applySelectedStyle ? this.theme.selectedText(line) : line;
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
renderDescriptionBlock(description, continuationPrefix, contentWidth) {
|
|
832
|
+
if (!description) return [];
|
|
833
|
+
const wrapped = wrapTextWithAnsi(description, contentWidth);
|
|
834
|
+
return wrapped.map((segment) => `${continuationPrefix}${this.theme.description(segment)}`);
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/option-list-view.ts?ext=rpiv-ask-user-question
|
|
839
|
+
var MAX_VISIBLE_OPTIONS = 10;
|
|
840
|
+
var OptionListView = class {
|
|
841
|
+
static {
|
|
842
|
+
__name(this, "OptionListView");
|
|
843
|
+
}
|
|
844
|
+
select;
|
|
845
|
+
constructor(config) {
|
|
846
|
+
this.select = new WrappingSelect(config.items, Math.min(config.items.length, MAX_VISIBLE_OPTIONS), config.theme, {
|
|
847
|
+
numberStartOffset: 0,
|
|
848
|
+
totalItemsForNumbering: config.items.length
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
setProps(props) {
|
|
852
|
+
this.select.setSelectedIndex(props.selectedIndex);
|
|
853
|
+
this.select.setFocused(props.focused);
|
|
854
|
+
this.select.setConfirmedIndex(props.confirmed?.index, props.confirmed?.labelOverride);
|
|
855
|
+
this.select.setInputBuffer(props.inputBuffer);
|
|
856
|
+
this.select.setInputCursorOffset(props.inputCursorOffset);
|
|
857
|
+
}
|
|
858
|
+
handleInput(_data) {
|
|
859
|
+
}
|
|
860
|
+
invalidate() {
|
|
861
|
+
this.select.invalidate();
|
|
862
|
+
}
|
|
863
|
+
render(width) {
|
|
864
|
+
return this.select.render(width);
|
|
865
|
+
}
|
|
866
|
+
focusedItemRowRange(width) {
|
|
867
|
+
return this.select.focusedItemRowRange(width);
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-box-renderer.ts?ext=rpiv-ask-user-question
|
|
872
|
+
var ANSI_SGR_RE = /\x1b\[[0-9;]*m/g;
|
|
873
|
+
var ANSI_OSC8_RE = /\x1b\]8;[^\x07\x1b]*(?:\x07|\x1b\\)/g;
|
|
874
|
+
var FENCE_MARKER_RE = /^`{3}/;
|
|
875
|
+
var BORDER_VERTICAL_OVERHEAD = 2;
|
|
876
|
+
var BORDER_HORIZONTAL_OVERHEAD = 2;
|
|
877
|
+
var BORDER_INNER_PADDING_HORIZONTAL = 1;
|
|
878
|
+
var BOX_MIN_CONTENT_WIDTH = 40;
|
|
879
|
+
function stripFenceMarkers(lines) {
|
|
880
|
+
return lines.filter((line) => {
|
|
881
|
+
const clean = line.replace(ANSI_SGR_RE, "").replace(ANSI_OSC8_RE, "");
|
|
882
|
+
return !FENCE_MARKER_RE.test(clean);
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
__name(stripFenceMarkers, "stripFenceMarkers");
|
|
886
|
+
function renderBorderedBox(lines, width, colorFn, hidden = 0) {
|
|
887
|
+
const dashSpan = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD);
|
|
888
|
+
const contentInner = Math.max(1, dashSpan - 2 * BORDER_INNER_PADDING_HORIZONTAL);
|
|
889
|
+
const pad = " ".repeat(BORDER_INNER_PADDING_HORIZONTAL);
|
|
890
|
+
const top = colorFn(`\u250C${"\u2500".repeat(dashSpan)}\u2510`);
|
|
891
|
+
const out = [top];
|
|
892
|
+
for (const line of lines) {
|
|
893
|
+
const padded = truncateToWidth(line, contentInner, "", true);
|
|
894
|
+
out.push(`${colorFn("\u2502")}${pad}${padded}${pad}${colorFn("\u2502")}`);
|
|
895
|
+
}
|
|
896
|
+
if (hidden > 0) {
|
|
897
|
+
const indicator = ` \u2702 \u2500\u2500 ${hidden} lines hidden \u2500\u2500 `;
|
|
898
|
+
const space = dashSpan - indicator.length;
|
|
899
|
+
const leftFill = "\u2500".repeat(Math.max(0, Math.floor(space / 2)));
|
|
900
|
+
const rightFill = "\u2500".repeat(Math.max(0, dashSpan - leftFill.length - indicator.length));
|
|
901
|
+
out.push(colorFn(`\u2514${leftFill}${indicator}${rightFill}\u2518`));
|
|
902
|
+
} else {
|
|
903
|
+
out.push(colorFn(`\u2514${"\u2500".repeat(dashSpan)}\u2518`));
|
|
904
|
+
}
|
|
905
|
+
return out;
|
|
906
|
+
}
|
|
907
|
+
__name(renderBorderedBox, "renderBorderedBox");
|
|
908
|
+
function computeBoxDimensions(contentLines, maxInnerWidth) {
|
|
909
|
+
let widest = Math.min(BOX_MIN_CONTENT_WIDTH, maxInnerWidth);
|
|
910
|
+
for (const line of contentLines) {
|
|
911
|
+
const w = visibleWidth(line.replace(/\s+$/, ""));
|
|
912
|
+
if (w > widest) widest = w;
|
|
913
|
+
}
|
|
914
|
+
const innerWidth = Math.min(widest, maxInnerWidth);
|
|
915
|
+
const boxWidth = innerWidth + BORDER_HORIZONTAL_OVERHEAD + 2 * BORDER_INNER_PADDING_HORIZONTAL;
|
|
916
|
+
return { innerWidth, boxWidth };
|
|
917
|
+
}
|
|
918
|
+
__name(computeBoxDimensions, "computeBoxDimensions");
|
|
919
|
+
|
|
920
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/markdown-content-cache.ts?ext=rpiv-ask-user-question
|
|
921
|
+
var MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE = 20;
|
|
922
|
+
var MAX_PREVIEW_HEIGHT_STACKED = 15;
|
|
923
|
+
var NO_PREVIEW_TEXT = "No preview available";
|
|
924
|
+
var NOTES_AFFORDANCE_OVERHEAD = 2;
|
|
925
|
+
var MarkdownContentCache = class {
|
|
926
|
+
static {
|
|
927
|
+
__name(this, "MarkdownContentCache");
|
|
928
|
+
}
|
|
929
|
+
previewTexts;
|
|
930
|
+
markdownCache;
|
|
931
|
+
cachedWidth;
|
|
932
|
+
theme;
|
|
933
|
+
markdownTheme;
|
|
934
|
+
constructor(question, theme, markdownTheme) {
|
|
935
|
+
this.theme = theme;
|
|
936
|
+
this.markdownTheme = markdownTheme;
|
|
937
|
+
this.previewTexts = /* @__PURE__ */ new Map();
|
|
938
|
+
for (let i = 0; i < question.options.length; i++) {
|
|
939
|
+
const raw = question.options[i]?.preview;
|
|
940
|
+
if (raw && raw.length > 0) this.previewTexts.set(i, raw);
|
|
941
|
+
}
|
|
942
|
+
this.markdownCache = /* @__PURE__ */ new Map();
|
|
943
|
+
}
|
|
944
|
+
hasAnyPreview() {
|
|
945
|
+
return this.previewTexts.size > 0;
|
|
946
|
+
}
|
|
947
|
+
has(optionIndex) {
|
|
948
|
+
return this.previewTexts.has(optionIndex);
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Compute the body lines for a given option at a given inner width. Width changes
|
|
952
|
+
* invalidate the per-Markdown render cache.
|
|
953
|
+
*/
|
|
954
|
+
bodyFor(optionIndex, innerWidth) {
|
|
955
|
+
if (this.cachedWidth !== innerWidth) {
|
|
956
|
+
for (const md2 of this.markdownCache.values()) md2.invalidate();
|
|
957
|
+
this.cachedWidth = innerWidth;
|
|
958
|
+
}
|
|
959
|
+
const text = this.previewTexts.get(optionIndex);
|
|
960
|
+
if (!text) {
|
|
961
|
+
const placeholder = this.theme.fg("dim", t("preview.no_preview", NO_PREVIEW_TEXT));
|
|
962
|
+
const pad = Math.max(0, innerWidth - visibleWidth(placeholder));
|
|
963
|
+
return [placeholder + " ".repeat(pad)];
|
|
964
|
+
}
|
|
965
|
+
let md = this.markdownCache.get(optionIndex);
|
|
966
|
+
if (!md) {
|
|
967
|
+
md = new Markdown(text, 0, 0, this.markdownTheme);
|
|
968
|
+
this.markdownCache.set(optionIndex, md);
|
|
969
|
+
}
|
|
970
|
+
return stripFenceMarkers(md.render(innerWidth));
|
|
971
|
+
}
|
|
972
|
+
invalidate() {
|
|
973
|
+
for (const md of this.markdownCache.values()) md.invalidate();
|
|
974
|
+
this.cachedWidth = void 0;
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
|
|
978
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts?ext=rpiv-ask-user-question
|
|
979
|
+
var NOTES_AFFORDANCE_TEXT = "Notes: press n to add notes";
|
|
980
|
+
var NOTES_EDIT_AFFORDANCE_TEXT = "Note saved: press n to edit";
|
|
981
|
+
function affordanceText(affordance) {
|
|
982
|
+
return affordance === "edit" ? t("preview.notes_edit_affordance", NOTES_EDIT_AFFORDANCE_TEXT) : t("preview.notes_affordance", NOTES_AFFORDANCE_TEXT);
|
|
983
|
+
}
|
|
984
|
+
__name(affordanceText, "affordanceText");
|
|
985
|
+
function contentBudgetFor(mode) {
|
|
986
|
+
const cap = mode === "side-by-side" ? MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE : MAX_PREVIEW_HEIGHT_STACKED;
|
|
987
|
+
return Math.max(1, cap - BORDER_VERTICAL_OVERHEAD - NOTES_AFFORDANCE_OVERHEAD);
|
|
988
|
+
}
|
|
989
|
+
__name(contentBudgetFor, "contentBudgetFor");
|
|
990
|
+
function fitBlock(rawRows, mode, maxRows) {
|
|
991
|
+
const contentRows = Math.min(rawRows, contentBudgetFor(mode));
|
|
992
|
+
if (maxRows === void 0 || BORDER_VERTICAL_OVERHEAD + contentRows + NOTES_AFFORDANCE_OVERHEAD <= maxRows) {
|
|
993
|
+
return { contentRows, affordance: true };
|
|
994
|
+
}
|
|
995
|
+
const fitted = Math.min(contentRows, maxRows - BORDER_VERTICAL_OVERHEAD);
|
|
996
|
+
return fitted >= 1 ? { contentRows: fitted, affordance: false } : void 0;
|
|
997
|
+
}
|
|
998
|
+
__name(fitBlock, "fitBlock");
|
|
999
|
+
function innerWidthFor(width) {
|
|
1000
|
+
return Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL);
|
|
1001
|
+
}
|
|
1002
|
+
__name(innerWidthFor, "innerWidthFor");
|
|
1003
|
+
var PreviewBlockRenderer = class {
|
|
1004
|
+
static {
|
|
1005
|
+
__name(this, "PreviewBlockRenderer");
|
|
1006
|
+
}
|
|
1007
|
+
theme;
|
|
1008
|
+
cache;
|
|
1009
|
+
constructor(config) {
|
|
1010
|
+
this.theme = config.theme;
|
|
1011
|
+
this.cache = new MarkdownContentCache(config.question, config.theme, config.markdownTheme);
|
|
1012
|
+
}
|
|
1013
|
+
hasAnyPreview() {
|
|
1014
|
+
return this.cache.hasAnyPreview();
|
|
1015
|
+
}
|
|
1016
|
+
has(optionIndex) {
|
|
1017
|
+
return this.cache.has(optionIndex);
|
|
1018
|
+
}
|
|
1019
|
+
invalidate() {
|
|
1020
|
+
this.cache.invalidate();
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Height contribution of the preview block — always equal to `renderBlock(...).length`
|
|
1024
|
+
* for the same arguments (the affordance overhead is not gated by the `NotesAffordance`).
|
|
1025
|
+
* `maxRows` is the row allowance the dialog can give the block; see `fitBlock`.
|
|
1026
|
+
*/
|
|
1027
|
+
blockHeight(width, optionIndex, mode, maxRows) {
|
|
1028
|
+
const rawRows = this.cache.bodyFor(optionIndex, innerWidthFor(width)).length;
|
|
1029
|
+
const fit = fitBlock(rawRows, mode, maxRows);
|
|
1030
|
+
return fit ? BORDER_VERTICAL_OVERHEAD + fit.contentRows + (fit.affordance ? NOTES_AFFORDANCE_OVERHEAD : 0) : 0;
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Render the preview block at `width`: bordered box, then (when it fits) a blank separator
|
|
1034
|
+
* and the notes-affordance row. `affordance` picks the text (`none` blanks it), shown only when
|
|
1035
|
+
* the option carries a preview; the row is emitted (empty when gated) whenever the block keeps
|
|
1036
|
+
* it, so the row count only depends on the fit. Under a `maxRows` allowance the box always closes and its bottom border
|
|
1037
|
+
* counts the lines left out; with no room for a box the block is empty.
|
|
1038
|
+
*/
|
|
1039
|
+
renderBlock(width, optionIndex, mode, affordance, maxRows) {
|
|
1040
|
+
const maxInnerWidth = innerWidthFor(width);
|
|
1041
|
+
const raw = this.cache.bodyFor(optionIndex, maxInnerWidth);
|
|
1042
|
+
const fit = fitBlock(raw.length, mode, maxRows);
|
|
1043
|
+
if (!fit) return [];
|
|
1044
|
+
const hidden = raw.length - fit.contentRows;
|
|
1045
|
+
const contentLines = hidden > 0 ? raw.slice(0, fit.contentRows) : raw;
|
|
1046
|
+
const { boxWidth } = computeBoxDimensions(contentLines, maxInnerWidth);
|
|
1047
|
+
const colorFn = /* @__PURE__ */ __name((s) => this.theme.fg("accent", s), "colorFn");
|
|
1048
|
+
const boxedLines = renderBorderedBox(contentLines, boxWidth, colorFn, hidden);
|
|
1049
|
+
if (!fit.affordance) return boxedLines;
|
|
1050
|
+
const text = affordance !== "none" && this.cache.has(optionIndex) ? affordanceText(affordance) : "";
|
|
1051
|
+
return [...boxedLines, "", text ? this.theme.fg("muted", text) : ""];
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-layout-decider.ts?ext=rpiv-ask-user-question
|
|
1056
|
+
var PREVIEW_MIN_WIDTH = 100;
|
|
1057
|
+
var PREVIEW_COLUMN_GAP = 2;
|
|
1058
|
+
var PREVIEW_PADDING_LEFT = 1;
|
|
1059
|
+
var STACKED_GAP_ROWS = 1;
|
|
1060
|
+
var MIN_LEFT = 30;
|
|
1061
|
+
var MAX_LEFT_RATIO = 0.5;
|
|
1062
|
+
var MIN_PREVIEW_WIDTH = 45;
|
|
1063
|
+
var CONFIRMED_OVERHEAD = 2;
|
|
1064
|
+
function decideLayout(terminalWidth, paneWidth) {
|
|
1065
|
+
return terminalWidth >= PREVIEW_MIN_WIDTH && paneWidth >= PREVIEW_MIN_WIDTH ? "side-by-side" : "stacked";
|
|
1066
|
+
}
|
|
1067
|
+
__name(decideLayout, "decideLayout");
|
|
1068
|
+
function adaptiveLeftWidth(items, totalForNumbering, paneWidth) {
|
|
1069
|
+
const prefixW = String(Math.max(1, totalForNumbering)).length + 4;
|
|
1070
|
+
const confirmedOverhead = CONFIRMED_OVERHEAD;
|
|
1071
|
+
let maxLabel = 0;
|
|
1072
|
+
for (const item of items) {
|
|
1073
|
+
const w = visibleWidth(item.label);
|
|
1074
|
+
if (w > maxLabel) maxLabel = w;
|
|
1075
|
+
}
|
|
1076
|
+
const desired = maxLabel + prefixW + confirmedOverhead;
|
|
1077
|
+
const ratioCapped = Math.min(desired, Math.floor(paneWidth * MAX_LEFT_RATIO));
|
|
1078
|
+
const available = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;
|
|
1079
|
+
return Math.max(MIN_LEFT, Math.min(ratioCapped, Math.max(1, available)));
|
|
1080
|
+
}
|
|
1081
|
+
__name(adaptiveLeftWidth, "adaptiveLeftWidth");
|
|
1082
|
+
function crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth) {
|
|
1083
|
+
let max = MIN_LEFT;
|
|
1084
|
+
for (let i = 0; i < tabs.length; i++) {
|
|
1085
|
+
const items = itemsByTab[i] ?? [];
|
|
1086
|
+
const totalForNumbering = items.length;
|
|
1087
|
+
const tabWidth = adaptiveLeftWidth(items, totalForNumbering, paneWidth);
|
|
1088
|
+
if (tabWidth > max) max = tabWidth;
|
|
1089
|
+
}
|
|
1090
|
+
return max;
|
|
1091
|
+
}
|
|
1092
|
+
__name(crossTabMaxLeftWidth, "crossTabMaxLeftWidth");
|
|
1093
|
+
function previewSourceWidth(question) {
|
|
1094
|
+
let max = 0;
|
|
1095
|
+
for (const option of question.options) {
|
|
1096
|
+
const text = option.preview;
|
|
1097
|
+
if (!text) continue;
|
|
1098
|
+
for (const line of text.split("\n")) {
|
|
1099
|
+
const w = visibleWidth(line);
|
|
1100
|
+
if (w > max) max = w;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
return max;
|
|
1104
|
+
}
|
|
1105
|
+
__name(previewSourceWidth, "previewSourceWidth");
|
|
1106
|
+
function crossTabPreviewBudget(questions, paneWidth) {
|
|
1107
|
+
let max = MIN_PREVIEW_WIDTH;
|
|
1108
|
+
for (const question of questions) {
|
|
1109
|
+
const rawWidth = previewSourceWidth(question);
|
|
1110
|
+
const capped = Math.min(rawWidth, paneWidth - PREVIEW_COLUMN_GAP - MIN_LEFT);
|
|
1111
|
+
const budget = capped + BORDER_HORIZONTAL_OVERHEAD + 2 * BORDER_INNER_PADDING_HORIZONTAL + PREVIEW_PADDING_LEFT;
|
|
1112
|
+
if (budget > max) max = budget;
|
|
1113
|
+
}
|
|
1114
|
+
return max;
|
|
1115
|
+
}
|
|
1116
|
+
__name(crossTabPreviewBudget, "crossTabPreviewBudget");
|
|
1117
|
+
function crossTabLeftWidthWithDonation(tabs, itemsByTab, questions, paneWidth) {
|
|
1118
|
+
const labelDriven = crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth);
|
|
1119
|
+
const previewBudget = crossTabPreviewBudget(questions, paneWidth);
|
|
1120
|
+
const slackDonation = paneWidth - PREVIEW_COLUMN_GAP - previewBudget;
|
|
1121
|
+
const previewSafetyCeiling = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;
|
|
1122
|
+
const ratioCeiling = Math.floor(paneWidth * MAX_LEFT_RATIO);
|
|
1123
|
+
const ceiling = Math.min(previewSafetyCeiling, ratioCeiling);
|
|
1124
|
+
return Math.min(Math.max(labelDriven, slackDonation), Math.max(1, ceiling));
|
|
1125
|
+
}
|
|
1126
|
+
__name(crossTabLeftWidthWithDonation, "crossTabLeftWidthWithDonation");
|
|
1127
|
+
function columnWidths(paneWidth, adaptiveLeft) {
|
|
1128
|
+
const gap = PREVIEW_COLUMN_GAP;
|
|
1129
|
+
const leftWidth = Math.min(adaptiveLeft, Math.max(1, paneWidth - gap - 1));
|
|
1130
|
+
const rightWidth = Math.max(1, paneWidth - leftWidth - gap);
|
|
1131
|
+
return { leftWidth, rightWidth, gap };
|
|
1132
|
+
}
|
|
1133
|
+
__name(columnWidths, "columnWidths");
|
|
1134
|
+
function bodyWidths(paneWidth, mode, adaptiveLeft) {
|
|
1135
|
+
if (mode === "stacked") return { optionsWidth: paneWidth, previewWidth: paneWidth };
|
|
1136
|
+
const { leftWidth, rightWidth } = columnWidths(paneWidth, adaptiveLeft);
|
|
1137
|
+
return { optionsWidth: leftWidth, previewWidth: Math.max(1, rightWidth - PREVIEW_PADDING_LEFT) };
|
|
1138
|
+
}
|
|
1139
|
+
__name(bodyWidths, "bodyWidths");
|
|
1140
|
+
|
|
1141
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-pane.ts?ext=rpiv-ask-user-question
|
|
1142
|
+
var MIN_BOX_ROWS = 3;
|
|
1143
|
+
var PreviewPane = class {
|
|
1144
|
+
static {
|
|
1145
|
+
__name(this, "PreviewPane");
|
|
1146
|
+
}
|
|
1147
|
+
question;
|
|
1148
|
+
getTerminalWidth;
|
|
1149
|
+
optionListView;
|
|
1150
|
+
previewBlock;
|
|
1151
|
+
theme;
|
|
1152
|
+
props;
|
|
1153
|
+
/**
|
|
1154
|
+
* Rows the dialog can give this body this frame, set by `DialogView` before each render;
|
|
1155
|
+
* undefined when unconstrained. A body taller than this fits itself instead of being cut
|
|
1156
|
+
* by the dialog's scroll window: the options scroll within their column around the focused
|
|
1157
|
+
* option, and the preview box shrinks but always closes (#210).
|
|
1158
|
+
*/
|
|
1159
|
+
rowAllowance;
|
|
1160
|
+
/**
|
|
1161
|
+
* Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`
|
|
1162
|
+
* before any render. Initialized to a throwing sentinel so missing injection is a hard fail
|
|
1163
|
+
* rather than a silent fallback to a magic constant — render is illegal until injected.
|
|
1164
|
+
*/
|
|
1165
|
+
globalLeftWidth = /* @__PURE__ */ __name(() => {
|
|
1166
|
+
throw new Error("PreviewPane.setGlobalLeftWidth must be called before render()");
|
|
1167
|
+
}, "globalLeftWidth");
|
|
1168
|
+
constructor(config) {
|
|
1169
|
+
this.question = config.question;
|
|
1170
|
+
this.getTerminalWidth = config.getTerminalWidth;
|
|
1171
|
+
this.optionListView = config.optionListView;
|
|
1172
|
+
this.previewBlock = config.previewBlock;
|
|
1173
|
+
this.theme = config.theme;
|
|
1174
|
+
this.props = { notesVisible: false, hasNote: false, selectedIndex: 0, focused: false, inputMode: false };
|
|
1175
|
+
}
|
|
1176
|
+
setGlobalLeftWidth(getter) {
|
|
1177
|
+
this.globalLeftWidth = getter;
|
|
1178
|
+
}
|
|
1179
|
+
getAdaptiveLeft(paneWidth) {
|
|
1180
|
+
return this.globalLeftWidth(paneWidth);
|
|
1181
|
+
}
|
|
1182
|
+
setProps(props) {
|
|
1183
|
+
this.props = props;
|
|
1184
|
+
}
|
|
1185
|
+
/** Render-time geometry from `DialogView` (not canonical state, so not a prop). */
|
|
1186
|
+
/** The notes affordance under the preview: shown while focused with the notes editor closed. */
|
|
1187
|
+
notesAffordance() {
|
|
1188
|
+
const { focused, notesVisible, hasNote } = this.props;
|
|
1189
|
+
if (!focused || notesVisible) return "none";
|
|
1190
|
+
return hasNote ? "edit" : "add";
|
|
1191
|
+
}
|
|
1192
|
+
setRowAllowance(rows) {
|
|
1193
|
+
this.rowAllowance = rows;
|
|
1194
|
+
}
|
|
1195
|
+
handleInput(_data) {
|
|
1196
|
+
}
|
|
1197
|
+
invalidate() {
|
|
1198
|
+
this.previewBlock.invalidate();
|
|
1199
|
+
this.optionListView.invalidate();
|
|
1200
|
+
}
|
|
1201
|
+
render(width) {
|
|
1202
|
+
if (this.question.multiSelect === true) return this.optionListView.render(width);
|
|
1203
|
+
if (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width);
|
|
1204
|
+
if (this.props.inputMode) return this.optionListView.render(width);
|
|
1205
|
+
return this.layout(width).lines;
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Options + preview for the current layout mode, fitted to `rowAllowance` when set. The
|
|
1209
|
+
* returned focus range is in the same row coordinates as `lines`, so `render`,
|
|
1210
|
+
* `focusedItemRowRange` and `naturalHeight` agree by construction.
|
|
1211
|
+
*/
|
|
1212
|
+
layout(width) {
|
|
1213
|
+
const mode = decideLayout(this.getTerminalWidth(), width);
|
|
1214
|
+
return mode === "side-by-side" ? this.layoutSideBySide(width, mode) : this.layoutStacked(width, mode);
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Stacked: options, a blank gap, then the preview block. When that exceeds the allowance,
|
|
1218
|
+
* keep the focused option (plus a row either side for the overflow arrows) and give the
|
|
1219
|
+
* preview the rest, so the preview of the option being considered stays in view instead
|
|
1220
|
+
* of falling below the dialog's scroll window.
|
|
1221
|
+
*/
|
|
1222
|
+
layoutStacked(width, mode) {
|
|
1223
|
+
const { selectedIndex } = this.props;
|
|
1224
|
+
const affordance = this.notesAffordance();
|
|
1225
|
+
const options = this.optionListView.render(width);
|
|
1226
|
+
const focus = this.optionListView.focusedItemRowRange(width);
|
|
1227
|
+
const gap = Array(STACKED_GAP_ROWS).fill("");
|
|
1228
|
+
const allowance = this.rowAllowance;
|
|
1229
|
+
const fullBlock = this.previewBlock.blockHeight(width, selectedIndex, mode);
|
|
1230
|
+
if (allowance === void 0 || options.length + STACKED_GAP_ROWS + fullBlock <= allowance) {
|
|
1231
|
+
return {
|
|
1232
|
+
lines: [...options, ...gap, ...this.previewBlock.renderBlock(width, selectedIndex, mode, affordance)],
|
|
1233
|
+
focus
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
const focusRows = focus ? focus[1] - focus[0] : 1;
|
|
1237
|
+
const previewRowsWith = /* @__PURE__ */ __name((optionsFloor) => Math.min(fullBlock, allowance - STACKED_GAP_ROWS - Math.min(options.length, optionsFloor)), "previewRowsWith");
|
|
1238
|
+
const roomy = previewRowsWith(focusRows + 2);
|
|
1239
|
+
const previewRows = roomy >= MIN_BOX_ROWS ? roomy : previewRowsWith(focusRows);
|
|
1240
|
+
const block = this.previewBlock.renderBlock(width, selectedIndex, mode, affordance, previewRows);
|
|
1241
|
+
const optionRows = allowance - (block.length > 0 ? STACKED_GAP_ROWS + block.length : 0);
|
|
1242
|
+
const windowed = windowLines(options, Math.max(1, optionRows), focus, this.theme);
|
|
1243
|
+
return {
|
|
1244
|
+
lines: block.length > 0 ? [...windowed.lines, ...gap, ...block] : windowed.lines,
|
|
1245
|
+
focus: windowed.focus
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Side-by-side: options column left, preview block right. Under an allowance the options
|
|
1250
|
+
* column scrolls around the focused option and the preview block shrinks, so neither
|
|
1251
|
+
* column exceeds it and the dialog never slices through the preview box.
|
|
1252
|
+
*/
|
|
1253
|
+
layoutSideBySide(width, mode) {
|
|
1254
|
+
const adaptiveLeft = this.getAdaptiveLeft(width);
|
|
1255
|
+
const { leftWidth, rightWidth, gap } = columnWidths(width, adaptiveLeft);
|
|
1256
|
+
const allowance = this.rowAllowance;
|
|
1257
|
+
const left = allowance === void 0 ? {
|
|
1258
|
+
lines: this.optionListView.render(leftWidth),
|
|
1259
|
+
focus: this.optionListView.focusedItemRowRange(leftWidth)
|
|
1260
|
+
} : windowLines(
|
|
1261
|
+
this.optionListView.render(leftWidth),
|
|
1262
|
+
allowance,
|
|
1263
|
+
this.optionListView.focusedItemRowRange(leftWidth),
|
|
1264
|
+
this.theme
|
|
1265
|
+
);
|
|
1266
|
+
const rightLines = this.renderPaddedPreviewLines(rightWidth, mode, allowance);
|
|
1267
|
+
const rows = Math.max(left.lines.length, rightLines.length);
|
|
1268
|
+
const gapStr = " ".repeat(gap);
|
|
1269
|
+
const out = [];
|
|
1270
|
+
for (let i = 0; i < rows; i++) {
|
|
1271
|
+
const leftRaw = left.lines[i] ?? "";
|
|
1272
|
+
const rightRaw = rightLines[i] ?? "";
|
|
1273
|
+
const leftClamped = truncateToWidth(leftRaw, leftWidth, "");
|
|
1274
|
+
const leftPad = " ".repeat(Math.max(0, leftWidth - visibleWidth(leftClamped)));
|
|
1275
|
+
const joined = `${leftClamped}${leftPad}${gapStr}${rightRaw}`;
|
|
1276
|
+
out.push(truncateToWidth(joined, width, ""));
|
|
1277
|
+
}
|
|
1278
|
+
return { lines: out, focus: left.focus };
|
|
1279
|
+
}
|
|
1280
|
+
focusedItemRowRange(width) {
|
|
1281
|
+
if (this.question.multiSelect === true) return this.optionListView.focusedItemRowRange(width);
|
|
1282
|
+
if (!this.previewBlock.hasAnyPreview()) return this.optionListView.focusedItemRowRange(width);
|
|
1283
|
+
if (this.props.inputMode) return this.optionListView.focusedItemRowRange(width);
|
|
1284
|
+
return this.layout(width).focus ?? [0, 1];
|
|
1285
|
+
}
|
|
1286
|
+
naturalHeight(width) {
|
|
1287
|
+
if (this.question.multiSelect === true) return this.optionListView.render(width).length;
|
|
1288
|
+
if (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;
|
|
1289
|
+
if (this.props.inputMode) return this.optionListView.render(width).length;
|
|
1290
|
+
if (this.rowAllowance !== void 0) return this.layout(width).lines.length;
|
|
1291
|
+
const mode = decideLayout(this.getTerminalWidth(), width);
|
|
1292
|
+
const adaptiveLeft = this.getAdaptiveLeft(width);
|
|
1293
|
+
const { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);
|
|
1294
|
+
const optionsHeight = this.optionListView.render(optionsWidth).length;
|
|
1295
|
+
const previewBlockHeight = this.previewBlock.blockHeight(previewWidth, this.props.selectedIndex, mode);
|
|
1296
|
+
if (mode === "side-by-side") return Math.max(optionsHeight, previewBlockHeight);
|
|
1297
|
+
return optionsHeight + STACKED_GAP_ROWS + previewBlockHeight;
|
|
1298
|
+
}
|
|
1299
|
+
maxNaturalHeight(width) {
|
|
1300
|
+
if (this.question.multiSelect === true) return this.optionListView.render(width).length;
|
|
1301
|
+
if (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;
|
|
1302
|
+
if (this.props.inputMode) return this.optionListView.render(width).length;
|
|
1303
|
+
const mode = decideLayout(this.getTerminalWidth(), width);
|
|
1304
|
+
const adaptiveLeft = this.getAdaptiveLeft(width);
|
|
1305
|
+
const { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);
|
|
1306
|
+
const optionsHeight = this.optionListView.render(optionsWidth).length;
|
|
1307
|
+
let maxPreviewBlock = 0;
|
|
1308
|
+
for (let i = 0; i < this.question.options.length; i++) {
|
|
1309
|
+
const h = this.previewBlock.blockHeight(previewWidth, i, mode);
|
|
1310
|
+
if (h > maxPreviewBlock) maxPreviewBlock = h;
|
|
1311
|
+
}
|
|
1312
|
+
const max = mode === "side-by-side" ? Math.max(optionsHeight, maxPreviewBlock) : optionsHeight + STACKED_GAP_ROWS + maxPreviewBlock;
|
|
1313
|
+
return this.rowAllowance === void 0 ? max : Math.min(max, this.rowAllowance);
|
|
1314
|
+
}
|
|
1315
|
+
renderPaddedPreviewLines(colWidth, mode, maxRows) {
|
|
1316
|
+
const inner = Math.max(1, colWidth - PREVIEW_PADDING_LEFT);
|
|
1317
|
+
const contentLines = this.previewBlock.renderBlock(
|
|
1318
|
+
inner,
|
|
1319
|
+
this.props.selectedIndex,
|
|
1320
|
+
mode,
|
|
1321
|
+
this.notesAffordance(),
|
|
1322
|
+
maxRows
|
|
1323
|
+
);
|
|
1324
|
+
const boxWidth = Math.max(1, visibleWidth(contentLines[0] ?? ""));
|
|
1325
|
+
const boxAlignedPad = Math.max(PREVIEW_PADDING_LEFT, colWidth - boxWidth);
|
|
1326
|
+
return contentLines.map((line) => {
|
|
1327
|
+
if (line === "") return "";
|
|
1328
|
+
const pad = Math.max(PREVIEW_PADDING_LEFT, Math.min(boxAlignedPad, colWidth - visibleWidth(line)));
|
|
1329
|
+
return `${" ".repeat(pad)}${truncateToWidth(line, colWidth - pad, "")}`;
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1334
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/submit-picker.ts?ext=rpiv-ask-user-question
|
|
1335
|
+
var ACTIVE_POINTER2 = "\u276F ";
|
|
1336
|
+
var INACTIVE_POINTER2 = " ";
|
|
1337
|
+
var NUMBER_SEPARATOR2 = ". ";
|
|
1338
|
+
var SUBMIT_LABEL = "Submit answers";
|
|
1339
|
+
var CANCEL_LABEL = "Cancel";
|
|
1340
|
+
var SubmitPicker = class {
|
|
1341
|
+
constructor(theme) {
|
|
1342
|
+
this.theme = theme;
|
|
1343
|
+
this.props = { rows: [{ active: false }, { active: false }] };
|
|
1344
|
+
}
|
|
1345
|
+
theme;
|
|
1346
|
+
static {
|
|
1347
|
+
__name(this, "SubmitPicker");
|
|
1348
|
+
}
|
|
1349
|
+
props;
|
|
1350
|
+
setProps(props) {
|
|
1351
|
+
this.props = props;
|
|
1352
|
+
}
|
|
1353
|
+
handleInput(_data) {
|
|
1354
|
+
}
|
|
1355
|
+
invalidate() {
|
|
1356
|
+
}
|
|
1357
|
+
naturalHeight(_width) {
|
|
1358
|
+
return 2;
|
|
1359
|
+
}
|
|
1360
|
+
render(width) {
|
|
1361
|
+
const lines = [];
|
|
1362
|
+
for (let i = 0; i < 2; i++) {
|
|
1363
|
+
const text = i === 0 ? t("submit.label", SUBMIT_LABEL) : t("submit.cancel", CANCEL_LABEL);
|
|
1364
|
+
const active = this.props.rows[i]?.active ?? false;
|
|
1365
|
+
const pointer = active ? ACTIVE_POINTER2 : INACTIVE_POINTER2;
|
|
1366
|
+
const number = `${i + 1}${NUMBER_SEPARATOR2}`;
|
|
1367
|
+
const label = active ? this.theme.fg("accent", this.theme.bold(text)) : this.theme.fg("text", text);
|
|
1368
|
+
lines.push(truncateToWidth(`${pointer}${number}${label}`, width, ""));
|
|
1369
|
+
}
|
|
1370
|
+
return lines;
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
|
|
1374
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/components/tab-bar.ts?ext=rpiv-ask-user-question
|
|
1375
|
+
var TabBar = class {
|
|
1376
|
+
constructor(theme) {
|
|
1377
|
+
this.theme = theme;
|
|
1378
|
+
this.props = { tabs: [], submit: { active: false, allAnswered: false } };
|
|
1379
|
+
}
|
|
1380
|
+
theme;
|
|
1381
|
+
static {
|
|
1382
|
+
__name(this, "TabBar");
|
|
1383
|
+
}
|
|
1384
|
+
props;
|
|
1385
|
+
setProps(props) {
|
|
1386
|
+
this.props = props;
|
|
1387
|
+
}
|
|
1388
|
+
handleInput(_data) {
|
|
1389
|
+
}
|
|
1390
|
+
invalidate() {
|
|
1391
|
+
}
|
|
1392
|
+
render(width) {
|
|
1393
|
+
const pieces = [" \u2190 "];
|
|
1394
|
+
for (const tab of this.props.tabs) {
|
|
1395
|
+
const box = tab.answered ? "\u25A0" : "\u25A1";
|
|
1396
|
+
const rawSeg = ` ${box} ${tab.label} `;
|
|
1397
|
+
const styled = tab.active ? this.theme.bg("selectedBg", this.theme.fg("text", rawSeg)) : this.theme.fg(tab.answered ? "success" : "muted", rawSeg);
|
|
1398
|
+
pieces.push(styled);
|
|
1399
|
+
pieces.push(" ");
|
|
1400
|
+
}
|
|
1401
|
+
const submitText = " \u2713 Submit ";
|
|
1402
|
+
const submitStyled = this.props.submit.active ? this.theme.bg("selectedBg", this.theme.fg("text", submitText)) : this.theme.fg(this.props.submit.allAnswered ? "success" : "dim", submitText);
|
|
1403
|
+
pieces.push(submitStyled);
|
|
1404
|
+
pieces.push(" \u2192");
|
|
1405
|
+
const tabLine = truncateToWidth(pieces.join(""), width, "");
|
|
1406
|
+
return [tabLine, ""];
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/focus.ts?ext=rpiv-ask-user-question
|
|
1411
|
+
function selectActiveView(state, totalQuestions) {
|
|
1412
|
+
if (state.notesVisible) return "notes";
|
|
1413
|
+
if (state.currentTab === totalQuestions) return "submit";
|
|
1414
|
+
return "options";
|
|
1415
|
+
}
|
|
1416
|
+
__name(selectActiveView, "selectActiveView");
|
|
1417
|
+
|
|
1418
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/view/props-adapter.ts?ext=rpiv-ask-user-question
|
|
1419
|
+
function getInputCursorOffset(input) {
|
|
1420
|
+
const lines = input.getLines();
|
|
1421
|
+
const cursor = input.getCursor();
|
|
1422
|
+
let offset = cursor.col;
|
|
1423
|
+
for (let i = 0; i < cursor.line; i++) offset += (lines[i]?.length ?? 0) + 1;
|
|
1424
|
+
return offset;
|
|
1425
|
+
}
|
|
1426
|
+
__name(getInputCursorOffset, "getInputCursorOffset");
|
|
1427
|
+
var QuestionnairePropsAdapter = class {
|
|
1428
|
+
static {
|
|
1429
|
+
__name(this, "QuestionnairePropsAdapter");
|
|
1430
|
+
}
|
|
1431
|
+
tui;
|
|
1432
|
+
questions;
|
|
1433
|
+
itemsByTab;
|
|
1434
|
+
tabsByIndex;
|
|
1435
|
+
inlineInput;
|
|
1436
|
+
globalBindings;
|
|
1437
|
+
perTabBindings;
|
|
1438
|
+
extraInvalidatables;
|
|
1439
|
+
constructor(config) {
|
|
1440
|
+
this.tui = config.tui;
|
|
1441
|
+
this.questions = config.questions;
|
|
1442
|
+
this.itemsByTab = config.itemsByTab;
|
|
1443
|
+
this.tabsByIndex = config.tabsByIndex;
|
|
1444
|
+
this.inlineInput = config.inlineInput;
|
|
1445
|
+
this.globalBindings = config.globalBindings;
|
|
1446
|
+
this.perTabBindings = config.perTabBindings;
|
|
1447
|
+
this.extraInvalidatables = config.extraInvalidatables ?? [];
|
|
1448
|
+
}
|
|
1449
|
+
apply(state) {
|
|
1450
|
+
const totalQuestions = this.questions.length;
|
|
1451
|
+
const activeView = selectActiveView(state, totalQuestions);
|
|
1452
|
+
const paneIndex = selectActivePreviewPaneIndex(state.currentTab, totalQuestions);
|
|
1453
|
+
const activePreviewPane = this.tabsByIndex[paneIndex]?.preview ?? this.tabsByIndex[0].preview;
|
|
1454
|
+
const ctx = {
|
|
1455
|
+
questions: this.questions,
|
|
1456
|
+
itemsByTab: this.itemsByTab,
|
|
1457
|
+
totalQuestions,
|
|
1458
|
+
activeView,
|
|
1459
|
+
inputBuffer: this.inlineInput.getText(),
|
|
1460
|
+
inputCursorOffset: getInputCursorOffset(this.inlineInput),
|
|
1461
|
+
activePreviewPane
|
|
1462
|
+
};
|
|
1463
|
+
for (const binding of this.globalBindings) {
|
|
1464
|
+
binding.apply(state, ctx);
|
|
1465
|
+
}
|
|
1466
|
+
for (let i = 0; i < this.tabsByIndex.length; i++) {
|
|
1467
|
+
const tab = this.tabsByIndex[i];
|
|
1468
|
+
const tabCtx = { ...ctx, tab, i };
|
|
1469
|
+
for (const binding of this.perTabBindings) {
|
|
1470
|
+
binding.apply(state, tabCtx);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
this.tui.requestRender();
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Invalidates every owned renderable. Called by the session in place of
|
|
1477
|
+
* the old `dialog.invalidate()` forwarding chain — DialogView no longer
|
|
1478
|
+
* reaches into siblings (tabBar, notesInput, activePreviewPane).
|
|
1479
|
+
* Iterates the same registries used by `apply()` plus
|
|
1480
|
+
* `extraInvalidatables` for components outside the binding system.
|
|
1481
|
+
*/
|
|
1482
|
+
invalidate() {
|
|
1483
|
+
for (const b of this.globalBindings) b.invalidate();
|
|
1484
|
+
for (const tab of this.tabsByIndex) {
|
|
1485
|
+
tab.optionList.invalidate();
|
|
1486
|
+
tab.preview.invalidate();
|
|
1487
|
+
tab.multiSelect?.invalidate();
|
|
1488
|
+
}
|
|
1489
|
+
for (const x of this.extraInvalidatables) x.invalidate();
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
|
|
1493
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts?ext=rpiv-ask-user-question
|
|
1494
|
+
function emptyMultiSelectProps(ctx) {
|
|
1495
|
+
return {
|
|
1496
|
+
rows: [],
|
|
1497
|
+
other: {
|
|
1498
|
+
active: false,
|
|
1499
|
+
checked: false,
|
|
1500
|
+
inputMode: false,
|
|
1501
|
+
inputBuffer: ctx.inputBuffer,
|
|
1502
|
+
inputCursorOffset: ctx.inputCursorOffset
|
|
1503
|
+
},
|
|
1504
|
+
nextActive: false,
|
|
1505
|
+
nextLabel: displayLabel("next")
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
__name(emptyMultiSelectProps, "emptyMultiSelectProps");
|
|
1509
|
+
function nextLabelFor(ctx) {
|
|
1510
|
+
const isLastQuestion = ctx.i === ctx.questions.length - 1;
|
|
1511
|
+
return isLastQuestion ? MULTI_SUBMIT_LABEL : displayLabel("next");
|
|
1512
|
+
}
|
|
1513
|
+
__name(nextLabelFor, "nextLabelFor");
|
|
1514
|
+
var selectMultiSelectProps = /* @__PURE__ */ __name((state, ctx) => {
|
|
1515
|
+
const question = ctx.questions[ctx.i];
|
|
1516
|
+
if (!question) {
|
|
1517
|
+
return emptyMultiSelectProps(ctx);
|
|
1518
|
+
}
|
|
1519
|
+
const focused = ctx.activeView === "options";
|
|
1520
|
+
const rows = [];
|
|
1521
|
+
for (let i = 0; i < question.options.length; i++) {
|
|
1522
|
+
rows.push({
|
|
1523
|
+
checked: state.multiSelectChecked.has(i),
|
|
1524
|
+
active: focused && i === state.optionIndex
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
const otherActive = focused && state.optionIndex === question.options.length;
|
|
1528
|
+
const nextActive = focused && state.optionIndex === question.options.length + 1;
|
|
1529
|
+
return {
|
|
1530
|
+
rows,
|
|
1531
|
+
other: {
|
|
1532
|
+
active: otherActive,
|
|
1533
|
+
checked: ctx.inputBuffer.trim().length > 0,
|
|
1534
|
+
inputMode: state.inputMode,
|
|
1535
|
+
inputBuffer: ctx.inputBuffer,
|
|
1536
|
+
inputCursorOffset: ctx.inputCursorOffset
|
|
1537
|
+
},
|
|
1538
|
+
nextActive,
|
|
1539
|
+
nextLabel: nextLabelFor(ctx)
|
|
1540
|
+
};
|
|
1541
|
+
}, "selectMultiSelectProps");
|
|
1542
|
+
var selectOptionListProps = /* @__PURE__ */ __name((state, ctx) => {
|
|
1543
|
+
const items = ctx.itemsByTab[ctx.i] ?? [];
|
|
1544
|
+
const focused = ctx.activeView === "options";
|
|
1545
|
+
const confirmed = selectConfirmedIndicator(ctx.questions, state.currentTab, state.answers, items);
|
|
1546
|
+
return {
|
|
1547
|
+
selectedIndex: state.optionIndex,
|
|
1548
|
+
focused,
|
|
1549
|
+
inputBuffer: ctx.inputBuffer,
|
|
1550
|
+
inputCursorOffset: ctx.inputCursorOffset,
|
|
1551
|
+
...confirmed ? { confirmed } : {}
|
|
1552
|
+
};
|
|
1553
|
+
}, "selectOptionListProps");
|
|
1554
|
+
var selectSubmitPickerProps = /* @__PURE__ */ __name((state, ctx) => {
|
|
1555
|
+
const focused = ctx.activeView === "submit";
|
|
1556
|
+
return {
|
|
1557
|
+
rows: [
|
|
1558
|
+
{ active: focused && state.submitChoiceIndex === 0 },
|
|
1559
|
+
{ active: focused && state.submitChoiceIndex === 1 }
|
|
1560
|
+
]
|
|
1561
|
+
};
|
|
1562
|
+
}, "selectSubmitPickerProps");
|
|
1563
|
+
var selectPreviewPaneProps = /* @__PURE__ */ __name((state, ctx) => ({
|
|
1564
|
+
notesVisible: state.notesVisible,
|
|
1565
|
+
hasNote: selectSavedNote(state.notesByTab, state.answers, ctx.i) !== void 0,
|
|
1566
|
+
selectedIndex: state.optionIndex,
|
|
1567
|
+
focused: ctx.activeView === "options",
|
|
1568
|
+
inputMode: state.inputMode
|
|
1569
|
+
}), "selectPreviewPaneProps");
|
|
1570
|
+
var selectTabBarProps = /* @__PURE__ */ __name((state, ctx) => {
|
|
1571
|
+
const tabs = ctx.questions.map((q, i) => ({
|
|
1572
|
+
label: q.header && q.header.length > 0 ? q.header : `Q${i + 1}`,
|
|
1573
|
+
answered: state.answers.has(i),
|
|
1574
|
+
active: i === state.currentTab
|
|
1575
|
+
}));
|
|
1576
|
+
return {
|
|
1577
|
+
tabs,
|
|
1578
|
+
submit: {
|
|
1579
|
+
active: state.currentTab === ctx.questions.length,
|
|
1580
|
+
allAnswered: state.answers.size === ctx.questions.length && ctx.questions.length > 0
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
}, "selectTabBarProps");
|
|
1584
|
+
var selectDialogProps = /* @__PURE__ */ __name((state, ctx) => ({
|
|
1585
|
+
state,
|
|
1586
|
+
activePreviewPane: ctx.activePreviewPane
|
|
1587
|
+
}), "selectDialogProps");
|
|
1588
|
+
|
|
1589
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts?ext=rpiv-ask-user-question
|
|
1590
|
+
function previewBodyHeights(pane) {
|
|
1591
|
+
return (width) => {
|
|
1592
|
+
const current = pane.naturalHeight(width);
|
|
1593
|
+
return { current, max: Math.max(current, pane.maxNaturalHeight(width)) };
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
__name(previewBodyHeights, "previewBodyHeights");
|
|
1597
|
+
function editorTheme(theme) {
|
|
1598
|
+
return {
|
|
1599
|
+
borderColor: /* @__PURE__ */ __name((text) => theme.fg("borderMuted", text), "borderColor"),
|
|
1600
|
+
selectList: {
|
|
1601
|
+
selectedPrefix: /* @__PURE__ */ __name((text) => theme.bg("selectedBg", theme.fg("accent", text)), "selectedPrefix"),
|
|
1602
|
+
selectedText: /* @__PURE__ */ __name((text) => theme.bg("selectedBg", theme.bold(text)), "selectedText"),
|
|
1603
|
+
description: /* @__PURE__ */ __name((text) => theme.fg("muted", text), "description"),
|
|
1604
|
+
scrollInfo: /* @__PURE__ */ __name((text) => theme.fg("dim", text), "scrollInfo"),
|
|
1605
|
+
noMatch: /* @__PURE__ */ __name((text) => theme.fg("warning", text), "noMatch")
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
__name(editorTheme, "editorTheme");
|
|
1610
|
+
function multiSelectBodyHeights(view) {
|
|
1611
|
+
return (width) => {
|
|
1612
|
+
const h = view.naturalHeight(width);
|
|
1613
|
+
return { current: h, max: h };
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
__name(multiSelectBodyHeights, "multiSelectBodyHeights");
|
|
1617
|
+
var isActiveTab = /* @__PURE__ */ __name((s, ctx) => ctx.i === selectActivePreviewPaneIndex(s.currentTab, ctx.totalQuestions), "isActiveTab");
|
|
1618
|
+
function buildQuestionnaire(config) {
|
|
1619
|
+
return new QuestionnaireBuilder(config).build();
|
|
1620
|
+
}
|
|
1621
|
+
__name(buildQuestionnaire, "buildQuestionnaire");
|
|
1622
|
+
var QuestionnaireBuilder = class {
|
|
1623
|
+
static {
|
|
1624
|
+
__name(this, "QuestionnaireBuilder");
|
|
1625
|
+
}
|
|
1626
|
+
tui;
|
|
1627
|
+
theme;
|
|
1628
|
+
questions;
|
|
1629
|
+
itemsByTab;
|
|
1630
|
+
isMulti;
|
|
1631
|
+
initialState;
|
|
1632
|
+
getCurrentTab;
|
|
1633
|
+
collapseKey;
|
|
1634
|
+
rowBudget;
|
|
1635
|
+
selectTheme;
|
|
1636
|
+
markdownTheme = getMarkdownTheme();
|
|
1637
|
+
notesInput;
|
|
1638
|
+
inlineInput;
|
|
1639
|
+
getTerminalWidth = /* @__PURE__ */ __name(() => this.tui.terminal.columns, "getTerminalWidth");
|
|
1640
|
+
getTerminalRows = /* @__PURE__ */ __name(() => this.rowBudget(this.tui.terminal.rows), "getTerminalRows");
|
|
1641
|
+
constructor(config) {
|
|
1642
|
+
this.tui = config.tui;
|
|
1643
|
+
this.theme = config.theme;
|
|
1644
|
+
this.questions = config.questions;
|
|
1645
|
+
this.itemsByTab = config.itemsByTab;
|
|
1646
|
+
this.isMulti = config.isMulti;
|
|
1647
|
+
this.initialState = config.initialState;
|
|
1648
|
+
this.getCurrentTab = config.getCurrentTab;
|
|
1649
|
+
this.collapseKey = config.collapseKey;
|
|
1650
|
+
this.rowBudget = config.rowBudget;
|
|
1651
|
+
this.selectTheme = this.makeSelectTheme();
|
|
1652
|
+
const textEditorTheme = editorTheme(this.theme);
|
|
1653
|
+
this.notesInput = new Editor(this.tui, textEditorTheme);
|
|
1654
|
+
this.inlineInput = new Editor(this.tui, textEditorTheme);
|
|
1655
|
+
this.notesInput.disableSubmit = true;
|
|
1656
|
+
this.inlineInput.disableSubmit = true;
|
|
1657
|
+
}
|
|
1658
|
+
build() {
|
|
1659
|
+
const tabs = this.buildTabComponents();
|
|
1660
|
+
this.injectGlobalLeftWidth(tabs);
|
|
1661
|
+
const submitPicker = this.buildSubmitPicker();
|
|
1662
|
+
const tabBar = this.buildTabBar();
|
|
1663
|
+
const heights = this.buildHeightComputers(tabs);
|
|
1664
|
+
const dialog = this.buildDialog(tabs, submitPicker, tabBar, heights);
|
|
1665
|
+
const globalBindings = this.buildGlobalBindings(dialog, submitPicker, tabBar);
|
|
1666
|
+
const perTabBindings = this.buildPerTabBindings();
|
|
1667
|
+
const adapter = this.buildAdapter(tabs, globalBindings, perTabBindings);
|
|
1668
|
+
return this.handle(adapter, dialog);
|
|
1669
|
+
}
|
|
1670
|
+
makeSelectTheme() {
|
|
1671
|
+
const t2 = this.theme;
|
|
1672
|
+
return {
|
|
1673
|
+
selectedText: /* @__PURE__ */ __name((s) => t2.fg("accent", t2.bold(s)), "selectedText"),
|
|
1674
|
+
description: /* @__PURE__ */ __name((s) => t2.fg("muted", s), "description"),
|
|
1675
|
+
scrollInfo: /* @__PURE__ */ __name((s) => t2.fg("dim", s), "scrollInfo")
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
buildTabComponents() {
|
|
1679
|
+
return this.questions.map((q, i) => this.buildTabFor(q, i));
|
|
1680
|
+
}
|
|
1681
|
+
buildTabFor(question, index) {
|
|
1682
|
+
const optionList = new OptionListView({
|
|
1683
|
+
items: this.itemsByTab[index] ?? [],
|
|
1684
|
+
theme: this.selectTheme
|
|
1685
|
+
});
|
|
1686
|
+
const previewBlock = new PreviewBlockRenderer({
|
|
1687
|
+
question,
|
|
1688
|
+
theme: this.theme,
|
|
1689
|
+
markdownTheme: this.markdownTheme
|
|
1690
|
+
});
|
|
1691
|
+
const preview = new PreviewPane({
|
|
1692
|
+
question,
|
|
1693
|
+
theme: this.theme,
|
|
1694
|
+
getTerminalWidth: this.getTerminalWidth,
|
|
1695
|
+
optionListView: optionList,
|
|
1696
|
+
previewBlock
|
|
1697
|
+
});
|
|
1698
|
+
const multiSelect = question.multiSelect ? new MultiSelectView(this.theme, question) : void 0;
|
|
1699
|
+
const bodyHeights = this.buildBodyHeights(question, preview, multiSelect);
|
|
1700
|
+
return { optionList, preview, multiSelect, bodyHeights };
|
|
1701
|
+
}
|
|
1702
|
+
buildBodyHeights(question, preview, multiSelect) {
|
|
1703
|
+
return question.multiSelect ? multiSelectBodyHeights(multiSelect) : previewBodyHeights(preview);
|
|
1704
|
+
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Compute cross-tab max adaptive left width and inject into each PreviewPane.
|
|
1707
|
+
* Mirrors buildHeightComputers pattern — iterates all tabs, takes max.
|
|
1708
|
+
* Called after buildTabComponents, before buildDialog (so setGlobalLeftWidth
|
|
1709
|
+
* is set before any rendering occurs).
|
|
1710
|
+
*/
|
|
1711
|
+
injectGlobalLeftWidth(tabs) {
|
|
1712
|
+
const questions = this.questions;
|
|
1713
|
+
const itemsByTab = this.itemsByTab;
|
|
1714
|
+
const tabsDescriptor = questions.map((q) => ({ multiSelect: q.multiSelect }));
|
|
1715
|
+
const globalLeftWidth = /* @__PURE__ */ __name((paneWidth) => crossTabLeftWidthWithDonation(tabsDescriptor, itemsByTab, questions, paneWidth), "globalLeftWidth");
|
|
1716
|
+
for (const tab of tabs) {
|
|
1717
|
+
tab.preview.setGlobalLeftWidth(globalLeftWidth);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
buildSubmitPicker() {
|
|
1721
|
+
return this.isMulti ? new SubmitPicker(this.theme) : void 0;
|
|
1722
|
+
}
|
|
1723
|
+
buildTabBar() {
|
|
1724
|
+
return this.isMulti ? new TabBar(this.theme) : void 0;
|
|
1725
|
+
}
|
|
1726
|
+
buildHeightComputers(tabs) {
|
|
1727
|
+
const global = /* @__PURE__ */ __name((width) => {
|
|
1728
|
+
let max = 0;
|
|
1729
|
+
for (const tab of tabs) {
|
|
1730
|
+
const h = tab.bodyHeights(width).max;
|
|
1731
|
+
if (h > max) max = h;
|
|
1732
|
+
}
|
|
1733
|
+
return Math.max(1, max);
|
|
1734
|
+
}, "global");
|
|
1735
|
+
const current = /* @__PURE__ */ __name((width) => {
|
|
1736
|
+
const idx = Math.min(this.getCurrentTab(), tabs.length - 1);
|
|
1737
|
+
return Math.max(0, tabs[idx]?.bodyHeights(width).current ?? 0);
|
|
1738
|
+
}, "current");
|
|
1739
|
+
return { global, current };
|
|
1740
|
+
}
|
|
1741
|
+
pickInitialActivePreview(tabs) {
|
|
1742
|
+
const idx = selectActivePreviewPaneIndex(this.initialState.currentTab, this.questions.length);
|
|
1743
|
+
return tabs[idx]?.preview ?? tabs[0].preview;
|
|
1744
|
+
}
|
|
1745
|
+
buildDialog(tabs, submitPicker, tabBar, heights) {
|
|
1746
|
+
return new DialogView(
|
|
1747
|
+
{
|
|
1748
|
+
theme: this.theme,
|
|
1749
|
+
questions: this.questions,
|
|
1750
|
+
tabBar,
|
|
1751
|
+
notesInput: this.notesInput,
|
|
1752
|
+
isMulti: this.isMulti,
|
|
1753
|
+
tabsByIndex: tabs,
|
|
1754
|
+
submitPicker,
|
|
1755
|
+
getBodyHeight: heights.global,
|
|
1756
|
+
getCurrentBodyHeight: heights.current,
|
|
1757
|
+
getTerminalRows: this.getTerminalRows,
|
|
1758
|
+
collapseKey: this.collapseKey
|
|
1759
|
+
},
|
|
1760
|
+
{ state: this.initialState, activePreviewPane: this.pickInitialActivePreview(tabs) }
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
buildGlobalBindings(dialog, submitPicker, tabBar) {
|
|
1764
|
+
return [
|
|
1765
|
+
globalBinding({ component: dialog, select: selectDialogProps }),
|
|
1766
|
+
...submitPicker ? [globalBinding({ component: submitPicker, select: selectSubmitPickerProps })] : [],
|
|
1767
|
+
...tabBar ? [globalBinding({ component: tabBar, select: selectTabBarProps })] : []
|
|
1768
|
+
];
|
|
1769
|
+
}
|
|
1770
|
+
buildPerTabBindings() {
|
|
1771
|
+
return [
|
|
1772
|
+
perTabBinding({
|
|
1773
|
+
resolve: /* @__PURE__ */ __name((tab) => tab.optionList, "resolve"),
|
|
1774
|
+
predicate: isActiveTab,
|
|
1775
|
+
select: selectOptionListProps
|
|
1776
|
+
}),
|
|
1777
|
+
perTabBinding({
|
|
1778
|
+
resolve: /* @__PURE__ */ __name((tab) => tab.preview, "resolve"),
|
|
1779
|
+
predicate: isActiveTab,
|
|
1780
|
+
select: selectPreviewPaneProps
|
|
1781
|
+
}),
|
|
1782
|
+
perTabBinding({
|
|
1783
|
+
resolve: /* @__PURE__ */ __name((tab) => tab.multiSelect, "resolve"),
|
|
1784
|
+
select: selectMultiSelectProps
|
|
1785
|
+
})
|
|
1786
|
+
];
|
|
1787
|
+
}
|
|
1788
|
+
buildAdapter(tabs, globalBindings, perTabBindings) {
|
|
1789
|
+
return new QuestionnairePropsAdapter({
|
|
1790
|
+
tui: this.tui,
|
|
1791
|
+
questions: this.questions,
|
|
1792
|
+
itemsByTab: this.itemsByTab,
|
|
1793
|
+
tabsByIndex: tabs,
|
|
1794
|
+
inlineInput: this.inlineInput,
|
|
1795
|
+
globalBindings,
|
|
1796
|
+
perTabBindings,
|
|
1797
|
+
extraInvalidatables: [this.notesInput]
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
handle(adapter, dialog) {
|
|
1801
|
+
return {
|
|
1802
|
+
adapter,
|
|
1803
|
+
notesInput: this.notesInput,
|
|
1804
|
+
inlineInput: this.inlineInput,
|
|
1805
|
+
render: /* @__PURE__ */ __name((w) => dialog.render(w), "render"),
|
|
1806
|
+
invalidate: /* @__PURE__ */ __name(() => adapter.invalidate(), "invalidate")
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
};
|
|
1810
|
+
|
|
1811
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts?ext=rpiv-ask-user-question
|
|
1812
|
+
var KEYBIND_UP = "tui.select.up";
|
|
1813
|
+
var KEYBIND_DOWN = "tui.select.down";
|
|
1814
|
+
var KEYBIND_CONFIRM = "tui.select.confirm";
|
|
1815
|
+
var KEYBIND_SUBMIT = "tui.input.submit";
|
|
1816
|
+
var KEYBIND_CANCEL = "tui.select.cancel";
|
|
1817
|
+
var KEYBIND_NEW_LINE = "tui.input.newLine";
|
|
1818
|
+
var KEYBIND_EDITOR_UP = "tui.editor.cursorUp";
|
|
1819
|
+
var KEYBIND_EDITOR_DOWN = "tui.editor.cursorDown";
|
|
1820
|
+
var KEYBIND_CLEAR = "tui.editor.deleteToLineStart";
|
|
1821
|
+
var KEYBIND_EXTERNAL_EDITOR = "app.editor.external";
|
|
1822
|
+
var NOTES_ACTIVATE_KEY = "n";
|
|
1823
|
+
var SPACE_KEY = " ";
|
|
1824
|
+
function isConfirm(kb, data) {
|
|
1825
|
+
return kb.matches(data, KEYBIND_CONFIRM) || kb.matches(data, KEYBIND_SUBMIT);
|
|
1826
|
+
}
|
|
1827
|
+
__name(isConfirm, "isConfirm");
|
|
1828
|
+
function wrapTab(index, total) {
|
|
1829
|
+
if (total <= 0) return 0;
|
|
1830
|
+
return (index % total + total) % total;
|
|
1831
|
+
}
|
|
1832
|
+
__name(wrapTab, "wrapTab");
|
|
1833
|
+
function totalTabs(runtime) {
|
|
1834
|
+
return runtime.isMulti ? runtime.questions.length + 1 : 1;
|
|
1835
|
+
}
|
|
1836
|
+
__name(totalTabs, "totalTabs");
|
|
1837
|
+
function computeAutoAdvanceTab(state, runtime) {
|
|
1838
|
+
if (!runtime.isMulti) return void 0;
|
|
1839
|
+
if (state.currentTab < runtime.questions.length - 1) return state.currentTab + 1;
|
|
1840
|
+
return runtime.questions.length;
|
|
1841
|
+
}
|
|
1842
|
+
__name(computeAutoAdvanceTab, "computeAutoAdvanceTab");
|
|
1843
|
+
function buildSingleSelectAnswer(state, runtime) {
|
|
1844
|
+
const q = runtime.questions[state.currentTab];
|
|
1845
|
+
if (!q) return null;
|
|
1846
|
+
const item = runtime.currentItem;
|
|
1847
|
+
if (state.inputMode) {
|
|
1848
|
+
const label = runtime.inputBuffer;
|
|
1849
|
+
return {
|
|
1850
|
+
questionIndex: state.currentTab,
|
|
1851
|
+
question: q.question,
|
|
1852
|
+
kind: "custom",
|
|
1853
|
+
answer: label.length > 0 ? label : null
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
if (!item) return null;
|
|
1857
|
+
if (item.kind === "other") {
|
|
1858
|
+
return null;
|
|
1859
|
+
}
|
|
1860
|
+
if (item.kind === "next") {
|
|
1861
|
+
return null;
|
|
1862
|
+
}
|
|
1863
|
+
return {
|
|
1864
|
+
questionIndex: state.currentTab,
|
|
1865
|
+
question: q.question,
|
|
1866
|
+
kind: "option",
|
|
1867
|
+
answer: item.label
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
__name(buildSingleSelectAnswer, "buildSingleSelectAnswer");
|
|
1871
|
+
function buildMultiSelected(state, runtime) {
|
|
1872
|
+
const q = runtime.questions[state.currentTab];
|
|
1873
|
+
if (!q) return [];
|
|
1874
|
+
const out = [];
|
|
1875
|
+
for (let i = 0; i < q.options.length; i++) {
|
|
1876
|
+
if (state.multiSelectChecked.has(i)) {
|
|
1877
|
+
const label = q.options[i]?.label;
|
|
1878
|
+
if (typeof label === "string") out.push(label);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
return out;
|
|
1882
|
+
}
|
|
1883
|
+
__name(buildMultiSelected, "buildMultiSelected");
|
|
1884
|
+
function buildMultiConfirmAction(state, runtime) {
|
|
1885
|
+
return {
|
|
1886
|
+
kind: "multi_confirm",
|
|
1887
|
+
selected: buildMultiSelected(state, runtime),
|
|
1888
|
+
answer: runtime.inputBuffer.trim().length > 0 ? runtime.inputBuffer : null,
|
|
1889
|
+
autoAdvanceTab: computeAutoAdvanceTab(state, runtime)
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
__name(buildMultiConfirmAction, "buildMultiConfirmAction");
|
|
1893
|
+
function tabSwitchAction(data, state, runtime) {
|
|
1894
|
+
if (!runtime.isMulti) return null;
|
|
1895
|
+
const total = totalTabs(runtime);
|
|
1896
|
+
if (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) {
|
|
1897
|
+
return { kind: "tab_switch", nextTab: wrapTab(state.currentTab + 1, total) };
|
|
1898
|
+
}
|
|
1899
|
+
if (matchesKey(data, Key.shift("tab")) || matchesKey(data, Key.left)) {
|
|
1900
|
+
return { kind: "tab_switch", nextTab: wrapTab(state.currentTab - 1, total) };
|
|
1901
|
+
}
|
|
1902
|
+
return null;
|
|
1903
|
+
}
|
|
1904
|
+
__name(tabSwitchAction, "tabSwitchAction");
|
|
1905
|
+
function nextNavOnDown(state, runtime) {
|
|
1906
|
+
return {
|
|
1907
|
+
kind: "nav",
|
|
1908
|
+
nextIndex: wrapTab(state.optionIndex + 1, Math.max(1, runtime.items.length)),
|
|
1909
|
+
inputValue: runtime.inputBuffer
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
__name(nextNavOnDown, "nextNavOnDown");
|
|
1913
|
+
function prevNavOnUp(state, runtime) {
|
|
1914
|
+
return {
|
|
1915
|
+
kind: "nav",
|
|
1916
|
+
nextIndex: wrapTab(state.optionIndex - 1, Math.max(1, runtime.items.length)),
|
|
1917
|
+
inputValue: runtime.inputBuffer
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
__name(prevNavOnUp, "prevNavOnUp");
|
|
1921
|
+
function routeCollapsed(kb, data) {
|
|
1922
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
1923
|
+
return { kind: "ignore" };
|
|
1924
|
+
}
|
|
1925
|
+
__name(routeCollapsed, "routeCollapsed");
|
|
1926
|
+
function routeNotesMode(kb, data) {
|
|
1927
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "notes_exit" };
|
|
1928
|
+
if (kb.matches(data, KEYBIND_NEW_LINE)) return { kind: "notes_forward", data };
|
|
1929
|
+
if (isConfirm(kb, data)) return { kind: "notes_exit" };
|
|
1930
|
+
return { kind: "notes_forward", data };
|
|
1931
|
+
}
|
|
1932
|
+
__name(routeNotesMode, "routeNotesMode");
|
|
1933
|
+
function routeInputMode(kb, data, state, runtime) {
|
|
1934
|
+
if (kb.matches(data, KEYBIND_NEW_LINE)) return { kind: "ignore" };
|
|
1935
|
+
if (isConfirm(kb, data)) {
|
|
1936
|
+
const q = runtime.questions[state.currentTab];
|
|
1937
|
+
if (q?.multiSelect) return buildMultiConfirmAction(state, runtime);
|
|
1938
|
+
const answer = buildSingleSelectAnswer(state, runtime);
|
|
1939
|
+
if (!answer) return { kind: "ignore" };
|
|
1940
|
+
return { kind: "confirm", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };
|
|
1941
|
+
}
|
|
1942
|
+
if (kb.matches(data, KEYBIND_CLEAR)) return { kind: "input_clear" };
|
|
1943
|
+
if (kb.matches(data, KEYBIND_EXTERNAL_EDITOR)) return { kind: "input_edit", value: runtime.inputBuffer };
|
|
1944
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
1945
|
+
if (kb.matches(data, KEYBIND_EDITOR_UP) && runtime.canMoveInputUp) return { kind: "ignore" };
|
|
1946
|
+
if (kb.matches(data, KEYBIND_EDITOR_DOWN) && runtime.canMoveInputDown) return { kind: "ignore" };
|
|
1947
|
+
if (kb.matches(data, KEYBIND_UP)) return prevNavOnUp(state, runtime);
|
|
1948
|
+
if (kb.matches(data, KEYBIND_DOWN)) return nextNavOnDown(state, runtime);
|
|
1949
|
+
return { kind: "ignore" };
|
|
1950
|
+
}
|
|
1951
|
+
__name(routeInputMode, "routeInputMode");
|
|
1952
|
+
function routeSubmitTab(kb, data, state, runtime) {
|
|
1953
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
1954
|
+
const tab = tabSwitchAction(data, state, runtime);
|
|
1955
|
+
if (tab) return tab;
|
|
1956
|
+
if (kb.matches(data, KEYBIND_UP) || kb.matches(data, KEYBIND_DOWN)) {
|
|
1957
|
+
const delta = kb.matches(data, KEYBIND_DOWN) ? 1 : -1;
|
|
1958
|
+
const next = wrapTab(state.submitChoiceIndex + delta, 2);
|
|
1959
|
+
return { kind: "submit_nav", nextIndex: next === 1 ? 1 : 0 };
|
|
1960
|
+
}
|
|
1961
|
+
if (isConfirm(kb, data)) {
|
|
1962
|
+
return state.submitChoiceIndex === 1 ? { kind: "cancel" } : { kind: "submit" };
|
|
1963
|
+
}
|
|
1964
|
+
if (data === NOTES_ACTIVATE_KEY) {
|
|
1965
|
+
return { kind: "notes_enter" };
|
|
1966
|
+
}
|
|
1967
|
+
return { kind: "ignore" };
|
|
1968
|
+
}
|
|
1969
|
+
__name(routeSubmitTab, "routeSubmitTab");
|
|
1970
|
+
function routeMultiSelectTab(kb, data, state, runtime) {
|
|
1971
|
+
const focusedKind = runtime.currentItem?.kind;
|
|
1972
|
+
const focusedMeta = focusedKind ? ROW_INTENT_META[focusedKind] : void 0;
|
|
1973
|
+
if (data === SPACE_KEY) {
|
|
1974
|
+
if (focusedMeta?.blocksMultiToggle) return { kind: "ignore" };
|
|
1975
|
+
if (focusedMeta?.activatesInputMode) return { kind: "ignore" };
|
|
1976
|
+
return { kind: "toggle", index: state.optionIndex };
|
|
1977
|
+
}
|
|
1978
|
+
if (isConfirm(kb, data)) {
|
|
1979
|
+
if (focusedMeta?.activatesInputMode) return { kind: "ignore" };
|
|
1980
|
+
if (!focusedMeta?.autoSubmitsInMulti) return { kind: "toggle", index: state.optionIndex };
|
|
1981
|
+
return buildMultiConfirmAction(state, runtime);
|
|
1982
|
+
}
|
|
1983
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
1984
|
+
return { kind: "ignore" };
|
|
1985
|
+
}
|
|
1986
|
+
__name(routeMultiSelectTab, "routeMultiSelectTab");
|
|
1987
|
+
function routeSingleSelectTab(kb, data, state, runtime) {
|
|
1988
|
+
if (isConfirm(kb, data)) {
|
|
1989
|
+
const answer = buildSingleSelectAnswer(state, runtime);
|
|
1990
|
+
if (!answer) return { kind: "ignore" };
|
|
1991
|
+
return { kind: "confirm", answer, autoAdvanceTab: computeAutoAdvanceTab(state, runtime) };
|
|
1992
|
+
}
|
|
1993
|
+
if (kb.matches(data, KEYBIND_CANCEL)) return { kind: "cancel" };
|
|
1994
|
+
return { kind: "ignore" };
|
|
1995
|
+
}
|
|
1996
|
+
__name(routeSingleSelectTab, "routeSingleSelectTab");
|
|
1997
|
+
function routeKey(data, state, runtime) {
|
|
1998
|
+
const kb = runtime.keybindings;
|
|
1999
|
+
if (typeof runtime.collapseKey === "string" && runtime.collapseKey !== "off" && matchesKey(data, runtime.collapseKey)) {
|
|
2000
|
+
return { kind: "toggle_collapsed" };
|
|
2001
|
+
}
|
|
2002
|
+
if (state.collapsed) return routeCollapsed(kb, data);
|
|
2003
|
+
if (state.notesVisible) return routeNotesMode(kb, data);
|
|
2004
|
+
if (state.inputMode) return routeInputMode(kb, data, state, runtime);
|
|
2005
|
+
if (runtime.isMulti && state.currentTab === runtime.questions.length) {
|
|
2006
|
+
return routeSubmitTab(kb, data, state, runtime);
|
|
2007
|
+
}
|
|
2008
|
+
const tab = tabSwitchAction(data, state, runtime);
|
|
2009
|
+
if (tab) return tab;
|
|
2010
|
+
const q = runtime.questions[state.currentTab];
|
|
2011
|
+
if (!q) return { kind: "ignore" };
|
|
2012
|
+
if (data === NOTES_ACTIVATE_KEY) {
|
|
2013
|
+
return { kind: "notes_enter" };
|
|
2014
|
+
}
|
|
2015
|
+
if (kb.matches(data, KEYBIND_UP)) {
|
|
2016
|
+
return prevNavOnUp(state, runtime);
|
|
2017
|
+
}
|
|
2018
|
+
if (kb.matches(data, KEYBIND_DOWN)) {
|
|
2019
|
+
return nextNavOnDown(state, runtime);
|
|
2020
|
+
}
|
|
2021
|
+
if (q.multiSelect) return routeMultiSelectTab(kb, data, state, runtime);
|
|
2022
|
+
return routeSingleSelectTab(kb, data, state, runtime);
|
|
2023
|
+
}
|
|
2024
|
+
__name(routeKey, "routeKey");
|
|
2025
|
+
|
|
2026
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts?ext=rpiv-ask-user-question
|
|
2027
|
+
function orderedAnswers(state, questions) {
|
|
2028
|
+
const out = [];
|
|
2029
|
+
for (let i = 0; i < questions.length; i++) {
|
|
2030
|
+
const a = state.answers.get(i);
|
|
2031
|
+
if (a) out.push(a);
|
|
2032
|
+
}
|
|
2033
|
+
return out;
|
|
2034
|
+
}
|
|
2035
|
+
__name(orderedAnswers, "orderedAnswers");
|
|
2036
|
+
function syncMultiSelectFromAnswers(answers, questions, tab) {
|
|
2037
|
+
const q = questions[tab];
|
|
2038
|
+
if (!q?.multiSelect) return /* @__PURE__ */ new Set();
|
|
2039
|
+
const saved = answers.get(tab);
|
|
2040
|
+
const labels = saved?.selected ?? [];
|
|
2041
|
+
const indices = /* @__PURE__ */ new Set();
|
|
2042
|
+
for (let i = 0; i < q.options.length; i++) {
|
|
2043
|
+
if (labels.includes(q.options[i].label)) indices.add(i);
|
|
2044
|
+
}
|
|
2045
|
+
return indices;
|
|
2046
|
+
}
|
|
2047
|
+
__name(syncMultiSelectFromAnswers, "syncMultiSelectFromAnswers");
|
|
2048
|
+
function selectedLabels(state, question) {
|
|
2049
|
+
const selected = [];
|
|
2050
|
+
for (let i = 0; i < question.options.length; i++) {
|
|
2051
|
+
if (state.multiSelectChecked.has(i)) selected.push(question.options[i].label);
|
|
2052
|
+
}
|
|
2053
|
+
return selected;
|
|
2054
|
+
}
|
|
2055
|
+
__name(selectedLabels, "selectedLabels");
|
|
2056
|
+
function persistMultiSelectAnswer(state, ctx) {
|
|
2057
|
+
const q = ctx.questions[state.currentTab];
|
|
2058
|
+
if (!q?.multiSelect) return state.answers;
|
|
2059
|
+
const selected = selectedLabels(state, q);
|
|
2060
|
+
const custom = customDraftValueFor(state, state.currentTab);
|
|
2061
|
+
const hasCustom = custom.trim().length > 0;
|
|
2062
|
+
const out = new Map(state.answers);
|
|
2063
|
+
if (selected.length === 0 && !hasCustom) {
|
|
2064
|
+
out.delete(state.currentTab);
|
|
2065
|
+
return out;
|
|
2066
|
+
}
|
|
2067
|
+
const pendingNotes = state.notesByTab.get(state.currentTab);
|
|
2068
|
+
out.set(state.currentTab, {
|
|
2069
|
+
questionIndex: state.currentTab,
|
|
2070
|
+
question: q.question,
|
|
2071
|
+
kind: "multi",
|
|
2072
|
+
answer: hasCustom ? custom : null,
|
|
2073
|
+
selected,
|
|
2074
|
+
...pendingNotes && pendingNotes.length > 0 ? { notes: pendingNotes } : {}
|
|
2075
|
+
});
|
|
2076
|
+
return out;
|
|
2077
|
+
}
|
|
2078
|
+
__name(persistMultiSelectAnswer, "persistMultiSelectAnswer");
|
|
2079
|
+
function notesValueFor(state, tab) {
|
|
2080
|
+
return state.notesByTab.get(tab) ?? state.answers.get(tab)?.notes ?? "";
|
|
2081
|
+
}
|
|
2082
|
+
__name(notesValueFor, "notesValueFor");
|
|
2083
|
+
function customDraftValueFor(state, tab) {
|
|
2084
|
+
const draft = state.customDraftsByTab.get(tab);
|
|
2085
|
+
if (draft !== void 0) return draft;
|
|
2086
|
+
const answer = state.answers.get(tab);
|
|
2087
|
+
if (answer?.kind !== "custom" && answer?.kind !== "multi") return "";
|
|
2088
|
+
return typeof answer.answer === "string" ? answer.answer : "";
|
|
2089
|
+
}
|
|
2090
|
+
__name(customDraftValueFor, "customDraftValueFor");
|
|
2091
|
+
function setCustomDraft(state, tab, value) {
|
|
2092
|
+
const drafts = new Map(state.customDraftsByTab);
|
|
2093
|
+
drafts.set(tab, value);
|
|
2094
|
+
return drafts;
|
|
2095
|
+
}
|
|
2096
|
+
__name(setCustomDraft, "setCustomDraft");
|
|
2097
|
+
function withoutCustomDraft(state, tab) {
|
|
2098
|
+
if (!state.customDraftsByTab.has(tab)) return state.customDraftsByTab;
|
|
2099
|
+
const drafts = new Map(state.customDraftsByTab);
|
|
2100
|
+
drafts.delete(tab);
|
|
2101
|
+
return drafts;
|
|
2102
|
+
}
|
|
2103
|
+
__name(withoutCustomDraft, "withoutCustomDraft");
|
|
2104
|
+
function switchTabResult(state, nextTab, ctx) {
|
|
2105
|
+
const notesValue = notesValueFor(state, nextTab);
|
|
2106
|
+
const transitioned = {
|
|
2107
|
+
...state,
|
|
2108
|
+
currentTab: nextTab,
|
|
2109
|
+
optionIndex: 0,
|
|
2110
|
+
inputMode: false,
|
|
2111
|
+
notesVisible: false,
|
|
2112
|
+
submitChoiceIndex: 0,
|
|
2113
|
+
multiSelectChecked: syncMultiSelectFromAnswers(state.answers, ctx.questions, nextTab),
|
|
2114
|
+
notesDraft: notesValue
|
|
2115
|
+
};
|
|
2116
|
+
return {
|
|
2117
|
+
state: transitioned,
|
|
2118
|
+
effects: [
|
|
2119
|
+
{ kind: "set_notes_focused", focused: false },
|
|
2120
|
+
{ kind: "set_notes_value", value: notesValue },
|
|
2121
|
+
{ kind: "set_input_buffer", value: customDraftValueFor(state, nextTab) }
|
|
2122
|
+
]
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
__name(switchTabResult, "switchTabResult");
|
|
2126
|
+
function doneFor(state, ctx, cancelled) {
|
|
2127
|
+
const globalNote = state.notesByTab.get(ctx.questions.length);
|
|
2128
|
+
const result = {
|
|
2129
|
+
answers: orderedAnswers(state, ctx.questions),
|
|
2130
|
+
cancelled,
|
|
2131
|
+
...globalNote && globalNote.length > 0 ? { globalNote } : {}
|
|
2132
|
+
};
|
|
2133
|
+
return { state, effects: [{ kind: "done", result }] };
|
|
2134
|
+
}
|
|
2135
|
+
__name(doneFor, "doneFor");
|
|
2136
|
+
var navHandler = /* @__PURE__ */ __name((state, action, ctx) => {
|
|
2137
|
+
const items = ctx.itemsByTab[state.currentTab] ?? [];
|
|
2138
|
+
const item = items[action.nextIndex];
|
|
2139
|
+
const inputMode = item ? ROW_INTENT_META[item.kind].activatesInputMode : false;
|
|
2140
|
+
const customDraftsByTab = state.inputMode ? setCustomDraft(state, state.currentTab, action.inputValue) : state.customDraftsByTab;
|
|
2141
|
+
let next = { ...state, optionIndex: action.nextIndex, inputMode, customDraftsByTab };
|
|
2142
|
+
if (state.inputMode && !inputMode && ctx.questions[state.currentTab]?.multiSelect) {
|
|
2143
|
+
next = { ...next, answers: persistMultiSelectAnswer(next, ctx) };
|
|
2144
|
+
}
|
|
2145
|
+
if (!inputMode) return { state: next, effects: [] };
|
|
2146
|
+
return {
|
|
2147
|
+
state: next,
|
|
2148
|
+
effects: [{ kind: "set_input_buffer", value: customDraftValueFor(next, state.currentTab) }]
|
|
2149
|
+
};
|
|
2150
|
+
}, "navHandler");
|
|
2151
|
+
var inputClearHandler = /* @__PURE__ */ __name((state, _action, _ctx) => ({
|
|
2152
|
+
state: { ...state, customDraftsByTab: setCustomDraft(state, state.currentTab, "") },
|
|
2153
|
+
effects: [{ kind: "clear_input_buffer" }]
|
|
2154
|
+
}), "inputClearHandler");
|
|
2155
|
+
var inputEditHandler = /* @__PURE__ */ __name((state, action, _ctx) => ({
|
|
2156
|
+
state,
|
|
2157
|
+
effects: [{ kind: "open_input_editor", value: action.value }]
|
|
2158
|
+
}), "inputEditHandler");
|
|
2159
|
+
var inputReplaceHandler = /* @__PURE__ */ __name((state, action, _ctx) => ({
|
|
2160
|
+
state: { ...state, customDraftsByTab: setCustomDraft(state, state.currentTab, action.value) },
|
|
2161
|
+
effects: [{ kind: "set_input_buffer", value: action.value }]
|
|
2162
|
+
}), "inputReplaceHandler");
|
|
2163
|
+
var tabSwitchHandler = /* @__PURE__ */ __name((state, action, ctx) => switchTabResult(state, action.nextTab, ctx), "tabSwitchHandler");
|
|
2164
|
+
var confirmHandler = /* @__PURE__ */ __name((state, action, ctx) => {
|
|
2165
|
+
let answer = action.answer;
|
|
2166
|
+
const q = ctx.questions[answer.questionIndex];
|
|
2167
|
+
if (answer.kind === "custom" && q?.multiSelect) {
|
|
2168
|
+
answer = { ...answer, kind: "multi", selected: selectedLabels(state, q) };
|
|
2169
|
+
} else if (answer.kind === "option" && answer.answer) {
|
|
2170
|
+
const matched = q?.options.find((o) => o.label === answer.answer);
|
|
2171
|
+
if (matched?.preview && matched.preview.length > 0) {
|
|
2172
|
+
answer = { ...answer, preview: matched.preview };
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
const pendingNotes = state.notesByTab.get(answer.questionIndex);
|
|
2176
|
+
if (pendingNotes && pendingNotes.length > 0) {
|
|
2177
|
+
answer = { ...answer, notes: pendingNotes };
|
|
2178
|
+
}
|
|
2179
|
+
const answers = new Map(state.answers);
|
|
2180
|
+
answers.set(answer.questionIndex, answer);
|
|
2181
|
+
const hasCustomAnswer = answer.kind === "custom" || answer.kind === "multi" && answer.answer !== null;
|
|
2182
|
+
const customDraftsByTab = hasCustomAnswer ? withoutCustomDraft(state, answer.questionIndex) : state.customDraftsByTab;
|
|
2183
|
+
const next = {
|
|
2184
|
+
...state,
|
|
2185
|
+
answers,
|
|
2186
|
+
customDraftsByTab
|
|
2187
|
+
};
|
|
2188
|
+
if (action.autoAdvanceTab !== void 0) return switchTabResult(next, action.autoAdvanceTab, ctx);
|
|
2189
|
+
return doneFor(next, ctx, false);
|
|
2190
|
+
}, "confirmHandler");
|
|
2191
|
+
var toggleHandler = /* @__PURE__ */ __name((state, action, ctx) => {
|
|
2192
|
+
const checked = new Set(state.multiSelectChecked);
|
|
2193
|
+
if (checked.has(action.index)) checked.delete(action.index);
|
|
2194
|
+
else checked.add(action.index);
|
|
2195
|
+
const intermediate = { ...state, multiSelectChecked: checked };
|
|
2196
|
+
const answers = persistMultiSelectAnswer(intermediate, ctx);
|
|
2197
|
+
return { state: { ...intermediate, answers }, effects: [] };
|
|
2198
|
+
}, "toggleHandler");
|
|
2199
|
+
var multiConfirmHandler = /* @__PURE__ */ __name((state, action, ctx) => {
|
|
2200
|
+
const q = ctx.questions[state.currentTab];
|
|
2201
|
+
if (!q) return { state, effects: [] };
|
|
2202
|
+
const pendingNotes = state.notesByTab.get(state.currentTab);
|
|
2203
|
+
const answers = new Map(state.answers);
|
|
2204
|
+
answers.set(state.currentTab, {
|
|
2205
|
+
questionIndex: state.currentTab,
|
|
2206
|
+
question: q.question,
|
|
2207
|
+
kind: "multi",
|
|
2208
|
+
answer: action.answer,
|
|
2209
|
+
selected: action.selected,
|
|
2210
|
+
...pendingNotes && pendingNotes.length > 0 ? { notes: pendingNotes } : {}
|
|
2211
|
+
});
|
|
2212
|
+
const synced = {
|
|
2213
|
+
...state,
|
|
2214
|
+
answers,
|
|
2215
|
+
customDraftsByTab: withoutCustomDraft(state, state.currentTab),
|
|
2216
|
+
multiSelectChecked: syncMultiSelectFromAnswers(answers, ctx.questions, state.currentTab)
|
|
2217
|
+
};
|
|
2218
|
+
if (action.autoAdvanceTab !== void 0) return switchTabResult(synced, action.autoAdvanceTab, ctx);
|
|
2219
|
+
return doneFor(synced, ctx, false);
|
|
2220
|
+
}, "multiConfirmHandler");
|
|
2221
|
+
var notesEnterHandler = /* @__PURE__ */ __name((state, _action, _ctx) => {
|
|
2222
|
+
const value = notesValueFor(state, state.currentTab);
|
|
2223
|
+
return {
|
|
2224
|
+
state: { ...state, notesVisible: true, notesDraft: value },
|
|
2225
|
+
effects: [
|
|
2226
|
+
{ kind: "set_notes_value", value },
|
|
2227
|
+
{ kind: "set_notes_focused", focused: true }
|
|
2228
|
+
]
|
|
2229
|
+
};
|
|
2230
|
+
}, "notesEnterHandler");
|
|
2231
|
+
var notesExitHandler = /* @__PURE__ */ __name((state, _action, _ctx) => {
|
|
2232
|
+
const trimmed = state.notesDraft.trim();
|
|
2233
|
+
const notes = new Map(state.notesByTab);
|
|
2234
|
+
const answers = new Map(state.answers);
|
|
2235
|
+
if (trimmed.length === 0) {
|
|
2236
|
+
notes.delete(state.currentTab);
|
|
2237
|
+
const prev = answers.get(state.currentTab);
|
|
2238
|
+
if (prev?.notes) {
|
|
2239
|
+
const stripped = { ...prev };
|
|
2240
|
+
delete stripped.notes;
|
|
2241
|
+
answers.set(state.currentTab, stripped);
|
|
2242
|
+
}
|
|
2243
|
+
} else {
|
|
2244
|
+
notes.set(state.currentTab, trimmed);
|
|
2245
|
+
const prev = answers.get(state.currentTab);
|
|
2246
|
+
if (prev) answers.set(state.currentTab, { ...prev, notes: trimmed });
|
|
2247
|
+
}
|
|
2248
|
+
return {
|
|
2249
|
+
state: { ...state, notesByTab: notes, answers, notesVisible: false },
|
|
2250
|
+
effects: [{ kind: "set_notes_focused", focused: false }]
|
|
2251
|
+
};
|
|
2252
|
+
}, "notesExitHandler");
|
|
2253
|
+
var cancelHandler = /* @__PURE__ */ __name((s, _a, c) => doneFor(s, c, true), "cancelHandler");
|
|
2254
|
+
var submitHandler = /* @__PURE__ */ __name((s, _a, c) => doneFor(s, c, false), "submitHandler");
|
|
2255
|
+
var submitNavHandler = /* @__PURE__ */ __name((s, a, _c) => ({
|
|
2256
|
+
state: { ...s, submitChoiceIndex: a.nextIndex },
|
|
2257
|
+
effects: []
|
|
2258
|
+
}), "submitNavHandler");
|
|
2259
|
+
var notesForwardHandler = /* @__PURE__ */ __name((s, a, _c) => ({
|
|
2260
|
+
state: s,
|
|
2261
|
+
effects: [{ kind: "forward_notes_keystroke", data: a.data }]
|
|
2262
|
+
}), "notesForwardHandler");
|
|
2263
|
+
var toggleCollapsedHandler = /* @__PURE__ */ __name((s, _a, _c) => ({
|
|
2264
|
+
state: { ...s, collapsed: !s.collapsed },
|
|
2265
|
+
effects: [{ kind: "set_overlay_hidden", hidden: !s.collapsed }]
|
|
2266
|
+
}), "toggleCollapsedHandler");
|
|
2267
|
+
var ignoreHandler = /* @__PURE__ */ __name((s, _a, _c) => ({ state: s, effects: [] }), "ignoreHandler");
|
|
2268
|
+
var HANDLERS = {
|
|
2269
|
+
nav: navHandler,
|
|
2270
|
+
input_clear: inputClearHandler,
|
|
2271
|
+
input_edit: inputEditHandler,
|
|
2272
|
+
input_replace: inputReplaceHandler,
|
|
2273
|
+
tab_switch: tabSwitchHandler,
|
|
2274
|
+
confirm: confirmHandler,
|
|
2275
|
+
toggle: toggleHandler,
|
|
2276
|
+
multi_confirm: multiConfirmHandler,
|
|
2277
|
+
cancel: cancelHandler,
|
|
2278
|
+
notes_enter: notesEnterHandler,
|
|
2279
|
+
notes_exit: notesExitHandler,
|
|
2280
|
+
notes_forward: notesForwardHandler,
|
|
2281
|
+
submit: submitHandler,
|
|
2282
|
+
submit_nav: submitNavHandler,
|
|
2283
|
+
toggle_collapsed: toggleCollapsedHandler,
|
|
2284
|
+
ignore: ignoreHandler
|
|
2285
|
+
};
|
|
2286
|
+
function reduce(state, action, ctx) {
|
|
2287
|
+
const handler = HANDLERS[action.kind];
|
|
2288
|
+
return handler(state, action, ctx);
|
|
2289
|
+
}
|
|
2290
|
+
__name(reduce, "reduce");
|
|
2291
|
+
|
|
2292
|
+
// node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts?ext=rpiv-ask-user-question
|
|
2293
|
+
function initialState() {
|
|
2294
|
+
return {
|
|
2295
|
+
currentTab: 0,
|
|
2296
|
+
optionIndex: 0,
|
|
2297
|
+
inputMode: false,
|
|
2298
|
+
notesVisible: false,
|
|
2299
|
+
answers: /* @__PURE__ */ new Map(),
|
|
2300
|
+
multiSelectChecked: /* @__PURE__ */ new Set(),
|
|
2301
|
+
customDraftsByTab: /* @__PURE__ */ new Map(),
|
|
2302
|
+
notesByTab: /* @__PURE__ */ new Map(),
|
|
2303
|
+
submitChoiceIndex: 0,
|
|
2304
|
+
notesDraft: "",
|
|
2305
|
+
collapsed: false
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
__name(initialState, "initialState");
|
|
2309
|
+
var QuestionnaireSession = class {
|
|
2310
|
+
static {
|
|
2311
|
+
__name(this, "QuestionnaireSession");
|
|
2312
|
+
}
|
|
2313
|
+
state = initialState();
|
|
2314
|
+
questions;
|
|
2315
|
+
isMulti;
|
|
2316
|
+
itemsByTab;
|
|
2317
|
+
notesInput;
|
|
2318
|
+
inlineInput;
|
|
2319
|
+
viewAdapter;
|
|
2320
|
+
keybindings;
|
|
2321
|
+
editInput;
|
|
2322
|
+
collapseKey;
|
|
2323
|
+
canReopenWhileHidden;
|
|
2324
|
+
inputEditorOpen = false;
|
|
2325
|
+
/**
|
|
2326
|
+
* Overlay handle captured by `ctx.ui.custom`'s `onHandle` callback. Lets the session
|
|
2327
|
+
* call `setHidden(true/false)` so pi-tui's overlay stack reflects the collapsed state
|
|
2328
|
+
* and overlay-aware consumers (e.g. `pi-station`) can resume normal behaviour.
|
|
2329
|
+
*/
|
|
2330
|
+
overlayHandle;
|
|
2331
|
+
tui;
|
|
2332
|
+
done;
|
|
2333
|
+
component;
|
|
2334
|
+
constructor(config) {
|
|
2335
|
+
this.tui = config.tui;
|
|
2336
|
+
this.done = config.done;
|
|
2337
|
+
this.questions = config.params.questions;
|
|
2338
|
+
this.isMulti = this.questions.length > 1;
|
|
2339
|
+
this.itemsByTab = config.itemsByTab;
|
|
2340
|
+
this.keybindings = config.keybindings;
|
|
2341
|
+
this.editInput = config.editInput;
|
|
2342
|
+
this.collapseKey = config.collapseKey;
|
|
2343
|
+
this.canReopenWhileHidden = config.canReopenWhileHidden;
|
|
2344
|
+
const built = buildQuestionnaire({
|
|
2345
|
+
tui: this.tui,
|
|
2346
|
+
theme: config.theme,
|
|
2347
|
+
questions: this.questions,
|
|
2348
|
+
itemsByTab: this.itemsByTab,
|
|
2349
|
+
isMulti: this.isMulti,
|
|
2350
|
+
initialState: this.state,
|
|
2351
|
+
getCurrentTab: /* @__PURE__ */ __name(() => this.state.currentTab, "getCurrentTab"),
|
|
2352
|
+
collapseKey: this.collapseKey,
|
|
2353
|
+
rowBudget: config.rowBudget
|
|
2354
|
+
});
|
|
2355
|
+
this.notesInput = built.notesInput;
|
|
2356
|
+
this.inlineInput = built.inlineInput;
|
|
2357
|
+
this.viewAdapter = built.adapter;
|
|
2358
|
+
this.component = this.assembleComponent(built, config.theme);
|
|
2359
|
+
this.viewAdapter.apply(this.state);
|
|
2360
|
+
}
|
|
2361
|
+
assembleComponent(built, theme) {
|
|
2362
|
+
const collapsedRender = this.buildCollapsedRender(theme);
|
|
2363
|
+
return {
|
|
2364
|
+
render: /* @__PURE__ */ __name((width) => this.state.collapsed ? collapsedRender(width) : built.render(width), "render"),
|
|
2365
|
+
invalidate: built.invalidate,
|
|
2366
|
+
handleInput: /* @__PURE__ */ __name((data) => this.dispatch(data), "handleInput")
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
/**
|
|
2370
|
+
* Collapsed render: a single dim row (inline presentation: in the editor slot, where it
|
|
2371
|
+
* keeps focus so the same key expands it). For the overlay, pi-tui sizes
|
|
2372
|
+
* the overlay to `min(lines.length, maxHeight)`, so returning one line shrinks
|
|
2373
|
+
* the bottom-anchored overlay from full-height to one row and the transcript
|
|
2374
|
+
* behind it becomes readable (#47). The overlay stays focused and in the
|
|
2375
|
+
* stack, so the collapse key still routes here to expand. `t` stays inside the
|
|
2376
|
+
* closure (live locale updates); the key display is static per session.
|
|
2377
|
+
*
|
|
2378
|
+
* With collapseKey "off" the router and raw listener never toggle `collapsed`,
|
|
2379
|
+
* but `toggleCollapsedExternal()` is a public ungated entry — fall back to the
|
|
2380
|
+
* cancel-only line rather than rendering a literal "Off to expand".
|
|
2381
|
+
*/
|
|
2382
|
+
buildCollapsedRender(theme) {
|
|
2383
|
+
const collapseKeyDisplay = formatKeySpecForDisplay(this.collapseKey);
|
|
2384
|
+
const collapsedHintLine = /* @__PURE__ */ __name(() => this.collapseKey === COLLAPSE_KEY_OFF ? t("hint.cancel", HINT_PART_CANCEL) : t("hint.expand_line", COLLAPSED_HINT_TEMPLATE).replace(KEY_PLACEHOLDER, collapseKeyDisplay), "collapsedHintLine");
|
|
2385
|
+
return (_width) => [theme.fg("dim", ` ${collapsedHintLine()} `)];
|
|
2386
|
+
}
|
|
2387
|
+
dispatch(data) {
|
|
2388
|
+
if (this.inputEditorOpen) return;
|
|
2389
|
+
const action = routeKey(data, this.state, this.runtime());
|
|
2390
|
+
if (action.kind === "ignore") {
|
|
2391
|
+
this.handleIgnoreInline(data);
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
this.commit(action);
|
|
2395
|
+
}
|
|
2396
|
+
commit(action) {
|
|
2397
|
+
const result = reduce(this.state, action, this.applyContext());
|
|
2398
|
+
this.state = result.state;
|
|
2399
|
+
for (const effect of result.effects) this.runEffect(effect);
|
|
2400
|
+
this.state = this.mirrorNotesDraft(this.state);
|
|
2401
|
+
this.viewAdapter.apply(this.state);
|
|
2402
|
+
}
|
|
2403
|
+
mirrorNotesDraft(s) {
|
|
2404
|
+
const draft = this.notesInput.getExpandedText?.() ?? this.notesInput.getText();
|
|
2405
|
+
return s.notesDraft === draft ? s : { ...s, notesDraft: draft };
|
|
2406
|
+
}
|
|
2407
|
+
runEffect(effect) {
|
|
2408
|
+
switch (effect.kind) {
|
|
2409
|
+
case "set_input_buffer":
|
|
2410
|
+
this.inlineInput.setText(effect.value);
|
|
2411
|
+
return;
|
|
2412
|
+
case "clear_input_buffer":
|
|
2413
|
+
this.inlineInput.setText("");
|
|
2414
|
+
return;
|
|
2415
|
+
case "open_input_editor":
|
|
2416
|
+
this.openInputEditorAsync(effect.value);
|
|
2417
|
+
return;
|
|
2418
|
+
case "set_notes_value":
|
|
2419
|
+
this.notesInput.setText(effect.value);
|
|
2420
|
+
return;
|
|
2421
|
+
case "set_notes_focused":
|
|
2422
|
+
this.notesInput.focused = effect.focused;
|
|
2423
|
+
return;
|
|
2424
|
+
case "forward_notes_keystroke":
|
|
2425
|
+
this.notesInput.handleInput(effect.data);
|
|
2426
|
+
return;
|
|
2427
|
+
case "set_overlay_hidden":
|
|
2428
|
+
if (this.canReopenWhileHidden) this.overlayHandle?.setHidden(effect.hidden);
|
|
2429
|
+
return;
|
|
2430
|
+
case "done":
|
|
2431
|
+
this.done(effect.result);
|
|
2432
|
+
return;
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
/** Opens Pi's configured external editor; on success commits the replacement buffer, on reported failure retains the draft. */
|
|
2436
|
+
openInputEditorAsync(value) {
|
|
2437
|
+
if (this.inputEditorOpen) return;
|
|
2438
|
+
this.inputEditorOpen = true;
|
|
2439
|
+
void this.editInput(value).then(
|
|
2440
|
+
(edited) => {
|
|
2441
|
+
this.inputEditorOpen = false;
|
|
2442
|
+
if (edited !== void 0) this.commit({ kind: "input_replace", value: edited });
|
|
2443
|
+
},
|
|
2444
|
+
() => {
|
|
2445
|
+
this.inputEditorOpen = false;
|
|
2446
|
+
}
|
|
2447
|
+
);
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Per-keystroke `ignore` fast path: delegates text editing to Pi's headless
|
|
2451
|
+
* multiline `Editor`, including paste, undo, cursor movement, and configured
|
|
2452
|
+
* `tui.input.newLine` handling. `viewAdapter.apply` then projects its public
|
|
2453
|
+
* text/cursor state without a reducer round-trip.
|
|
2454
|
+
*/
|
|
2455
|
+
handleIgnoreInline(data) {
|
|
2456
|
+
if (!this.state.inputMode) return;
|
|
2457
|
+
this.inlineInput.handleInput(data);
|
|
2458
|
+
this.viewAdapter.apply(this.state);
|
|
2459
|
+
}
|
|
2460
|
+
runtime() {
|
|
2461
|
+
const cursor = this.inlineInput.getCursor();
|
|
2462
|
+
const lastLine = this.inlineInput.getLines().length - 1;
|
|
2463
|
+
return {
|
|
2464
|
+
keybindings: this.keybindings,
|
|
2465
|
+
inputBuffer: this.inlineInput.getExpandedText?.() ?? this.inlineInput.getText(),
|
|
2466
|
+
canMoveInputUp: cursor.line > 0,
|
|
2467
|
+
canMoveInputDown: cursor.line < lastLine,
|
|
2468
|
+
questions: this.questions,
|
|
2469
|
+
isMulti: this.isMulti,
|
|
2470
|
+
currentItem: this.currentItem(),
|
|
2471
|
+
items: this.itemsByTab[this.state.currentTab] ?? [],
|
|
2472
|
+
collapseKey: this.collapseKey
|
|
2473
|
+
};
|
|
2474
|
+
}
|
|
2475
|
+
applyContext() {
|
|
2476
|
+
return {
|
|
2477
|
+
questions: this.questions,
|
|
2478
|
+
itemsByTab: this.itemsByTab
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
currentItem() {
|
|
2482
|
+
const arr = this.itemsByTab[this.state.currentTab] ?? [];
|
|
2483
|
+
return this.state.optionIndex < arr.length ? arr[this.state.optionIndex] : void 0;
|
|
2484
|
+
}
|
|
2485
|
+
/**
|
|
2486
|
+
* Setter for the overlay handle, called by `ctx.ui.custom`'s `onHandle` callback once
|
|
2487
|
+
* the TUI has created the overlay. Until this is called, `set_overlay_hidden` effects
|
|
2488
|
+
* are no-ops — the session still tracks `state.collapsed` for the view layer.
|
|
2489
|
+
*/
|
|
2490
|
+
setOverlayHandle(handle) {
|
|
2491
|
+
this.overlayHandle = handle;
|
|
2492
|
+
}
|
|
2493
|
+
/**
|
|
2494
|
+
* Public toggle used by the raw terminal input listener registered in `execute()`.
|
|
2495
|
+
* pi-tui does not route input to a hidden overlay's `component.handleInput`, so the
|
|
2496
|
+
* raw listener (which fires for terminal data regardless of overlay visibility)
|
|
2497
|
+
* reaches the session through this method instead of the dispatch path. Routed
|
|
2498
|
+
* through `commit` so the transition stays in the reducer and the overlay hide
|
|
2499
|
+
* happens via the `set_overlay_hidden` effect like every other side effect.
|
|
2500
|
+
*/
|
|
2501
|
+
toggleCollapsedExternal() {
|
|
2502
|
+
if (!this.inputEditorOpen) this.commit({ kind: "toggle_collapsed" });
|
|
2503
|
+
}
|
|
2504
|
+
};
|
|
2505
|
+
export {
|
|
2506
|
+
QuestionnaireSession
|
|
2507
|
+
};
|