wezard 2.1.2 → 2.1.3

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.
@@ -1177,8 +1177,34 @@ export const parseInputBox = (cap) => {
1177
1177
  const body = [rows[0].replace(BOX_PROMPT_RE, ""), ...rows.slice(1)].join("\n").replace(/[\s\u200b\ufeff]+/gu, " ").trim();
1178
1178
  return { known: true, body };
1179
1179
  };
1180
+ // 空框里的灰字不是输入: 幽灵提示 (`Try "…"`) 和 CC 预测的下一句 (prompt suggestion,
1181
+ // 回车不采纳) 都以 SGR 2 (dim) 渲染。纯文本 capture 看不出区别 —— 预测句
1182
+ // `commit 这次改动…` 曾让一条 `commit` 被判成「已在框里」, 于是没贴, 只按了三下
1183
+ // 空回车, 消息就此消失。所以框一律带样式读, dim 段整段丢掉, 其余转义剥净。
1184
+ const SGR_SPLIT_RE = /\x1b\[([0-9;:]*)m/u;
1185
+ const OTHER_ESC_RE = /\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;?]*[A-Za-z]/gu;
1186
+ /** 一段 SGR 参数走完后 dim 的开关; 38/48/58 的颜色参数整段跳过, 免得 `38;5;2` 里的 2 被当成 dim。 */
1187
+ const dimAfter = (dim, ps) => {
1188
+ if (!ps.length)
1189
+ return dim;
1190
+ const [p, ...rest] = ps;
1191
+ if (p === "38" || p === "48" || p === "58")
1192
+ return dimAfter(dim, rest[0] === "5" ? rest.slice(2) : rest[0] === "2" ? rest.slice(4) : rest);
1193
+ return dimAfter(p === "" || p === "0" || p === "22" ? false : p === "2" ? true : dim, rest);
1194
+ };
1195
+ export const dropDim = (styled) => {
1196
+ // split 带捕获组: 偶数位是正文, 奇数位是 SGR 参数。
1197
+ const parts = styled.split(new RegExp(SGR_SPLIT_RE, "gu"));
1198
+ return parts
1199
+ .reduce((acc, part, i) => (i % 2 ? { ...acc, dim: dimAfter(acc.dim, part.split(/[;:]/u)) } : acc.dim ? acc : { ...acc, out: acc.out + part }), { dim: false, out: "" })
1200
+ .out.replace(OTHER_ESC_RE, "");
1201
+ };
1202
+ const captureUndimmed = async (target, rows) => {
1203
+ const r = await runTmux(["capture-pane", "-t", target, "-p", "-e", "-S", `-${rows}`]);
1204
+ return r.ok ? dropDim(r.stdout) : "";
1205
+ };
1180
1206
  // 24 行足够容纳展开后的多行粘贴 (折叠态只占一行)。
1181
- const readInputBox = async (target) => parseInputBox(await capturePaneTail(target, 24));
1207
+ const readInputBox = async (target) => parseInputBox(await captureUndimmed(target, 24));
1182
1208
  // ── 注入门控的会话证人 (cc-session) ─────────────────────────────────
1183
1209
  // 能从会话自己写的 json 读到的判据, 就不读 pane: 就绪看注册表的 status, 提交看
1184
1210
  // 注册表的 busy 翻转或 jsonl 里新落的那一行。输入框里「贴进去没有」是唯一只有
@@ -1379,7 +1405,7 @@ const injectViaTmuxText = async (target, text, log, freshSpawn, witness = NO_WIT
1379
1405
  const sawLanded = async (timeoutMs) => {
1380
1406
  const t0 = Date.now();
1381
1407
  while (Date.now() - t0 < timeoutMs) {
1382
- const cap = await capturePaneTail(target, 24);
1408
+ const cap = await captureUndimmed(target, 24);
1383
1409
  const box = parseInputBox(cap);
1384
1410
  if (boxVerify && box.known ? !boxIntact(box) : Boolean(headFp) && stripWs(cap).includes(headFp))
1385
1411
  return true;
@@ -1390,7 +1416,7 @@ const injectViaTmuxText = async (target, text, log, freshSpawn, witness = NO_WIT
1390
1416
  // 窄窗口 = 只有输入框本身。tailFp 紧挨光标, 提交前必在窗内、提交后消失。
1391
1417
  // 仅用于 parseInputBox 认不出布局的后端。
1392
1418
  const inputBoxStillHasTail = async () => {
1393
- const pane = await capturePaneTail(target, 5);
1419
+ const pane = await captureUndimmed(target, 5);
1394
1420
  return Boolean(tailFp) && stripWs(pane).includes(tailFp);
1395
1421
  };
1396
1422
  let pasteSeen = alreadyTyped;