fix(tui): excluded 4-digit hash colors from inline swatch parsing

- Updated hex color validation to accept only 3-, 6-, and 8-digit forms as swatchable.
- Adjusted strict prose filtering to gate only 3-digit values without hex letters.
- Added a regression test confirming 4-digit #TAG snapshot tags do not render swatches in prose or code spans.
This commit is contained in:
can1357
2026-06-02 04:38:29 +02:00
parent 8cd8fd9b70
commit c1f1e8022d
3 changed files with 20 additions and 5 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Stopped painting inline color swatches for 4-digit hex runs in Markdown rendering. The `#RGBA` CSS form collides with hashline `#TAG` snapshot tags (4 hex digits, e.g. `#6C5E`), which were sprouting spurious RGB swatches in prose and codespans. Only `#RGB`, `#RRGGBB`, and `#RRGGBBAA` qualify now.
## [15.7.6] - 2026-06-01
### Fixed
+7 -5
View File
@@ -146,22 +146,24 @@ const DEFAULT_COLOR_SWATCH_GLYPH = "■";
// entities like ☃ and paths like foo#fff) and not trailed by more hex
// (so over-long runs never produce a misleading swatch). Length/letter rules
// are enforced in classifyHexColor since the alternation can't express "exactly
// 3, 4, 6, or 8".
// 3, 6, or 8".
const HEX_COLOR_REGEX = /(?<![\w#&])#([0-9a-fA-F]{3,8})(?![0-9a-fA-F])/g;
const HEX_COLOR_EXACT_REGEX = /^#([0-9a-fA-F]{3,8})$/;
/**
* Decide whether a run of hex digits denotes a renderable CSS color.
*
* Only the canonical CSS lengths (#RGB, #RGBA, #RRGGBB, #RRGGBBAA) qualify. In
* `strict` mode (bare prose) a 3/4-digit run must contain a hex letter, so the
* Only the canonical CSS lengths (#RGB, #RRGGBB, #RRGGBBAA) qualify. The 4-digit
* #RGBA form is deliberately excluded: it collides with hashline `#TAG` snapshot
* tags (4 hex digits, e.g. #6C5E), which would otherwise sprout spurious swatches.
* In `strict` mode (bare prose) a 3-digit run must contain a hex letter, so the
* far more common short issue/PR references (#123, #1011) don't sprout swatches.
* Codespans opt out of strictness — the backticks already signal "this is a color".
*/
function classifyHexColor(hex: string, strict: boolean): boolean {
const n = hex.length;
if (n !== 3 && n !== 4 && n !== 6 && n !== 8) return false;
if (strict && n <= 4 && !/[a-fA-F]/.test(hex)) return false;
if (n !== 3 && n !== 6 && n !== 8) return false;
if (strict && n === 3 && !/[a-fA-F]/.test(hex)) return false;
return true;
}
+9
View File
@@ -1187,6 +1187,15 @@ describe("Inline color swatches", () => {
expect(out.includes(swatchFor("fff"))).toBeTruthy();
});
it("does not swatch 4-digit hashline #TAG snapshot tags", () => {
// Hashline tags are 4 hex digits with letters (e.g. #6C5E) and would
// otherwise be read as #RGBA colors. Neither prose nor codespans swatch them.
const prose = new Markdown("Re-anchor on #6C5E before editing.", 0, 0, defaultMarkdownTheme).render(80).join("");
expect(prose.includes("■")).toBe(false);
const code = new Markdown("Tag `#6C5E` stays plain.", 0, 0, defaultMarkdownTheme).render(80).join("");
expect(code.includes("■")).toBe(false);
});
it("uses the theme's colorSwatch symbol when provided", () => {
const themed = { ...defaultMarkdownTheme, symbols: { ...defaultMarkdownTheme.symbols, colorSwatch: "▢" } };
const out = new Markdown("Accent #C5FFD6.", 0, 0, themed).render(80).join("\n");