laohuang 0.8.5 → 0.8.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.
Files changed (3) hide show
  1. package/dist/bin.js +1035 -718
  2. package/dist/bin.js.map +4 -4
  3. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/main.ts
4
4
  import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
5
- import { dirname as dirname8, join as join13 } from "node:path";
5
+ import { dirname as dirname8, join as join14 } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
 
8
8
  // ../../packages/core/agent-runtime/dist/agent.js
@@ -7862,10 +7862,614 @@ function enterTerminalRawMode(input, console2 = input === process.stdin ? getWin
7862
7862
  }
7863
7863
  }
7864
7864
 
7865
+ // ../../packages/terminal/tui/dist/tui/terminal-session.js
7866
+ function inputTimeout(kind, env = process.env) {
7867
+ if (kind === "none" || kind === "paste")
7868
+ return null;
7869
+ if (kind === "negotiation")
7870
+ return 150;
7871
+ if (kind === "sequence")
7872
+ return 50;
7873
+ const configured = Number(env.LAOHUANG_ESC_TIMEOUT);
7874
+ if (Number.isFinite(configured) && configured > 0)
7875
+ return configured;
7876
+ return env.SSH_CONNECTION || env.SSH_TTY || env.SSH_CLIENT ? 100 : 10;
7877
+ }
7878
+ async function drainTerminalInput(input, maxMs = 1e3, idleMs = 50) {
7879
+ if (!input?.off)
7880
+ return;
7881
+ await new Promise((resolve8) => {
7882
+ let idle;
7883
+ const finish = () => {
7884
+ clearTimeout(idle);
7885
+ clearTimeout(deadline);
7886
+ input.off?.("data", onData);
7887
+ resolve8();
7888
+ };
7889
+ const onData = () => {
7890
+ clearTimeout(idle);
7891
+ idle = setTimeout(finish, idleMs);
7892
+ };
7893
+ const deadline = setTimeout(finish, maxMs);
7894
+ input.on("data", onData);
7895
+ idle = setTimeout(finish, idleMs);
7896
+ });
7897
+ }
7898
+ function refreshTerminalDimensions() {
7899
+ if (process.platform === "win32")
7900
+ return;
7901
+ try {
7902
+ process.kill(process.pid, "SIGWINCH");
7903
+ } catch {
7904
+ }
7905
+ }
7906
+ function supportsTerminalHyperlinks(env = process.env) {
7907
+ if (env.LAOHUANG_HYPERLINKS !== void 0)
7908
+ return env.LAOHUANG_HYPERLINKS === "1";
7909
+ if (!process.stdout.isTTY || env.TERM === "dumb")
7910
+ return false;
7911
+ return Boolean(env.WT_SESSION || env.KITTY_WINDOW_ID || env.VTE_VERSION || ["iTerm.app", "WezTerm", "vscode", "ghostty"].includes(env.TERM_PROGRAM ?? ""));
7912
+ }
7913
+
7914
+ // ../../node_modules/get-east-asian-width/lookup-data.js
7915
+ var ambiguousMinimalCodePoint = 161;
7916
+ var ambiguousMaximumCodePoint = 1114109;
7917
+ var ambiguousRanges = [161, 161, 164, 164, 167, 168, 170, 170, 173, 174, 176, 180, 182, 186, 188, 191, 198, 198, 208, 208, 215, 216, 222, 225, 230, 230, 232, 234, 236, 237, 240, 240, 242, 243, 247, 250, 252, 252, 254, 254, 257, 257, 273, 273, 275, 275, 283, 283, 294, 295, 299, 299, 305, 307, 312, 312, 319, 322, 324, 324, 328, 331, 333, 333, 338, 339, 358, 359, 363, 363, 462, 462, 464, 464, 466, 466, 468, 468, 470, 470, 472, 472, 474, 474, 476, 476, 593, 593, 609, 609, 708, 708, 711, 711, 713, 715, 717, 717, 720, 720, 728, 731, 733, 733, 735, 735, 768, 879, 913, 929, 931, 937, 945, 961, 963, 969, 1025, 1025, 1040, 1103, 1105, 1105, 8208, 8208, 8211, 8214, 8216, 8217, 8220, 8221, 8224, 8226, 8228, 8231, 8240, 8240, 8242, 8243, 8245, 8245, 8251, 8251, 8254, 8254, 8308, 8308, 8319, 8319, 8321, 8324, 8364, 8364, 8451, 8451, 8453, 8453, 8457, 8457, 8467, 8467, 8470, 8470, 8481, 8482, 8486, 8486, 8491, 8491, 8531, 8532, 8539, 8542, 8544, 8555, 8560, 8569, 8585, 8585, 8592, 8601, 8632, 8633, 8658, 8658, 8660, 8660, 8679, 8679, 8704, 8704, 8706, 8707, 8711, 8712, 8715, 8715, 8719, 8719, 8721, 8721, 8725, 8725, 8730, 8730, 8733, 8736, 8739, 8739, 8741, 8741, 8743, 8748, 8750, 8750, 8756, 8759, 8764, 8765, 8776, 8776, 8780, 8780, 8786, 8786, 8800, 8801, 8804, 8807, 8810, 8811, 8814, 8815, 8834, 8835, 8838, 8839, 8853, 8853, 8857, 8857, 8869, 8869, 8895, 8895, 8978, 8978, 9312, 9449, 9451, 9547, 9552, 9587, 9600, 9615, 9618, 9621, 9632, 9633, 9635, 9641, 9650, 9651, 9654, 9655, 9660, 9661, 9664, 9665, 9670, 9672, 9675, 9675, 9678, 9681, 9698, 9701, 9711, 9711, 9733, 9734, 9737, 9737, 9742, 9743, 9756, 9756, 9758, 9758, 9792, 9792, 9794, 9794, 9824, 9825, 9827, 9829, 9831, 9834, 9836, 9837, 9839, 9839, 9886, 9887, 9919, 9919, 9926, 9933, 9935, 9939, 9941, 9953, 9955, 9955, 9960, 9961, 9963, 9969, 9972, 9972, 9974, 9977, 9979, 9980, 9982, 9983, 10045, 10045, 10102, 10111, 11094, 11097, 12872, 12879, 57344, 63743, 65024, 65039, 65533, 65533, 127232, 127242, 127248, 127277, 127280, 127337, 127344, 127373, 127375, 127376, 127387, 127404, 917760, 917999, 983040, 1048573, 1048576, 1114109];
7918
+ var fullwidthMinimalCodePoint = 12288;
7919
+ var fullwidthMaximumCodePoint = 65510;
7920
+ var fullwidthRanges = [12288, 12288, 65281, 65376, 65504, 65510];
7921
+ var wideMinimalCodePoint = 4352;
7922
+ var wideMaximumCodePoint = 262141;
7923
+ var wideRanges = [4352, 4447, 8986, 8987, 9001, 9002, 9193, 9196, 9200, 9200, 9203, 9203, 9725, 9726, 9748, 9749, 9776, 9783, 9800, 9811, 9855, 9855, 9866, 9871, 9875, 9875, 9889, 9889, 9898, 9899, 9917, 9918, 9924, 9925, 9934, 9934, 9940, 9940, 9962, 9962, 9970, 9971, 9973, 9973, 9978, 9978, 9981, 9981, 9989, 9989, 9994, 9995, 10024, 10024, 10060, 10060, 10062, 10062, 10067, 10069, 10071, 10071, 10133, 10135, 10160, 10160, 10175, 10175, 11035, 11036, 11088, 11088, 11093, 11093, 11904, 11929, 11931, 12019, 12032, 12245, 12272, 12287, 12289, 12350, 12353, 12438, 12441, 12543, 12549, 12591, 12593, 12686, 12688, 12773, 12783, 12830, 12832, 12871, 12880, 42124, 42128, 42182, 43360, 43388, 44032, 55203, 63744, 64255, 65040, 65049, 65072, 65106, 65108, 65126, 65128, 65131, 94176, 94180, 94192, 94198, 94208, 101589, 101631, 101662, 101760, 101874, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 119552, 119638, 119648, 119670, 126980, 126980, 127183, 127183, 127374, 127374, 127377, 127386, 127488, 127490, 127504, 127547, 127552, 127560, 127568, 127569, 127584, 127589, 127744, 127776, 127789, 127797, 127799, 127868, 127870, 127891, 127904, 127946, 127951, 127955, 127968, 127984, 127988, 127988, 127992, 128062, 128064, 128064, 128066, 128252, 128255, 128317, 128331, 128334, 128336, 128359, 128378, 128378, 128405, 128406, 128420, 128420, 128507, 128591, 128640, 128709, 128716, 128716, 128720, 128722, 128725, 128728, 128732, 128735, 128747, 128748, 128756, 128764, 128992, 129003, 129008, 129008, 129292, 129338, 129340, 129349, 129351, 129535, 129648, 129660, 129664, 129674, 129678, 129734, 129736, 129736, 129741, 129756, 129759, 129770, 129775, 129784, 131072, 196605, 196608, 262141];
7924
+
7925
+ // ../../node_modules/get-east-asian-width/utilities.js
7926
+ var isInRange = (ranges, codePoint) => {
7927
+ let low = 0;
7928
+ let high = Math.floor(ranges.length / 2) - 1;
7929
+ while (low <= high) {
7930
+ const mid = Math.floor((low + high) / 2);
7931
+ const i = mid * 2;
7932
+ if (codePoint < ranges[i]) {
7933
+ high = mid - 1;
7934
+ } else if (codePoint > ranges[i + 1]) {
7935
+ low = mid + 1;
7936
+ } else {
7937
+ return true;
7938
+ }
7939
+ }
7940
+ return false;
7941
+ };
7942
+
7943
+ // ../../node_modules/get-east-asian-width/lookup.js
7944
+ var commonCjkCodePoint = 19968;
7945
+ var [wideFastPathStart, wideFastPathEnd] = /* @__PURE__ */ findWideFastPathRange(wideRanges);
7946
+ function findWideFastPathRange(ranges) {
7947
+ let fastPathStart = ranges[0];
7948
+ let fastPathEnd = ranges[1];
7949
+ for (let index = 0; index < ranges.length; index += 2) {
7950
+ const start = ranges[index];
7951
+ const end = ranges[index + 1];
7952
+ if (commonCjkCodePoint >= start && commonCjkCodePoint <= end) {
7953
+ return [start, end];
7954
+ }
7955
+ if (end - start > fastPathEnd - fastPathStart) {
7956
+ fastPathStart = start;
7957
+ fastPathEnd = end;
7958
+ }
7959
+ }
7960
+ return [fastPathStart, fastPathEnd];
7961
+ }
7962
+ var isAmbiguous = (codePoint) => {
7963
+ if (codePoint < ambiguousMinimalCodePoint || codePoint > ambiguousMaximumCodePoint) {
7964
+ return false;
7965
+ }
7966
+ return isInRange(ambiguousRanges, codePoint);
7967
+ };
7968
+ var isFullWidth = (codePoint) => {
7969
+ if (codePoint < fullwidthMinimalCodePoint || codePoint > fullwidthMaximumCodePoint) {
7970
+ return false;
7971
+ }
7972
+ return isInRange(fullwidthRanges, codePoint);
7973
+ };
7974
+ var isWide = (codePoint) => {
7975
+ if (codePoint >= wideFastPathStart && codePoint <= wideFastPathEnd) {
7976
+ return true;
7977
+ }
7978
+ if (codePoint < wideMinimalCodePoint || codePoint > wideMaximumCodePoint) {
7979
+ return false;
7980
+ }
7981
+ return isInRange(wideRanges, codePoint);
7982
+ };
7983
+
7984
+ // ../../node_modules/get-east-asian-width/index.js
7985
+ function validate(codePoint) {
7986
+ if (!Number.isSafeInteger(codePoint)) {
7987
+ throw new TypeError(`Expected a code point, got \`${typeof codePoint}\`.`);
7988
+ }
7989
+ }
7990
+ function eastAsianWidth(codePoint, { ambiguousAsWide = false } = {}) {
7991
+ validate(codePoint);
7992
+ if (isFullWidth(codePoint) || isWide(codePoint) || ambiguousAsWide && isAmbiguous(codePoint)) {
7993
+ return 2;
7994
+ }
7995
+ return 1;
7996
+ }
7997
+
7998
+ // ../../packages/terminal/tui/dist/tui/terminal-text.js
7999
+ var segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
8000
+ var nonPrinting = new RegExp("^(?:\\p{Default_Ignorable_Code_Point}|\\p{Control}|\\p{Format}|\\p{Mark})$", "u");
8001
+ var spacingMark = new RegExp(String.raw`^(?:[\p{Spacing_Mark}--[\u1734\u302E\u302F]]|[\u065F\u0F7F\u102B\u102C\u1031\u1033-\u1035\u1038\u103A-\u103E])$`, "v");
8002
+ var emoji = new RegExp(String.raw`^\p{RGI_Emoji}$`, "v");
8003
+ var TERMINAL_RESET = "\x1B[0m\x1B]8;;\x07";
8004
+ var widthCache = /* @__PURE__ */ new Map();
8005
+ function graphemeClusters(text2) {
8006
+ return Array.from(segmenter.segment(text2), ({ segment }) => segment);
8007
+ }
8008
+ function clusterWidth(cluster) {
8009
+ if (cluster === " ")
8010
+ return 3;
8011
+ if (emoji.test(cluster))
8012
+ return 2;
8013
+ const chars = [...cluster];
8014
+ let width = 0;
8015
+ let foundBase = false;
8016
+ let followsMark = false;
8017
+ for (const char of chars) {
8018
+ const cp = char.codePointAt(0);
8019
+ if (spacingMark.test(char)) {
8020
+ width += 1;
8021
+ followsMark = false;
8022
+ } else if (nonPrinting.test(char)) {
8023
+ if (new RegExp("\\p{Mark}", "u").test(char))
8024
+ followsMark = true;
8025
+ } else if (!foundBase) {
8026
+ if (cp >= 127462 && cp <= 127487)
8027
+ return 2;
8028
+ width += eastAsianWidth(cp);
8029
+ foundBase = true;
8030
+ followsMark = false;
8031
+ } else {
8032
+ if (followsMark || cp >= 65280 && cp <= 65519)
8033
+ width += eastAsianWidth(cp);
8034
+ else if (cp === 3635 || cp === 3763)
8035
+ width += 1;
8036
+ followsMark = false;
8037
+ }
8038
+ }
8039
+ return width;
8040
+ }
8041
+ function normalizeDisplayCluster(cluster) {
8042
+ return cluster === " " ? " " : cluster.replace(/[\uD800-\uDFFF]/gu, "\uFFFD").replace(/\u0e33/g, "\u0E4D\u0E32").replace(/\u0eb3/g, "\u0ECD\u0EB2");
8043
+ }
8044
+ function escapeEnd(text2, start) {
8045
+ const marker = text2[start + 1];
8046
+ if (marker === "[") {
8047
+ let end = start + 2;
8048
+ while (end < text2.length) {
8049
+ const cp = text2.charCodeAt(end++);
8050
+ if (cp >= 64 && cp <= 126)
8051
+ break;
8052
+ }
8053
+ return end;
8054
+ }
8055
+ if (marker === "]" || marker === "P" || marker === "_" || marker === "^" || marker === "X") {
8056
+ let end = start + 2;
8057
+ while (end < text2.length) {
8058
+ if (text2[end] === "\x07")
8059
+ return end + 1;
8060
+ if (text2[end] === "\x1B" && text2[end + 1] === "\\")
8061
+ return end + 2;
8062
+ end++;
8063
+ }
8064
+ return end;
8065
+ }
8066
+ return Math.min(text2.length, start + 2);
8067
+ }
8068
+ function stripTerminalControls(text2) {
8069
+ let result = "";
8070
+ for (let index = 0; index < text2.length; ) {
8071
+ if (text2[index] === "\x1B")
8072
+ index = escapeEnd(text2, index);
8073
+ else {
8074
+ const char = text2[index++];
8075
+ if (!/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/u.test(char))
8076
+ result += char;
8077
+ }
8078
+ }
8079
+ return result;
8080
+ }
8081
+ function visibleWidth(text2) {
8082
+ if (/^[\x20-\x7e]*$/u.test(text2))
8083
+ return text2.length;
8084
+ const cached = widthCache.get(text2);
8085
+ if (cached !== void 0)
8086
+ return cached;
8087
+ let width = 0;
8088
+ for (const { segment } of segmenter.segment(stripTerminalControls(text2)))
8089
+ width += clusterWidth(segment);
8090
+ if (text2.length <= 4096) {
8091
+ if (widthCache.size >= 512)
8092
+ widthCache.delete(widthCache.keys().next().value);
8093
+ widthCache.set(text2, width);
8094
+ }
8095
+ return width;
8096
+ }
8097
+ var TerminalStyle = class {
8098
+ codes = /* @__PURE__ */ new Map();
8099
+ link = "";
8100
+ accept(sequence) {
8101
+ if (sequence.startsWith("\x1B]8;")) {
8102
+ const match = /^\x1b\]8;[^;]*;([^\x00-\x1f\x7f]*)(?:\x07|\x1b\\)$/u.exec(sequence);
8103
+ if (match)
8104
+ this.link = match[1] ? `\x1B]8;;${match[1]}\x07` : "";
8105
+ return;
8106
+ }
8107
+ if (!/^\x1b\[[\d;]*m$/u.test(sequence))
8108
+ return;
8109
+ const parts = sequence.slice(2, -1).split(";").map(Number);
8110
+ for (let index = 0; index < parts.length; index++) {
8111
+ const code = parts[index];
8112
+ if (code === 0)
8113
+ this.codes.clear();
8114
+ else if (code === 38 || code === 48) {
8115
+ const count = parts[index + 1] === 5 ? 3 : parts[index + 1] === 2 ? 5 : 1;
8116
+ if (index + count <= parts.length)
8117
+ this.codes.set(code, parts.slice(index, index + count).join(";"));
8118
+ index += count - 1;
8119
+ } else if (code >= 30 && code <= 37 || code >= 90 && code <= 97)
8120
+ this.codes.set(38, String(code));
8121
+ else if (code >= 40 && code <= 47 || code >= 100 && code <= 107)
8122
+ this.codes.set(48, String(code));
8123
+ else if (code === 39)
8124
+ this.codes.delete(38);
8125
+ else if (code === 49)
8126
+ this.codes.delete(48);
8127
+ else if (code === 22) {
8128
+ this.codes.delete(1);
8129
+ this.codes.delete(2);
8130
+ } else if (code >= 23 && code <= 29)
8131
+ this.codes.delete(code - 20);
8132
+ else if (code >= 1 && code <= 9)
8133
+ this.codes.set(code, String(code));
8134
+ }
8135
+ }
8136
+ prefix() {
8137
+ return (this.codes.size ? `\x1B[${[...this.codes.values()].join(";")}m` : "") + this.link;
8138
+ }
8139
+ };
8140
+ function terminalCells(text2) {
8141
+ const style = new TerminalStyle();
8142
+ const runs = [];
8143
+ let clean = "";
8144
+ for (let index = 0; index < text2.length; ) {
8145
+ if (text2[index] === "\x1B") {
8146
+ const end = escapeEnd(text2, index);
8147
+ style.accept(text2.slice(index, end));
8148
+ index = end;
8149
+ } else {
8150
+ const end = text2.indexOf("\x1B", index);
8151
+ runs.push({ start: clean.length, prefix: style.prefix() });
8152
+ clean += stripTerminalControls(text2.slice(index, end < 0 ? text2.length : end));
8153
+ index = end < 0 ? text2.length : end;
8154
+ }
8155
+ }
8156
+ const cells = [];
8157
+ let run = 0;
8158
+ for (const { segment, index } of segmenter.segment(clean)) {
8159
+ while (run + 1 < runs.length && runs[run + 1].start <= index)
8160
+ run++;
8161
+ cells.push({ text: normalizeDisplayCluster(segment), width: clusterWidth(segment), prefix: runs[run]?.prefix ?? "" });
8162
+ }
8163
+ return cells;
8164
+ }
8165
+ function encodeCells(cells) {
8166
+ let prefix = "";
8167
+ let result = "";
8168
+ for (const cell of cells) {
8169
+ if (cell.prefix !== prefix) {
8170
+ if (prefix)
8171
+ result += prefix.includes("\x1B]8;") ? TERMINAL_RESET : "\x1B[0m";
8172
+ result += cell.prefix;
8173
+ prefix = cell.prefix;
8174
+ }
8175
+ result += cell.text;
8176
+ }
8177
+ return result + (prefix ? prefix.includes("\x1B]8;") ? TERMINAL_RESET : "\x1B[0m" : "");
8178
+ }
8179
+ function normalizeTerminalOutput(text2) {
8180
+ if (!/[\x00-\x09\x0b\x0c\x0e-\x1f\x7f-\x9f\u0e33\u0eb3\uD800-\uDFFF]/u.test(text2))
8181
+ return text2;
8182
+ return encodeCells(terminalCells(text2));
8183
+ }
8184
+ function truncateToWidth(text2, width) {
8185
+ if (width <= 0)
8186
+ return "";
8187
+ if (/^[\x20-\x7e]*$/u.test(text2))
8188
+ return text2.slice(0, width);
8189
+ const cells = [];
8190
+ let used = 0;
8191
+ for (const cell of terminalCells(text2)) {
8192
+ if (used + cell.width > width)
8193
+ break;
8194
+ cells.push(cell);
8195
+ used += cell.width;
8196
+ }
8197
+ return encodeCells(cells);
8198
+ }
8199
+ function sliceByColumn(text2, start, width) {
8200
+ const cells = [];
8201
+ let column = 0;
8202
+ let used = 0;
8203
+ for (const cell of terminalCells(text2)) {
8204
+ const end = column + cell.width;
8205
+ if (end > start && column < start + width) {
8206
+ const overlap = Math.min(end, start + width) - Math.max(column, start);
8207
+ cells.push(overlap === cell.width ? cell : { ...cell, text: " ".repeat(overlap), width: overlap });
8208
+ used += overlap;
8209
+ }
8210
+ column = end;
8211
+ if (column >= start + width)
8212
+ break;
8213
+ }
8214
+ return encodeCells(cells) + " ".repeat(Math.max(0, width - used));
8215
+ }
8216
+ function previousGraphemeBoundary(text2, offset) {
8217
+ let previous = 0;
8218
+ for (const { index } of segmenter.segment(text2)) {
8219
+ if (index >= offset)
8220
+ break;
8221
+ previous = index;
8222
+ }
8223
+ return previous;
8224
+ }
8225
+ function nextGraphemeBoundary(text2, offset) {
8226
+ for (const { index } of segmenter.segment(text2))
8227
+ if (index > offset)
8228
+ return index;
8229
+ return text2.length;
8230
+ }
8231
+ function projectInput(text2, cursor, width, prompt, mask) {
8232
+ const target = Math.max(1, width);
8233
+ const displayedPrompt = truncateToWidth(stripTerminalControls(prompt), Math.max(0, target - 1));
8234
+ const promptWidth = visibleWidth(displayedPrompt);
8235
+ const contentWidth = Math.max(1, target - promptWidth);
8236
+ const rows = [];
8237
+ let row = "";
8238
+ let column = 0;
8239
+ let cursorRow = 0;
8240
+ let cursorColumn = 0;
8241
+ const placeCursor = () => {
8242
+ cursorRow = rows.length + (column === contentWidth ? 1 : 0);
8243
+ cursorColumn = promptWidth + (column === contentWidth ? 0 : column);
8244
+ };
8245
+ for (const { segment, index } of segmenter.segment(text2)) {
8246
+ if (cursor >= index)
8247
+ placeCursor();
8248
+ if (/^[\r\n]+$/u.test(segment)) {
8249
+ rows.push(row);
8250
+ row = "";
8251
+ column = 0;
8252
+ if (cursor >= index + segment.length)
8253
+ placeCursor();
8254
+ continue;
8255
+ }
8256
+ let display = mask ? "*" : normalizeDisplayCluster(segment.replace(/\x1b/g, "\u241B").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/gu, ""));
8257
+ let cells = mask ? 1 : visibleWidth(display);
8258
+ if (cells > contentWidth) {
8259
+ display = "\uFFFD";
8260
+ cells = 1;
8261
+ }
8262
+ if (column > 0 && column + cells > contentWidth) {
8263
+ rows.push(row);
8264
+ row = "";
8265
+ column = 0;
8266
+ if (cursor >= index)
8267
+ placeCursor();
8268
+ }
8269
+ row += display;
8270
+ column += cells;
8271
+ if (cursor >= index + segment.length)
8272
+ placeCursor();
8273
+ }
8274
+ rows.push(row);
8275
+ if (column === contentWidth)
8276
+ rows.push("");
8277
+ return { rows, prompt: displayedPrompt, promptWidth, cursorRow: Math.min(cursorRow, rows.length - 1), cursorColumn: Math.min(target - 1, cursorColumn) };
8278
+ }
8279
+
8280
+ // ../../packages/terminal/tui/dist/tui/render-model.js
8281
+ function span(text2, style) {
8282
+ return style === void 0 ? { text: text2 } : { text: text2, style };
8283
+ }
8284
+ function line(...spans) {
8285
+ return { spans };
8286
+ }
8287
+ function plainLine(text2) {
8288
+ return line(span(text2));
8289
+ }
8290
+ function lineText(value) {
8291
+ return value.spans.map((item) => item.text).join("");
8292
+ }
8293
+ function styledClusters(spans) {
8294
+ const sources = spans.map((source) => ({ ...source, text: stripTerminalControls(source.text) }));
8295
+ const text2 = sources.map((source) => source.text).join("");
8296
+ let sourceIndex = 0;
8297
+ let sourceEnd = sources[0]?.text.length ?? 0;
8298
+ let offset = 0;
8299
+ const result = [];
8300
+ for (const cluster of graphemeClusters(text2)) {
8301
+ while (sourceIndex + 1 < sources.length && sourceEnd <= offset) {
8302
+ sourceEnd += sources[++sourceIndex].text.length;
8303
+ }
8304
+ result.push(span(normalizeDisplayCluster(cluster), sources[sourceIndex]?.style));
8305
+ offset += cluster.length;
8306
+ }
8307
+ return result;
8308
+ }
8309
+ function wrapStyledSpans(spans, width) {
8310
+ const target = Math.max(1, width);
8311
+ const lines2 = [];
8312
+ let row = [];
8313
+ let used = 0;
8314
+ for (const source of styledClusters(spans)) {
8315
+ if (/^[\r\n]+$/u.test(source.text)) {
8316
+ lines2.push(line(...mergeAdjacent(row)));
8317
+ row = [];
8318
+ used = 0;
8319
+ continue;
8320
+ }
8321
+ const measured = visibleWidth(source.text);
8322
+ const cell = measured > target ? span("\uFFFD", source.style) : source;
8323
+ const cellWidth = measured > target ? 1 : measured;
8324
+ if (used > 0 && used + cellWidth > target) {
8325
+ lines2.push(line(...mergeAdjacent(row)));
8326
+ row = [];
8327
+ used = 0;
8328
+ }
8329
+ row.push(cell);
8330
+ used += cellWidth;
8331
+ }
8332
+ lines2.push(line(...mergeAdjacent(row)));
8333
+ return lines2;
8334
+ }
8335
+ function truncateStyledLine(value, width, ellipsis = "\u2026") {
8336
+ if (width <= 0)
8337
+ return plainLine("");
8338
+ const result = [];
8339
+ let used = 0;
8340
+ let truncated = false;
8341
+ for (const source of styledClusters(value.spans)) {
8342
+ const measured = textWidth(source.text);
8343
+ if (used + measured > width) {
8344
+ truncated = true;
8345
+ break;
8346
+ }
8347
+ result.push(source);
8348
+ used += measured;
8349
+ }
8350
+ if (truncated && ellipsis) {
8351
+ const suffix = truncateStyledLine(plainLine(ellipsis), width, "");
8352
+ const suffixWidth = styledLineWidth(suffix);
8353
+ while (result.length && used + suffixWidth > width)
8354
+ used -= textWidth(result.pop().text);
8355
+ result.push(...suffix.spans.map((part) => span(part.text, result.at(-1)?.style)));
8356
+ }
8357
+ return line(...mergeAdjacent(result));
8358
+ }
8359
+ function textWidth(text2) {
8360
+ let width = 0;
8361
+ for (const cluster of graphemeClusters(text2))
8362
+ width += clusterWidth(cluster);
8363
+ return width;
8364
+ }
8365
+ function padStyledLine(value, width, background) {
8366
+ const padding = width - styledLineWidth(value);
8367
+ if (padding <= 0) {
8368
+ return value;
8369
+ }
8370
+ return line(...value.spans, span(" ".repeat(padding), background === void 0 ? void 0 : { background }));
8371
+ }
8372
+ function styledLineWidth(value) {
8373
+ return textWidth(lineText(value));
8374
+ }
8375
+ function mergeAdjacent(spans) {
8376
+ const merged = [];
8377
+ for (const item of spans) {
8378
+ const previous = merged.at(-1);
8379
+ if (previous !== void 0 && previous.style === item.style) {
8380
+ merged[merged.length - 1] = span(previous.text + item.text, item.style);
8381
+ continue;
8382
+ }
8383
+ merged.push(item);
8384
+ }
8385
+ return merged;
8386
+ }
8387
+
8388
+ // ../../packages/terminal/tui/dist/keybindings/key-id.js
8389
+ function makeKeyInput(id, options = {}) {
8390
+ return {
8391
+ id,
8392
+ text: options.text ?? null,
8393
+ ctrl: options.ctrl ?? false,
8394
+ alt: options.alt ?? false,
8395
+ shift: options.shift ?? false
8396
+ };
8397
+ }
8398
+
8399
+ // ../../packages/terminal/tui/dist/tui/terminal-writer.js
8400
+ var BoundedTerminalWriter = class _BoundedTerminalWriter {
8401
+ static MAX_CHARS = 1024 * 1024;
8402
+ #buffer = "";
8403
+ #write;
8404
+ constructor(write) {
8405
+ this.#write = write;
8406
+ }
8407
+ append(text2) {
8408
+ let offset = 0;
8409
+ while (offset < text2.length) {
8410
+ const capacity = _BoundedTerminalWriter.MAX_CHARS - this.#buffer.length;
8411
+ let end = Math.min(text2.length, offset + capacity);
8412
+ if (end < text2.length && /[\uD800-\uDBFF]/u.test(text2[end - 1] ?? "") && /[\uDC00-\uDFFF]/u.test(text2[end] ?? ""))
8413
+ end--;
8414
+ if (end === offset) {
8415
+ this.flush();
8416
+ continue;
8417
+ }
8418
+ this.#buffer += text2.slice(offset, end);
8419
+ offset = end;
8420
+ if (this.#buffer.length === _BoundedTerminalWriter.MAX_CHARS)
8421
+ this.flush();
8422
+ }
8423
+ }
8424
+ flush() {
8425
+ if (!this.#buffer)
8426
+ return;
8427
+ this.#write(this.#buffer);
8428
+ this.#buffer = "";
8429
+ }
8430
+ };
8431
+
8432
+ // ../../packages/terminal/tui/dist/tui/render-diagnostics.js
8433
+ import { mkdtempSync, writeFileSync as writeFileSync4 } from "node:fs";
8434
+ import { tmpdir } from "node:os";
8435
+ import { join as join8 } from "node:path";
8436
+ var RenderWidthError = class extends Error {
8437
+ expected;
8438
+ actual;
8439
+ renderedLine;
8440
+ source;
8441
+ constructor(expected, actual, renderedLine, source = "unknown component") {
8442
+ super(`rendered line exceeds terminal width: ${actual} > ${expected} (${source})`);
8443
+ this.expected = expected;
8444
+ this.actual = actual;
8445
+ this.renderedLine = renderedLine;
8446
+ this.source = source;
8447
+ }
8448
+ };
8449
+ function recordRenderFailure(error) {
8450
+ if (!(error instanceof RenderWidthError))
8451
+ return error;
8452
+ try {
8453
+ const directory = mkdtempSync(join8(tmpdir(), "laohuang-tui-error-"));
8454
+ const path5 = join8(directory, "render.json");
8455
+ writeFileSync4(path5, JSON.stringify({
8456
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
8457
+ source: error.source,
8458
+ expected: error.expected,
8459
+ actual: error.actual,
8460
+ renderedLine: error.renderedLine.slice(0, 4096)
8461
+ }, null, 2), { mode: 384 });
8462
+ error.message += `; diagnostic: ${path5}`;
8463
+ } catch {
8464
+ }
8465
+ return error;
8466
+ }
8467
+
7865
8468
  // ../../packages/terminal/tui/dist/tui/screen.js
7866
8469
  var MainScreenRenderer = class _MainScreenRenderer {
7867
8470
  #terminal;
7868
8471
  #previousLines = [];
8472
+ #previousSources = [];
7869
8473
  #previousWidth = 0;
7870
8474
  #previousHeight = 0;
7871
8475
  #previousViewportTop = 0;
@@ -7873,10 +8477,12 @@ var MainScreenRenderer = class _MainScreenRenderer {
7873
8477
  #hardwareCursorRow = 0;
7874
8478
  #maxLinesRendered = 0;
7875
8479
  #clearOnShrink;
8480
+ #termux;
7876
8481
  #closed = false;
7877
8482
  constructor(terminal, env = process.env) {
7878
8483
  this.#terminal = terminal;
7879
8484
  this.#clearOnShrink = env.LAOHUANG_CLEAR_ON_SHRINK === "1";
8485
+ this.#termux = Boolean(env.TERMUX_VERSION);
7880
8486
  }
7881
8487
  render(frame) {
7882
8488
  if (this.#closed) {
@@ -7885,7 +8491,8 @@ var MainScreenRenderer = class _MainScreenRenderer {
7885
8491
  const size = this.#terminal.getSize();
7886
8492
  const width = Math.max(1, size.columns);
7887
8493
  const height = Math.max(1, size.rows);
7888
- const newLines = frame.lines;
8494
+ const sources = frame.lines;
8495
+ const newLines = sources.map((text2, index) => this.#previousSources[index] === text2 ? this.#previousLines[index] : normalizeTerminalOutput(text2));
7889
8496
  const widthChanged = this.#previousWidth !== 0 && this.#previousWidth !== width;
7890
8497
  const heightChanged = this.#previousHeight !== 0 && this.#previousHeight !== height;
7891
8498
  _MainScreenRenderer.#validateLines(newLines, width, widthChanged || heightChanged ? [] : this.#previousLines);
@@ -7899,17 +8506,18 @@ var MainScreenRenderer = class _MainScreenRenderer {
7899
8506
  return targetScreenRow - currentScreenRow;
7900
8507
  };
7901
8508
  const fullRender = (clear) => {
7902
- let buffer2 = "\x1B[?2026h";
8509
+ const buffer2 = new BoundedTerminalWriter((text2) => this.#terminal.write(text2));
8510
+ buffer2.append("\x1B[?2026h");
7903
8511
  if (clear) {
7904
- buffer2 += "\x1B[2J\x1B[H\x1B[3J";
8512
+ buffer2.append("\x1B[2J\x1B[H\x1B[3J");
7905
8513
  }
7906
8514
  newLines.forEach((line2, index) => {
7907
8515
  if (index > 0) {
7908
- buffer2 += "\r\n";
8516
+ buffer2.append("\r\n");
7909
8517
  }
7910
- buffer2 += line2;
8518
+ buffer2.append(line2);
7911
8519
  });
7912
- buffer2 += "\x1B[?2026l";
8520
+ buffer2.append("\x1B[?2026l");
7913
8521
  this.#cursorRow = Math.max(0, newLines.length - 1);
7914
8522
  this.#hardwareCursorRow = this.#cursorRow;
7915
8523
  if (clear) {
@@ -7919,16 +8527,17 @@ var MainScreenRenderer = class _MainScreenRenderer {
7919
8527
  }
7920
8528
  const bufferLength = Math.max(height, newLines.length);
7921
8529
  this.#previousViewportTop = Math.max(0, bufferLength - height);
7922
- buffer2 += this.#positionHardwareCursor(frame, width, newLines.length);
8530
+ buffer2.append(this.#positionHardwareCursor(frame, width, newLines.length));
7923
8531
  this.#commitState(newLines, width, height);
7924
- this.#terminal.write(buffer2);
8532
+ this.#previousSources = [...sources];
8533
+ buffer2.flush();
7925
8534
  this.#terminal.flush();
7926
8535
  };
7927
8536
  if (this.#previousLines.length === 0 && !widthChanged && !heightChanged) {
7928
8537
  fullRender(false);
7929
8538
  return;
7930
8539
  }
7931
- if (widthChanged || heightChanged) {
8540
+ if (widthChanged || heightChanged && !this.#termux) {
7932
8541
  fullRender(true);
7933
8542
  return;
7934
8543
  }
@@ -7938,11 +8547,12 @@ var MainScreenRenderer = class _MainScreenRenderer {
7938
8547
  }
7939
8548
  const span2 = _MainScreenRenderer.#changedSpan(this.#previousLines, newLines);
7940
8549
  if (span2 === null) {
7941
- const buffer2 = this.#positionHardwareCursor(frame, width, newLines.length);
7942
8550
  this.#previousViewportTop = prevViewportTop;
8551
+ const cursorBuffer = this.#positionHardwareCursor(frame, width, newLines.length);
8552
+ this.#previousSources = [...sources];
7943
8553
  this.#previousHeight = height;
7944
- if (buffer2) {
7945
- this.#terminal.write(buffer2);
8554
+ if (cursorBuffer) {
8555
+ this.#terminal.write(cursorBuffer);
7946
8556
  }
7947
8557
  this.#terminal.flush();
7948
8558
  return;
@@ -7955,7 +8565,8 @@ var MainScreenRenderer = class _MainScreenRenderer {
7955
8565
  }
7956
8566
  const appendStart = appendedLines && firstChanged === this.#previousLines.length && firstChanged > 0;
7957
8567
  if (firstChanged >= newLines.length) {
7958
- let buffer2 = "\x1B[?2026h";
8568
+ const buffer2 = new BoundedTerminalWriter((text2) => this.#terminal.write(text2));
8569
+ buffer2.append("\x1B[?2026h");
7959
8570
  const targetRow = Math.max(0, newLines.length - 1);
7960
8571
  if (targetRow < prevViewportTop) {
7961
8572
  fullRender(true);
@@ -7963,11 +8574,11 @@ var MainScreenRenderer = class _MainScreenRenderer {
7963
8574
  }
7964
8575
  const lineDiff2 = computeLineDiff(targetRow);
7965
8576
  if (lineDiff2 > 0) {
7966
- buffer2 += `\x1B[${lineDiff2}B`;
8577
+ buffer2.append(`\x1B[${lineDiff2}B`);
7967
8578
  } else if (lineDiff2 < 0) {
7968
- buffer2 += `\x1B[${-lineDiff2}A`;
8579
+ buffer2.append(`\x1B[${-lineDiff2}A`);
7969
8580
  }
7970
- buffer2 += "\r";
8581
+ buffer2.append("\r");
7971
8582
  const extraLines = this.#previousLines.length - newLines.length;
7972
8583
  if (extraLines > height) {
7973
8584
  fullRender(true);
@@ -7975,25 +8586,26 @@ var MainScreenRenderer = class _MainScreenRenderer {
7975
8586
  }
7976
8587
  const clearStartOffset = newLines.length === 0 ? 0 : 1;
7977
8588
  if (extraLines > 0 && clearStartOffset > 0) {
7978
- buffer2 += `\x1B[${clearStartOffset}B`;
8589
+ buffer2.append(`\x1B[${clearStartOffset}B`);
7979
8590
  }
7980
8591
  for (let index = 0; index < extraLines; index += 1) {
7981
- buffer2 += "\r\x1B[2K";
8592
+ buffer2.append("\r\x1B[2K");
7982
8593
  if (index < extraLines - 1) {
7983
- buffer2 += "\x1B[1B";
8594
+ buffer2.append("\x1B[1B");
7984
8595
  }
7985
8596
  }
7986
8597
  const moveBack = Math.max(0, extraLines - 1 + clearStartOffset);
7987
8598
  if (moveBack > 0) {
7988
- buffer2 += `\x1B[${moveBack}A`;
8599
+ buffer2.append(`\x1B[${moveBack}A`);
7989
8600
  }
7990
- buffer2 += "\x1B[?2026l";
8601
+ buffer2.append("\x1B[?2026l");
7991
8602
  this.#cursorRow = targetRow;
7992
8603
  this.#hardwareCursorRow = targetRow;
7993
- buffer2 += this.#positionHardwareCursor(frame, width, newLines.length);
8604
+ buffer2.append(this.#positionHardwareCursor(frame, width, newLines.length));
7994
8605
  this.#previousViewportTop = prevViewportTop;
7995
8606
  this.#commitState(newLines, width, height);
7996
- this.#terminal.write(buffer2);
8607
+ this.#previousSources = [...sources];
8608
+ buffer2.flush();
7997
8609
  this.#terminal.flush();
7998
8610
  return;
7999
8611
  }
@@ -8001,56 +8613,58 @@ var MainScreenRenderer = class _MainScreenRenderer {
8001
8613
  fullRender(true);
8002
8614
  return;
8003
8615
  }
8004
- let buffer = "\x1B[?2026h";
8616
+ const buffer = new BoundedTerminalWriter((text2) => this.#terminal.write(text2));
8617
+ buffer.append("\x1B[?2026h");
8005
8618
  const prevViewportBottom = prevViewportTop + height - 1;
8006
8619
  const moveTargetRow = appendStart ? firstChanged - 1 : firstChanged;
8007
8620
  if (moveTargetRow > prevViewportBottom) {
8008
8621
  const currentScreenRow = Math.max(0, Math.min(height - 1, hardwareCursorRow - prevViewportTop));
8009
8622
  const moveToBottom = height - 1 - currentScreenRow;
8010
8623
  if (moveToBottom > 0) {
8011
- buffer += `\x1B[${moveToBottom}B`;
8624
+ buffer.append(`\x1B[${moveToBottom}B`);
8012
8625
  }
8013
8626
  const scroll = moveTargetRow - prevViewportBottom;
8014
- buffer += "\r\n".repeat(scroll);
8627
+ buffer.append("\r\n".repeat(scroll));
8015
8628
  viewportTop += scroll;
8016
8629
  hardwareCursorRow = moveTargetRow;
8017
8630
  }
8018
8631
  const lineDiff = computeLineDiff(moveTargetRow);
8019
8632
  if (lineDiff > 0) {
8020
- buffer += `\x1B[${lineDiff}B`;
8633
+ buffer.append(`\x1B[${lineDiff}B`);
8021
8634
  } else if (lineDiff < 0) {
8022
- buffer += `\x1B[${-lineDiff}A`;
8635
+ buffer.append(`\x1B[${-lineDiff}A`);
8023
8636
  }
8024
- buffer += appendStart ? "\r\n" : "\r";
8637
+ buffer.append(appendStart ? "\r\n" : "\r");
8025
8638
  const renderEnd = Math.min(lastChanged, newLines.length - 1);
8026
8639
  for (let index = firstChanged; index <= renderEnd; index += 1) {
8027
8640
  if (index > firstChanged) {
8028
- buffer += "\r\n";
8641
+ buffer.append("\r\n");
8029
8642
  }
8030
- buffer += "\x1B[2K";
8031
- buffer += newLines[index];
8643
+ buffer.append("\x1B[2K");
8644
+ buffer.append(newLines[index]);
8032
8645
  }
8033
8646
  let finalCursorRow = renderEnd;
8034
8647
  if (this.#previousLines.length > newLines.length) {
8035
8648
  if (renderEnd < newLines.length - 1) {
8036
8649
  const moveDown = newLines.length - 1 - renderEnd;
8037
- buffer += `\x1B[${moveDown}B`;
8650
+ buffer.append(`\x1B[${moveDown}B`);
8038
8651
  finalCursorRow = newLines.length - 1;
8039
8652
  }
8040
8653
  const extraLines = this.#previousLines.length - newLines.length;
8041
8654
  for (let index = newLines.length; index < this.#previousLines.length; index += 1) {
8042
- buffer += "\r\n\x1B[2K";
8655
+ buffer.append("\r\n\x1B[2K");
8043
8656
  }
8044
- buffer += `\x1B[${extraLines}A`;
8657
+ buffer.append(`\x1B[${extraLines}A`);
8045
8658
  }
8046
- buffer += "\x1B[?2026l";
8659
+ buffer.append("\x1B[?2026l");
8047
8660
  this.#cursorRow = Math.max(0, newLines.length - 1);
8048
8661
  this.#hardwareCursorRow = finalCursorRow;
8049
8662
  this.#maxLinesRendered = Math.max(this.#maxLinesRendered, newLines.length);
8050
8663
  this.#previousViewportTop = Math.max(prevViewportTop, finalCursorRow - height + 1);
8051
- buffer += this.#positionHardwareCursor(frame, width, newLines.length);
8664
+ buffer.append(this.#positionHardwareCursor(frame, width, newLines.length));
8052
8665
  this.#commitState(newLines, width, height);
8053
- this.#terminal.write(buffer);
8666
+ this.#previousSources = [...sources];
8667
+ buffer.flush();
8054
8668
  this.#terminal.flush();
8055
8669
  }
8056
8670
  close() {
@@ -8059,7 +8673,7 @@ var MainScreenRenderer = class _MainScreenRenderer {
8059
8673
  }
8060
8674
  this.#closed = true;
8061
8675
  try {
8062
- this.#terminal.write("\x1B[0 q\x1B[?25h");
8676
+ this.#terminal.write("\x1B[?2026l\x1B[0m\x1B]8;;\x07\x1B[0 q\x1B[?25h");
8063
8677
  this.#terminal.flush();
8064
8678
  } finally {
8065
8679
  this.#terminal.restore();
@@ -8110,340 +8724,17 @@ var MainScreenRenderer = class _MainScreenRenderer {
8110
8724
  static #validateLines(lines2, width, previous) {
8111
8725
  lines2.forEach((line2, index) => {
8112
8726
  if (previous[index] === line2)
8113
- return;
8114
- if (/[\r\n]/u.test(line2)) {
8115
- throw new Error(`rendered line ${index} contains a physical newline`);
8116
- }
8117
- const lineWidth = visibleWidth(line2);
8118
- if (lineWidth > width) {
8119
- throw new Error(`rendered line ${index} exceeds terminal width (${lineWidth} > ${width})`);
8120
- }
8121
- });
8122
- }
8123
- };
8124
- var WIDTH_CACHE_SIZE = 512;
8125
- var WIDTH_CACHE_MAX_TEXT_LENGTH = 4096;
8126
- var widthCache = /* @__PURE__ */ new Map();
8127
- function visibleWidth(text2) {
8128
- if (/^[\x20-\x7e]*$/u.test(text2))
8129
- return text2.length;
8130
- const cached = widthCache.get(text2);
8131
- if (cached !== void 0)
8132
- return cached;
8133
- const stripped = stripTerminalControls(text2).replace(/\t/g, " ");
8134
- let width = 0;
8135
- for (const cluster of graphemeClusters(stripped)) {
8136
- width += clusterWidth(cluster);
8137
- }
8138
- if (text2.length <= WIDTH_CACHE_MAX_TEXT_LENGTH) {
8139
- if (widthCache.size >= WIDTH_CACHE_SIZE) {
8140
- const oldest = widthCache.keys().next().value;
8141
- if (oldest !== void 0)
8142
- widthCache.delete(oldest);
8143
- }
8144
- widthCache.set(text2, width);
8145
- }
8146
- return width;
8147
- }
8148
- function truncateToWidth(text2, width) {
8149
- if (width <= 0 || !text2) {
8150
- return "";
8151
- }
8152
- const result = [];
8153
- const sgrPrefix = [];
8154
- let used = 0;
8155
- let index = 0;
8156
- while (index < text2.length) {
8157
- if (text2[index] === "\x1B") {
8158
- const end2 = consumeEscapeSequence(text2, index);
8159
- const sequence = text2.slice(index, end2);
8160
- if (isSgrSequence(sequence)) {
8161
- result.push(sequence);
8162
- updateSgrPrefix(sgrPrefix, sequence);
8163
- }
8164
- index = end2;
8165
- continue;
8166
- }
8167
- const nextEscape = text2.indexOf("\x1B", index);
8168
- const end = nextEscape === -1 ? text2.length : nextEscape;
8169
- for (const cluster of graphemeClusters(text2.slice(index, end))) {
8170
- const [renderedCluster, widthOfCluster] = displayCluster(cluster);
8171
- if (used + widthOfCluster > width) {
8172
- if (sgrPrefix.length > 0) {
8173
- result.push("\x1B[0m");
8174
- }
8175
- return result.join("");
8176
- }
8177
- result.push(renderedCluster);
8178
- used += widthOfCluster;
8179
- }
8180
- index = end;
8181
- }
8182
- if (sgrPrefix.length > 0) {
8183
- result.push("\x1B[0m");
8184
- }
8185
- return result.join("");
8186
- }
8187
- function stripTerminalControls(text2) {
8188
- let result = "";
8189
- let index = 0;
8190
- while (index < text2.length) {
8191
- if (text2[index] !== "\x1B") {
8192
- result += text2[index];
8193
- index += 1;
8194
- continue;
8195
- }
8196
- index = consumeEscapeSequence(text2, index);
8197
- }
8198
- return result;
8199
- }
8200
- function consumeEscapeSequence(text2, start) {
8201
- if (start + 1 >= text2.length) {
8202
- return start + 1;
8203
- }
8204
- const marker = text2[start + 1];
8205
- if (marker === "[") {
8206
- let index = start + 2;
8207
- while (index < text2.length) {
8208
- const code = text2.charCodeAt(index);
8209
- if (code >= 64 && code <= 126) {
8210
- return index + 1;
8211
- }
8212
- index += 1;
8213
- }
8214
- return text2.length;
8215
- }
8216
- if (marker === "]") {
8217
- return consumeStringSequence(text2, start + 2, true);
8218
- }
8219
- if (marker === "P") {
8220
- return consumeStringSequence(text2, start + 2, false);
8221
- }
8222
- if (marker === "_") {
8223
- return consumeStringSequence(text2, start + 2, true);
8224
- }
8225
- return start + 2;
8226
- }
8227
- function consumeStringSequence(text2, start, allowBel) {
8228
- let index = start;
8229
- while (index < text2.length) {
8230
- if (allowBel && text2[index] === "\x07") {
8231
- return index + 1;
8232
- }
8233
- if (text2.slice(index, index + 2) === "\x1B\\") {
8234
- return index + 2;
8235
- }
8236
- index += 1;
8237
- }
8238
- return text2.length;
8239
- }
8240
- function isSgrSequence(sequence) {
8241
- return sequence.startsWith("\x1B[") && sequence.endsWith("m");
8242
- }
8243
- function updateSgrPrefix(prefix, sequence) {
8244
- const params = sequence.slice(2, -1).replace(/:/g, ";").split(";").filter((part) => part.length > 0);
8245
- if (params.length === 0 || params.every((param) => param === "0")) {
8246
- prefix.length = 0;
8247
- return;
8248
- }
8249
- if (params.includes("0")) {
8250
- prefix.splice(0, prefix.length, sequence);
8251
- return;
8252
- }
8253
- prefix.push(sequence);
8254
- }
8255
- function displayCluster(cluster) {
8256
- if (cluster === " ") {
8257
- return [" ", 3];
8258
- }
8259
- return [cluster, clusterWidth(cluster)];
8260
- }
8261
- var graphemeSegmenter = new Intl.Segmenter("en", {
8262
- granularity: "grapheme"
8263
- });
8264
- function graphemeClusters(text2) {
8265
- const clusters = [];
8266
- for (const { segment } of graphemeSegmenter.segment(text2)) {
8267
- clusters.push(segment);
8268
- }
8269
- return clusters;
8270
- }
8271
- function isRegionalIndicator(codePoint) {
8272
- return codePoint >= 127462 && codePoint <= 127487;
8273
- }
8274
- function clusterWidth(cluster) {
8275
- if (!cluster) {
8276
- return 0;
8277
- }
8278
- if (cluster.includes("\u200D") || cluster.includes("\uFE0F") || [...cluster].some((char) => isRegionalIndicator(char.codePointAt(0)))) {
8279
- return 2;
8280
- }
8281
- let width = 0;
8282
- for (const char of cluster) {
8283
- width += charCellWidth(char);
8284
- }
8285
- return Math.max(0, width);
8286
- }
8287
- function charCellWidth(char) {
8288
- const cp = char.codePointAt(0);
8289
- if (cp === 8203 || cp >= 8204 && cp <= 8207) {
8290
- return 0;
8291
- }
8292
- if (cp < 32 || cp >= 127 && cp < 160) {
8293
- return 0;
8294
- }
8295
- if (isZeroWidth(cp)) {
8296
- return 0;
8297
- }
8298
- if (isWide(cp)) {
8299
- return 2;
8300
- }
8301
- return 1;
8302
- }
8303
- function isZeroWidth(cp) {
8304
- return cp >= 768 && cp <= 879 || cp >= 1155 && cp <= 1161 || cp >= 1425 && cp <= 1469 || cp === 1471 || cp >= 1473 && cp <= 1474 || cp >= 1476 && cp <= 1477 || cp === 1479 || cp >= 1552 && cp <= 1562 || cp >= 1611 && cp <= 1631 || cp === 1648 || cp >= 1750 && cp <= 1756 || cp >= 3633 && cp <= 3642 || cp >= 3655 && cp <= 3662 || cp >= 6832 && cp <= 6911 || cp >= 7616 && cp <= 7679 || cp === 8205 || cp >= 8400 && cp <= 8447 || cp >= 65024 && cp <= 65039 || cp >= 65056 && cp <= 65071 || cp >= 917760 && cp <= 917999;
8305
- }
8306
- function isWide(cp) {
8307
- return cp >= 4352 && cp <= 4447 || cp >= 8986 && cp <= 8987 || cp >= 9001 && cp <= 9002 || cp >= 9193 && cp <= 9196 || cp === 9200 || cp === 9203 || cp >= 9725 && cp <= 9726 || cp >= 9748 && cp <= 9749 || cp >= 9800 && cp <= 9811 || cp === 9855 || cp === 9875 || cp === 9889 || cp >= 9898 && cp <= 9899 || cp >= 9917 && cp <= 9918 || cp >= 9924 && cp <= 9925 || cp === 9934 || cp === 9940 || cp === 9962 || cp >= 9970 && cp <= 9971 || cp === 9973 || cp >= 9978 && cp <= 9981 || cp === 9989 || cp >= 9994 && cp <= 9995 || cp === 10024 || cp === 10060 || cp === 10062 || cp >= 10067 && cp <= 10069 || cp === 10071 || cp >= 10133 && cp <= 10135 || cp === 10160 || cp === 10175 || cp >= 11035 && cp <= 11036 || cp === 11088 || cp === 11093 || cp >= 11904 && cp <= 12287 || cp >= 12288 && cp <= 12350 || cp >= 12353 && cp <= 13311 || cp >= 13312 && cp <= 19903 || cp >= 19968 && cp <= 40959 || cp >= 40960 && cp <= 42191 || cp >= 44032 && cp <= 55203 || cp >= 63744 && cp <= 64255 || cp >= 65040 && cp <= 65049 || cp >= 65072 && cp <= 65135 || cp >= 65280 && cp <= 65376 || cp >= 65504 && cp <= 65510 || cp >= 94176 && cp <= 94180 || cp >= 127744 && cp <= 128591 || cp >= 129280 && cp <= 129535 || cp >= 129648 && cp <= 129791 || cp >= 131072 && cp <= 196605 || cp >= 196608 && cp <= 262141;
8308
- }
8309
-
8310
- // ../../packages/terminal/tui/dist/tui/render-model.js
8311
- function span(text2, style) {
8312
- return style === void 0 ? { text: text2 } : { text: text2, style };
8313
- }
8314
- function line(...spans) {
8315
- return { spans };
8316
- }
8317
- function plainLine(text2) {
8318
- return line(span(text2));
8319
- }
8320
- function lineText(value) {
8321
- return value.spans.map((item) => item.text).join("");
8322
- }
8323
- function wrapStyledSpans(spans, width) {
8324
- const targetWidth = Math.max(1, width);
8325
- const lines2 = [];
8326
- let current = [];
8327
- let currentWidth = 0;
8328
- let skipLineFeed = false;
8329
- const append = (text2, style) => {
8330
- const previous = current.at(-1);
8331
- if (previous !== void 0 && previous.style === style) {
8332
- current[current.length - 1] = span(previous.text + text2, style);
8333
- return;
8334
- }
8335
- current.push(span(text2, style));
8336
- };
8337
- const finish = () => {
8338
- lines2.push(line(...current));
8339
- current = [];
8340
- currentWidth = 0;
8341
- };
8342
- for (const source of spans) {
8343
- for (const character of source.text) {
8344
- if (character === "\r") {
8345
- finish();
8346
- skipLineFeed = true;
8347
- continue;
8348
- }
8349
- if (character === "\n") {
8350
- if (skipLineFeed) {
8351
- skipLineFeed = false;
8352
- continue;
8353
- }
8354
- finish();
8355
- continue;
8356
- }
8357
- skipLineFeed = false;
8358
- const characterWidth = charCellWidth(character);
8359
- if (characterWidth > targetWidth) {
8360
- throw new Error(`code point exceeds wrap width: ${characterWidth} > ${targetWidth}`);
8361
- }
8362
- if (currentWidth > 0 && currentWidth + characterWidth > targetWidth) {
8363
- finish();
8364
- }
8365
- append(character, source.style);
8366
- currentWidth += characterWidth;
8367
- }
8368
- }
8369
- finish();
8370
- return lines2;
8371
- }
8372
- function truncateStyledLine(value, width, ellipsis = "\u2026") {
8373
- if (width <= 0) {
8374
- return plainLine("");
8375
- }
8376
- const result = [];
8377
- let used = 0;
8378
- let truncated = false;
8379
- let trailingStyle;
8380
- for (const source of value.spans) {
8381
- for (const character of source.text) {
8382
- const characterWidth = charCellWidth(character);
8383
- if (used + characterWidth > width) {
8384
- truncated = true;
8385
- trailingStyle = source.style;
8386
- break;
8387
- }
8388
- result.push(span(character, source.style));
8389
- used += characterWidth;
8390
- trailingStyle = source.style;
8391
- }
8392
- if (truncated) {
8393
- break;
8394
- }
8395
- }
8396
- if (!truncated) {
8397
- return line(...mergeAdjacent(result));
8398
- }
8399
- const ellipsisWidth = ellipsis === "" ? 0 : charCellWidth(ellipsis);
8400
- while (result.length > 0 && used + ellipsisWidth > width) {
8401
- const removed = result.pop();
8402
- used -= charCellWidth(removed.text);
8403
- trailingStyle = removed.style;
8404
- }
8405
- if (ellipsisWidth <= width) {
8406
- result.push(span(ellipsis, trailingStyle));
8407
- }
8408
- return line(...mergeAdjacent(result));
8409
- }
8410
- function padStyledLine(value, width, background) {
8411
- const padding = width - styledLineWidth(value);
8412
- if (padding <= 0) {
8413
- return value;
8414
- }
8415
- return line(...value.spans, span(" ".repeat(padding), background === void 0 ? void 0 : { background }));
8416
- }
8417
- function styledLineWidth(value) {
8418
- let width = 0;
8419
- for (const character of lineText(value)) {
8420
- width += charCellWidth(character);
8421
- }
8422
- return width;
8423
- }
8424
- function mergeAdjacent(spans) {
8425
- const merged = [];
8426
- for (const item of spans) {
8427
- const previous = merged.at(-1);
8428
- if (previous !== void 0 && previous.style === item.style) {
8429
- merged[merged.length - 1] = span(previous.text + item.text, item.style);
8430
- continue;
8431
- }
8432
- merged.push(item);
8433
- }
8434
- return merged;
8435
- }
8436
-
8437
- // ../../packages/terminal/tui/dist/keybindings/key-id.js
8438
- function makeKeyInput(id, options = {}) {
8439
- return {
8440
- id,
8441
- text: options.text ?? null,
8442
- ctrl: options.ctrl ?? false,
8443
- alt: options.alt ?? false,
8444
- shift: options.shift ?? false
8445
- };
8446
- }
8727
+ return;
8728
+ if (/[\r\n]/u.test(line2)) {
8729
+ throw new Error(`rendered line ${index} contains a physical newline`);
8730
+ }
8731
+ const lineWidth = visibleWidth(line2);
8732
+ if (lineWidth > width) {
8733
+ throw new RenderWidthError(width, lineWidth, line2, `screen row ${index}`);
8734
+ }
8735
+ });
8736
+ }
8737
+ };
8447
8738
 
8448
8739
  // ../../packages/terminal/tui/dist/tui/editor.js
8449
8740
  var InputActionKind = {
@@ -8551,20 +8842,24 @@ var StdinBuffer = class {
8551
8842
  if (data.length === 0) {
8552
8843
  return [];
8553
8844
  }
8554
- let chunk = Buffer.from(data);
8555
- if (chunk.length === 1 && chunk[0] > 127) {
8556
- chunk = Buffer.concat([ESCAPE, Buffer.from([chunk[0] - 128])]);
8557
- }
8845
+ const chunk = Buffer.from(data);
8558
8846
  this.buffer = Buffer.concat([this.buffer, chunk]);
8559
8847
  const events = [];
8560
8848
  this.processBuffer(events);
8561
8849
  return events;
8562
8850
  }
8851
+ pendingKind() {
8852
+ if (this.pasteMode)
8853
+ return "paste";
8854
+ if (this.buffer.length === 0)
8855
+ return "none";
8856
+ return this.buffer.length === 1 && this.buffer[0] === ESC ? "escape" : "sequence";
8857
+ }
8563
8858
  flush() {
8564
8859
  if (this.pasteMode || this.buffer.length === 0) {
8565
8860
  return [];
8566
8861
  }
8567
- const sequence = this.buffer;
8862
+ const sequence = this.buffer.length === 1 && this.buffer[0] > 127 ? Buffer.from([ESC, this.buffer[0] - 128]) : this.buffer;
8568
8863
  this.buffer = Buffer.alloc(0);
8569
8864
  this.pendingKittyPrintableCodepoint = null;
8570
8865
  return [{ kind: BufferedInputKind.Sequence, data: sequence }];
@@ -8653,6 +8948,9 @@ var TerminalInputFilter = class {
8653
8948
  this.disableModifyOtherKeys = options.disableModifyOtherKeys ?? (() => {
8654
8949
  });
8655
8950
  }
8951
+ get negotiationPending() {
8952
+ return this.pendingNegotiationPrefix.length > 0;
8953
+ }
8656
8954
  feed(sequence) {
8657
8955
  const text2 = Buffer.from(sequence).toString("latin1");
8658
8956
  return this.feedText(text2).map((item) => Buffer.from(item, "latin1"));
@@ -8831,7 +9129,7 @@ var RawInputDecoder = class {
8831
9129
  this.buffer = Buffer.alloc(0);
8832
9130
  return [inputAction(InputActionKind.Dismiss)];
8833
9131
  }
8834
- if (this.buffer.length > 1 && this.buffer[0] === ESC && this.buffer[1] === 91) {
9132
+ if (this.buffer.length > 1 && this.buffer[0] === ESC) {
8835
9133
  this.buffer = Buffer.alloc(0);
8836
9134
  }
8837
9135
  return this.feed(Buffer.alloc(0));
@@ -8841,6 +9139,15 @@ var RawInputDecoder = class {
8841
9139
  return ESCAPE_INCOMPLETE;
8842
9140
  }
8843
9141
  const second = this.buffer[1];
9142
+ if (second === 93 || second === 80 || second === 95 || second === 94 || second === 88) {
9143
+ for (let index = 2; index < this.buffer.length; index++) {
9144
+ if (this.buffer[index] === 7 || this.buffer[index] === ESC && this.buffer[index + 1] === 92) {
9145
+ this.buffer = this.buffer.subarray(index + (this.buffer[index] === 7 ? 1 : 2));
9146
+ return ESCAPE_CONSUMED;
9147
+ }
9148
+ }
9149
+ return ESCAPE_INCOMPLETE;
9150
+ }
8844
9151
  if (second === 10 || second === 13) {
8845
9152
  this.buffer = this.buffer.subarray(2);
8846
9153
  return {
@@ -8887,7 +9194,9 @@ var RawInputDecoder = class {
8887
9194
  this.buffer = this.buffer.subarray(final + 1);
8888
9195
  return kind !== void 0 ? { status: "action", action: inputAction(kind) } : ESCAPE_CONSUMED;
8889
9196
  }
8890
- if (second === 79 && this.buffer.length >= 3) {
9197
+ if (second === 79) {
9198
+ if (this.buffer.length < 3)
9199
+ return ESCAPE_INCOMPLETE;
8891
9200
  const sequence = this.buffer.subarray(0, 3).toString("latin1");
8892
9201
  const kind = ESCAPE_ACTIONS.get(sequence);
8893
9202
  this.buffer = this.buffer.subarray(3);
@@ -9019,7 +9328,7 @@ function completeSequenceStatus(data) {
9019
9328
  if (second === 93) {
9020
9329
  return endsWithEscapeBackslash(data) || data[data.length - 1] === 7 ? "complete" : "incomplete";
9021
9330
  }
9022
- if (second === 80 || second === 95) {
9331
+ if (second === 80 || second === 95 || second === 94 || second === 88) {
9023
9332
  return endsWithEscapeBackslash(data) ? "complete" : "incomplete";
9024
9333
  }
9025
9334
  if (second === 79) {
@@ -9269,13 +9578,6 @@ function decodeModifyOtherKeysPrintable(sequence) {
9269
9578
  return null;
9270
9579
  }
9271
9580
  }
9272
- function displayWidth(text2) {
9273
- let width = 0;
9274
- for (const char of text2) {
9275
- width += charCellWidth(char);
9276
- }
9277
- return width;
9278
- }
9279
9581
  var EditorState = class _EditorState {
9280
9582
  /** Window for the Pi-style double-Ctrl+C-to-exit gesture, in milliseconds. */
9281
9583
  static DOUBLE_CANCEL_EXIT_MS = 500;
@@ -9345,36 +9647,7 @@ var EditorState = class _EditorState {
9345
9647
  };
9346
9648
  }
9347
9649
  #renderProjection(width, prompt, mask) {
9348
- const boundedWidth = Math.max(3, width);
9349
- const promptWidth = Math.max(1, displayWidth(prompt));
9350
- const contentWidth = Math.max(1, boundedWidth - promptWidth);
9351
- const displayText = mask ? "*".repeat(this.text.length) : this.text;
9352
- const sourceLines = displayText.split("\n");
9353
- const rows = [];
9354
- for (const source of sourceLines) {
9355
- rows.push(...wrapLine(source, contentWidth));
9356
- }
9357
- const lastLine = sourceLines[sourceLines.length - 1];
9358
- if (displayText && !displayText.endsWith("\n") && displayWidth(lastLine) % contentWidth === 0) {
9359
- rows.push("");
9360
- }
9361
- const before = displayText.slice(0, this.cursor);
9362
- const beforeLines = before.split("\n");
9363
- let priorRows = 0;
9364
- for (const line2 of beforeLines.slice(0, -1)) {
9365
- priorRows += wrapLine(line2, contentWidth).length;
9366
- }
9367
- const current = beforeLines[beforeLines.length - 1];
9368
- const [currentRow, currentColumn] = cursorPosition(current, contentWidth);
9369
- const cursorRow = priorRows + currentRow;
9370
- const cursorColumn = promptWidth + currentColumn;
9371
- return {
9372
- rows,
9373
- prompt,
9374
- promptWidth,
9375
- cursorRow: Math.min(cursorRow, rows.length - 1),
9376
- cursorColumn: Math.min(cursorColumn, boundedWidth - 1)
9377
- };
9650
+ return projectInput(this.text, this.cursor, width, prompt, mask);
9378
9651
  }
9379
9652
  submit() {
9380
9653
  const submitted = this.text;
@@ -9426,16 +9699,18 @@ var EditorState = class _EditorState {
9426
9699
  this.insert(action.text);
9427
9700
  } else if (action.kind === InputActionKind.Backspace && this.cursor > 0) {
9428
9701
  this.clearCompletions();
9429
- this.text = this.text.slice(0, this.cursor - 1) + this.text.slice(this.cursor);
9430
- this.cursor -= 1;
9702
+ const previous = previousGraphemeBoundary(this.text, this.cursor);
9703
+ const end = nextGraphemeBoundary(this.text, previous);
9704
+ this.text = this.text.slice(0, previous) + this.text.slice(end);
9705
+ this.cursor = previous;
9431
9706
  } else if (action.kind === InputActionKind.DeleteToLineStart) {
9432
9707
  this.deleteToLineStart();
9433
9708
  } else if (action.kind === InputActionKind.CursorLeft) {
9434
9709
  this.clearCompletions();
9435
- this.cursor = Math.max(0, this.cursor - 1);
9710
+ this.cursor = previousGraphemeBoundary(this.text, this.cursor);
9436
9711
  } else if (action.kind === InputActionKind.CursorRight) {
9437
9712
  this.clearCompletions();
9438
- this.cursor = Math.min(this.text.length, this.cursor + 1);
9713
+ this.cursor = nextGraphemeBoundary(this.text, this.cursor);
9439
9714
  } else if (action.kind === InputActionKind.HistoryUp) {
9440
9715
  this.historyUp();
9441
9716
  } else if (action.kind === InputActionKind.HistoryDown) {
@@ -9523,42 +9798,68 @@ var EditorState = class _EditorState {
9523
9798
  this.selectedCompletion = null;
9524
9799
  }
9525
9800
  };
9526
- function wrapLine(text2, width) {
9527
- const rows = [];
9528
- let row = "";
9529
- let rowWidth = 0;
9530
- for (const char of text2) {
9531
- const charWidth = Math.max(1, charCellWidth(char));
9532
- if (row && rowWidth + charWidth > width) {
9533
- rows.push(row);
9534
- row = "";
9535
- rowWidth = 0;
9536
- }
9537
- row += char;
9538
- rowWidth += charWidth;
9801
+
9802
+ // ../../packages/terminal/tui/dist/tui/terminal-input-decoder.js
9803
+ var TerminalInputDecoder = class {
9804
+ #stdinBuffer = new StdinBuffer();
9805
+ #filter;
9806
+ #decoder = new RawInputDecoder();
9807
+ constructor(hooks, options = {}) {
9808
+ this.#filter = new TerminalInputFilter({
9809
+ ...options,
9810
+ enableModifyOtherKeys: () => {
9811
+ hooks.enableModifyOtherKeys();
9812
+ },
9813
+ disableModifyOtherKeys: () => {
9814
+ hooks.disableModifyOtherKeys();
9815
+ }
9816
+ });
9539
9817
  }
9540
- if (row || rows.length === 0) {
9541
- rows.push(row);
9818
+ get kittyProtocolActive() {
9819
+ return this.#filter.kittyProtocolActive;
9542
9820
  }
9543
- return rows;
9544
- }
9545
- function cursorPosition(text2, width) {
9546
- let row = 0;
9547
- let column = 0;
9548
- for (const char of text2) {
9549
- const charWidth = Math.max(1, charCellWidth(char));
9550
- if (column && column + charWidth > width) {
9551
- row += 1;
9552
- column = 0;
9821
+ set kittyProtocolActive(value) {
9822
+ this.#filter.kittyProtocolActive = value;
9823
+ }
9824
+ pendingKind() {
9825
+ return this.#filter.negotiationPending ? "negotiation" : this.#stdinBuffer.pendingKind();
9826
+ }
9827
+ feed(data) {
9828
+ const actions = [];
9829
+ for (const event of this.#stdinBuffer.feed(data)) {
9830
+ if (event.kind === BufferedInputKind.Paste) {
9831
+ actions.push(inputAction(InputActionKind.Insert, event.data.toString("utf8")));
9832
+ continue;
9833
+ }
9834
+ for (const sequence of this.#filter.feed(event.data)) {
9835
+ actions.push(...this.#decoder.feed(sequence));
9836
+ }
9553
9837
  }
9554
- column += charWidth;
9555
- if (column === width) {
9556
- row += 1;
9557
- column = 0;
9838
+ return actions;
9839
+ }
9840
+ flush() {
9841
+ const actions = [];
9842
+ for (const event of this.#stdinBuffer.flush()) {
9843
+ if (event.kind === BufferedInputKind.Paste) {
9844
+ actions.push(inputAction(InputActionKind.Insert, event.data.toString("utf8")));
9845
+ continue;
9846
+ }
9847
+ for (const sequence of this.#filter.feed(event.data)) {
9848
+ actions.push(...this.#decoder.feed(sequence));
9849
+ }
9850
+ }
9851
+ for (const sequence of this.#filter.flush()) {
9852
+ actions.push(...this.#decoder.feed(sequence));
9558
9853
  }
9854
+ actions.push(...this.#decoder.flush());
9855
+ return actions;
9559
9856
  }
9560
- return [row, column];
9561
- }
9857
+ clear() {
9858
+ this.#stdinBuffer.clear();
9859
+ this.#filter.clear();
9860
+ this.#decoder = new RawInputDecoder();
9861
+ }
9862
+ };
9562
9863
 
9563
9864
  // ../../packages/terminal/tui/dist/keybindings/default-keybindings.js
9564
9865
  var DEFAULT_KEYBINDINGS = [
@@ -10381,12 +10682,12 @@ var TranscriptStore = class {
10381
10682
  var StyledLineCompiler = class {
10382
10683
  #cache = /* @__PURE__ */ new WeakMap();
10383
10684
  compile(values, width, theme) {
10384
- return values.map((value) => {
10685
+ return values.map((value, index) => {
10385
10686
  const cached = this.#cache.get(value);
10386
10687
  if (cached !== void 0 && cached.width === width && cached.theme === theme) {
10387
10688
  return cached.text;
10388
10689
  }
10389
- const text2 = compileStyledLine(truncateStyledLine(value, width, ""), width, theme);
10690
+ const text2 = compileStyledLine(truncateStyledLine(value, width, ""), width, theme, `styled frame row ${index + 1}`);
10390
10691
  this.#cache.set(value, { width, theme, text: text2 });
10391
10692
  return text2;
10392
10693
  });
@@ -10395,16 +10696,27 @@ var StyledLineCompiler = class {
10395
10696
  this.#cache = /* @__PURE__ */ new WeakMap();
10396
10697
  }
10397
10698
  };
10398
- function compileStyledLine(value, width, theme) {
10399
- const text2 = value.spans.map((item) => compileSpan(item, theme)).join("");
10699
+ function compileStyledLine(value, width, theme, source = "styled line") {
10700
+ const text2 = compileSpans(value.spans, theme);
10400
10701
  const renderedWidth = visibleWidth(text2);
10401
10702
  if (renderedWidth > width) {
10402
- throw new Error(`rendered line exceeds terminal width: ${renderedWidth} > ${width}`);
10703
+ throw new RenderWidthError(width, renderedWidth, text2, source);
10403
10704
  }
10404
10705
  return text2;
10405
10706
  }
10406
10707
  function compileStyledLines(values, width, theme) {
10407
- return values.map((value) => compileStyledLine(value, width, theme));
10708
+ return values.map((value, index) => compileStyledLine(value, width, theme, `styled frame row ${index + 1}`));
10709
+ }
10710
+ function compileSpans(values, theme) {
10711
+ const normalized = [];
10712
+ for (const value of styledClusters(values)) {
10713
+ const previous = normalized.at(-1);
10714
+ if (previous && previous.style === value.style)
10715
+ normalized[normalized.length - 1] = { text: previous.text + value.text, style: value.style };
10716
+ else
10717
+ normalized.push(value);
10718
+ }
10719
+ return normalized.map((value) => compileSpan(value, theme)).join("");
10408
10720
  }
10409
10721
  function compileSpan(value, theme) {
10410
10722
  if (value.style === void 0 || value.text.length === 0) {
@@ -10429,7 +10741,9 @@ function compileSpan(value, theme) {
10429
10741
  if (value.style.background !== void 0) {
10430
10742
  codes.push(theme.sgr(value.style.background, { background: true }).slice(2, -1));
10431
10743
  }
10432
- return codes.length === 0 ? value.text : `\x1B[${codes.join(";")}m${value.text}\x1B[0m`;
10744
+ const text2 = codes.length === 0 ? value.text : `\x1B[${codes.join(";")}m${value.text}\x1B[0m`;
10745
+ const url = value.style.hyperlink;
10746
+ return url && !/[\x00-\x20\x7f]/u.test(url) ? `\x1B]8;;${url}\x07${text2}\x1B]8;;\x07` : text2;
10433
10747
  }
10434
10748
 
10435
10749
  // ../../packages/terminal/tui/dist/tui/components/completion-list.js
@@ -10455,14 +10769,11 @@ var CompletionPopup = class {
10455
10769
  const description = item.description.replace(/[\r\n]+/gu, " ").trim();
10456
10770
  const value = line(span(selected ? "\u203A " : " ", selected ? { foreground: "accent" } : void 0), span(item.value, selected ? { foreground: "accent" } : void 0), ...description ? [span(` ${description}`, { foreground: "muted" })] : []);
10457
10771
  const boundedWidth = Math.max(1, width);
10458
- return displayWidth2(lineText(value)) <= boundedWidth ? value : truncateStyledLine(value, boundedWidth, "");
10772
+ return displayWidth(lineText(value)) <= boundedWidth ? value : truncateStyledLine(value, boundedWidth, "");
10459
10773
  }
10460
10774
  };
10461
- function displayWidth2(value) {
10462
- let width = 0;
10463
- for (const character of value)
10464
- width += charCellWidth(character);
10465
- return width;
10775
+ function displayWidth(value) {
10776
+ return visibleWidth(value);
10466
10777
  }
10467
10778
 
10468
10779
  // ../../packages/terminal/tui/dist/tui/components/composer.js
@@ -10539,10 +10850,10 @@ var StatusLine = class {
10539
10850
  return { lines: [] };
10540
10851
  }
10541
10852
  const clippedLeft = truncateStyledLine(left, width, "");
10542
- const leftWidth = displayWidth3(lineText(clippedLeft));
10853
+ const leftWidth = displayWidth2(lineText(clippedLeft));
10543
10854
  const gap = leftWidth > 0 && lineText(right).length > 0 ? 1 : 0;
10544
10855
  const clippedRight = truncateStyledLine(right, Math.max(0, width - leftWidth - gap), "");
10545
- const rightWidth = displayWidth3(lineText(clippedRight));
10856
+ const rightWidth = displayWidth2(lineText(clippedRight));
10546
10857
  const padding = rightWidth > 0 ? Math.max(gap, width - leftWidth - rightWidth) : 0;
10547
10858
  return {
10548
10859
  lines: [line(...clippedLeft.spans, ...rightWidth > 0 ? [span(" ".repeat(padding)), ...clippedRight.spans] : [])]
@@ -10597,15 +10908,12 @@ function metadataLine(items) {
10597
10908
  ]));
10598
10909
  }
10599
10910
  function fits(leftItems, right, width) {
10600
- const leftWidth = displayWidth3(lineText(metadataLine(leftItems)));
10601
- const rightWidth = displayWidth3(lineText(right));
10911
+ const leftWidth = displayWidth2(lineText(metadataLine(leftItems)));
10912
+ const rightWidth = displayWidth2(lineText(right));
10602
10913
  return leftWidth + rightWidth + (leftWidth > 0 && rightWidth > 0 ? 1 : 0) <= width;
10603
10914
  }
10604
- function displayWidth3(value) {
10605
- let width = 0;
10606
- for (const character of value)
10607
- width += charCellWidth(character);
10608
- return width;
10915
+ function displayWidth2(value) {
10916
+ return visibleWidth(value);
10609
10917
  }
10610
10918
 
10611
10919
  // ../../packages/terminal/tui/dist/tui/markdown.js
@@ -10617,7 +10925,7 @@ function renderMarkdownStyledLines(text2, width) {
10617
10925
  if (!clean.trim()) {
10618
10926
  return [];
10619
10927
  }
10620
- const layoutWidth = Math.max(12, width);
10928
+ const layoutWidth = Math.max(1, width);
10621
10929
  const lines2 = [];
10622
10930
  let previous = null;
10623
10931
  for (const block of renderBlocks(clean, layoutWidth, Math.max(1, width))) {
@@ -10641,7 +10949,8 @@ function styleKey(style) {
10641
10949
  style.italic ? "i" : "",
10642
10950
  style.underline ? "u" : "",
10643
10951
  style.foreground ?? "",
10644
- style.background ?? ""
10952
+ style.background ?? "",
10953
+ style.hyperlink ?? ""
10645
10954
  ].join("|");
10646
10955
  }
10647
10956
  function renderBlocks(text2, layoutWidth, tableWidth) {
@@ -10676,6 +10985,10 @@ function renderBlocks(text2, layoutWidth, tableWidth) {
10676
10985
  index += 1;
10677
10986
  break;
10678
10987
  }
10988
+ if (index === lines2.length - 1 && body.trim().length > 0 && body.trim().length < 3 && body.trim() === fence[0].repeat(body.trim().length)) {
10989
+ index += 1;
10990
+ break;
10991
+ }
10679
10992
  codeLines.push(...body ? wrapSegments([{ style: codeStyle, text: body }], layoutWidth) : [[]]);
10680
10993
  index += 1;
10681
10994
  }
@@ -10808,7 +11121,7 @@ function renderListItems(items, prefixFor, layoutWidth) {
10808
11121
  const marker = { foreground: "accent" };
10809
11122
  const lines2 = [];
10810
11123
  for (let index = 0; index < items.length; index += 1) {
10811
- const prefix = prefixFor(index);
11124
+ const prefix = prefixFor(index).slice(0, Math.max(0, layoutWidth - 1));
10812
11125
  const wrapped = wrapSegments(items[index], Math.max(1, layoutWidth - prefix.length));
10813
11126
  if (wrapped.length === 0) {
10814
11127
  lines2.push([{ style: marker, text: prefix }]);
@@ -10840,13 +11153,7 @@ function splitTableRow(line2) {
10840
11153
  return rest.split("|").map((cell) => cell.trim());
10841
11154
  }
10842
11155
  function segmentsWidth(segments) {
10843
- let width = 0;
10844
- for (const segment of segments) {
10845
- for (const cluster of graphemeClusters(segment.text)) {
10846
- width += clusterWidth(cluster);
10847
- }
10848
- }
10849
- return width;
11156
+ return styledClusters(segments).reduce((width, item) => width + graphemeClusters(item.text).reduce((total, cluster) => total + clusterWidth(cluster), 0), 0);
10850
11157
  }
10851
11158
  function renderTable(header, rows, layoutWidth, rawLines) {
10852
11159
  const columnCount = header.length;
@@ -10863,10 +11170,8 @@ function renderTable(header, rows, layoutWidth, rawLines) {
10863
11170
  const minWidths = Array(columnCount).fill(1);
10864
11171
  for (const row of [headerCells, ...bodyCells]) {
10865
11172
  for (let i = 0; i < columnCount; i += 1) {
10866
- for (const segment of row[i]) {
10867
- for (const cluster of graphemeClusters(segment.text)) {
10868
- minWidths[i] = Math.max(minWidths[i], clusterWidth(cluster));
10869
- }
11173
+ for (const cluster of styledClusters(row[i])) {
11174
+ minWidths[i] = Math.max(minWidths[i], visibleWidth(cluster.text));
10870
11175
  }
10871
11176
  }
10872
11177
  }
@@ -10956,10 +11261,12 @@ var INLINE_PATTERNS = [
10956
11261
  },
10957
11262
  {
10958
11263
  // Links and images: visible text plus the URL in parentheses, matching
10959
- // Rich's hyperlinks=False output, never OSC-8 controls. Rich renders
10960
- // the link text as plain (unparsed) content.
11264
+ // Keep the destination readable when terminal hyperlinks are unavailable.
10961
11265
  pattern: /!?\[([^\]]*)\]\(([^)\s]+)\)/,
10962
- expand: (base, match) => [
11266
+ expand: (base, match) => supportsTerminalHyperlinks() && !/[\x00-\x20\x7f]/u.test(match[2]) ? [{
11267
+ style: { ...base, underline: true, foreground: "link", hyperlink: match[2] },
11268
+ text: match[1]
11269
+ }] : [
10963
11270
  {
10964
11271
  style: { ...base, underline: true, foreground: "link" },
10965
11272
  text: match[1]
@@ -11051,15 +11358,14 @@ function mergeSegments(segments) {
11051
11358
  }
11052
11359
  function wrapSegments(segments, width) {
11053
11360
  const clusters = [];
11054
- for (const segment of segments) {
11055
- for (const cluster of graphemeClusters(segment.text)) {
11056
- clusters.push({
11057
- style: segment.style,
11058
- text: cluster === " " ? " " : cluster,
11059
- width: clusterWidth(cluster),
11060
- space: cluster === " "
11061
- });
11062
- }
11361
+ for (const item of styledClusters(segments)) {
11362
+ const measured = graphemeClusters(item.text).reduce((total, cluster) => total + clusterWidth(cluster), 0);
11363
+ clusters.push({
11364
+ style: item.style ?? {},
11365
+ text: measured > width ? "\uFFFD" : item.text,
11366
+ width: measured > width ? 1 : measured,
11367
+ space: item.text === " "
11368
+ });
11063
11369
  }
11064
11370
  const words = [];
11065
11371
  let current = [];
@@ -11141,24 +11447,7 @@ function serializeLine(segments) {
11141
11447
  return line(...segments.map((segment) => span(segment.text, segment.style)));
11142
11448
  }
11143
11449
  function truncateMarkdownLine(value, width) {
11144
- const spans = [];
11145
- let used = 0;
11146
- for (const source of value.spans) {
11147
- let text2 = "";
11148
- for (const character of source.text) {
11149
- const characterWidth = charCellWidth(character);
11150
- if (used + characterWidth > width) {
11151
- if (text2)
11152
- spans.push(span(text2, source.style));
11153
- return line(...spans);
11154
- }
11155
- text2 += character;
11156
- used += characterWidth;
11157
- }
11158
- if (text2)
11159
- spans.push(span(text2, source.style));
11160
- }
11161
- return line(...spans);
11450
+ return truncateStyledLine(value, width, "");
11162
11451
  }
11163
11452
 
11164
11453
  // ../../packages/terminal/tui/dist/tui/components/messages/assistant-message.js
@@ -11195,9 +11484,10 @@ var Text = class {
11195
11484
  if (this.#cache?.width === context.width && this.#cache.source === source) {
11196
11485
  return this.#cache.result;
11197
11486
  }
11198
- const contentWidth = Math.max(1, context.width - this.#options.paddingX * 2);
11487
+ const paddingX = Math.min(this.#options.paddingX, Math.max(0, Math.floor((context.width - 1) / 2)));
11488
+ const contentWidth = Math.max(1, context.width - paddingX * 2);
11199
11489
  const wrapped = wrapStyledSpans(spans, contentWidth);
11200
- const result = { lines: applyTextPadding(wrapped, context.width, this.#options) };
11490
+ const result = { lines: applyTextPadding(wrapped, context.width, { ...this.#options, paddingX }) };
11201
11491
  this.#cache = { width: context.width, source, result };
11202
11492
  return result;
11203
11493
  }
@@ -11291,12 +11581,13 @@ var Box = class {
11291
11581
  }
11292
11582
  render(context) {
11293
11583
  const width = Math.max(0, context.width);
11294
- const child = this.#child.render({ ...context, width: Math.max(1, width - this.#paddingX * 2) });
11584
+ const paddingX = Math.min(this.#paddingX, Math.max(0, Math.floor((width - 1) / 2)));
11585
+ const child = this.#child.render({ ...context, width: Math.max(1, width - paddingX * 2) });
11295
11586
  const blank = () => line(span(" ".repeat(width), backgroundStyle2(this.#background)));
11296
- const lines2 = child.lines.map((value) => this.#renderLine(value, width));
11587
+ const lines2 = child.lines.map((value) => this.#renderLine(value, width, paddingX));
11297
11588
  const cursor = child.cursor === void 0 ? void 0 : {
11298
11589
  row: child.cursor.row + this.#paddingY,
11299
- column: child.cursor.column + this.#paddingX
11590
+ column: Math.max(0, Math.min(width - 1, child.cursor.column + paddingX))
11300
11591
  };
11301
11592
  return {
11302
11593
  lines: [
@@ -11310,8 +11601,8 @@ var Box = class {
11310
11601
  invalidate() {
11311
11602
  this.#child.invalidate();
11312
11603
  }
11313
- #renderLine(value, width) {
11314
- const left = span(" ".repeat(this.#paddingX), backgroundStyle2(this.#background));
11604
+ #renderLine(value, width, paddingX) {
11605
+ const left = span(" ".repeat(paddingX), backgroundStyle2(this.#background));
11315
11606
  const content = value.spans.map((item) => span(item.text, this.#background === void 0 ? item.style : { ...item.style, background: this.#background }));
11316
11607
  return padStyledLine(truncateStyledLine(line(left, ...content), width, ""), width, this.#background);
11317
11608
  }
@@ -11557,7 +11848,7 @@ var Transcript = class {
11557
11848
  return this.renderWithMetadata(context);
11558
11849
  }
11559
11850
  renderWithMetadata(context) {
11560
- const usableWidth = Math.max(12, context.width);
11851
+ const usableWidth = Math.max(1, context.width);
11561
11852
  const lines2 = [];
11562
11853
  let activeStart = null;
11563
11854
  const seenKeys = /* @__PURE__ */ new Set();
@@ -11914,17 +12205,12 @@ var SearchInput = class {
11914
12205
  }
11915
12206
  render(context) {
11916
12207
  const width = Math.max(1, context.width);
11917
- const contentWidth = Math.max(1, width - displayWidth4(PROMPT));
11918
- const displayText = this.#secret ? "\u2022".repeat(this.#editor.text.length) : this.#editor.text;
11919
- const lines2 = renderLines(displayText || this.#placeholder, contentWidth, displayText.length === 0 && this.#placeholder.length > 0);
11920
- const structuredLines = lines2.map((value, index) => truncateStyledLine(line(span(index === 0 ? PROMPT : " ".repeat(displayWidth4(PROMPT)), index === 0 ? { foreground: "accent" } : void 0), span(value, displayText.length === 0 && this.#placeholder.length > 0 ? { foreground: "muted" } : void 0)), width, ""));
11921
- const cursor = this.focused ? cursorMetadata(displayText.slice(0, this.#editor.cursor), contentWidth) : void 0;
11922
- return cursor === void 0 ? { lines: structuredLines } : {
11923
- lines: structuredLines,
11924
- cursor: {
11925
- row: Math.min(cursor.row, structuredLines.length - 1),
11926
- column: Math.min(displayWidth4(PROMPT) + cursor.column, width - 1)
11927
- }
12208
+ const empty = this.#editor.text.length === 0;
12209
+ const projection = projectInput(empty ? this.#placeholder : this.#editor.text, this.#editor.cursor, width, PROMPT, this.#secret && !empty);
12210
+ const lines2 = projection.rows.map((value, index) => line(span(index === 0 ? projection.prompt : " ".repeat(projection.promptWidth), index === 0 ? { foreground: "accent" } : void 0), span(this.#secret && !empty ? value.replace(/\*/g, "\u2022") : value, empty ? { foreground: "muted" } : void 0)));
12211
+ return {
12212
+ lines: lines2,
12213
+ ...this.focused ? { cursor: { row: empty ? 0 : projection.cursorRow, column: empty ? projection.promptWidth : projection.cursorColumn } } : {}
11928
12214
  };
11929
12215
  }
11930
12216
  handleInput(event) {
@@ -11981,52 +12267,6 @@ function actionFor(event) {
11981
12267
  return null;
11982
12268
  }
11983
12269
  }
11984
- function renderLines(value, width, placeholder) {
11985
- if (placeholder) {
11986
- return [value];
11987
- }
11988
- const lines2 = [];
11989
- for (const source of value.split("\n")) {
11990
- lines2.push(...wrapStyledSpans([span(source)], width).map((item) => item.spans.map((part) => part.text).join("")));
11991
- }
11992
- const last = value.split("\n").at(-1) ?? "";
11993
- if (value && !value.endsWith("\n") && displayWidth4(last) % width === 0) {
11994
- lines2.push("");
11995
- }
11996
- return lines2;
11997
- }
11998
- function cursorMetadata(before, width) {
11999
- const segments = before.split("\n");
12000
- let row = 0;
12001
- for (const segment of segments.slice(0, -1)) {
12002
- row += wrappedRowCount(segment, width);
12003
- }
12004
- const current = segments.at(-1) ?? "";
12005
- let column = 0;
12006
- for (const character of current) {
12007
- const characterWidth = Math.max(1, charCellWidth(character));
12008
- if (column > 0 && column + characterWidth > width) {
12009
- row += 1;
12010
- column = 0;
12011
- }
12012
- column += characterWidth;
12013
- if (column === width) {
12014
- row += 1;
12015
- column = 0;
12016
- }
12017
- }
12018
- return { row, column };
12019
- }
12020
- function wrappedRowCount(value, width) {
12021
- return wrapStyledSpans([span(value)], width).length;
12022
- }
12023
- function displayWidth4(value) {
12024
- let width = 0;
12025
- for (const character of value) {
12026
- width += charCellWidth(character);
12027
- }
12028
- return width;
12029
- }
12030
12270
 
12031
12271
  // ../../packages/terminal/tui/dist/tui/components/primitives/select-list.js
12032
12272
  var DEFAULT_PRIMARY_COLUMN_WIDTH = 32;
@@ -12111,13 +12351,13 @@ var SelectList = class {
12111
12351
  }
12112
12352
  #renderItem(item, selected, width, primaryColumnWidth) {
12113
12353
  const prefix = selected ? span("\u2192 ", { foreground: "accent" }) : span(" ");
12114
- const prefixWidth = displayWidth5(prefix.text);
12354
+ const prefixWidth = displayWidth3(prefix.text);
12115
12355
  const description = item.description === void 0 ? void 0 : normalizeDescription(item.description);
12116
12356
  if (description && width > 40) {
12117
12357
  const effectivePrimaryWidth = Math.max(1, Math.min(primaryColumnWidth, width - prefixWidth - 4));
12118
12358
  const label = truncateText(displayValue(item), Math.max(1, effectivePrimaryWidth - PRIMARY_COLUMN_GAP));
12119
- const spacing = " ".repeat(Math.max(1, effectivePrimaryWidth - displayWidth5(label)));
12120
- const remainingWidth = width - prefixWidth - displayWidth5(label) - displayWidth5(spacing) - 2;
12359
+ const spacing = " ".repeat(Math.max(1, effectivePrimaryWidth - displayWidth3(label)));
12360
+ const remainingWidth = width - prefixWidth - displayWidth3(label) - displayWidth3(spacing) - 2;
12121
12361
  if (remainingWidth > MIN_DESCRIPTION_WIDTH) {
12122
12362
  return truncateStyledLine(line(prefix, span(label, selected ? { foreground: "accent" } : void 0), span(spacing), span(truncateText(description, remainingWidth), { foreground: "muted" })), width, "");
12123
12363
  }
@@ -12125,7 +12365,7 @@ var SelectList = class {
12125
12365
  return truncateStyledLine(line(prefix, span(truncateText(displayValue(item), Math.max(1, width - prefixWidth - 2)), selected ? { foreground: "accent" } : void 0)), width, "");
12126
12366
  }
12127
12367
  #primaryColumnWidth() {
12128
- const widest = this.#filteredItems.reduce((value, item) => Math.max(value, displayWidth5(displayValue(item)) + PRIMARY_COLUMN_GAP), 0);
12368
+ const widest = this.#filteredItems.reduce((value, item) => Math.max(value, displayWidth3(displayValue(item)) + PRIMARY_COLUMN_GAP), 0);
12129
12369
  return Math.max(1, Math.min(DEFAULT_PRIMARY_COLUMN_WIDTH, Math.max(DEFAULT_PRIMARY_COLUMN_WIDTH, widest)));
12130
12370
  }
12131
12371
  };
@@ -12138,12 +12378,8 @@ function normalizeDescription(value) {
12138
12378
  function truncateText(value, width) {
12139
12379
  return lineText(truncateStyledLine(line(span(value)), width, ""));
12140
12380
  }
12141
- function displayWidth5(value) {
12142
- let width = 0;
12143
- for (const character of value) {
12144
- width += charCellWidth(character);
12145
- }
12146
- return width;
12381
+ function displayWidth3(value) {
12382
+ return visibleWidth(value);
12147
12383
  }
12148
12384
 
12149
12385
  // ../../packages/terminal/tui/dist/tui/components/primitives/v-stack.js
@@ -12426,7 +12662,7 @@ var ViewHost = class {
12426
12662
  openSelection(request) {
12427
12663
  return new Promise((resolve8) => {
12428
12664
  const component = this.#selectionComponent(request);
12429
- this.#open({ id: request.id, component, resolve: resolve8 }, "selector");
12665
+ this.#open({ id: request.id, component, resolve: resolve8 }, "selector", request.floating);
12430
12666
  });
12431
12667
  }
12432
12668
  openPrompt(request) {
@@ -12436,13 +12672,15 @@ var ViewHost = class {
12436
12672
  onSubmit: (value) => this.#close(request.id, value),
12437
12673
  onCancel: () => this.#close(request.id, null)
12438
12674
  });
12439
- this.#open({ id: request.id, component, resolve: resolve8 }, "modal");
12675
+ this.#open({ id: request.id, component, resolve: resolve8 }, "modal", request.floating);
12440
12676
  });
12441
12677
  }
12442
12678
  handleInput(event) {
12443
12679
  return this.#topView()?.component.handleInput?.(event) ?? false;
12444
12680
  }
12445
12681
  render(context) {
12682
+ if (this.#overlays.top()?.placement === "floating")
12683
+ return { lines: [] };
12446
12684
  return this.#topView()?.component.render(context) ?? { lines: [] };
12447
12685
  }
12448
12686
  closeActive(value) {
@@ -12459,10 +12697,12 @@ var ViewHost = class {
12459
12697
  activeId() {
12460
12698
  return this.#topView()?.id ?? null;
12461
12699
  }
12462
- #open(view, priority) {
12700
+ #open(view, priority, floating) {
12463
12701
  this.#close(view.id, null);
12464
12702
  this.#views.push(view);
12465
- if (priority === "selector") {
12703
+ if (floating !== void 0) {
12704
+ this.#overlays.open({ id: view.id, component: view.component, priority, placement: "floating", options: floating });
12705
+ } else if (priority === "selector") {
12466
12706
  this.#overlays.open(createSelectorOverlay(view.id, view.component));
12467
12707
  } else {
12468
12708
  this.#overlays.open(createModalOverlay(view.id, view.component));
@@ -12550,7 +12790,61 @@ var PRIORITY_ORDER = {
12550
12790
  };
12551
12791
  var OverlayManager = class {
12552
12792
  #entries = [];
12793
+ #hidden = /* @__PURE__ */ new Set();
12794
+ #width = 80;
12795
+ #height = 24;
12796
+ setViewport(width, height) {
12797
+ this.#width = Math.max(1, width);
12798
+ this.#height = Math.max(1, height);
12799
+ this.#syncFocus();
12800
+ }
12801
+ setHidden(id, hidden) {
12802
+ if (hidden)
12803
+ this.#hidden.add(id);
12804
+ else
12805
+ this.#hidden.delete(id);
12806
+ this.#syncFocus();
12807
+ }
12808
+ composite(frame, theme) {
12809
+ const overlays = this.#entries.filter((entry) => entry.placement === "floating" && this.#visible(entry)).sort((left, right) => PRIORITY_ORDER[left.priority] - PRIORITY_ORDER[right.priority]);
12810
+ if (overlays.length === 0)
12811
+ return frame;
12812
+ const lines2 = [...frame.lines];
12813
+ while (lines2.length < this.#height)
12814
+ lines2.push("");
12815
+ const viewport = Math.max(0, lines2.length - this.#height);
12816
+ let cursorRow = frame.cursorRow;
12817
+ let cursorCol = frame.cursorCol;
12818
+ for (const entry of overlays) {
12819
+ const options = entry.options ?? {};
12820
+ const requested = typeof options.width === "string" ? this.#width * Number.parseFloat(options.width) / 100 : options.width ?? Math.min(80, this.#width);
12821
+ const width = Math.max(1, Math.min(this.#width, Number.isFinite(requested) ? Math.floor(requested) : this.#width));
12822
+ const rendered = entry.component.render({ width, theme });
12823
+ const height = Math.max(0, Math.min(this.#height, options.maxHeight ?? this.#height, rendered.lines.length));
12824
+ if (height === 0)
12825
+ continue;
12826
+ const row = Math.max(0, Math.min(this.#height - height, Math.floor(options.row ?? (this.#height - height) / 2)));
12827
+ const column = Math.max(0, Math.min(this.#width - width, Math.floor(options.column ?? (this.#width - width) / 2)));
12828
+ const content = compileStyledLines(rendered.lines.slice(0, height).map((value) => truncateStyledLine(value, width, "")), width, theme);
12829
+ for (let index = 0; index < height; index++) {
12830
+ const target = viewport + row + index;
12831
+ const before = sliceByColumn(lines2[target], 0, column);
12832
+ const after = sliceByColumn(lines2[target], column + width, this.#width - column - width);
12833
+ lines2[target] = before + TERMINAL_RESET + sliceByColumn(content[index], 0, width) + TERMINAL_RESET + after;
12834
+ }
12835
+ if (entry.component.focused) {
12836
+ cursorRow = viewport + row + Math.max(0, Math.min(height - 1, rendered.cursor?.row ?? 0));
12837
+ cursorCol = column + Math.max(0, Math.min(width - 1, rendered.cursor?.column ?? 0));
12838
+ }
12839
+ }
12840
+ return { ...frame, lines: lines2, activeStart: Math.min(frame.activeStart ?? lines2.length, viewport), cursorRow, cursorCol };
12841
+ }
12553
12842
  open(entry) {
12843
+ for (const value of [entry.options?.row, entry.options?.column, entry.options?.maxHeight]) {
12844
+ if (value !== void 0 && (!Number.isInteger(value) || value < 0))
12845
+ throw new Error("Overlay coordinates and height must be non-negative integers");
12846
+ }
12847
+ this.#hidden.delete(entry.id);
12554
12848
  const previous = this.#entries.filter((item) => item.id === entry.id);
12555
12849
  for (const item of previous) {
12556
12850
  item.component.focused = false;
@@ -12560,6 +12854,7 @@ var OverlayManager = class {
12560
12854
  this.#syncFocus();
12561
12855
  }
12562
12856
  close(id) {
12857
+ this.#hidden.delete(id);
12563
12858
  const closing = this.#entries.filter((entry) => entry.id === id);
12564
12859
  for (const entry of closing) {
12565
12860
  entry.component.focused = false;
@@ -12570,6 +12865,8 @@ var OverlayManager = class {
12570
12865
  top() {
12571
12866
  let top = null;
12572
12867
  for (const entry of this.#entries) {
12868
+ if (!this.#visible(entry) || entry.options?.nonCapturing)
12869
+ continue;
12573
12870
  if (top === null || PRIORITY_ORDER[entry.priority] >= PRIORITY_ORDER[top.priority]) {
12574
12871
  top = entry;
12575
12872
  }
@@ -12582,69 +12879,12 @@ var OverlayManager = class {
12582
12879
  entry.component.focused = entry === top;
12583
12880
  }
12584
12881
  }
12585
- };
12586
-
12587
- // ../../packages/terminal/tui/dist/tui/terminal-input-decoder.js
12588
- var TerminalInputDecoder = class {
12589
- #stdinBuffer = new StdinBuffer();
12590
- #filter;
12591
- #decoder = new RawInputDecoder();
12592
- constructor(hooks, options = {}) {
12593
- this.#filter = new TerminalInputFilter({
12594
- ...options,
12595
- enableModifyOtherKeys: () => {
12596
- hooks.enableModifyOtherKeys();
12597
- },
12598
- disableModifyOtherKeys: () => {
12599
- hooks.disableModifyOtherKeys();
12600
- }
12601
- });
12602
- }
12603
- get kittyProtocolActive() {
12604
- return this.#filter.kittyProtocolActive;
12605
- }
12606
- set kittyProtocolActive(value) {
12607
- this.#filter.kittyProtocolActive = value;
12608
- }
12609
- feed(data) {
12610
- const actions = [];
12611
- for (const event of this.#stdinBuffer.feed(data)) {
12612
- if (event.kind === BufferedInputKind.Paste) {
12613
- actions.push(inputAction(InputActionKind.Insert, event.data.toString("utf8")));
12614
- continue;
12615
- }
12616
- for (const sequence of this.#filter.feed(event.data)) {
12617
- actions.push(...this.#decoder.feed(sequence));
12618
- }
12619
- }
12620
- return actions;
12621
- }
12622
- flush() {
12623
- const actions = [];
12624
- for (const event of this.#stdinBuffer.flush()) {
12625
- if (event.kind === BufferedInputKind.Paste) {
12626
- actions.push(inputAction(InputActionKind.Insert, event.data.toString("utf8")));
12627
- continue;
12628
- }
12629
- for (const sequence of this.#filter.feed(event.data)) {
12630
- actions.push(...this.#decoder.feed(sequence));
12631
- }
12632
- }
12633
- for (const sequence of this.#filter.flush()) {
12634
- actions.push(...this.#decoder.feed(sequence));
12635
- }
12636
- actions.push(...this.#decoder.flush());
12637
- return actions;
12638
- }
12639
- clear() {
12640
- this.#stdinBuffer.clear();
12641
- this.#filter.clear();
12642
- this.#decoder = new RawInputDecoder();
12882
+ #visible(entry) {
12883
+ return !this.#hidden.has(entry.id) && (entry.options?.visible?.(this.#width, this.#height) ?? true);
12643
12884
  }
12644
12885
  };
12645
12886
 
12646
12887
  // ../../packages/terminal/tui/dist/tui/ui.js
12647
- var _a;
12648
12888
  var WELCOME_TEXT = "Welcome to LaoHuang Code!";
12649
12889
  var WELCOME_HELP_TEXT = "Send /help for help information.";
12650
12890
  var LocalMessage = class {
@@ -12757,8 +12997,7 @@ function editorActionForKey(key) {
12757
12997
  return inputAction(InputActionKind.Eof);
12758
12998
  return null;
12759
12999
  }
12760
- var InteractiveTerminalLoop = class {
12761
- static ESCAPE_TIMEOUT_MS = 50;
13000
+ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
12762
13001
  static WORK_QUEUE_LIMIT = 4096;
12763
13002
  #ui;
12764
13003
  #driver;
@@ -12782,6 +13021,8 @@ var InteractiveTerminalLoop = class {
12782
13021
  #escapeTimer = null;
12783
13022
  #wakeupEnabled = false;
12784
13023
  #wakeupScheduled = false;
13024
+ #renderTimer = null;
13025
+ #lastRenderAt = 0;
12785
13026
  #disposeResize = null;
12786
13027
  writeError = null;
12787
13028
  constructor(ui, driver, editor, decoderFactory) {
@@ -12826,7 +13067,7 @@ var InteractiveTerminalLoop = class {
12826
13067
  if (previous >= 0)
12827
13068
  this.#work.splice(previous, 1);
12828
13069
  }
12829
- if (this.#work.length >= _a.WORK_QUEUE_LIMIT - 128 && this.#ui.isHighFrequencyDisplayEvent(event)) {
13070
+ if (this.#work.length >= _InteractiveTerminalLoop.WORK_QUEUE_LIMIT - 128 && this.#ui.isHighFrequencyDisplayEvent(event)) {
12830
13071
  this.#ui.recordDisplayDrop(event);
12831
13072
  return;
12832
13073
  }
@@ -12842,10 +13083,19 @@ var InteractiveTerminalLoop = class {
12842
13083
  }
12843
13084
  /** Synchronously consume queued work; this is also the test hook. */
12844
13085
  drain() {
12845
- const changed = this.#drainWork();
12846
- const dropped = this.#ui.flushDisplayDropMarker();
12847
- if (changed || dropped || this.#needsRender) {
12848
- this.#render();
13086
+ if (this.#renderTimer !== null) {
13087
+ clearTimeout(this.#renderTimer);
13088
+ this.#renderTimer = null;
13089
+ }
13090
+ try {
13091
+ const changed = this.#drainWork();
13092
+ const dropped = this.#ui.flushDisplayDropMarker();
13093
+ if (changed || dropped || this.#needsRender) {
13094
+ this.#render();
13095
+ }
13096
+ } catch (error) {
13097
+ this.writeError = recordRenderFailure(error);
13098
+ this.requestExit();
12849
13099
  }
12850
13100
  }
12851
13101
  requestRender() {
@@ -12868,6 +13118,10 @@ var InteractiveTerminalLoop = class {
12868
13118
  focusedComponentId() {
12869
13119
  return this.#focus.current();
12870
13120
  }
13121
+ setOverlayHidden(id, hidden) {
13122
+ this.#overlays.setHidden(id, hidden);
13123
+ this.requestRender();
13124
+ }
12871
13125
  renderActiveView(context) {
12872
13126
  return this.#viewHost.render(context);
12873
13127
  }
@@ -12880,6 +13134,10 @@ var InteractiveTerminalLoop = class {
12880
13134
  return;
12881
13135
  }
12882
13136
  this.#closed = true;
13137
+ if (this.#renderTimer !== null) {
13138
+ clearTimeout(this.#renderTimer);
13139
+ this.#renderTimer = null;
13140
+ }
12883
13141
  this.#running = false;
12884
13142
  this.#cancelPendingViews();
12885
13143
  this.#viewHost.closeAll();
@@ -12888,7 +13146,12 @@ var InteractiveTerminalLoop = class {
12888
13146
  this.#escapeTimer = null;
12889
13147
  }
12890
13148
  this.#stopResizeWatcher();
12891
- this.#stopTerminalModes();
13149
+ try {
13150
+ this.#stopTerminalModes();
13151
+ } catch (error) {
13152
+ if (this.writeError === null)
13153
+ this.writeError = error;
13154
+ }
12892
13155
  try {
12893
13156
  this.#renderer.close();
12894
13157
  } catch (error) {
@@ -12903,17 +13166,32 @@ var InteractiveTerminalLoop = class {
12903
13166
  this.#driver.enterRawMode?.();
12904
13167
  this.startTerminalModes();
12905
13168
  const onData = (data) => {
12906
- this.feedInputBytes(data);
12907
- this.drain();
12908
- this.#armEscapeTimer();
13169
+ try {
13170
+ this.feedInputBytes(data);
13171
+ this.drain();
13172
+ this.#armEscapeTimer();
13173
+ } catch (error) {
13174
+ this.writeError = error;
13175
+ this.requestExit();
13176
+ }
12909
13177
  };
12910
13178
  const onEnd = () => {
12911
- this.#applyActions(this.#decoder.flush());
12912
- this.#applyEof();
12913
- this.drain();
13179
+ try {
13180
+ this.#applyActions(this.#decoder.flush());
13181
+ this.#applyEof();
13182
+ this.drain();
13183
+ } catch (error) {
13184
+ this.writeError = error;
13185
+ this.requestExit();
13186
+ }
13187
+ };
13188
+ const onError = (error) => {
13189
+ this.writeError = error;
13190
+ this.requestExit();
12914
13191
  };
12915
13192
  input?.on("data", onData);
12916
13193
  input?.on("end", onEnd);
13194
+ input?.addListener?.("error", onError);
12917
13195
  this.#wakeupEnabled = true;
12918
13196
  try {
12919
13197
  input?.resume?.();
@@ -12926,7 +13204,13 @@ var InteractiveTerminalLoop = class {
12926
13204
  this.#wakeupEnabled = false;
12927
13205
  input?.off?.("data", onData);
12928
13206
  input?.off?.("end", onEnd);
12929
- input?.pause?.();
13207
+ try {
13208
+ this.#stopTerminalModes();
13209
+ await drainTerminalInput(input);
13210
+ } finally {
13211
+ input?.pause?.();
13212
+ input?.removeListener?.("error", onError);
13213
+ }
12930
13214
  }
12931
13215
  } catch (error) {
12932
13216
  this.writeError = error;
@@ -12998,11 +13282,21 @@ var InteractiveTerminalLoop = class {
12998
13282
  if (this.#escapeTimer !== null) {
12999
13283
  clearTimeout(this.#escapeTimer);
13000
13284
  }
13285
+ const timeout = inputTimeout(this.#decoder.pendingKind?.() ?? "sequence");
13286
+ if (timeout === null) {
13287
+ this.#escapeTimer = null;
13288
+ return;
13289
+ }
13001
13290
  this.#escapeTimer = setTimeout(() => {
13002
13291
  this.#escapeTimer = null;
13003
- this.#applyActions(this.#decoder.flush());
13004
- this.drain();
13005
- }, _a.ESCAPE_TIMEOUT_MS);
13292
+ try {
13293
+ this.#applyActions(this.#decoder.flush());
13294
+ this.drain();
13295
+ } catch (error) {
13296
+ this.writeError = error;
13297
+ this.requestExit();
13298
+ }
13299
+ }, timeout);
13006
13300
  }
13007
13301
  /**
13008
13302
  * Production wakeup: while run() is active, queued work is rendered on the
@@ -13017,11 +13311,26 @@ var InteractiveTerminalLoop = class {
13017
13311
  this.#wakeupScheduled = true;
13018
13312
  setImmediate(() => {
13019
13313
  this.#wakeupScheduled = false;
13020
- this.drain();
13314
+ if (this.#closed || this.#renderTimer !== null)
13315
+ return;
13316
+ const delay2 = Math.max(0, 16 - (performance.now() - this.#lastRenderAt));
13317
+ this.#renderTimer = setTimeout(() => {
13318
+ this.#renderTimer = null;
13319
+ if (!this.#closed)
13320
+ this.drain();
13321
+ }, delay2);
13021
13322
  });
13022
13323
  }
13023
13324
  #drainWork() {
13024
13325
  let changed = false;
13326
+ let budget = 128;
13327
+ for (let index = this.#work.length - 1; index >= budget; index--) {
13328
+ if (this.#work[index]?.type === "input") {
13329
+ budget = index + 1;
13330
+ break;
13331
+ }
13332
+ }
13333
+ let processed = 0;
13025
13334
  let item = this.#work.shift();
13026
13335
  while (item !== void 0) {
13027
13336
  if (item.type === "input") {
@@ -13046,19 +13355,27 @@ var InteractiveTerminalLoop = class {
13046
13355
  this.#ui.applyProjectedEvent(item.event);
13047
13356
  changed = true;
13048
13357
  }
13358
+ processed++;
13359
+ if (this.#wakeupEnabled && processed >= budget && this.#work.length > 0) {
13360
+ this.#scheduleWakeup();
13361
+ break;
13362
+ }
13049
13363
  item = this.#work.shift();
13050
13364
  }
13051
13365
  return changed;
13052
13366
  }
13053
13367
  #render() {
13054
13368
  this.#needsRender = false;
13369
+ this.#lastRenderAt = performance.now();
13055
13370
  try {
13056
- this.#renderer.render(this.#ui.buildFrame({
13057
- width: Math.max(1, this.#driver.getSize().columns),
13371
+ const size = this.#driver.getSize();
13372
+ this.#overlays.setViewport(Math.max(1, size.columns - 1), Math.max(1, size.rows));
13373
+ this.#renderer.render(this.#overlays.composite(this.#ui.buildFrame({
13374
+ width: Math.max(1, size.columns),
13058
13375
  editor: this.#editor
13059
- }));
13376
+ }), this.#ui.theme));
13060
13377
  } catch (error) {
13061
- this.writeError = error;
13378
+ this.writeError = recordRenderFailure(error);
13062
13379
  this.requestExit();
13063
13380
  }
13064
13381
  }
@@ -13276,7 +13593,6 @@ var InteractiveTerminalLoop = class {
13276
13593
  this.#work = retained;
13277
13594
  }
13278
13595
  };
13279
- _a = InteractiveTerminalLoop;
13280
13596
  var FALLBACK_MARKDOWN_WIDTH = 80;
13281
13597
  function resolveFallbackMarkdownWidth() {
13282
13598
  const columns = process.stdout?.columns;
@@ -13572,7 +13888,7 @@ var TerminalUI = class {
13572
13888
  #buildHistoryFrameParts(width) {
13573
13889
  const rendered = this.#transcriptView.renderWithMetadata({ width, theme: this.theme });
13574
13890
  return {
13575
- lines: this.#lineCompiler.compile(rendered.lines, Math.max(12, width), this.theme),
13891
+ lines: this.#lineCompiler.compile(rendered.lines, Math.max(1, width), this.theme),
13576
13892
  activeStart: rendered.activeStart
13577
13893
  };
13578
13894
  }
@@ -13856,6 +14172,7 @@ var StdTerminalDriver = class {
13856
14172
  return;
13857
14173
  }
13858
14174
  this.#restoreRawMode ??= enterTerminalRawMode(process.stdin);
14175
+ refreshTerminalDimensions();
13859
14176
  }
13860
14177
  write(data) {
13861
14178
  if (this.#debugLogPath !== null) {
@@ -14267,7 +14584,7 @@ function resolveBashPath(options = {}) {
14267
14584
  // ../../packages/shell/bash-local/dist/bash-output.js
14268
14585
  import { mkdir as mkdir2, mkdtemp, open, readdir, rm, stat, writeFile as writeFile2 } from "node:fs/promises";
14269
14586
  import { homedir as homedir3 } from "node:os";
14270
- import { join as join8, resolve as resolve3 } from "node:path";
14587
+ import { join as join9, resolve as resolve3 } from "node:path";
14271
14588
  var BASH_OUTPUT_MAX_BYTES = 50 * 1024;
14272
14589
  var BASH_OUTPUT_MAX_LINES = 2e3;
14273
14590
  var OUTPUT_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -14317,7 +14634,7 @@ var BashOutput = class {
14317
14634
  if (!Number.isSafeInteger(limit) || limit < 1)
14318
14635
  throw new RangeError("Bash output limits must be positive integers");
14319
14636
  }
14320
- this.#directory = resolve3(options.outputDirectory ?? join8(homedir3(), ".laohuang", "tool-output"));
14637
+ this.#directory = resolve3(options.outputDirectory ?? join9(homedir3(), ".laohuang", "tool-output"));
14321
14638
  }
14322
14639
  append(stream, text2) {
14323
14640
  const next = this.#pending.then(() => this.#append(stream, text2));
@@ -14354,10 +14671,10 @@ var BashOutput = class {
14354
14671
  try {
14355
14672
  await mkdir2(this.#directory, { recursive: true, mode: 448 });
14356
14673
  await this.#removeExpired();
14357
- this.#archiveDirectory = await mkdtemp(join8(this.#directory, "bash-"));
14674
+ this.#archiveDirectory = await mkdtemp(join9(this.#directory, "bash-"));
14358
14675
  const files = {
14359
- stdout: join8(this.#archiveDirectory, "stdout.log"),
14360
- stderr: join8(this.#archiveDirectory, "stderr.log")
14676
+ stdout: join9(this.#archiveDirectory, "stdout.log"),
14677
+ stderr: join9(this.#archiveDirectory, "stderr.log")
14361
14678
  };
14362
14679
  for (const stream of ["stdout", "stderr"])
14363
14680
  this.#handles[stream] = await open(files[stream], "wx", 384);
@@ -14396,7 +14713,7 @@ var BashOutput = class {
14396
14713
  }
14397
14714
  if (this.#archiveDirectory !== null) {
14398
14715
  try {
14399
- await writeFile2(join8(this.#archiveDirectory, "completed"), "", { mode: 384 });
14716
+ await writeFile2(join9(this.#archiveDirectory, "completed"), "", { mode: 384 });
14400
14717
  } catch {
14401
14718
  }
14402
14719
  }
@@ -14405,9 +14722,9 @@ var BashOutput = class {
14405
14722
  for (const entry of await readdir(this.#directory, { withFileTypes: true })) {
14406
14723
  if (!entry.isDirectory() || !entry.name.startsWith("bash-"))
14407
14724
  continue;
14408
- const directory = join8(this.#directory, entry.name);
14725
+ const directory = join9(this.#directory, entry.name);
14409
14726
  try {
14410
- if (Date.now() - (await stat(join8(directory, "completed"))).mtimeMs > OUTPUT_RETENTION_MS) {
14727
+ if (Date.now() - (await stat(join9(directory, "completed"))).mtimeMs > OUTPUT_RETENTION_MS) {
14411
14728
  await rm(directory, { recursive: true, force: true });
14412
14729
  }
14413
14730
  } catch {
@@ -16972,7 +17289,7 @@ var SessionController = class {
16972
17289
 
16973
17290
  // src/create-mcp-runtime.ts
16974
17291
  import { randomUUID as randomUUID12 } from "node:crypto";
16975
- import { dirname as dirname6, join as join10, resolve as resolve6 } from "node:path";
17292
+ import { dirname as dirname6, join as join11, resolve as resolve6 } from "node:path";
16976
17293
 
16977
17294
  // ../../packages/core/mcp/dist/config.js
16978
17295
  import { resolve as resolve4 } from "node:path";
@@ -17243,7 +17560,7 @@ import { createHash as createHash6 } from "node:crypto";
17243
17560
  // ../../packages/core/mcp/dist/output.js
17244
17561
  import { randomUUID as randomUUID11 } from "node:crypto";
17245
17562
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
17246
- import { join as join9 } from "node:path";
17563
+ import { join as join10 } from "node:path";
17247
17564
  function bounded(result, limit) {
17248
17565
  if (Buffer.byteLength(JSON.stringify(result), "utf8") <= limit)
17249
17566
  return result;
@@ -17304,13 +17621,13 @@ async function adaptMcpResult(result, options) {
17304
17621
  if (!oversized && media.length === 0)
17305
17622
  return preview;
17306
17623
  try {
17307
- const directory = join9(options.artifactRoot, randomUUID11());
17624
+ const directory = join10(options.artifactRoot, randomUUID11());
17308
17625
  await mkdir3(directory, { recursive: true, mode: 448 });
17309
- const path5 = join9(directory, "result.json");
17626
+ const path5 = join10(directory, "result.json");
17310
17627
  await writeFile3(path5, JSON.stringify(business), { mode: 384, flag: "wx" });
17311
17628
  const attachments = [];
17312
17629
  for (const [index, item] of media.entries()) {
17313
- const path6 = join9(directory, `attachment-${index}.bin`);
17630
+ const path6 = join10(directory, `attachment-${index}.bin`);
17314
17631
  const decoded = Buffer.from(item.data, "base64");
17315
17632
  if (decoded.toString("base64").replace(/=+$/, "") !== item.data.replace(/=+$/, ""))
17316
17633
  throw new Error("Invalid base64 attachment");
@@ -18287,7 +18604,7 @@ var ConnectionManager = class {
18287
18604
  // src/create-mcp-runtime.ts
18288
18605
  async function createMcpRuntime(options) {
18289
18606
  const directory = dirname6(resolve6(options.configPath));
18290
- const paths = [.../* @__PURE__ */ new Set([join10(directory, "mcp.json"), resolve6(options.projectRoot, ".laohuang/mcp.json")])];
18607
+ const paths = [.../* @__PURE__ */ new Set([join11(directory, "mcp.json"), resolve6(options.projectRoot, ".laohuang/mcp.json")])];
18291
18608
  const readSources = async (reload) => {
18292
18609
  const sources = [];
18293
18610
  let invalid2 = false;
@@ -18317,8 +18634,8 @@ ${url}`, tone: "info" });
18317
18634
  projectRoot: options.projectRoot,
18318
18635
  version: options.version,
18319
18636
  env: options.env,
18320
- credentials: new McpCredentialFileStore(join10(directory, "mcp-credentials")),
18321
- artifactRoot: join10(directory, "mcp-artifacts", randomUUID12()),
18637
+ credentials: new McpCredentialFileStore(join11(directory, "mcp-credentials")),
18638
+ artifactRoot: join11(directory, "mcp-artifacts", randomUUID12()),
18322
18639
  onAuthorization: ({ serverId, url }) => showAuthorization(serverId, url),
18323
18640
  onLoginState: (active) => {
18324
18641
  if (active) process.on("SIGINT", cancel);
@@ -18902,12 +19219,12 @@ async function runTerminalUi(ui, driver, onSubmit) {
18902
19219
  // src/update-installation.ts
18903
19220
  import { spawn as spawn2 } from "node:child_process";
18904
19221
  import { lstat, readFile as readFile3, realpath as realpath2 } from "node:fs/promises";
18905
- import { basename as basename6, delimiter, dirname as dirname7, isAbsolute, join as join12, relative, resolve as resolve7, sep, win32 as win324 } from "node:path";
19222
+ import { basename as basename6, delimiter, dirname as dirname7, isAbsolute, join as join13, relative, resolve as resolve7, sep, win32 as win324 } from "node:path";
18906
19223
 
18907
19224
  // src/update.ts
18908
19225
  import { randomUUID as randomUUID13 } from "node:crypto";
18909
19226
  import { open as open2, readFile as readFile2, realpath, unlink as unlink3 } from "node:fs/promises";
18910
- import { join as join11 } from "node:path";
19227
+ import { join as join12 } from "node:path";
18911
19228
  function compareStableVersions(left, right) {
18912
19229
  const parse = (value) => {
18913
19230
  if (!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value) || value.includes("\n")) {
@@ -18933,7 +19250,7 @@ var defaultLockIO = {
18933
19250
  };
18934
19251
  async function installedVersion(installation) {
18935
19252
  if (await realpath(installation.packageRoot) !== installation.packageRoot) throw new Error("Installation target changed");
18936
- const value = JSON.parse(await readFile2(join11(installation.packageRoot, "package.json"), "utf8"));
19253
+ const value = JSON.parse(await readFile2(join12(installation.packageRoot, "package.json"), "utf8"));
18937
19254
  if (typeof value !== "object" || value === null || !("name" in value) || value.name !== "laohuang" || !("version" in value) || typeof value.version !== "string") {
18938
19255
  throw new Error("Installed laohuang package is missing or invalid");
18939
19256
  }
@@ -19133,9 +19450,9 @@ function createNpmRunner(cliPath, environ, options = {}) {
19133
19450
  };
19134
19451
  }
19135
19452
  async function npmCandidates(environ) {
19136
- const paths = [environ.npm_execpath, join12(dirname7(process.execPath), "node_modules/npm/bin/npm-cli.js"), join12(dirname7(process.execPath), "../lib/node_modules/npm/bin/npm-cli.js")];
19453
+ const paths = [environ.npm_execpath, join13(dirname7(process.execPath), "node_modules/npm/bin/npm-cli.js"), join13(dirname7(process.execPath), "../lib/node_modules/npm/bin/npm-cli.js")];
19137
19454
  for (const directory of (environ.PATH ?? environ.Path ?? "").split(delimiter).filter(Boolean)) {
19138
- paths.push(join12(directory, "npm"), join12(directory, "node_modules/npm/bin/npm-cli.js"));
19455
+ paths.push(join13(directory, "npm"), join13(directory, "node_modules/npm/bin/npm-cli.js"));
19139
19456
  }
19140
19457
  const result = /* @__PURE__ */ new Set();
19141
19458
  for (const path5 of paths) {
@@ -19143,7 +19460,7 @@ async function npmCandidates(environ) {
19143
19460
  try {
19144
19461
  const actual = await realpath2(path5);
19145
19462
  if (basename6(actual) !== "npm-cli.js") continue;
19146
- const manifest = await json(join12(dirname7(actual), "../package.json"));
19463
+ const manifest = await json(join13(dirname7(actual), "../package.json"));
19147
19464
  if (manifest.name === "npm") result.add(actual);
19148
19465
  } catch {
19149
19466
  }
@@ -19160,8 +19477,8 @@ async function resolveNpmInstallation(entryPath, environ, options = {}) {
19160
19477
  let packageRoot = dirname7(entry);
19161
19478
  let manifest = {};
19162
19479
  while (true) {
19163
- if (await exists(join12(packageRoot, "package.json"))) {
19164
- manifest = await json(join12(packageRoot, "package.json"));
19480
+ if (await exists(join13(packageRoot, "package.json"))) {
19481
+ manifest = await json(join13(packageRoot, "package.json"));
19165
19482
  break;
19166
19483
  }
19167
19484
  const parent = dirname7(packageRoot);
@@ -19190,7 +19507,7 @@ async function resolveNpmInstallation(entryPath, environ, options = {}) {
19190
19507
  if (checked.code !== 0 || await realpath2(checked.stdout.trim()) !== dirname7(packageRoot)) continue;
19191
19508
  globals.push({ packageRoot, installedVersion: manifest.version, npm: {
19192
19509
  run: (args, runOptions) => npm.run(args[0] === "view" ? [...args, "--global", "--prefix", prefix] : args, runOptions)
19193
- }, workingDirectory: prefix, installArgs: ["--global", "--prefix", prefix], lockPath: join12(dirname7(packageRoot), ".laohuang-update.lock") });
19510
+ }, workingDirectory: prefix, installArgs: ["--global", "--prefix", prefix], lockPath: join13(dirname7(packageRoot), ".laohuang-update.lock") });
19194
19511
  } catch {
19195
19512
  options.signal?.throwIfAborted();
19196
19513
  }
@@ -19202,12 +19519,12 @@ async function resolveNpmInstallation(entryPath, environ, options = {}) {
19202
19519
  const owners = [];
19203
19520
  for (let root = dirname7(dirname7(packageRoot)); ; root = dirname7(root)) {
19204
19521
  options.signal?.throwIfAborted();
19205
- const lockPath = join12(root, "package-lock.json");
19522
+ const lockPath = join13(root, "package-lock.json");
19206
19523
  if (await exists(lockPath)) {
19207
19524
  for (const other of ["pnpm-lock.yaml", "yarn.lock", "bun.lock", "bun.lockb", "npm-shrinkwrap.json"]) {
19208
- if (await exists(join12(root, other))) throw new Error("Ambiguous or unsupported package manager ownership");
19525
+ if (await exists(join13(root, other))) throw new Error("Ambiguous or unsupported package manager ownership");
19209
19526
  }
19210
- const rootManifest = await json(join12(root, "package.json"));
19527
+ const rootManifest = await json(join13(root, "package.json"));
19211
19528
  if (typeof rootManifest.packageManager === "string" && !rootManifest.packageManager.startsWith("npm@")) throw new Error("Installation belongs to another package manager");
19212
19529
  const lock = await json(lockPath);
19213
19530
  const packages = object(lock.packages);
@@ -19239,14 +19556,14 @@ async function resolveNpmInstallation(entryPath, environ, options = {}) {
19239
19556
  }
19240
19557
  const owner = await realpath2(resolve7(root, key));
19241
19558
  if (!inside(root, owner)) continue;
19242
- const declaration = await json(join12(owner, "package.json"));
19559
+ const declaration = await json(join13(owner, "package.json"));
19243
19560
  for (const bucket of ["dependencies", "devDependencies", "optionalDependencies"]) {
19244
19561
  const range = object(declaration[bucket]).laohuang;
19245
19562
  if (typeof range !== "string") continue;
19246
19563
  let resolvedPackage;
19247
19564
  for (let directory = owner; inside(root, directory); directory = dirname7(directory)) {
19248
- if (await exists(join12(directory, "node_modules/laohuang"))) {
19249
- resolvedPackage = await realpath2(join12(directory, "node_modules/laohuang"));
19565
+ if (await exists(join13(directory, "node_modules/laohuang"))) {
19566
+ resolvedPackage = await realpath2(join13(directory, "node_modules/laohuang"));
19250
19567
  break;
19251
19568
  }
19252
19569
  if (directory === root) break;
@@ -19263,7 +19580,7 @@ async function resolveNpmInstallation(entryPath, environ, options = {}) {
19263
19580
  },
19264
19581
  workingDirectory: root,
19265
19582
  installArgs: ["--global=false", "--prefix", root, "--package-lock=true", "--save=true", "--save-peer=false", "--save-dev=false", "--save-optional=false", "--save-prod=false", bucket === "devDependencies" ? "--save-dev" : bucket === "optionalDependencies" ? "--save-optional" : "--save-prod", ...bucket === "devDependencies" ? ["--include=dev"] : [], .../^[~^]/.test(range) ? ["--save-exact=false", `--save-prefix=${range[0]}`] : ["--save-exact"], ...key ? ["--workspace", key, "--include-workspace-root=false"] : ["--workspaces=false"]],
19266
- lockPath: join12(root, ".laohuang-update.lock")
19583
+ lockPath: join13(root, ".laohuang-update.lock")
19267
19584
  });
19268
19585
  }
19269
19586
  }
@@ -19517,10 +19834,10 @@ async function main(argv, options = {}) {
19517
19834
  const configPath = options.configPath ?? defaultConfigPath(environ);
19518
19835
  const manager = new ConfigManager(configPath);
19519
19836
  const credentials = new CredentialStore(
19520
- options.credentialsPath ?? join13(dirname8(configPath), "credentials.json")
19837
+ options.credentialsPath ?? join14(dirname8(configPath), "credentials.json")
19521
19838
  );
19522
19839
  const modelCatalogStore = new ModelCatalogStore(
19523
- options.modelsPath ?? join13(dirname8(configPath), "models.json")
19840
+ options.modelsPath ?? join14(dirname8(configPath), "models.json")
19524
19841
  );
19525
19842
  const modelPlatform = await createPiAiPlatform({
19526
19843
  credentials,
@@ -20143,7 +20460,7 @@ async function main(argv, options = {}) {
20143
20460
  }
20144
20461
  }
20145
20462
  function defaultSessionsRoot(environ) {
20146
- return join13(getHomeDirectory({ env: environ }), ".laohuang", "sessions");
20463
+ return join14(getHomeDirectory({ env: environ }), ".laohuang", "sessions");
20147
20464
  }
20148
20465
  function defaultContextPolicy() {
20149
20466
  return {