chore: gitignore esbuild test bundles and release dir

This commit is contained in:
954801926@qq.com committed 2026-09-06 20:16:23 +08:00
1 parent 0663e5affa
commit fa83097528
3 files changed
+6 -3607

No files matched your search

+6
View File
@@ -19,3 +19,9 @@ research/
*.tsbuildinfo
.sftp-test-*.bin
.sftp-dl-*
tests/.commands-store.cjs
tests/.session-e2e.cjs
tests/.hl-split-smoke.cjs
tests/.zmodem-e2e.cjs
release/
-236
View File
@@ -1,236 +0,0 @@
// src/renderer/src/terminal/highlightEngine.ts
var ESCAPE_RE = /(\x1b\[[0-9;:?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[()][0-9A-B]|\x1b[=>])/g;
var MAX_PER_RULE = 300;
var MAX_CHUNK = 512 * 1024;
function hexToRgb(hex) {
let h = (hex || "").trim().replace(/^#/, "");
if (h.length === 3) h = h.replace(/./g, (c) => c + c);
if (!/^[0-9a-fA-F]{6}$/.test(h)) return { r: 255, g: 255, b: 255 };
const n = parseInt(h, 16);
return { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
}
function tokenize(input) {
const out = [];
let acc = "";
let i = 0;
let m;
while ((m = ESCAPE_RE.exec(input)) !== null) {
if (m.index > i) acc += input.slice(i, m.index);
if (acc.length > 0) {
out.push({ kind: "text", value: acc });
acc = "";
}
out.push({ kind: "seq", value: m[0] });
i = m.index + m[0].length;
}
if (i < input.length) acc += input.slice(i);
if (acc.length > 0) out.push({ kind: "text", value: acc });
return out;
}
function compileRules(rules2) {
const out = [];
for (const rule of rules2) {
if (!rule || rule.enabled === false) continue;
const pattern = rule.pattern;
if (typeof pattern !== "string" || pattern.length === 0) continue;
let regex;
try {
regex = new RegExp(pattern, "g");
} catch {
continue;
}
const fg = typeof rule.color?.fg === "string" ? rule.color.fg : "";
if (!/^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$/.test(fg)) continue;
const bg = typeof rule.color?.bg === "string" ? rule.color.bg : void 0;
out.push({ id: rule.id, regex, fg, bg, priority: rule.priority });
}
out.sort((a, b) => a.priority - b.priority);
return out;
}
function wrap(text, fgHex, bgHex) {
const f = hexToRgb(fgHex);
let s = `\x1B[38;2;${f.r};${f.g};${f.b}`;
if (bgHex) {
const b = hexToRgb(bgHex);
s += `;48;2;${b.r};${b.g};${b.b}`;
}
return `${s}m${text}\x1B[0m`;
}
function overlaps(claimed, s, e) {
for (const c of claimed) {
if (s < c.end && e > c.start) return true;
}
return false;
}
function applyRun(text, rules2, budgets) {
if (text.length === 0) return "";
const claimed = [];
for (let ri = 0; ri < rules2.length; ri++) {
const rule = rules2[ri];
if (budgets[ri] <= 0) continue;
rule.regex.lastIndex = 0;
let m;
let made = 0;
while ((m = rule.regex.exec(text)) !== null) {
const start = m.index;
const end = start + m[0].length;
if (end <= start) {
rule.regex.lastIndex++;
continue;
}
if (!overlaps(claimed, start, end)) {
claimed.push({ start, end, fg: rule.fg, bg: rule.bg });
made++;
budgets[ri]--;
if (budgets[ri] <= 0) break;
}
if (rule.regex.lastIndex === start) rule.regex.lastIndex++;
}
}
if (claimed.length === 0) return text;
claimed.sort((a, b) => a.start - b.start);
let out = "";
let pos = 0;
for (const c of claimed) {
if (c.start > pos) out += text.slice(pos, c.start);
out += wrap(text.slice(c.start, c.end), c.fg, c.bg);
pos = c.end;
}
if (pos < text.length) out += text.slice(pos);
return out;
}
function applyHighlights(chunk, rules2) {
if (!chunk || rules2.length === 0) return chunk;
if (chunk.length > MAX_CHUNK) return chunk;
let budgets = rules2.map(() => MAX_PER_RULE);
let usedBudget = false;
let out = "";
for (const token of tokenize(chunk)) {
if (token.kind === "seq") {
out += token.value;
continue;
}
if (token.value.includes("\x1B")) {
out += token.value;
continue;
}
const before = budgets.reduce((a, b) => a + b, 0);
out += applyRun(token.value, rules2, budgets);
const after = budgets.reduce((a, b) => a + b, 0);
if (after < before) usedBudget = true;
}
return usedBudget ? out : chunk;
}
function isIncompleteEscape(seg) {
if (seg === "\x1B") return true;
if (/^\x1b\[[0-9;:?]*$/.test(seg)) return true;
if (seg.startsWith("\x1B]") && !seg.includes("\x07") && !seg.includes("\x1B\\")) return true;
return false;
}
function lastEscapeEnd(input) {
let end = -1;
let m;
ESCAPE_RE.lastIndex = 0;
while ((m = ESCAPE_RE.exec(input)) !== null) end = m.index + m[0].length;
return end;
}
function findIncompleteIndex(input) {
const idx = input.lastIndexOf("\x1B");
if (idx < 0) return -1;
return isIncompleteEscape(input.slice(idx)) ? idx : -1;
}
var MAX_CARRY = 1024;
var HighlightStream = class {
rules;
buffer = "";
constructor(rules2) {
this.rules = rules2;
}
/** Swap the active rules without touching any buffered content. */
setRules(rules2) {
this.rules = rules2;
}
/** True while content is held back (a pending flush will release it). */
hasPending() {
return this.buffer.length > 0;
}
/** Join held bytes with `chunk`, return content safe to hand to xterm. */
push(chunk) {
this.buffer += chunk;
const incompleteAt = findIncompleteIndex(this.buffer);
if (incompleteAt >= 0) {
const head2 = this.buffer.slice(0, incompleteAt);
const tail = this.buffer.slice(incompleteAt);
if (tail.length > MAX_CARRY) {
this.buffer = "";
return applyHighlights(head2, this.rules) + tail;
}
this.buffer = tail;
return applyHighlights(head2, this.rules);
}
const lastEnd = lastEscapeEnd(this.buffer);
if (lastEnd < 0) {
if (this.buffer.length > MAX_CHUNK) {
const raw = this.buffer;
this.buffer = "";
return applyHighlights(raw, this.rules);
}
return "";
}
const head = this.buffer.slice(0, lastEnd);
this.buffer = this.buffer.slice(lastEnd);
return applyHighlights(head, this.rules);
}
/** Flush any held content (session end) — highlighted as a final run. */
flush() {
const b = this.buffer;
this.buffer = "";
return applyHighlights(b, this.rules);
}
};
// tests/hl-split-smoke.mjs
var rules = compileRules([
{ id: "okstate", pattern: "\\b(SUCCESS|PASS|OK|DONE)\\b", enabled: true, priority: 5, color: { fg: "#3fb950" } },
{ id: "badstate", pattern: "\\b(ERROR|FAILED)\\b", enabled: true, priority: 6, color: { fg: "#f85149" } },
{ id: "numbers", pattern: "\\b\\d+(?:\\.\\d+)?(?:%)?\\b", enabled: true, priority: 25, color: { fg: "#f2cc60" } },
{ id: "url", pattern: "https?://[^\\s]+", enabled: true, priority: 30, color: { fg: "#58a6ff" } }
]);
var banner = "\x1B]0;kimi \u2014 session\x07\x1B[38;2;79;168;255m\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\x1B[0m\r\n\x1B[38;2;136;136;136mWelcome to Kimi Code!\x1B[0m \x1B[38;2;79;168;255m\u2502\x1B[0m send /help\r\n\x1B[38;2;224;224;224mModel: MiniMax-M3 0.41.038 ERROR 99% https://example.com/x\x1B[0m\r\n";
var reference = applyHighlights(banner, rules);
var failed = 0;
var splitAt = (s, i) => [s.slice(0, i), s.slice(i)];
for (let i = 1; i < banner.length - 1; i++) {
const [a, b] = splitAt(banner, i);
const stream = new HighlightStream(rules);
const out = stream.push(a) + stream.push(b) + stream.flush();
if (out !== reference) {
failed++;
if (failed <= 3) {
console.log(`MISMATCH at split ${i}:`);
console.log(" got: " + JSON.stringify(out.slice(0, 120)));
console.log(" expected: " + JSON.stringify(reference.slice(0, 120)));
}
}
}
{
const i = Math.floor(banner.length / 3);
const j = Math.floor(banner.length * 2 / 3);
const stream = new HighlightStream(rules);
const out = stream.push(banner.slice(0, i)) + stream.push(banner.slice(i, j)) + stream.push(banner.slice(j)) + stream.flush();
if (out !== reference) failed++;
}
{
const stream = new HighlightStream(rules);
let out = "";
for (const ch of banner) out += stream.push(ch);
out += stream.flush();
if (out !== reference) failed++;
}
if (failed === 0) {
console.log("SPLIT-SMOKE PASS: all split points byte-identical to single-chunk reference");
process.exit(0);
} else {
console.log(`SPLIT-SMOKE FAIL: ${failed} mismatches`);
process.exit(1);
}
File diff suppressed because it is too large. Load diff