From d491a755c2b543426c78d9b95ff5a28b87e5e54c Mon Sep 17 00:00:00 2001 From: kevin9327 <5299031+kevin9327@users.noreply.github.com> Date: Tue, 29 Sep 2026 06:38:15 +0900 Subject: [PATCH] Keep a one-letter or one-digit value instead of dropping it as a bullet The last pass of mergeText drops every text block of one character, to spend no tokens on list bullets and separators. A table cell is a block of its own, so a value that is a single digit or letter went too: the Wikipedia infobox for Barack Obama reads "Children" and then "Parents", with the 2 in between gone, and a grade, a count, or a one-character CJK word in a cell vanished the same way. A one-character block is now dropped only when it is not a letter or a digit. Bullets, pipes, and middle dots are punctuation or symbols, so they still go. Co-Authored-By: Claude Opus 5.5 --- src/distill.js | 6 ++++-- tests/distill.test.js | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/distill.js b/src/distill.js index d9274c1..b686a10 100644 --- a/src/distill.js +++ b/src/distill.js @@ -1445,9 +1445,11 @@ function mergeText(blocks) { out.push(b); } } - // Single stray characters (list bullets, separators) cost tokens and say nothing. + // Single stray characters (list bullets, separators) cost tokens and say + // nothing. A single letter or digit is a value, though: a table cell holds + // one as a block of its own, and an infobox read "Children" with no number. return out .filter((b) => b.type !== 'break') - .filter((b) => b.type !== 'text' || b.text.length > 1) + .filter((b) => b.type !== 'text' || b.text.length > 1 || /[\p{L}\p{N}]/u.test(b.text)) .map(({ host, pre, post, ...block }) => block); } diff --git a/tests/distill.test.js b/tests/distill.test.js index d365fda..6ad47f7 100644 --- a/tests/distill.test.js +++ b/tests/distill.test.js @@ -347,6 +347,23 @@ test('an entity does not put spaces inside a word', () => { assert.ok(text.includes('interface & its restraint'), 'a real space was swallowed'); }); +test('a one-character value is kept, a lone bullet or separator is not', () => { + // A Wikipedia infobox puts each value in a cell of its own, so a single + // digit is a whole block: "Children" was printed with no number after it. + const p = distill(` + + + + +
SpouseMichelle Robinson
Children2
GradeA
Available是
`, 'https://example.test/'); + const texts = p.blocks.map((b) => b.text); + const after = (label) => texts[texts.indexOf(label) + 1]; + assert.equal(after('Children'), '2', `the value went missing: ${JSON.stringify(texts)}`); + assert.equal(after('Grade'), 'A'); + assert.equal(after('Available'), '是'); + assert.ok(!texts.includes('|') && !texts.includes('•'), 'a separator was kept as content'); +}); + test('an icon button is named by its aria-label, a nameless one is dropped', () => { const blocks = social().blocks; assert.ok(blocks.some((b) => b.type === 'button' && b.text === 'Follow'), 'a labelled button went missing');