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.
- package/dist/daemon/mirror-bridge.js +29 -3
- package/dist/daemon/mirror-bridge.js.map +1 -1
- package/dist/shared/role-view.js +21 -3
- package/dist/shared/role-view.js.map +1 -1
- package/dist/shared/session-label.js +1 -1
- package/dist/shared/session-label.js.map +1 -1
- package/package.json +1 -1
- package/web/chat.css +15 -1
- package/web/chat.js +177 -71
|
@@ -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
|
|
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
|
|
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
|
|
1419
|
+
const pane = await captureUndimmed(target, 5);
|
|
1394
1420
|
return Boolean(tailFp) && stripWs(pane).includes(tailFp);
|
|
1395
1421
|
};
|
|
1396
1422
|
let pasteSeen = alreadyTyped;
|