From eea9fb6b72a236cd52b10c5b0cd95f95fda42000 Mon Sep 17 00:00:00 2001 From: Yunfei He Date: Sun, 31 May 2026 20:59:55 +0800 Subject: [PATCH] fix(runtime): tokenizeAnsi('') returns a single empty text token, matching Ink (#95) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ink's tokenizeAnsi has no empty-string early return — '' falls through to the no-control-chars branch and yields [{type:'text', value:''}]. vue had an extra `if (text.length === 0) return []` guard that diverged from Ink at the tokenizer boundary. Removed it (the production caller sanitizeAnsi short-circuits '' before tokenizing, so no behavior changes downstream) and flipped the test. Co-authored-by: Claude Opus 4.8 (1M context) --- packages/runtime/src/paint/ansi-tokenizer.test.ts | 6 ++++-- packages/runtime/src/paint/ansi-tokenizer.ts | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/runtime/src/paint/ansi-tokenizer.test.ts b/packages/runtime/src/paint/ansi-tokenizer.test.ts index b345977..3f5c021 100644 --- a/packages/runtime/src/paint/ansi-tokenizer.test.ts +++ b/packages/runtime/src/paint/ansi-tokenizer.test.ts @@ -44,9 +44,11 @@ describe("ansi-tokenizer", () => { expect(tokens[0]!.type).toBe("csi"); }); - test("returns empty for empty string", () => { + test("empty string returns a single empty text token (Ink parity)", () => { + // Ink's tokenizeAnsi('') falls through to the no-control-chars branch and + // returns [{type:'text', value:''}] — it has no empty-string early return. const tokens = tokenizeAnsi(""); - expect(tokens).toHaveLength(0); + expect(tokens).toEqual([{ type: "text", value: "" }]); }); // --- Ink parity tests --- diff --git a/packages/runtime/src/paint/ansi-tokenizer.ts b/packages/runtime/src/paint/ansi-tokenizer.ts index f9567ed..fafddd8 100644 --- a/packages/runtime/src/paint/ansi-tokenizer.ts +++ b/packages/runtime/src/paint/ansi-tokenizer.ts @@ -322,10 +322,8 @@ const malformedFromIndex = ( }; export const tokenizeAnsi = (text: string): AnsiToken[] => { - if (text.length === 0) { - return []; - } - + // No empty-string early return: Ink falls through to the no-control-chars + // branch so tokenizeAnsi('') === [{type:'text', value:''}] (ansi-tokenizer.ts). if (!hasAnsiControlCharacters(text)) { return [{ type: "text", value: text }]; }