reasonix 0.52.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/README.zh-CN.md +1 -0
- package/dashboard/dist/app.css +1 -1
- package/dashboard/dist/app.js +13 -13
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/{acp-NEUYWGUU.js → acp-ABNDGEYC.js} +63 -30
- package/dist/cli/acp-ABNDGEYC.js.map +1 -0
- package/dist/cli/chat-377YZV56.js +49 -0
- package/dist/cli/{chunk-FY5UERSG.js → chunk-2WZT27GR.js} +9 -9
- package/dist/cli/{chunk-B4MOGWHW.js → chunk-4EHRIP5U.js} +7 -7
- package/dist/cli/chunk-4EHRIP5U.js.map +1 -0
- package/dist/cli/{chunk-RCC73DWQ.js → chunk-4SBXAHR6.js} +4 -4
- package/dist/cli/{chunk-5YLEKX2V.js → chunk-4V4TKQMB.js} +4 -4
- package/dist/cli/{chunk-5YLEKX2V.js.map → chunk-4V4TKQMB.js.map} +1 -1
- package/dist/cli/chunk-7ZO6H6ZK.js +54 -0
- package/dist/cli/chunk-7ZO6H6ZK.js.map +1 -0
- package/dist/cli/{chunk-3OXD5CBM.js → chunk-A6GSOADP.js} +17870 -16070
- package/dist/cli/chunk-A6GSOADP.js.map +1 -0
- package/dist/cli/{chunk-Z663GVUB.js → chunk-APOSDBAU.js} +3 -3
- package/dist/cli/{chunk-CTRM32BP.js → chunk-B5JISV5I.js} +2 -2
- package/dist/cli/{chunk-HNZ4727T.js → chunk-DFHI2MRB.js} +412 -152
- package/dist/cli/chunk-DFHI2MRB.js.map +1 -0
- package/dist/cli/{chunk-CGVW5W7N.js → chunk-EPIHGOM3.js} +14 -14
- package/dist/cli/{chunk-CGVW5W7N.js.map → chunk-EPIHGOM3.js.map} +1 -1
- package/dist/cli/{chunk-77JIQ7SL.js → chunk-EQFZIHKJ.js} +8 -8
- package/dist/cli/chunk-EQFZIHKJ.js.map +1 -0
- package/dist/cli/{chunk-XNMXVL6C.js → chunk-FB27YXPX.js} +2 -2
- package/dist/cli/{chunk-ARBGTNHM.js → chunk-FK7NXDRP.js} +2 -2
- package/dist/cli/{chunk-AOYUW3HR.js → chunk-GCNBIWK7.js} +22 -2
- package/dist/cli/chunk-GCNBIWK7.js.map +1 -0
- package/dist/cli/{chunk-XBYHNZ5Z.js → chunk-GMQVINZK.js} +13 -5
- package/dist/cli/chunk-GMQVINZK.js.map +1 -0
- package/dist/cli/{chunk-MVLPXZAA.js → chunk-GOASYYZ4.js} +43 -11
- package/dist/cli/{chunk-MVLPXZAA.js.map → chunk-GOASYYZ4.js.map} +1 -1
- package/dist/cli/{chunk-WPY7AFS6.js → chunk-I4SH5Z7S.js} +2 -2
- package/dist/cli/{chunk-MW64SQUE.js → chunk-J26XOB2T.js} +2 -2
- package/dist/cli/{chunk-DLTE4GBY.js → chunk-J4MYMBJ7.js} +3 -3
- package/dist/cli/{chunk-CFJY64UA.js → chunk-LRO63VNK.js} +2 -2
- package/dist/cli/{chunk-XUZHBQSM.js → chunk-MQJR7YQ2.js} +2 -2
- package/dist/cli/{chunk-CPCUMMSR.js → chunk-NVI4XPOQ.js} +3 -3
- package/dist/cli/{chunk-RHQOGG43.js → chunk-OHSVEXFF.js} +3 -3
- package/dist/cli/chunk-OHSVEXFF.js.map +1 -0
- package/dist/cli/{chunk-AMSK3ZLB.js → chunk-P5SUHDUQ.js} +2 -2
- package/dist/cli/{chunk-GFJJEW3Z.js → chunk-QSKDP3OS.js} +55 -5
- package/dist/cli/chunk-QSKDP3OS.js.map +1 -0
- package/dist/cli/{chunk-D6WRFR6V.js → chunk-R7JMQMLD.js} +6 -5
- package/dist/cli/chunk-R7JMQMLD.js.map +1 -0
- package/dist/cli/{chunk-VVPV5HU6.js → chunk-RRZIIMAF.js} +2 -2
- package/dist/cli/{chunk-GNRKXRRE.js → chunk-S3QII236.js} +369 -359
- package/dist/cli/{chunk-GNRKXRRE.js.map → chunk-S3QII236.js.map} +1 -1
- package/dist/cli/{chunk-HI6THNAZ.js → chunk-TGP7JGHN.js} +32 -14
- package/dist/cli/chunk-TGP7JGHN.js.map +1 -0
- package/dist/cli/{chunk-CLHMV6OL.js → chunk-U7G72DHQ.js} +83 -42
- package/dist/cli/chunk-U7G72DHQ.js.map +1 -0
- package/dist/cli/{chunk-OMNRXZNA.js → chunk-URAI4YRL.js} +2 -2
- package/dist/cli/{chunk-6QBUXA73.js → chunk-V4AXMN4X.js} +2 -2
- package/dist/cli/{chunk-2W4F3RIZ.js → chunk-XHP6NYOT.js} +3 -2
- package/dist/cli/{chunk-2W4F3RIZ.js.map → chunk-XHP6NYOT.js.map} +1 -1
- package/dist/cli/{code-WN6D4VZO.js → code-JPFZJYVW.js} +34 -34
- package/dist/cli/{commands-DHETOY7O.js → commands-IUL2CLKH.js} +4 -4
- package/dist/cli/{commit-BBUYAKZV.js → commit-JT7LYBTL.js} +3 -3
- package/dist/cli/{config-KV7VV5LG.js → config-T4RWI5NG.js} +6 -2
- package/dist/cli/{desktop-LJVXWXNF.js → desktop-AUBW2SLL.js} +122 -38
- package/dist/cli/desktop-AUBW2SLL.js.map +1 -0
- package/dist/cli/devtools-O5HOMAGZ.js +3 -0
- package/dist/cli/diff-NINZHUJR.js +165 -0
- package/dist/cli/diff-NINZHUJR.js.map +1 -0
- package/dist/cli/{doctor-GI5LOEZL.js → doctor-OMAYGY4F.js} +10 -10
- package/dist/cli/{events-LBKMLFM4.js → events-5IVFJ4H3.js} +5 -5
- package/dist/cli/index.js +38 -38
- package/dist/cli/{mcp-DKEJK5ND.js → mcp-ECGJACAP.js} +3 -3
- package/dist/cli/{mcp-browse-V7KWDY32.js → mcp-browse-NGEOHVJB.js} +15 -15
- package/dist/cli/mcp-browse-NGEOHVJB.js.map +1 -0
- package/dist/cli/{mcp-inspect-MTABNHVM.js → mcp-inspect-ZIMNRG7G.js} +5 -5
- package/dist/cli/{prompt-ATI7DKHF.js → prompt-JCC3A7AA.js} +5 -5
- package/dist/cli/{prune-sessions-YQQSZTZS.js → prune-sessions-TE4BJYO2.js} +4 -4
- package/dist/cli/{replay-ZJQ4I4CJ.js → replay-2UUTCRTG.js} +29 -29
- package/dist/cli/replay-2UUTCRTG.js.map +1 -0
- package/dist/cli/{run-HFPRNWJY.js → run-ABQYOPVM.js} +22 -22
- package/dist/cli/{server-UHKO2VVM.js → server-MPCXIW2O.js} +27 -25
- package/dist/cli/{server-UHKO2VVM.js.map → server-MPCXIW2O.js.map} +1 -1
- package/dist/cli/{sessions-IQEWWUH3.js → sessions-YBPRGIAF.js} +16 -16
- package/dist/cli/{setup-5BYKCL62.js → setup-A34LF2QE.js} +42 -42
- package/dist/cli/setup-A34LF2QE.js.map +1 -0
- package/dist/cli/{stats-OFCGOQMZ.js → stats-GKG5JZQX.js} +6 -6
- package/dist/cli/stats-GKG5JZQX.js.map +1 -0
- package/dist/cli/{version-EODUFAAI.js → version-JD6QSM4X.js} +16 -16
- package/dist/index.d.ts +36 -5
- package/dist/index.js +443 -73
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/dist/cli/acp-NEUYWGUU.js.map +0 -1
- package/dist/cli/chat-QA6IVFJD.js +0 -49
- package/dist/cli/chunk-3OXD5CBM.js.map +0 -1
- package/dist/cli/chunk-77JIQ7SL.js.map +0 -1
- package/dist/cli/chunk-AOYUW3HR.js.map +0 -1
- package/dist/cli/chunk-B4MOGWHW.js.map +0 -1
- package/dist/cli/chunk-CLHMV6OL.js.map +0 -1
- package/dist/cli/chunk-D6WRFR6V.js.map +0 -1
- package/dist/cli/chunk-GFJJEW3Z.js.map +0 -1
- package/dist/cli/chunk-HI6THNAZ.js.map +0 -1
- package/dist/cli/chunk-HNZ4727T.js.map +0 -1
- package/dist/cli/chunk-I3NE5S63.js +0 -54
- package/dist/cli/chunk-I3NE5S63.js.map +0 -1
- package/dist/cli/chunk-RHQOGG43.js.map +0 -1
- package/dist/cli/chunk-XBYHNZ5Z.js.map +0 -1
- package/dist/cli/desktop-LJVXWXNF.js.map +0 -1
- package/dist/cli/diff-2JHMQAHI.js +0 -165
- package/dist/cli/diff-2JHMQAHI.js.map +0 -1
- package/dist/cli/mcp-browse-V7KWDY32.js.map +0 -1
- package/dist/cli/replay-ZJQ4I4CJ.js.map +0 -1
- package/dist/cli/setup-5BYKCL62.js.map +0 -1
- /package/dist/cli/{chat-QA6IVFJD.js.map → chat-377YZV56.js.map} +0 -0
- /package/dist/cli/{chunk-FY5UERSG.js.map → chunk-2WZT27GR.js.map} +0 -0
- /package/dist/cli/{chunk-RCC73DWQ.js.map → chunk-4SBXAHR6.js.map} +0 -0
- /package/dist/cli/{chunk-Z663GVUB.js.map → chunk-APOSDBAU.js.map} +0 -0
- /package/dist/cli/{chunk-CTRM32BP.js.map → chunk-B5JISV5I.js.map} +0 -0
- /package/dist/cli/{chunk-XNMXVL6C.js.map → chunk-FB27YXPX.js.map} +0 -0
- /package/dist/cli/{chunk-ARBGTNHM.js.map → chunk-FK7NXDRP.js.map} +0 -0
- /package/dist/cli/{chunk-WPY7AFS6.js.map → chunk-I4SH5Z7S.js.map} +0 -0
- /package/dist/cli/{chunk-MW64SQUE.js.map → chunk-J26XOB2T.js.map} +0 -0
- /package/dist/cli/{chunk-DLTE4GBY.js.map → chunk-J4MYMBJ7.js.map} +0 -0
- /package/dist/cli/{chunk-CFJY64UA.js.map → chunk-LRO63VNK.js.map} +0 -0
- /package/dist/cli/{chunk-XUZHBQSM.js.map → chunk-MQJR7YQ2.js.map} +0 -0
- /package/dist/cli/{chunk-CPCUMMSR.js.map → chunk-NVI4XPOQ.js.map} +0 -0
- /package/dist/cli/{chunk-AMSK3ZLB.js.map → chunk-P5SUHDUQ.js.map} +0 -0
- /package/dist/cli/{chunk-VVPV5HU6.js.map → chunk-RRZIIMAF.js.map} +0 -0
- /package/dist/cli/{chunk-OMNRXZNA.js.map → chunk-URAI4YRL.js.map} +0 -0
- /package/dist/cli/{chunk-6QBUXA73.js.map → chunk-V4AXMN4X.js.map} +0 -0
- /package/dist/cli/{code-WN6D4VZO.js.map → code-JPFZJYVW.js.map} +0 -0
- /package/dist/cli/{commands-DHETOY7O.js.map → commands-IUL2CLKH.js.map} +0 -0
- /package/dist/cli/{commit-BBUYAKZV.js.map → commit-JT7LYBTL.js.map} +0 -0
- /package/dist/cli/{config-KV7VV5LG.js.map → config-T4RWI5NG.js.map} +0 -0
- /package/dist/cli/{doctor-GI5LOEZL.js.map → devtools-O5HOMAGZ.js.map} +0 -0
- /package/dist/cli/{prompt-ATI7DKHF.js.map → doctor-OMAYGY4F.js.map} +0 -0
- /package/dist/cli/{events-LBKMLFM4.js.map → events-5IVFJ4H3.js.map} +0 -0
- /package/dist/cli/{mcp-DKEJK5ND.js.map → mcp-ECGJACAP.js.map} +0 -0
- /package/dist/cli/{mcp-inspect-MTABNHVM.js.map → mcp-inspect-ZIMNRG7G.js.map} +0 -0
- /package/dist/cli/{stats-OFCGOQMZ.js.map → prompt-JCC3A7AA.js.map} +0 -0
- /package/dist/cli/{prune-sessions-YQQSZTZS.js.map → prune-sessions-TE4BJYO2.js.map} +0 -0
- /package/dist/cli/{run-HFPRNWJY.js.map → run-ABQYOPVM.js.map} +0 -0
- /package/dist/cli/{sessions-IQEWWUH3.js.map → sessions-YBPRGIAF.js.map} +0 -0
- /package/dist/cli/{version-EODUFAAI.js.map → version-JD6QSM4X.js.map} +0 -0
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
|
|
3
3
|
import {
|
|
4
4
|
useColor
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OHSVEXFF.js";
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from "./chunk-
|
|
7
|
+
Box_default,
|
|
8
|
+
Text,
|
|
9
|
+
require_react,
|
|
10
|
+
use_input_default
|
|
11
|
+
} from "./chunk-A6GSOADP.js";
|
|
12
12
|
import {
|
|
13
13
|
__toESM
|
|
14
14
|
} from "./chunk-TUK7OWJA.js";
|
|
@@ -509,7 +509,7 @@ function useKeystroke(handler, isActive = true) {
|
|
|
509
509
|
if (!bus || !isActive) return void 0;
|
|
510
510
|
return bus.subscribe((ev) => handlerRef.current(ev));
|
|
511
511
|
}, [bus, isActive]);
|
|
512
|
-
|
|
512
|
+
use_input_default(
|
|
513
513
|
(input, key) => {
|
|
514
514
|
if (bus) return;
|
|
515
515
|
handlerRef.current({
|
|
@@ -568,7 +568,7 @@ function SingleSelect({
|
|
|
568
568
|
onCancel();
|
|
569
569
|
}
|
|
570
570
|
});
|
|
571
|
-
return /* @__PURE__ */ import_react2.default.createElement(
|
|
571
|
+
return /* @__PURE__ */ import_react2.default.createElement(Box_default, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ import_react2.default.createElement(
|
|
572
572
|
SelectRow,
|
|
573
573
|
{
|
|
574
574
|
key: item.value,
|
|
@@ -578,7 +578,7 @@ function SingleSelect({
|
|
|
578
578
|
color,
|
|
579
579
|
inlineHint: inlineHints
|
|
580
580
|
}
|
|
581
|
-
)), footer ? /* @__PURE__ */ import_react2.default.createElement(
|
|
581
|
+
)), footer ? /* @__PURE__ */ import_react2.default.createElement(Box_default, { marginTop: 1 }, /* @__PURE__ */ import_react2.default.createElement(Text, { dim: true }, footer)) : null);
|
|
582
582
|
}
|
|
583
583
|
function MultiSelect({
|
|
584
584
|
items,
|
|
@@ -617,7 +617,7 @@ function MultiSelect({
|
|
|
617
617
|
onCancel();
|
|
618
618
|
}
|
|
619
619
|
});
|
|
620
|
-
return /* @__PURE__ */ import_react2.default.createElement(
|
|
620
|
+
return /* @__PURE__ */ import_react2.default.createElement(Box_default, { flexDirection: "column" }, items.map((item, i) => {
|
|
621
621
|
const checked = selected.has(item.value);
|
|
622
622
|
const marker = checked ? "[x]" : "[ ]";
|
|
623
623
|
return /* @__PURE__ */ import_react2.default.createElement(
|
|
@@ -631,7 +631,7 @@ function MultiSelect({
|
|
|
631
631
|
inlineHint: inlineHints
|
|
632
632
|
}
|
|
633
633
|
);
|
|
634
|
-
}), footer ? /* @__PURE__ */ import_react2.default.createElement(
|
|
634
|
+
}), footer ? /* @__PURE__ */ import_react2.default.createElement(Box_default, { marginTop: 1 }, /* @__PURE__ */ import_react2.default.createElement(Text, { dim: true }, footer)) : null);
|
|
635
635
|
}
|
|
636
636
|
function SelectRow({
|
|
637
637
|
item,
|
|
@@ -643,9 +643,9 @@ function SelectRow({
|
|
|
643
643
|
const rowColor = item.disabled ? color.info : active ? color.primary : void 0;
|
|
644
644
|
const labelText = `${marker} ${item.label}`;
|
|
645
645
|
if (inlineHint) {
|
|
646
|
-
return /* @__PURE__ */ import_react2.default.createElement(
|
|
646
|
+
return /* @__PURE__ */ import_react2.default.createElement(Box_default, { flexDirection: "row", flexWrap: "nowrap", minHeight: 1 }, /* @__PURE__ */ import_react2.default.createElement(Text, { color: rowColor, bold: active, dim: item.disabled, wrap: "truncate" }, labelText), item.hint ? /* @__PURE__ */ import_react2.default.createElement(Text, { dim: true, wrap: "truncate" }, ` ${item.hint}`) : null);
|
|
647
647
|
}
|
|
648
|
-
return /* @__PURE__ */ import_react2.default.createElement(
|
|
648
|
+
return /* @__PURE__ */ import_react2.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react2.default.createElement(Box_default, null, /* @__PURE__ */ import_react2.default.createElement(Text, { color: rowColor, bold: active, dim: item.disabled }, labelText)), item.hint ? /* @__PURE__ */ import_react2.default.createElement(Box_default, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ import_react2.default.createElement(Text, { dim: true }, item.hint)) : null);
|
|
649
649
|
}
|
|
650
650
|
function findNextEnabled(items, from, step) {
|
|
651
651
|
if (items.length === 0) return 0;
|
|
@@ -663,4 +663,4 @@ export {
|
|
|
663
663
|
SingleSelect,
|
|
664
664
|
MultiSelect
|
|
665
665
|
};
|
|
666
|
-
//# sourceMappingURL=chunk-
|
|
666
|
+
//# sourceMappingURL=chunk-EPIHGOM3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/ui/keystroke-context.tsx","../../src/cli/ui/stdin-reader.ts","../../src/cli/ui/Select.tsx"],"sourcesContent":["/**\n * KeystrokeContext — React surface in front of the raw stdin reader.\n *\n * Replaces Ink's `useInput` chain. Reasonix's components no longer\n * import `useInput` from \"ink\"; they call `useKeystroke(handler,\n * isActive)` from this module. The provider mounted once at App\n * level owns a `StdinReader`, subscribes a single fan-out function\n * to it, and dispatches each parsed `KeyEvent` to every active\n * consumer.\n *\n * Why a Context instead of a singleton import: the provider can be\n * disabled in tests / replay mode without touching the components,\n * and the lifecycle (start/stop on mount/unmount) is tied to the\n * React tree rather than a global side effect.\n *\n * Why not just keep Ink's useInput: Ink's parse-keypress uses a\n * 100 ms intra-CSI timeout that's too short for Windows ConPTY,\n * leaking arrow-key bytes / paste markers into the buffer. Our\n * reader uses 250 ms and recognises the ESC-stripped variants too\n * — see `stdin-reader.ts`.\n */\n\nimport { useInput } from \"ink\";\n// biome-ignore lint/style/useImportType: tsconfig jsx=react needs React as a runtime value\nimport React, { createContext, useContext, useEffect, useRef } from \"react\";\nimport { type KeyEvent, type StdinReader, getStdinReader } from \"./stdin-reader.js\";\n\ninterface KeystrokeBus {\n /** Subscribe — returns an unsubscribe function. */\n subscribe(handler: KeystrokeHandler): () => void;\n}\n\nexport type KeystrokeHandler = (ev: KeyEvent) => void;\n\n/** Minimum surface KeystrokeProvider needs from a key source. StdinReader implements this; the Rust input adapter does too. */\nexport interface KeystrokeReader {\n start(): void;\n subscribe(handler: KeystrokeHandler): () => void;\n}\n\nconst KeystrokeContext = createContext<KeystrokeBus | null>(null);\n\nexport interface KeystrokeProviderProps {\n children: React.ReactNode;\n /** Optional reader override. Tests inject a synthetic reader so they can `feed()` chunks instead of touching real stdin. Production callers leave this unset and get the singleton. */\n reader?: KeystrokeReader;\n}\n\nexport function KeystrokeProvider({\n children,\n reader: providedReader,\n}: KeystrokeProviderProps): React.ReactElement {\n const handlersRef = useRef<Set<KeystrokeHandler>>(new Set());\n // Ref so the bus value's identity is stable across re-renders —\n // consumers don't accidentally re-subscribe every render.\n const busRef = useRef<KeystrokeBus | null>(null);\n if (busRef.current === null) {\n busRef.current = {\n subscribe(handler) {\n handlersRef.current.add(handler);\n return () => {\n handlersRef.current.delete(handler);\n };\n },\n };\n }\n\n useEffect(() => {\n const reader = providedReader ?? getStdinReader();\n reader.start();\n const unsubscribe = reader.subscribe((ev) => {\n // Snapshot the handler set so handlers added/removed during\n // dispatch don't perturb iteration. Cheap — typical N=1-3.\n for (const fn of [...handlersRef.current]) fn(ev);\n });\n return () => {\n unsubscribe();\n // Don't `stop()` the singleton on every unmount — multiple\n // mounts (test reruns, hot-reload) must not tear down stdin.\n // The singleton's own start() is idempotent; stop() is the\n // process-exit handler's job.\n };\n }, [providedReader]);\n\n return <KeystrokeContext.Provider value={busRef.current}>{children}</KeystrokeContext.Provider>;\n}\n\n/** Subscribe to keystroke events; falls back to Ink's useInput when no KeystrokeProvider is mounted. */\nexport function useKeystroke(handler: KeystrokeHandler, isActive = true): void {\n const bus = useContext(KeystrokeContext);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n if (!bus || !isActive) return undefined;\n return bus.subscribe((ev) => handlerRef.current(ev));\n }, [bus, isActive]);\n\n useInput(\n (input, key) => {\n if (bus) return;\n handlerRef.current({\n input,\n upArrow: key.upArrow,\n downArrow: key.downArrow,\n leftArrow: key.leftArrow,\n rightArrow: key.rightArrow,\n return: key.return,\n escape: key.escape,\n backspace: key.backspace,\n delete: key.delete,\n tab: key.tab,\n shift: key.shift,\n ctrl: key.ctrl,\n meta: key.meta,\n pageUp: key.pageUp,\n pageDown: key.pageDown,\n });\n },\n { isActive: !bus && isActive },\n );\n}\n\n/**\n * Lower-level hook for components that need a stable subscription\n * across the lifetime of the consumer (typically StdinReader-aware\n * unit tests).\n */\nexport function useKeystrokeBus(): KeystrokeBus | null {\n return useContext(KeystrokeContext);\n}\n\n/** Test helper — assemble a KeyEvent with sensible defaults. */\nexport function makeKeyEvent(overrides: Partial<KeyEvent> = {}): KeyEvent {\n return { input: \"\", ...overrides };\n}\n","/** Sole stdin owner; 250 ms ESC-ambiguity timer (ConPTY splits sequences past parse-keypress's 100 ms). */\n\nimport { stdin } from \"node:process\";\n\nexport interface KeyEvent {\n /** Empty for control keys (arrows / Enter / Esc); holds the letter for Ctrl+/Alt+. */\n input: string;\n upArrow?: boolean;\n downArrow?: boolean;\n leftArrow?: boolean;\n rightArrow?: boolean;\n pageUp?: boolean;\n pageDown?: boolean;\n home?: boolean;\n end?: boolean;\n delete?: boolean;\n backspace?: boolean;\n tab?: boolean;\n return?: boolean;\n escape?: boolean;\n shift?: boolean;\n ctrl?: boolean;\n meta?: boolean;\n /** Bracketed-paste content; consumers MUST NOT re-interpret as keystrokes (e.g. `\\n` ≠ submit). */\n paste?: boolean;\n /** xterm SGR mode 1006 wheel-up. */\n mouseScrollUp?: boolean;\n /** Mouse wheel down — symmetric to `mouseScrollUp`. */\n mouseScrollDown?: boolean;\n /** Left-button press; row/col are 1-based. */\n mouseClick?: boolean;\n /** Left-button motion (button held during drag). Mode 1002 only. */\n mouseDrag?: boolean;\n /** Any-button release. Mode 1002 only. */\n mouseRelease?: boolean;\n mouseRow?: number;\n mouseCol?: number;\n}\n\ntype Subscriber = (ev: KeyEvent) => void;\n\n/** ESC ambiguity timeout. Long enough for ConPTY-split sequences. */\nconst ESC_TIMEOUT_MS = 250;\n\n/** Bracketed-paste markers (DECSET 2004). */\nconst PASTE_START = \"\\x1b[200~\";\nconst PASTE_END = \"\\x1b[201~\";\n/** ESC-stripped variants — ConPTY occasionally eats the leading ESC. */\nconst PASTE_START_BARE = \"[200~\";\nconst PASTE_END_BARE = \"[201~\";\n\nconst CSI_TAIL_MAP: ReadonlyArray<{ tail: string; ev: KeyEvent }> = [\n { tail: \"A\", ev: { input: \"\", upArrow: true } },\n { tail: \"B\", ev: { input: \"\", downArrow: true } },\n { tail: \"C\", ev: { input: \"\", rightArrow: true } },\n { tail: \"D\", ev: { input: \"\", leftArrow: true } },\n { tail: \"H\", ev: { input: \"\", home: true } },\n { tail: \"F\", ev: { input: \"\", end: true } },\n { tail: \"1~\", ev: { input: \"\", home: true } },\n { tail: \"4~\", ev: { input: \"\", end: true } },\n { tail: \"5~\", ev: { input: \"\", pageUp: true } },\n { tail: \"6~\", ev: { input: \"\", pageDown: true } },\n { tail: \"3~\", ev: { input: \"\", delete: true } },\n { tail: \"Z\", ev: { input: \"\", shift: true, tab: true } },\n // Some Windows hosts (PowerShell 7.x conhost path) emit the\n // modifier-encoded back-tab `\\x1b[1;2Z` instead of bare `\\x1b[Z`.\n // Issue #373 — without this entry Shift+Tab is silently dropped.\n { tail: \"1;2Z\", ev: { input: \"\", shift: true, tab: true } },\n // modifyOtherKeys (xterm CSI > 4 ; 2 m) sequences for Enter / Tab\n // with modifiers. Only fired when App.tsx has enabled the mode at\n // startup; otherwise Shift+Enter stays indistinguishable from Enter.\n // Modifier encoding: 2=shift, 3=alt, 4=alt+shift, 5=ctrl,\n // 6=ctrl+shift, 7=ctrl+alt, 8=ctrl+alt+shift. Keycodes: 9=Tab, 13=Enter.\n { tail: \"27;2;9~\", ev: { input: \"\", tab: true, shift: true } },\n { tail: \"27;2;13~\", ev: { input: \"\", return: true, shift: true } },\n { tail: \"27;5;13~\", ev: { input: \"\", return: true, ctrl: true } },\n { tail: \"27;6;13~\", ev: { input: \"\", return: true, ctrl: true, shift: true } },\n // Kitty keyboard protocol — same idea, different envelope:\n // `\\x1b[<keycode>;<mod>u`. Some terminals (kitty, recent Windows\n // Terminal previews) prefer this shape. Harmless to map here too.\n { tail: \"9;2u\", ev: { input: \"\", tab: true, shift: true } },\n { tail: \"13;2u\", ev: { input: \"\", return: true, shift: true } },\n { tail: \"13;5u\", ev: { input: \"\", return: true, ctrl: true } },\n { tail: \"13;6u\", ev: { input: \"\", return: true, ctrl: true, shift: true } },\n];\n\n/** SS3 sequences (`\\x1bO<letter>`) — some terminals send these for arrows. */\nconst SS3_MAP: Record<string, KeyEvent> = {\n A: { input: \"\", upArrow: true },\n B: { input: \"\", downArrow: true },\n C: { input: \"\", rightArrow: true },\n D: { input: \"\", leftArrow: true },\n H: { input: \"\", home: true },\n F: { input: \"\", end: true },\n};\n\n/** ESC-stripped CSI lookahead — ConPTY occasionally drops the leading ESC. */\nfunction tryEscapelessCsi(chunk: string, i: number): { advance: number; ev: KeyEvent } | null {\n if (chunk[i] !== \"[\") return null;\n // Paste start as a special case (handled by caller).\n // Try each known tail.\n for (const entry of CSI_TAIL_MAP) {\n const candidate = `[${entry.tail}`;\n if (chunk.slice(i, i + candidate.length) === candidate) {\n // Don't grab `[D` out of typed/pasted `[DEBUG]` — issue #1771.\n const after = chunk[i + candidate.length];\n if (after !== undefined && !isCsiContinuationByte(after)) return null;\n return { advance: candidate.length, ev: entry.ev };\n }\n }\n return null;\n}\n\nfunction isCsiContinuationByte(ch: string): boolean {\n if (ch === \"[\" || ch === \"\\x1b\") return true;\n const code = ch.charCodeAt(0);\n return code < 0x20 || code === 0x7f;\n}\n\n/** `[<btn;col;row[Mm]` — SGR mouse report body (without leading ESC). */\nconst SGR_MOUSE_ESCAPELESS_RE = /^\\[<\\d+;\\d+;\\d+[Mm]/;\nconst LEGACY_MOUSE_ESCAPELESS_PREFIX = \"[M\";\nconst LEGACY_MOUSE_ESCAPELESS_LEN = LEGACY_MOUSE_ESCAPELESS_PREFIX.length + 3;\n\nfunction decodeSgrMouseBody(body: string): KeyEvent | null {\n const m = /^<(\\d+);(\\d+);(\\d+)([Mm])$/.exec(body);\n if (!m) return null;\n const btn = Number.parseInt(m[1]!, 10);\n const col = Number.parseInt(m[2]!, 10);\n const row = Number.parseInt(m[3]!, 10);\n if (!Number.isFinite(btn) || !Number.isFinite(col) || !Number.isFinite(row)) return null;\n const tail = m[4]!;\n if (tail === \"m\") return { input: \"\", mouseRelease: true, mouseRow: row, mouseCol: col };\n if (btn === 64) return { input: \"\", mouseScrollUp: true, mouseRow: row, mouseCol: col };\n if (btn === 65) return { input: \"\", mouseScrollDown: true, mouseRow: row, mouseCol: col };\n if (btn === 0) return { input: \"\", mouseClick: true, mouseRow: row, mouseCol: col };\n if (btn === 32) return { input: \"\", mouseDrag: true, mouseRow: row, mouseCol: col };\n return null;\n}\n\n/** ConPTY can strip the ESC off SGR mouse reports — match the bare shape and drop, issue #867. */\nfunction tryEscapelessSgrMouse(\n chunk: string,\n i: number,\n): { advance: number; ev: KeyEvent | null } | null {\n if (chunk[i] !== \"[\") return null;\n const m = SGR_MOUSE_ESCAPELESS_RE.exec(chunk.slice(i));\n if (!m) return null;\n const body = m[0].slice(1);\n return { advance: m[0].length, ev: decodeSgrMouseBody(body) };\n}\n\nfunction tryEscapelessLegacyMouse(chunk: string, i: number): { advance: number } | null {\n if (\n chunk.slice(i, i + LEGACY_MOUSE_ESCAPELESS_PREFIX.length) !== LEGACY_MOUSE_ESCAPELESS_PREFIX\n ) {\n return null;\n }\n if (chunk.length - i < LEGACY_MOUSE_ESCAPELESS_LEN) return null;\n return { advance: LEGACY_MOUSE_ESCAPELESS_LEN };\n}\n\nfunction isCsiFinal(ch: string): boolean {\n const code = ch.charCodeAt(0);\n return code >= 0x40 && code <= 0x7e;\n}\n\n/** Unknown sequence → null → caller drops bytes silently (don't insert as text). */\nfunction lookupCsi(tail: string): KeyEvent | null {\n for (const entry of CSI_TAIL_MAP) {\n if (entry.tail === tail) return entry.ev;\n }\n return null;\n}\n\n/** modifyOtherKeys / Kitty: reconstruct the keystroke from `<codepoint>` + `<mod>`. */\nfunction decodeModifiedKey(cp: number, mod: number): KeyEvent | null {\n if (mod < 1 || mod > 8) return null;\n const bits = mod - 1;\n const shift = (bits & 1) !== 0;\n const alt = (bits & 2) !== 0;\n const ctrl = (bits & 4) !== 0;\n if (cp >= 0x20 && cp <= 0x7e && !ctrl && !alt) {\n const ev: KeyEvent = { input: String.fromCharCode(cp) };\n if (shift) ev.shift = true;\n return ev;\n }\n if (cp >= 0x20 && cp <= 0x7e && alt && !ctrl) {\n const ev: KeyEvent = { input: String.fromCharCode(cp), meta: true };\n if (shift) ev.shift = true;\n return ev;\n }\n if (cp >= 0x41 && cp <= 0x7a && ctrl && !alt) {\n const ev: KeyEvent = { input: String.fromCharCode(cp).toLowerCase(), ctrl: true };\n if (shift) ev.shift = true;\n return ev;\n }\n return null;\n}\n\n/** Generic modifyOtherKeys / Kitty envelope — picks up the keys lookupCsi misses (`@`, `_`, `[`, `\\`, `]`, `^` under `>4;2m`). */\nfunction tryDecodeGenericCsi(seq: string): KeyEvent | null {\n let m = /^27;(\\d+);(\\d+)~$/.exec(seq);\n if (m) return decodeModifiedKey(Number.parseInt(m[2]!, 10), Number.parseInt(m[1]!, 10));\n m = /^(\\d+);(\\d+)u$/.exec(seq);\n if (m) return decodeModifiedKey(Number.parseInt(m[1]!, 10), Number.parseInt(m[2]!, 10));\n m = /^(\\d+)u$/.exec(seq);\n if (m) return decodeModifiedKey(Number.parseInt(m[1]!, 10), 1);\n return null;\n}\n\n// Bidi controls + zero-width invisibles that browsers smuggle into the clipboard (e.g. a B-site tab title with RLE/PDF wrappers). They render as 0 cells but still occupy buffer offsets, so cursor + line-split math drifts. ZWJ / ZWNJ / variation selectors / combining marks are NOT in the class — emoji sequences and accented letters keep their semantics. Issue #849.\nconst PASTE_INVISIBLE_RE = /[\\u200B\\u200E\\u200F\\u202A-\\u202E\\u2060\\u2066-\\u2069\\u00AD\\uFEFF]/g;\n\nexport function sanitizePasteText(s: string): string {\n // `ev.paste` bypasses the multiline reducer, so normalize Windows\n // clipboard line endings here before raw CR can reach Ink's <Text>.\n return s.replace(PASTE_INVISIBLE_RE, \"\").replace(/\\r\\n?/g, \"\\n\");\n}\n\n/** Heuristic paste-burst detector — wraps raw multi-line chunks when the terminal didn't (#522). */\nexport function looksLikeUnbracketedPaste(chunk: string): boolean {\n if (chunk.length < 2) return false;\n if (chunk.includes(PASTE_START) || chunk.includes(PASTE_START_BARE)) return false;\n if (chunk.includes(PASTE_END) || chunk.includes(PASTE_END_BARE)) return false;\n // ESC anywhere = real keypress / control sequence, not a paste burst.\n if (chunk.includes(\"\\x1b\")) return false;\n // \\r\\n is one terminal-converted Enter, not two breaks — fold first.\n const norm = chunk.replace(/\\r\\n/g, \"\\n\");\n if (norm === \"\\r\" || norm === \"\\n\") return false;\n let breaks = 0;\n let firstBreakIdx = -1;\n for (let i = 0; i < norm.length; i++) {\n const c = norm[i];\n if (c === \"\\r\" || c === \"\\n\") {\n if (firstBreakIdx < 0) firstBreakIdx = i;\n breaks++;\n }\n }\n if (breaks >= 2) return true;\n // 1 break with non-empty text on BOTH sides — paste burst. (\"abc\\r\"\n // alone stays as type-then-Enter so a fast typist still submits.)\n if (breaks === 1) return firstBreakIdx > 0 && firstBreakIdx < norm.length - 1;\n return false;\n}\n\nexport class StdinReader {\n private subscribers = new Set<Subscriber>();\n private state: \"idle\" | \"esc\" | \"csi\" | \"ss3\" | \"paste\" | \"legacyMouse\" = \"idle\";\n /** Buffer for partial sequences across chunks. */\n private csiBuf = \"\";\n private legacyMouseBuf = \"\";\n /** Buffer for paste content. */\n private pasteBuf = \"\";\n private escTimer: NodeJS.Timeout | null = null;\n // Deferred-dispatch handle paired with `escTimer`. The timer\n // queues an Immediate that runs in the event loop's CHECK phase —\n // i.e. AFTER the POLL phase where stdin 'data' events fire — so\n // a multi-byte sequence whose chunks queued up while the loop was\n // blocked (heavy render, etc.) gets a chance to be processed\n // BEFORE we emit a bogus standalone-Esc. Fixes the \"I didn't press\n // Esc but it aborted the turn\" class of bug: previously the timer's\n // setTimeout callback ran in the timers phase ahead of poll, so a\n // split sequence like `\\x1b` + `[A` would dispatch escape+upArrow\n // even though the user only pressed Up.\n private escImmediate: NodeJS.Immediate | null = null;\n private started = false;\n /** The actual `data` listener — kept as a field so `stop()` can detach it. */\n private listener: ((chunk: Buffer | string) => void) | null = null;\n\n start(): void {\n if (this.started) return;\n // bun leaves `isTTY` undefined in a real terminal, so probe setRawMode directly.\n try {\n stdin.setRawMode(true);\n } catch {\n return;\n }\n stdin.setEncoding(\"utf8\");\n stdin.resume();\n this.listener = (chunk) =>\n this.handleChunk(typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\"));\n stdin.on(\"data\", this.listener);\n this.started = true;\n }\n\n stop(): void {\n if (!this.started) return;\n if (this.listener) {\n stdin.off(\"data\", this.listener);\n this.listener = null;\n }\n try {\n stdin.setRawMode(false);\n } catch {\n // setRawMode may throw if stdin is already closed; ignore.\n }\n stdin.pause();\n this.cancelEscTimer();\n this.state = \"idle\";\n this.csiBuf = \"\";\n this.legacyMouseBuf = \"\";\n this.pasteBuf = \"\";\n this.started = false;\n }\n\n subscribe(fn: Subscriber): () => void {\n this.subscribers.add(fn);\n return () => {\n this.subscribers.delete(fn);\n };\n }\n\n /** Test seam — drives the parser without a real TTY. */\n feed(chunk: string): void {\n this.handleChunk(chunk);\n }\n\n private dispatch(ev: KeyEvent): void {\n for (const sub of this.subscribers) sub(ev);\n }\n\n private cancelEscTimer(): void {\n if (this.escTimer) {\n clearTimeout(this.escTimer);\n this.escTimer = null;\n }\n if (this.escImmediate) {\n clearImmediate(this.escImmediate);\n this.escImmediate = null;\n }\n }\n\n private scheduleEscTimer(): void {\n this.cancelEscTimer();\n this.escTimer = setTimeout(() => {\n this.escTimer = null;\n // Defer the actual dispatch to the CHECK phase so any pending\n // stdin 'data' events that queued up during a long render still\n // get a chance to consume the rest of a split sequence. The\n // chunk handler cancels this Immediate at its start, so a\n // sequence completing first wins; only a truly-orphaned `\\x1b`\n // reaches the dispatch below.\n this.escImmediate = setImmediate(() => {\n this.escImmediate = null;\n if (this.state === \"esc\") {\n this.state = \"idle\";\n this.dispatch({ input: \"\", escape: true });\n }\n });\n }, ESC_TIMEOUT_MS);\n }\n\n private handleChunk(rawChunk: string): void {\n this.cancelEscTimer();\n // Paste rescue when DECSET 2004 markers don't arrive (multiplexers\n // strip them, some Windows pipes too) — otherwise each \\r in a\n // multi-line paste fires Enter and the loop submits N prompts (#522).\n const chunk =\n this.state === \"idle\" && looksLikeUnbracketedPaste(rawChunk)\n ? PASTE_START + rawChunk + PASTE_END\n : rawChunk;\n let i = 0;\n while (i < chunk.length) {\n // ── paste accumulator ──\n if (this.state === \"paste\") {\n // Look for end marker (with or without ESC).\n const endA = chunk.indexOf(PASTE_END, i);\n const endB = chunk.indexOf(PASTE_END_BARE, i);\n let endIdx = -1;\n let endLen = 0;\n if (endA !== -1 && (endB === -1 || endA <= endB)) {\n endIdx = endA;\n endLen = PASTE_END.length;\n } else if (endB !== -1) {\n endIdx = endB;\n endLen = PASTE_END_BARE.length;\n }\n if (endIdx === -1) {\n this.pasteBuf += chunk.slice(i);\n i = chunk.length;\n break;\n }\n this.pasteBuf += chunk.slice(i, endIdx);\n this.dispatch({ input: sanitizePasteText(this.pasteBuf), paste: true });\n this.pasteBuf = \"\";\n this.state = \"idle\";\n i = endIdx + endLen;\n continue;\n }\n\n // ── CSI accumulator ──\n if (this.state === \"csi\") {\n const ch = chunk[i]!;\n if (this.csiBuf.length === 0 && ch === \"M\") {\n this.state = \"legacyMouse\";\n this.legacyMouseBuf = \"\";\n i++;\n continue;\n }\n this.csiBuf += ch;\n if (isCsiFinal(ch)) {\n this.dispatchCsi(this.csiBuf);\n this.csiBuf = \"\";\n // Only reset state if `dispatchCsi` didn't already mutate it\n // (it transitions to `paste` for the `200~` start marker —\n // resetting here would clobber that and the paste content\n // would be parsed as keystrokes).\n if (this.state === \"csi\") this.state = \"idle\";\n }\n i++;\n continue;\n }\n\n if (this.state === \"legacyMouse\") {\n const need = 3 - this.legacyMouseBuf.length;\n const take = Math.min(need, chunk.length - i);\n this.legacyMouseBuf += chunk.slice(i, i + take);\n i += take;\n if (this.legacyMouseBuf.length === 3) {\n this.legacyMouseBuf = \"\";\n this.state = \"idle\";\n }\n continue;\n }\n\n // ── SS3 single-byte tail ──\n if (this.state === \"ss3\") {\n const ev = SS3_MAP[chunk[i]!];\n if (ev) this.dispatch(ev);\n this.state = \"idle\";\n i++;\n continue;\n }\n\n // ── ESC pending ──\n if (this.state === \"esc\") {\n const ch = chunk[i]!;\n if (ch === \"[\") {\n this.state = \"csi\";\n this.csiBuf = \"\";\n i++;\n continue;\n }\n if (ch === \"O\") {\n this.state = \"ss3\";\n i++;\n continue;\n }\n // Alt+Enter: ESC + CR (or ESC + LF). Universal newline shortcut on terminals\n // that don't support modifyOtherKeys (Shift+Enter falls through to plain Enter there).\n if (ch === \"\\r\" || ch === \"\\n\") {\n this.dispatch({ input: \"\", return: true, meta: true });\n this.state = \"idle\";\n i++;\n continue;\n }\n // ESC + any other char = Alt+key (rare; we still dispatch).\n this.dispatch({ input: ch, meta: true });\n this.state = \"idle\";\n i++;\n continue;\n }\n\n // ── idle ──\n const ch = chunk[i]!;\n\n if (ch === \"\\x1b\") {\n this.state = \"esc\";\n i++;\n continue;\n }\n\n // ESC-stripped paste-start (ConPTY): bare `[200~` at idle.\n if (chunk.slice(i, i + PASTE_START_BARE.length) === PASTE_START_BARE) {\n this.state = \"paste\";\n this.pasteBuf = \"\";\n i += PASTE_START_BARE.length;\n continue;\n }\n // ESC-stripped CSI tails — recover before treating `[` as text.\n const escapeless = tryEscapelessCsi(chunk, i);\n if (escapeless) {\n this.dispatch(escapeless.ev);\n i += escapeless.advance;\n continue;\n }\n const mouseEscapeless = tryEscapelessSgrMouse(chunk, i);\n if (mouseEscapeless) {\n if (mouseEscapeless.ev) this.dispatch(mouseEscapeless.ev);\n i += mouseEscapeless.advance;\n continue;\n }\n const legacyMouseEscapeless = tryEscapelessLegacyMouse(chunk, i);\n if (legacyMouseEscapeless) {\n i += legacyMouseEscapeless.advance;\n continue;\n }\n\n // Single-byte control keys.\n // \\r (CR, 0x0D) is Enter on every terminal in raw mode.\n // \\n (LF, 0x0A) is what Ctrl+J emits — keep it distinct so the\n // multiline reducer can map it to \"insert newline\" instead of\n // \"submit\". Pastes containing \\n still arrive via either the\n // bracketed-paste accumulator or a multi-byte printable chunk\n // that includes the newline; neither hits this single-byte\n // branch, so this split is safe.\n if (ch === \"\\r\") {\n this.dispatch({ input: \"\", return: true });\n i++;\n continue;\n }\n if (ch === \"\\n\") {\n this.dispatch({ input: \"j\", ctrl: true });\n i++;\n continue;\n }\n if (ch === \"\\t\") {\n this.dispatch({ input: \"\", tab: true });\n i++;\n continue;\n }\n if (ch === \"\\x7f\" || ch === \"\\b\") {\n this.dispatch({ input: \"\", backspace: true });\n i++;\n continue;\n }\n if (ch === \"\\x03\") {\n // Ctrl+C — terminate the process. Raw mode disables the\n // default SIGINT, so we have to handle it ourselves.\n this.dispatch({ input: \"c\", ctrl: true });\n i++;\n continue;\n }\n\n const code = ch.charCodeAt(0);\n // Other Ctrl+letter (0x01-0x1A → A-Z, except already-handled).\n if (code >= 1 && code <= 26) {\n const letter = String.fromCharCode(0x60 + code); // a..z\n this.dispatch({ input: letter, ctrl: true });\n i++;\n continue;\n }\n\n // Regular printable input. Coalesce a run of printable chars\n // into one event so a multi-byte UTF-8 paste-burst arrives as\n // one `input` rather than N adjacent events.\n let end = i + 1;\n while (end < chunk.length) {\n const c = chunk[end]!;\n if (c === \"\\x1b\" || c === \"\\r\" || c === \"\\n\" || c === \"\\t\") break;\n if (c === \"\\x7f\" || c === \"\\b\" || c === \"\\x03\") break;\n const cc = c.charCodeAt(0);\n if (cc >= 1 && cc <= 26) break;\n // Don't swallow into a printable run if a CSI / paste prefix\n // starts at this position.\n if (\n c === \"[\" &&\n (tryEscapelessCsi(chunk, end) ||\n tryEscapelessSgrMouse(chunk, end) ||\n tryEscapelessLegacyMouse(chunk, end))\n ) {\n break;\n }\n if (chunk.slice(end, end + PASTE_START_BARE.length) === PASTE_START_BARE) break;\n end++;\n }\n this.dispatch({ input: chunk.slice(i, end) });\n i = end;\n }\n\n // After processing, if we're still in `esc` state, schedule the\n // ambiguity timer. The next chunk may carry the rest of the CSI;\n // if not, the timer fires and dispatches a standalone Esc.\n if (this.state === \"esc\") {\n this.scheduleEscTimer();\n }\n }\n\n private dispatchCsi(seq: string): void {\n // seq is the bytes after `\\x1b[`, e.g. \"A\", \"5~\", \"200~\", \"Z\".\n if (seq === \"200~\") {\n this.state = \"paste\";\n this.pasteBuf = \"\";\n return;\n }\n if (seq === \"201~\") {\n // Stray paste-end — we shouldn't reach here outside paste mode,\n // but if we do, drop it silently.\n return;\n }\n // SGR mouse report — surface wheel/click/drag/release, drop the rest. Always consumes the bytes even when the button isn't one we map (issue #867).\n if (seq.length > 1 && seq.charCodeAt(0) === 60 /* '<' */) {\n const ev = decodeSgrMouseBody(seq);\n if (ev) this.dispatch(ev);\n return;\n }\n const ev = lookupCsi(seq);\n if (ev) {\n this.dispatch(ev);\n return;\n }\n const generic = tryDecodeGenericCsi(seq);\n if (generic) {\n this.dispatch(generic);\n return;\n }\n // Unknown CSI → drop. Do NOT insert raw bytes as text.\n }\n}\n\n/** Singleton — one reader per process. */\nlet singleton: StdinReader | null = null;\n\nexport function getStdinReader(): StdinReader {\n if (!singleton) singleton = new StdinReader();\n return singleton;\n}\n","/** Arrow-key list components for Ink — single-select and multi-select. */\n\nimport { Box, Text } from \"ink\";\nimport React, { useState } from \"react\";\nimport { useKeystroke } from \"./keystroke-context.js\";\nimport type { KeyEvent } from \"./stdin-reader.js\";\nimport { type UiColor, useColor } from \"./theme.js\";\n\nexport interface SelectItem<V extends string = string> {\n value: V;\n label: string;\n /** Optional descriptive text rendered dimmed. */\n hint?: string;\n /** Disabled rows render dimmed and are skipped on nav. */\n disabled?: boolean;\n}\n\nexport interface SingleSelectProps<V extends string> {\n items: SelectItem<V>[];\n initialValue?: V;\n onSubmit: (value: V) => void;\n onCancel?: () => void;\n /** Fired when Tab is pressed on the currently highlighted item. */\n onTab?: (value: V) => void;\n /** Optional dim footer beneath the list. */\n footer?: string;\n /** Render item hints on the same row as the label instead of a second row. */\n inlineHints?: boolean;\n /** Ignore matching keystrokes so an enclosing component can own them. */\n ignoreKey?: (ev: KeyEvent) => boolean;\n}\n\nexport function SingleSelect<V extends string>({\n items,\n initialValue,\n onSubmit,\n onTab,\n onCancel,\n footer,\n inlineHints = false,\n ignoreKey,\n}: SingleSelectProps<V>) {\n const color = useColor();\n const initialIndex = Math.max(\n 0,\n items.findIndex((i) => i.value === initialValue && !i.disabled),\n );\n const [index, setIndex] = useState(initialIndex === -1 ? 0 : initialIndex);\n\n useKeystroke((ev) => {\n if (ev.paste || ignoreKey?.(ev)) return;\n if (ev.upArrow) {\n setIndex((i) => findNextEnabled(items, i, -1));\n } else if (ev.downArrow) {\n setIndex((i) => findNextEnabled(items, i, +1));\n } else if (ev.return) {\n const chosen = items[index];\n if (chosen && !chosen.disabled) onSubmit(chosen.value);\n } else if (ev.tab) {\n const chosen = items[index];\n if (chosen && !chosen.disabled) onTab?.(chosen.value);\n } else if (ev.escape && onCancel) {\n onCancel();\n }\n });\n\n return (\n <Box flexDirection=\"column\">\n {items.map((item, i) => (\n <SelectRow\n key={item.value}\n item={item}\n active={i === index}\n marker={i === index ? \"▸\" : \" \"}\n color={color}\n inlineHint={inlineHints}\n />\n ))}\n {footer ? (\n <Box marginTop={1}>\n <Text dimColor>{footer}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nexport interface MultiSelectProps<V extends string> {\n items: SelectItem<V>[];\n initialSelected?: V[];\n onSubmit: (values: V[]) => void;\n onCancel?: () => void;\n /** Footer hint under the list — e.g. \"[Space] toggle · [Enter] confirm\". */\n footer?: string;\n /** Render item hints on the same row as the label instead of a second row. */\n inlineHints?: boolean;\n /** Ignore matching keystrokes so an enclosing component can own them. */\n ignoreKey?: (ev: KeyEvent) => boolean;\n}\n\nexport function MultiSelect<V extends string>({\n items,\n initialSelected = [],\n onSubmit,\n onCancel,\n footer,\n inlineHints = false,\n ignoreKey,\n}: MultiSelectProps<V>) {\n const color = useColor();\n const [index, setIndex] = useState(() => {\n const first = items.findIndex((i) => !i.disabled);\n return first === -1 ? 0 : first;\n });\n const [selected, setSelected] = useState<Set<V>>(new Set(initialSelected));\n\n useKeystroke((ev) => {\n if (ev.paste || ignoreKey?.(ev)) return;\n if (ev.upArrow) {\n setIndex((i) => findNextEnabled(items, i, -1));\n } else if (ev.downArrow) {\n setIndex((i) => findNextEnabled(items, i, +1));\n } else if (ev.input === \" \") {\n const item = items[index];\n if (!item || item.disabled) return;\n setSelected((prev) => {\n const next = new Set(prev);\n if (next.has(item.value)) next.delete(item.value);\n else next.add(item.value);\n return next;\n });\n } else if (ev.return) {\n const ordered = items.filter((i) => selected.has(i.value)).map((i) => i.value);\n onSubmit(ordered);\n } else if (ev.escape && onCancel) {\n onCancel();\n }\n });\n\n return (\n <Box flexDirection=\"column\">\n {items.map((item, i) => {\n const checked = selected.has(item.value);\n const marker = checked ? \"[x]\" : \"[ ]\";\n return (\n <SelectRow\n key={item.value}\n item={item}\n active={i === index}\n marker={`${i === index ? \"▸\" : \" \"} ${marker}`}\n color={color}\n inlineHint={inlineHints}\n />\n );\n })}\n {footer ? (\n <Box marginTop={1}>\n <Text dimColor>{footer}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction SelectRow<V extends string>({\n item,\n active,\n marker,\n color,\n inlineHint = false,\n}: {\n item: SelectItem<V>;\n active: boolean;\n marker: string;\n color: UiColor;\n inlineHint?: boolean;\n}) {\n const rowColor = item.disabled ? color.info : active ? color.primary : undefined;\n const labelText = `${marker} ${item.label}`;\n if (inlineHint) {\n return (\n <Box flexDirection=\"row\" flexWrap=\"nowrap\" minHeight={1}>\n <Text color={rowColor} bold={active} dimColor={item.disabled} wrap=\"truncate\">\n {labelText}\n </Text>\n {item.hint ? <Text dimColor wrap=\"truncate\">{` ${item.hint}`}</Text> : null}\n </Box>\n );\n }\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={rowColor} bold={active} dimColor={item.disabled}>\n {labelText}\n </Text>\n </Box>\n {item.hint ? (\n <Box paddingLeft={marker.length + 1}>\n <Text dimColor>{item.hint}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction findNextEnabled<V extends string>(\n items: SelectItem<V>[],\n from: number,\n step: -1 | 1,\n): number {\n if (items.length === 0) return 0;\n let i = from;\n for (let tries = 0; tries < items.length; tries++) {\n i = (i + step + items.length) % items.length;\n if (!items[i]?.disabled) return i;\n }\n return from;\n}\n"],"mappings":"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","names":["ch","ev","React","import_react","React"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/ui/keystroke-context.tsx","../../src/cli/ui/stdin-reader.ts","../../src/cli/ui/Select.tsx"],"sourcesContent":["/**\n * KeystrokeContext — React surface in front of the raw stdin reader.\n *\n * Replaces Ink's `useInput` chain. Reasonix's components no longer\n * import `useInput` from \"ink\"; they call `useKeystroke(handler,\n * isActive)` from this module. The provider mounted once at App\n * level owns a `StdinReader`, subscribes a single fan-out function\n * to it, and dispatches each parsed `KeyEvent` to every active\n * consumer.\n *\n * Why a Context instead of a singleton import: the provider can be\n * disabled in tests / replay mode without touching the components,\n * and the lifecycle (start/stop on mount/unmount) is tied to the\n * React tree rather than a global side effect.\n *\n * Why not just keep Ink's useInput: Ink's parse-keypress uses a\n * 100 ms intra-CSI timeout that's too short for Windows ConPTY,\n * leaking arrow-key bytes / paste markers into the buffer. Our\n * reader uses 250 ms and recognises the ESC-stripped variants too\n * — see `stdin-reader.ts`.\n */\n\nimport { useInput } from \"ink\";\n// biome-ignore lint/style/useImportType: tsconfig jsx=react needs React as a runtime value\nimport React, { createContext, useContext, useEffect, useRef } from \"react\";\nimport { type KeyEvent, type StdinReader, getStdinReader } from \"./stdin-reader.js\";\n\ninterface KeystrokeBus {\n /** Subscribe — returns an unsubscribe function. */\n subscribe(handler: KeystrokeHandler): () => void;\n}\n\nexport type KeystrokeHandler = (ev: KeyEvent) => void;\n\n/** Minimum surface KeystrokeProvider needs from a key source. StdinReader implements this; the Rust input adapter does too. */\nexport interface KeystrokeReader {\n start(): void;\n subscribe(handler: KeystrokeHandler): () => void;\n}\n\nconst KeystrokeContext = createContext<KeystrokeBus | null>(null);\n\nexport interface KeystrokeProviderProps {\n children: React.ReactNode;\n /** Optional reader override. Tests inject a synthetic reader so they can `feed()` chunks instead of touching real stdin. Production callers leave this unset and get the singleton. */\n reader?: KeystrokeReader;\n}\n\nexport function KeystrokeProvider({\n children,\n reader: providedReader,\n}: KeystrokeProviderProps): React.ReactElement {\n const handlersRef = useRef<Set<KeystrokeHandler>>(new Set());\n // Ref so the bus value's identity is stable across re-renders —\n // consumers don't accidentally re-subscribe every render.\n const busRef = useRef<KeystrokeBus | null>(null);\n if (busRef.current === null) {\n busRef.current = {\n subscribe(handler) {\n handlersRef.current.add(handler);\n return () => {\n handlersRef.current.delete(handler);\n };\n },\n };\n }\n\n useEffect(() => {\n const reader = providedReader ?? getStdinReader();\n reader.start();\n const unsubscribe = reader.subscribe((ev) => {\n // Snapshot the handler set so handlers added/removed during\n // dispatch don't perturb iteration. Cheap — typical N=1-3.\n for (const fn of [...handlersRef.current]) fn(ev);\n });\n return () => {\n unsubscribe();\n // Don't `stop()` the singleton on every unmount — multiple\n // mounts (test reruns, hot-reload) must not tear down stdin.\n // The singleton's own start() is idempotent; stop() is the\n // process-exit handler's job.\n };\n }, [providedReader]);\n\n return <KeystrokeContext.Provider value={busRef.current}>{children}</KeystrokeContext.Provider>;\n}\n\n/** Subscribe to keystroke events; falls back to Ink's useInput when no KeystrokeProvider is mounted. */\nexport function useKeystroke(handler: KeystrokeHandler, isActive = true): void {\n const bus = useContext(KeystrokeContext);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n if (!bus || !isActive) return undefined;\n return bus.subscribe((ev) => handlerRef.current(ev));\n }, [bus, isActive]);\n\n useInput(\n (input, key) => {\n if (bus) return;\n handlerRef.current({\n input,\n upArrow: key.upArrow,\n downArrow: key.downArrow,\n leftArrow: key.leftArrow,\n rightArrow: key.rightArrow,\n return: key.return,\n escape: key.escape,\n backspace: key.backspace,\n delete: key.delete,\n tab: key.tab,\n shift: key.shift,\n ctrl: key.ctrl,\n meta: key.meta,\n pageUp: key.pageUp,\n pageDown: key.pageDown,\n });\n },\n { isActive: !bus && isActive },\n );\n}\n\n/**\n * Lower-level hook for components that need a stable subscription\n * across the lifetime of the consumer (typically StdinReader-aware\n * unit tests).\n */\nexport function useKeystrokeBus(): KeystrokeBus | null {\n return useContext(KeystrokeContext);\n}\n\n/** Test helper — assemble a KeyEvent with sensible defaults. */\nexport function makeKeyEvent(overrides: Partial<KeyEvent> = {}): KeyEvent {\n return { input: \"\", ...overrides };\n}\n","/** Sole stdin owner; 250 ms ESC-ambiguity timer (ConPTY splits sequences past parse-keypress's 100 ms). */\n\nimport { stdin } from \"node:process\";\n\nexport interface KeyEvent {\n /** Empty for control keys (arrows / Enter / Esc); holds the letter for Ctrl+/Alt+. */\n input: string;\n upArrow?: boolean;\n downArrow?: boolean;\n leftArrow?: boolean;\n rightArrow?: boolean;\n pageUp?: boolean;\n pageDown?: boolean;\n home?: boolean;\n end?: boolean;\n delete?: boolean;\n backspace?: boolean;\n tab?: boolean;\n return?: boolean;\n escape?: boolean;\n shift?: boolean;\n ctrl?: boolean;\n meta?: boolean;\n /** Bracketed-paste content; consumers MUST NOT re-interpret as keystrokes (e.g. `\\n` ≠ submit). */\n paste?: boolean;\n /** xterm SGR mode 1006 wheel-up. */\n mouseScrollUp?: boolean;\n /** Mouse wheel down — symmetric to `mouseScrollUp`. */\n mouseScrollDown?: boolean;\n /** Left-button press; row/col are 1-based. */\n mouseClick?: boolean;\n /** Left-button motion (button held during drag). Mode 1002 only. */\n mouseDrag?: boolean;\n /** Any-button release. Mode 1002 only. */\n mouseRelease?: boolean;\n mouseRow?: number;\n mouseCol?: number;\n}\n\ntype Subscriber = (ev: KeyEvent) => void;\n\n/** ESC ambiguity timeout. Long enough for ConPTY-split sequences. */\nconst ESC_TIMEOUT_MS = 250;\n\n/** Bracketed-paste markers (DECSET 2004). */\nconst PASTE_START = \"\\x1b[200~\";\nconst PASTE_END = \"\\x1b[201~\";\n/** ESC-stripped variants — ConPTY occasionally eats the leading ESC. */\nconst PASTE_START_BARE = \"[200~\";\nconst PASTE_END_BARE = \"[201~\";\n\nconst CSI_TAIL_MAP: ReadonlyArray<{ tail: string; ev: KeyEvent }> = [\n { tail: \"A\", ev: { input: \"\", upArrow: true } },\n { tail: \"B\", ev: { input: \"\", downArrow: true } },\n { tail: \"C\", ev: { input: \"\", rightArrow: true } },\n { tail: \"D\", ev: { input: \"\", leftArrow: true } },\n { tail: \"H\", ev: { input: \"\", home: true } },\n { tail: \"F\", ev: { input: \"\", end: true } },\n { tail: \"1~\", ev: { input: \"\", home: true } },\n { tail: \"4~\", ev: { input: \"\", end: true } },\n { tail: \"5~\", ev: { input: \"\", pageUp: true } },\n { tail: \"6~\", ev: { input: \"\", pageDown: true } },\n { tail: \"3~\", ev: { input: \"\", delete: true } },\n { tail: \"Z\", ev: { input: \"\", shift: true, tab: true } },\n // Some Windows hosts (PowerShell 7.x conhost path) emit the\n // modifier-encoded back-tab `\\x1b[1;2Z` instead of bare `\\x1b[Z`.\n // Issue #373 — without this entry Shift+Tab is silently dropped.\n { tail: \"1;2Z\", ev: { input: \"\", shift: true, tab: true } },\n // modifyOtherKeys (xterm CSI > 4 ; 2 m) sequences for Enter / Tab\n // with modifiers. Only fired when App.tsx has enabled the mode at\n // startup; otherwise Shift+Enter stays indistinguishable from Enter.\n // Modifier encoding: 2=shift, 3=alt, 4=alt+shift, 5=ctrl,\n // 6=ctrl+shift, 7=ctrl+alt, 8=ctrl+alt+shift. Keycodes: 9=Tab, 13=Enter.\n { tail: \"27;2;9~\", ev: { input: \"\", tab: true, shift: true } },\n { tail: \"27;2;13~\", ev: { input: \"\", return: true, shift: true } },\n { tail: \"27;5;13~\", ev: { input: \"\", return: true, ctrl: true } },\n { tail: \"27;6;13~\", ev: { input: \"\", return: true, ctrl: true, shift: true } },\n // Kitty keyboard protocol — same idea, different envelope:\n // `\\x1b[<keycode>;<mod>u`. Some terminals (kitty, recent Windows\n // Terminal previews) prefer this shape. Harmless to map here too.\n { tail: \"9;2u\", ev: { input: \"\", tab: true, shift: true } },\n { tail: \"13;2u\", ev: { input: \"\", return: true, shift: true } },\n { tail: \"13;5u\", ev: { input: \"\", return: true, ctrl: true } },\n { tail: \"13;6u\", ev: { input: \"\", return: true, ctrl: true, shift: true } },\n];\n\n/** SS3 sequences (`\\x1bO<letter>`) — some terminals send these for arrows. */\nconst SS3_MAP: Record<string, KeyEvent> = {\n A: { input: \"\", upArrow: true },\n B: { input: \"\", downArrow: true },\n C: { input: \"\", rightArrow: true },\n D: { input: \"\", leftArrow: true },\n H: { input: \"\", home: true },\n F: { input: \"\", end: true },\n};\n\n/** ESC-stripped CSI lookahead — ConPTY occasionally drops the leading ESC. */\nfunction tryEscapelessCsi(chunk: string, i: number): { advance: number; ev: KeyEvent } | null {\n if (chunk[i] !== \"[\") return null;\n // Paste start as a special case (handled by caller).\n // Try each known tail.\n for (const entry of CSI_TAIL_MAP) {\n const candidate = `[${entry.tail}`;\n if (chunk.slice(i, i + candidate.length) === candidate) {\n // Don't grab `[D` out of typed/pasted `[DEBUG]` — issue #1771.\n const after = chunk[i + candidate.length];\n if (after !== undefined && !isCsiContinuationByte(after)) return null;\n return { advance: candidate.length, ev: entry.ev };\n }\n }\n return null;\n}\n\nfunction isCsiContinuationByte(ch: string): boolean {\n if (ch === \"[\" || ch === \"\\x1b\") return true;\n const code = ch.charCodeAt(0);\n return code < 0x20 || code === 0x7f;\n}\n\n/** `[<btn;col;row[Mm]` — SGR mouse report body (without leading ESC). */\nconst SGR_MOUSE_ESCAPELESS_RE = /^\\[<\\d+;\\d+;\\d+[Mm]/;\nconst LEGACY_MOUSE_ESCAPELESS_PREFIX = \"[M\";\nconst LEGACY_MOUSE_ESCAPELESS_LEN = LEGACY_MOUSE_ESCAPELESS_PREFIX.length + 3;\n\nfunction decodeSgrMouseBody(body: string): KeyEvent | null {\n const m = /^<(\\d+);(\\d+);(\\d+)([Mm])$/.exec(body);\n if (!m) return null;\n const btn = Number.parseInt(m[1]!, 10);\n const col = Number.parseInt(m[2]!, 10);\n const row = Number.parseInt(m[3]!, 10);\n if (!Number.isFinite(btn) || !Number.isFinite(col) || !Number.isFinite(row)) return null;\n const tail = m[4]!;\n if (tail === \"m\") return { input: \"\", mouseRelease: true, mouseRow: row, mouseCol: col };\n if (btn === 64) return { input: \"\", mouseScrollUp: true, mouseRow: row, mouseCol: col };\n if (btn === 65) return { input: \"\", mouseScrollDown: true, mouseRow: row, mouseCol: col };\n if (btn === 0) return { input: \"\", mouseClick: true, mouseRow: row, mouseCol: col };\n if (btn === 32) return { input: \"\", mouseDrag: true, mouseRow: row, mouseCol: col };\n return null;\n}\n\n/** ConPTY can strip the ESC off SGR mouse reports — match the bare shape and drop, issue #867. */\nfunction tryEscapelessSgrMouse(\n chunk: string,\n i: number,\n): { advance: number; ev: KeyEvent | null } | null {\n if (chunk[i] !== \"[\") return null;\n const m = SGR_MOUSE_ESCAPELESS_RE.exec(chunk.slice(i));\n if (!m) return null;\n const body = m[0].slice(1);\n return { advance: m[0].length, ev: decodeSgrMouseBody(body) };\n}\n\nfunction tryEscapelessLegacyMouse(chunk: string, i: number): { advance: number } | null {\n if (\n chunk.slice(i, i + LEGACY_MOUSE_ESCAPELESS_PREFIX.length) !== LEGACY_MOUSE_ESCAPELESS_PREFIX\n ) {\n return null;\n }\n if (chunk.length - i < LEGACY_MOUSE_ESCAPELESS_LEN) return null;\n return { advance: LEGACY_MOUSE_ESCAPELESS_LEN };\n}\n\nfunction isCsiFinal(ch: string): boolean {\n const code = ch.charCodeAt(0);\n return code >= 0x40 && code <= 0x7e;\n}\n\n/** Unknown sequence → null → caller drops bytes silently (don't insert as text). */\nfunction lookupCsi(tail: string): KeyEvent | null {\n for (const entry of CSI_TAIL_MAP) {\n if (entry.tail === tail) return entry.ev;\n }\n return null;\n}\n\n/** modifyOtherKeys / Kitty: reconstruct the keystroke from `<codepoint>` + `<mod>`. */\nfunction decodeModifiedKey(cp: number, mod: number): KeyEvent | null {\n if (mod < 1 || mod > 8) return null;\n const bits = mod - 1;\n const shift = (bits & 1) !== 0;\n const alt = (bits & 2) !== 0;\n const ctrl = (bits & 4) !== 0;\n if (cp >= 0x20 && cp <= 0x7e && !ctrl && !alt) {\n const ev: KeyEvent = { input: String.fromCharCode(cp) };\n if (shift) ev.shift = true;\n return ev;\n }\n if (cp >= 0x20 && cp <= 0x7e && alt && !ctrl) {\n const ev: KeyEvent = { input: String.fromCharCode(cp), meta: true };\n if (shift) ev.shift = true;\n return ev;\n }\n if (cp >= 0x41 && cp <= 0x7a && ctrl && !alt) {\n const ev: KeyEvent = { input: String.fromCharCode(cp).toLowerCase(), ctrl: true };\n if (shift) ev.shift = true;\n return ev;\n }\n return null;\n}\n\n/** Generic modifyOtherKeys / Kitty envelope — picks up the keys lookupCsi misses (`@`, `_`, `[`, `\\`, `]`, `^` under `>4;2m`). */\nfunction tryDecodeGenericCsi(seq: string): KeyEvent | null {\n let m = /^27;(\\d+);(\\d+)~$/.exec(seq);\n if (m) return decodeModifiedKey(Number.parseInt(m[2]!, 10), Number.parseInt(m[1]!, 10));\n m = /^(\\d+);(\\d+)u$/.exec(seq);\n if (m) return decodeModifiedKey(Number.parseInt(m[1]!, 10), Number.parseInt(m[2]!, 10));\n m = /^(\\d+)u$/.exec(seq);\n if (m) return decodeModifiedKey(Number.parseInt(m[1]!, 10), 1);\n return null;\n}\n\n// Bidi controls + zero-width invisibles that browsers smuggle into the clipboard (e.g. a B-site tab title with RLE/PDF wrappers). They render as 0 cells but still occupy buffer offsets, so cursor + line-split math drifts. ZWJ / ZWNJ / variation selectors / combining marks are NOT in the class — emoji sequences and accented letters keep their semantics. Issue #849.\nconst PASTE_INVISIBLE_RE = /[\\u200B\\u200E\\u200F\\u202A-\\u202E\\u2060\\u2066-\\u2069\\u00AD\\uFEFF]/g;\n\nexport function sanitizePasteText(s: string): string {\n // `ev.paste` bypasses the multiline reducer, so normalize Windows\n // clipboard line endings here before raw CR can reach Ink's <Text>.\n return s.replace(PASTE_INVISIBLE_RE, \"\").replace(/\\r\\n?/g, \"\\n\");\n}\n\n/** Heuristic paste-burst detector — wraps raw multi-line chunks when the terminal didn't (#522). */\nexport function looksLikeUnbracketedPaste(chunk: string): boolean {\n if (chunk.length < 2) return false;\n if (chunk.includes(PASTE_START) || chunk.includes(PASTE_START_BARE)) return false;\n if (chunk.includes(PASTE_END) || chunk.includes(PASTE_END_BARE)) return false;\n // ESC anywhere = real keypress / control sequence, not a paste burst.\n if (chunk.includes(\"\\x1b\")) return false;\n // \\r\\n is one terminal-converted Enter, not two breaks — fold first.\n const norm = chunk.replace(/\\r\\n/g, \"\\n\");\n if (norm === \"\\r\" || norm === \"\\n\") return false;\n let breaks = 0;\n let firstBreakIdx = -1;\n for (let i = 0; i < norm.length; i++) {\n const c = norm[i];\n if (c === \"\\r\" || c === \"\\n\") {\n if (firstBreakIdx < 0) firstBreakIdx = i;\n breaks++;\n }\n }\n if (breaks >= 2) return true;\n // 1 break with non-empty text on BOTH sides — paste burst. (\"abc\\r\"\n // alone stays as type-then-Enter so a fast typist still submits.)\n if (breaks === 1) return firstBreakIdx > 0 && firstBreakIdx < norm.length - 1;\n return false;\n}\n\nexport class StdinReader {\n private subscribers = new Set<Subscriber>();\n private state: \"idle\" | \"esc\" | \"csi\" | \"ss3\" | \"paste\" | \"legacyMouse\" = \"idle\";\n /** Buffer for partial sequences across chunks. */\n private csiBuf = \"\";\n private legacyMouseBuf = \"\";\n /** Buffer for paste content. */\n private pasteBuf = \"\";\n private escTimer: NodeJS.Timeout | null = null;\n // Deferred-dispatch handle paired with `escTimer`. The timer\n // queues an Immediate that runs in the event loop's CHECK phase —\n // i.e. AFTER the POLL phase where stdin 'data' events fire — so\n // a multi-byte sequence whose chunks queued up while the loop was\n // blocked (heavy render, etc.) gets a chance to be processed\n // BEFORE we emit a bogus standalone-Esc. Fixes the \"I didn't press\n // Esc but it aborted the turn\" class of bug: previously the timer's\n // setTimeout callback ran in the timers phase ahead of poll, so a\n // split sequence like `\\x1b` + `[A` would dispatch escape+upArrow\n // even though the user only pressed Up.\n private escImmediate: NodeJS.Immediate | null = null;\n private started = false;\n /** The actual `data` listener — kept as a field so `stop()` can detach it. */\n private listener: ((chunk: Buffer | string) => void) | null = null;\n\n start(): void {\n if (this.started) return;\n // bun leaves `isTTY` undefined in a real terminal, so probe setRawMode directly.\n try {\n stdin.setRawMode(true);\n } catch {\n return;\n }\n stdin.setEncoding(\"utf8\");\n stdin.resume();\n this.listener = (chunk) =>\n this.handleChunk(typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\"));\n stdin.on(\"data\", this.listener);\n this.started = true;\n }\n\n stop(): void {\n if (!this.started) return;\n if (this.listener) {\n stdin.off(\"data\", this.listener);\n this.listener = null;\n }\n try {\n stdin.setRawMode(false);\n } catch {\n // setRawMode may throw if stdin is already closed; ignore.\n }\n stdin.pause();\n this.cancelEscTimer();\n this.state = \"idle\";\n this.csiBuf = \"\";\n this.legacyMouseBuf = \"\";\n this.pasteBuf = \"\";\n this.started = false;\n }\n\n subscribe(fn: Subscriber): () => void {\n this.subscribers.add(fn);\n return () => {\n this.subscribers.delete(fn);\n };\n }\n\n /** Test seam — drives the parser without a real TTY. */\n feed(chunk: string): void {\n this.handleChunk(chunk);\n }\n\n private dispatch(ev: KeyEvent): void {\n for (const sub of this.subscribers) sub(ev);\n }\n\n private cancelEscTimer(): void {\n if (this.escTimer) {\n clearTimeout(this.escTimer);\n this.escTimer = null;\n }\n if (this.escImmediate) {\n clearImmediate(this.escImmediate);\n this.escImmediate = null;\n }\n }\n\n private scheduleEscTimer(): void {\n this.cancelEscTimer();\n this.escTimer = setTimeout(() => {\n this.escTimer = null;\n // Defer the actual dispatch to the CHECK phase so any pending\n // stdin 'data' events that queued up during a long render still\n // get a chance to consume the rest of a split sequence. The\n // chunk handler cancels this Immediate at its start, so a\n // sequence completing first wins; only a truly-orphaned `\\x1b`\n // reaches the dispatch below.\n this.escImmediate = setImmediate(() => {\n this.escImmediate = null;\n if (this.state === \"esc\") {\n this.state = \"idle\";\n this.dispatch({ input: \"\", escape: true });\n }\n });\n }, ESC_TIMEOUT_MS);\n }\n\n private handleChunk(rawChunk: string): void {\n this.cancelEscTimer();\n // Paste rescue when DECSET 2004 markers don't arrive (multiplexers\n // strip them, some Windows pipes too) — otherwise each \\r in a\n // multi-line paste fires Enter and the loop submits N prompts (#522).\n const chunk =\n this.state === \"idle\" && looksLikeUnbracketedPaste(rawChunk)\n ? PASTE_START + rawChunk + PASTE_END\n : rawChunk;\n let i = 0;\n while (i < chunk.length) {\n // ── paste accumulator ──\n if (this.state === \"paste\") {\n // Look for end marker (with or without ESC).\n const endA = chunk.indexOf(PASTE_END, i);\n const endB = chunk.indexOf(PASTE_END_BARE, i);\n let endIdx = -1;\n let endLen = 0;\n if (endA !== -1 && (endB === -1 || endA <= endB)) {\n endIdx = endA;\n endLen = PASTE_END.length;\n } else if (endB !== -1) {\n endIdx = endB;\n endLen = PASTE_END_BARE.length;\n }\n if (endIdx === -1) {\n this.pasteBuf += chunk.slice(i);\n i = chunk.length;\n break;\n }\n this.pasteBuf += chunk.slice(i, endIdx);\n this.dispatch({ input: sanitizePasteText(this.pasteBuf), paste: true });\n this.pasteBuf = \"\";\n this.state = \"idle\";\n i = endIdx + endLen;\n continue;\n }\n\n // ── CSI accumulator ──\n if (this.state === \"csi\") {\n const ch = chunk[i]!;\n if (this.csiBuf.length === 0 && ch === \"M\") {\n this.state = \"legacyMouse\";\n this.legacyMouseBuf = \"\";\n i++;\n continue;\n }\n this.csiBuf += ch;\n if (isCsiFinal(ch)) {\n this.dispatchCsi(this.csiBuf);\n this.csiBuf = \"\";\n // Only reset state if `dispatchCsi` didn't already mutate it\n // (it transitions to `paste` for the `200~` start marker —\n // resetting here would clobber that and the paste content\n // would be parsed as keystrokes).\n if (this.state === \"csi\") this.state = \"idle\";\n }\n i++;\n continue;\n }\n\n if (this.state === \"legacyMouse\") {\n const need = 3 - this.legacyMouseBuf.length;\n const take = Math.min(need, chunk.length - i);\n this.legacyMouseBuf += chunk.slice(i, i + take);\n i += take;\n if (this.legacyMouseBuf.length === 3) {\n this.legacyMouseBuf = \"\";\n this.state = \"idle\";\n }\n continue;\n }\n\n // ── SS3 single-byte tail ──\n if (this.state === \"ss3\") {\n const ev = SS3_MAP[chunk[i]!];\n if (ev) this.dispatch(ev);\n this.state = \"idle\";\n i++;\n continue;\n }\n\n // ── ESC pending ──\n if (this.state === \"esc\") {\n const ch = chunk[i]!;\n if (ch === \"[\") {\n this.state = \"csi\";\n this.csiBuf = \"\";\n i++;\n continue;\n }\n if (ch === \"O\") {\n this.state = \"ss3\";\n i++;\n continue;\n }\n // Alt+Enter: ESC + CR (or ESC + LF). Universal newline shortcut on terminals\n // that don't support modifyOtherKeys (Shift+Enter falls through to plain Enter there).\n if (ch === \"\\r\" || ch === \"\\n\") {\n this.dispatch({ input: \"\", return: true, meta: true });\n this.state = \"idle\";\n i++;\n continue;\n }\n // ESC + any other char = Alt+key (rare; we still dispatch).\n this.dispatch({ input: ch, meta: true });\n this.state = \"idle\";\n i++;\n continue;\n }\n\n // ── idle ──\n const ch = chunk[i]!;\n\n if (ch === \"\\x1b\") {\n this.state = \"esc\";\n i++;\n continue;\n }\n\n // ESC-stripped paste-start (ConPTY): bare `[200~` at idle.\n if (chunk.slice(i, i + PASTE_START_BARE.length) === PASTE_START_BARE) {\n this.state = \"paste\";\n this.pasteBuf = \"\";\n i += PASTE_START_BARE.length;\n continue;\n }\n // ESC-stripped CSI tails — recover before treating `[` as text.\n const escapeless = tryEscapelessCsi(chunk, i);\n if (escapeless) {\n this.dispatch(escapeless.ev);\n i += escapeless.advance;\n continue;\n }\n const mouseEscapeless = tryEscapelessSgrMouse(chunk, i);\n if (mouseEscapeless) {\n if (mouseEscapeless.ev) this.dispatch(mouseEscapeless.ev);\n i += mouseEscapeless.advance;\n continue;\n }\n const legacyMouseEscapeless = tryEscapelessLegacyMouse(chunk, i);\n if (legacyMouseEscapeless) {\n i += legacyMouseEscapeless.advance;\n continue;\n }\n\n // Single-byte control keys.\n // \\r (CR, 0x0D) is Enter on every terminal in raw mode.\n // \\n (LF, 0x0A) is what Ctrl+J emits — keep it distinct so the\n // multiline reducer can map it to \"insert newline\" instead of\n // \"submit\". Pastes containing \\n still arrive via either the\n // bracketed-paste accumulator or a multi-byte printable chunk\n // that includes the newline; neither hits this single-byte\n // branch, so this split is safe.\n if (ch === \"\\r\") {\n this.dispatch({ input: \"\", return: true });\n i++;\n continue;\n }\n if (ch === \"\\n\") {\n this.dispatch({ input: \"j\", ctrl: true });\n i++;\n continue;\n }\n if (ch === \"\\t\") {\n this.dispatch({ input: \"\", tab: true });\n i++;\n continue;\n }\n if (ch === \"\\x7f\" || ch === \"\\b\") {\n this.dispatch({ input: \"\", backspace: true });\n i++;\n continue;\n }\n if (ch === \"\\x03\") {\n // Ctrl+C — terminate the process. Raw mode disables the\n // default SIGINT, so we have to handle it ourselves.\n this.dispatch({ input: \"c\", ctrl: true });\n i++;\n continue;\n }\n\n const code = ch.charCodeAt(0);\n // Other Ctrl+letter (0x01-0x1A → A-Z, except already-handled).\n if (code >= 1 && code <= 26) {\n const letter = String.fromCharCode(0x60 + code); // a..z\n this.dispatch({ input: letter, ctrl: true });\n i++;\n continue;\n }\n\n // Regular printable input. Coalesce a run of printable chars\n // into one event so a multi-byte UTF-8 paste-burst arrives as\n // one `input` rather than N adjacent events.\n let end = i + 1;\n while (end < chunk.length) {\n const c = chunk[end]!;\n if (c === \"\\x1b\" || c === \"\\r\" || c === \"\\n\" || c === \"\\t\") break;\n if (c === \"\\x7f\" || c === \"\\b\" || c === \"\\x03\") break;\n const cc = c.charCodeAt(0);\n if (cc >= 1 && cc <= 26) break;\n // Don't swallow into a printable run if a CSI / paste prefix\n // starts at this position.\n if (\n c === \"[\" &&\n (tryEscapelessCsi(chunk, end) ||\n tryEscapelessSgrMouse(chunk, end) ||\n tryEscapelessLegacyMouse(chunk, end))\n ) {\n break;\n }\n if (chunk.slice(end, end + PASTE_START_BARE.length) === PASTE_START_BARE) break;\n end++;\n }\n this.dispatch({ input: chunk.slice(i, end) });\n i = end;\n }\n\n // After processing, if we're still in `esc` state, schedule the\n // ambiguity timer. The next chunk may carry the rest of the CSI;\n // if not, the timer fires and dispatches a standalone Esc.\n if (this.state === \"esc\") {\n this.scheduleEscTimer();\n }\n }\n\n private dispatchCsi(seq: string): void {\n // seq is the bytes after `\\x1b[`, e.g. \"A\", \"5~\", \"200~\", \"Z\".\n if (seq === \"200~\") {\n this.state = \"paste\";\n this.pasteBuf = \"\";\n return;\n }\n if (seq === \"201~\") {\n // Stray paste-end — we shouldn't reach here outside paste mode,\n // but if we do, drop it silently.\n return;\n }\n // SGR mouse report — surface wheel/click/drag/release, drop the rest. Always consumes the bytes even when the button isn't one we map (issue #867).\n if (seq.length > 1 && seq.charCodeAt(0) === 60 /* '<' */) {\n const ev = decodeSgrMouseBody(seq);\n if (ev) this.dispatch(ev);\n return;\n }\n const ev = lookupCsi(seq);\n if (ev) {\n this.dispatch(ev);\n return;\n }\n const generic = tryDecodeGenericCsi(seq);\n if (generic) {\n this.dispatch(generic);\n return;\n }\n // Unknown CSI → drop. Do NOT insert raw bytes as text.\n }\n}\n\n/** Singleton — one reader per process. */\nlet singleton: StdinReader | null = null;\n\nexport function getStdinReader(): StdinReader {\n if (!singleton) singleton = new StdinReader();\n return singleton;\n}\n","/** Arrow-key list components for Ink — single-select and multi-select. */\n\nimport { Box, Text } from \"ink\";\nimport React, { useState } from \"react\";\nimport { useKeystroke } from \"./keystroke-context.js\";\nimport type { KeyEvent } from \"./stdin-reader.js\";\nimport { type UiColor, useColor } from \"./theme.js\";\n\nexport interface SelectItem<V extends string = string> {\n value: V;\n label: string;\n /** Optional descriptive text rendered dimmed. */\n hint?: string;\n /** Disabled rows render dimmed and are skipped on nav. */\n disabled?: boolean;\n}\n\nexport interface SingleSelectProps<V extends string> {\n items: SelectItem<V>[];\n initialValue?: V;\n onSubmit: (value: V) => void;\n onCancel?: () => void;\n /** Fired when Tab is pressed on the currently highlighted item. */\n onTab?: (value: V) => void;\n /** Optional dim footer beneath the list. */\n footer?: string;\n /** Render item hints on the same row as the label instead of a second row. */\n inlineHints?: boolean;\n /** Ignore matching keystrokes so an enclosing component can own them. */\n ignoreKey?: (ev: KeyEvent) => boolean;\n}\n\nexport function SingleSelect<V extends string>({\n items,\n initialValue,\n onSubmit,\n onTab,\n onCancel,\n footer,\n inlineHints = false,\n ignoreKey,\n}: SingleSelectProps<V>) {\n const color = useColor();\n const initialIndex = Math.max(\n 0,\n items.findIndex((i) => i.value === initialValue && !i.disabled),\n );\n const [index, setIndex] = useState(initialIndex === -1 ? 0 : initialIndex);\n\n useKeystroke((ev) => {\n if (ev.paste || ignoreKey?.(ev)) return;\n if (ev.upArrow) {\n setIndex((i) => findNextEnabled(items, i, -1));\n } else if (ev.downArrow) {\n setIndex((i) => findNextEnabled(items, i, +1));\n } else if (ev.return) {\n const chosen = items[index];\n if (chosen && !chosen.disabled) onSubmit(chosen.value);\n } else if (ev.tab) {\n const chosen = items[index];\n if (chosen && !chosen.disabled) onTab?.(chosen.value);\n } else if (ev.escape && onCancel) {\n onCancel();\n }\n });\n\n return (\n <Box flexDirection=\"column\">\n {items.map((item, i) => (\n <SelectRow\n key={item.value}\n item={item}\n active={i === index}\n marker={i === index ? \"▸\" : \" \"}\n color={color}\n inlineHint={inlineHints}\n />\n ))}\n {footer ? (\n <Box marginTop={1}>\n <Text dim>{footer}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nexport interface MultiSelectProps<V extends string> {\n items: SelectItem<V>[];\n initialSelected?: V[];\n onSubmit: (values: V[]) => void;\n onCancel?: () => void;\n /** Footer hint under the list — e.g. \"[Space] toggle · [Enter] confirm\". */\n footer?: string;\n /** Render item hints on the same row as the label instead of a second row. */\n inlineHints?: boolean;\n /** Ignore matching keystrokes so an enclosing component can own them. */\n ignoreKey?: (ev: KeyEvent) => boolean;\n}\n\nexport function MultiSelect<V extends string>({\n items,\n initialSelected = [],\n onSubmit,\n onCancel,\n footer,\n inlineHints = false,\n ignoreKey,\n}: MultiSelectProps<V>) {\n const color = useColor();\n const [index, setIndex] = useState(() => {\n const first = items.findIndex((i) => !i.disabled);\n return first === -1 ? 0 : first;\n });\n const [selected, setSelected] = useState<Set<V>>(new Set(initialSelected));\n\n useKeystroke((ev) => {\n if (ev.paste || ignoreKey?.(ev)) return;\n if (ev.upArrow) {\n setIndex((i) => findNextEnabled(items, i, -1));\n } else if (ev.downArrow) {\n setIndex((i) => findNextEnabled(items, i, +1));\n } else if (ev.input === \" \") {\n const item = items[index];\n if (!item || item.disabled) return;\n setSelected((prev) => {\n const next = new Set(prev);\n if (next.has(item.value)) next.delete(item.value);\n else next.add(item.value);\n return next;\n });\n } else if (ev.return) {\n const ordered = items.filter((i) => selected.has(i.value)).map((i) => i.value);\n onSubmit(ordered);\n } else if (ev.escape && onCancel) {\n onCancel();\n }\n });\n\n return (\n <Box flexDirection=\"column\">\n {items.map((item, i) => {\n const checked = selected.has(item.value);\n const marker = checked ? \"[x]\" : \"[ ]\";\n return (\n <SelectRow\n key={item.value}\n item={item}\n active={i === index}\n marker={`${i === index ? \"▸\" : \" \"} ${marker}`}\n color={color}\n inlineHint={inlineHints}\n />\n );\n })}\n {footer ? (\n <Box marginTop={1}>\n <Text dim>{footer}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction SelectRow<V extends string>({\n item,\n active,\n marker,\n color,\n inlineHint = false,\n}: {\n item: SelectItem<V>;\n active: boolean;\n marker: string;\n color: UiColor;\n inlineHint?: boolean;\n}) {\n const rowColor = item.disabled ? color.info : active ? color.primary : undefined;\n const labelText = `${marker} ${item.label}`;\n if (inlineHint) {\n return (\n <Box flexDirection=\"row\" flexWrap=\"nowrap\" minHeight={1}>\n <Text color={rowColor} bold={active} dim={item.disabled} wrap=\"truncate\">\n {labelText}\n </Text>\n {item.hint ? <Text dim wrap=\"truncate\">{` ${item.hint}`}</Text> : null}\n </Box>\n );\n }\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={rowColor} bold={active} dim={item.disabled}>\n {labelText}\n </Text>\n </Box>\n {item.hint ? (\n <Box paddingLeft={marker.length + 1}>\n <Text dim>{item.hint}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction findNextEnabled<V extends string>(\n items: SelectItem<V>[],\n from: number,\n step: -1 | 1,\n): number {\n if (items.length === 0) return 0;\n let i = from;\n for (let tries = 0; tries < items.length; tries++) {\n i = (i + step + items.length) % items.length;\n if (!items[i]?.disabled) return i;\n }\n return from;\n}\n"],"mappings":"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","names":["ch","ev","React","import_react","React"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "./chunk-
|
|
4
|
+
Text,
|
|
5
|
+
require_react,
|
|
6
|
+
useStdout
|
|
7
|
+
} from "./chunk-A6GSOADP.js";
|
|
8
8
|
import {
|
|
9
9
|
__toESM
|
|
10
10
|
} from "./chunk-TUK7OWJA.js";
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
// src/cli/ui/primitives.tsx
|
|
13
13
|
var import_react = __toESM(require_react(), 1);
|
|
14
14
|
function ChromeRule() {
|
|
15
|
-
const { stdout } =
|
|
15
|
+
const { stdout } = useStdout();
|
|
16
16
|
const cols = stdout?.columns ?? 80;
|
|
17
17
|
const w = Math.max(20, cols - 2);
|
|
18
|
-
return /* @__PURE__ */ import_react.default.createElement(
|
|
18
|
+
return /* @__PURE__ */ import_react.default.createElement(Text, { dim: true }, "\u2500".repeat(w));
|
|
19
19
|
}
|
|
20
20
|
function formatTokens(n) {
|
|
21
21
|
if (n < 1e3) return String(n);
|
|
@@ -29,7 +29,7 @@ function Bar({
|
|
|
29
29
|
dim
|
|
30
30
|
}) {
|
|
31
31
|
const filled = Math.max(0, Math.min(cells, Math.round(ratio * cells)));
|
|
32
|
-
return /* @__PURE__ */ import_react.default.createElement(
|
|
32
|
+
return /* @__PURE__ */ import_react.default.createElement(Text, null, /* @__PURE__ */ import_react.default.createElement(Text, { color, dim }, "\u25B0".repeat(filled)), /* @__PURE__ */ import_react.default.createElement(Text, { dim: true }, "\u25B1".repeat(cells - filled)));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export {
|
|
@@ -37,4 +37,4 @@ export {
|
|
|
37
37
|
formatTokens,
|
|
38
38
|
Bar
|
|
39
39
|
};
|
|
40
|
-
//# sourceMappingURL=chunk-
|
|
40
|
+
//# sourceMappingURL=chunk-EQFZIHKJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/ui/primitives.tsx"],"sourcesContent":["import { type Color, Text, useStdout } from \"ink\";\n// biome-ignore lint/style/useImportType: tsconfig jsx=react needs React in value scope for JSX compilation\nimport React from \"react\";\nimport { t } from \"../../i18n/index.js\";\nimport { COLOR } from \"./theme.js\";\n\n/**\n * Faint full-width horizontal rule. Width tracks the terminal columns\n * minus 2 cells so it lines up exactly under content rendered inside\n * a `paddingX={1}` parent — the standard chrome layout. Used by the\n * top chrome bar, the replay StatsPanel, and the bottom ctx footer.\n */\nexport function ChromeRule(): React.ReactElement {\n const { stdout } = useStdout();\n const cols = stdout?.columns ?? 80;\n const w = Math.max(20, cols - 2);\n return <Text dim>{\"─\".repeat(w)}</Text>;\n}\n\n/** Compact decimal-K token formatter — `1234 → \"1.2K\"`, `131000 → \"131K\"`. Base-1000 matches DeepSeek's \"1M context\" / \"128K\" wording and the web dashboard's display, so the CLI bottom bar and the web bar agree on ctx capacity. */\nexport function formatTokens(n: number): string {\n if (n < 1000) return String(n);\n const k = n / 1000;\n return k >= 100 ? `${k.toFixed(0)}K` : `${k.toFixed(1)}K`;\n}\n\n/**\n * Filled / empty progress bar. `▰▱` glyphs have distinct shapes so the\n * boundary stays visible even when the terminal collapses to 8-color slots.\n */\nexport function Bar({\n ratio,\n color,\n cells = 14,\n dim,\n}: {\n ratio: number;\n color: Color;\n cells?: number;\n dim?: boolean;\n}): React.ReactElement {\n const filled = Math.max(0, Math.min(cells, Math.round(ratio * cells)));\n return (\n <Text>\n <Text color={color} dim={dim}>\n {\"▰\".repeat(filled)}\n </Text>\n <Text dim>{\"▱\".repeat(cells - filled)}</Text>\n </Text>\n );\n}\n\n/**\n * `▣ ctx ▰▰▱▱… 14K/128K (11%)` — the canonical context-pressure cell.\n * Used by the persistent footer (chat) and StatsPanel (replay). Color\n * thresholds match the `/compact` warning policy in the loop:\n * green <60% · amber 60-80% · red ≥80% (with `· /compact` hint).\n */\nexport function ContextCell({\n ratio,\n promptTokens,\n ctxMax,\n showBar,\n}: {\n ratio: number;\n promptTokens: number;\n ctxMax: number;\n showBar?: boolean;\n}): React.ReactElement {\n if (promptTokens === 0) {\n return (\n <Text>\n <Text color={COLOR.info} dim>\n {\"▣ ctx \"}\n </Text>\n <Text dim>{`\\u2014 ${t(\"common.noTurns\")}`}</Text>\n </Text>\n );\n }\n const color = ratio >= 0.8 ? COLOR.err : ratio >= 0.6 ? COLOR.warn : COLOR.ok;\n const pct = Math.round(ratio * 100);\n return (\n <Text>\n <Text color={COLOR.info}>{\"▣ ctx \"}</Text>\n <Bar ratio={ratio} color={color} cells={showBar ? 14 : 10} />\n <Text> </Text>\n <Text color={color} bold>\n {formatTokens(promptTokens)}/{formatTokens(ctxMax)}\n </Text>\n <Text dim> ({pct}%)</Text>\n {ratio >= 0.8 ? (\n <Text color={COLOR.err} bold>\n {\" · /compact\"}\n </Text>\n ) : null}\n </Text>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAEA,mBAAkB;AAUX,SAAS,aAAiC;AAC/C,QAAM,EAAE,OAAO,IAAI,UAAU;AAC7B,QAAM,OAAO,QAAQ,WAAW;AAChC,QAAM,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC;AAC/B,SAAO,6BAAAA,QAAA,cAAC,QAAK,KAAG,QAAE,SAAI,OAAO,CAAC,CAAE;AAClC;AAGO,SAAS,aAAa,GAAmB;AAC9C,MAAI,IAAI,IAAM,QAAO,OAAO,CAAC;AAC7B,QAAM,IAAI,IAAI;AACd,SAAO,KAAK,MAAM,GAAG,EAAE,QAAQ,CAAC,CAAC,MAAM,GAAG,EAAE,QAAQ,CAAC,CAAC;AACxD;AAMO,SAAS,IAAI;AAAA,EAClB;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AACF,GAKuB;AACrB,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AACrE,SACE,6BAAAA,QAAA,cAAC,YACC,6BAAAA,QAAA,cAAC,QAAK,OAAc,OACjB,SAAI,OAAO,MAAM,CACpB,GACA,6BAAAA,QAAA,cAAC,QAAK,KAAG,QAAE,SAAI,OAAO,QAAQ,MAAM,CAAE,CACxC;AAEJ;","names":["React"]}
|
|
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.requi
|
|
|
3
3
|
import {
|
|
4
4
|
sanitizeName,
|
|
5
5
|
sessionsDir
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-P5SUHDUQ.js";
|
|
7
7
|
|
|
8
8
|
// src/adapters/event-sink-jsonl.ts
|
|
9
9
|
import { chmodSync, createWriteStream, mkdirSync } from "fs";
|
|
@@ -51,4 +51,4 @@ export {
|
|
|
51
51
|
eventLogPath,
|
|
52
52
|
openEventSink
|
|
53
53
|
};
|
|
54
|
-
//# sourceMappingURL=chunk-
|
|
54
|
+
//# sourceMappingURL=chunk-FB27YXPX.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
cacheSavingsUsd,
|
|
5
5
|
claudeEquivalentCost,
|
|
6
6
|
costUsd
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-V4AXMN4X.js";
|
|
8
8
|
|
|
9
9
|
// src/telemetry/usage.ts
|
|
10
10
|
import {
|
|
@@ -229,4 +229,4 @@ export {
|
|
|
229
229
|
aggregateUsage,
|
|
230
230
|
formatLogSize
|
|
231
231
|
};
|
|
232
|
-
//# sourceMappingURL=chunk-
|
|
232
|
+
//# sourceMappingURL=chunk-FK7NXDRP.js.map
|
|
@@ -17085,6 +17085,20 @@ function loadExaApiKey(path = defaultConfigPath()) {
|
|
|
17085
17085
|
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
17086
17086
|
return void 0;
|
|
17087
17087
|
}
|
|
17088
|
+
function loadOllamaApiKey(path = defaultConfigPath()) {
|
|
17089
|
+
if (process.env.OLLAMA_API_KEY) return process.env.OLLAMA_API_KEY.trim();
|
|
17090
|
+
if (process.env.ollamaApiKey) return process.env.ollamaApiKey.trim();
|
|
17091
|
+
const cfg = readConfig(path).ollamaApiKey;
|
|
17092
|
+
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
17093
|
+
return void 0;
|
|
17094
|
+
}
|
|
17095
|
+
function loadBraveApiKey(path = defaultConfigPath()) {
|
|
17096
|
+
if (process.env.BRAVE_SEARCH_API_KEY) return process.env.BRAVE_SEARCH_API_KEY.trim();
|
|
17097
|
+
if (process.env.BRAVE_API_KEY) return process.env.BRAVE_API_KEY.trim();
|
|
17098
|
+
const cfg = readConfig(path).braveApiKey;
|
|
17099
|
+
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
17100
|
+
return void 0;
|
|
17101
|
+
}
|
|
17088
17102
|
var DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
17089
17103
|
var DEFAULT_EMBED_MODEL = "nomic-embed-text";
|
|
17090
17104
|
var DEFAULT_TIMEOUT_MS = 18e4;
|
|
@@ -17502,6 +17516,8 @@ function webSearchEngine(path = defaultConfigPath()) {
|
|
|
17502
17516
|
if (cfg === "tavily") return "tavily";
|
|
17503
17517
|
if (cfg === "perplexity") return "perplexity";
|
|
17504
17518
|
if (cfg === "exa") return "exa";
|
|
17519
|
+
if (cfg === "brave") return "brave";
|
|
17520
|
+
if (cfg === "ollama") return "ollama";
|
|
17505
17521
|
return "bing";
|
|
17506
17522
|
}
|
|
17507
17523
|
function webSearchEndpoint(path = defaultConfigPath()) {
|
|
@@ -17511,8 +17527,10 @@ function webSearchEndpoint(path = defaultConfigPath()) {
|
|
|
17511
17527
|
}
|
|
17512
17528
|
function saveApiKey(key, path = defaultConfigPath()) {
|
|
17513
17529
|
const cfg = readConfig(path);
|
|
17514
|
-
|
|
17530
|
+
const trimmed = key.trim();
|
|
17531
|
+
cfg.apiKey = trimmed;
|
|
17515
17532
|
writeConfig(cfg, path);
|
|
17533
|
+
if (trimmed) process.env.DEEPSEEK_API_KEY = trimmed;
|
|
17516
17534
|
}
|
|
17517
17535
|
function findProjectKey(cfg, rootDir) {
|
|
17518
17536
|
const projects = cfg.projects;
|
|
@@ -17977,6 +17995,8 @@ export {
|
|
|
17977
17995
|
loadTavilyApiKey,
|
|
17978
17996
|
loadPerplexityApiKey,
|
|
17979
17997
|
loadExaApiKey,
|
|
17998
|
+
loadOllamaApiKey,
|
|
17999
|
+
loadBraveApiKey,
|
|
17980
18000
|
defaultConfigPath,
|
|
17981
18001
|
readConfig,
|
|
17982
18002
|
loadDashboardEnabled,
|
|
@@ -18063,4 +18083,4 @@ export {
|
|
|
18063
18083
|
loadQQConfig,
|
|
18064
18084
|
saveQQConfig
|
|
18065
18085
|
};
|
|
18066
|
-
//# sourceMappingURL=chunk-
|
|
18086
|
+
//# sourceMappingURL=chunk-GCNBIWK7.js.map
|