Skip to content

Commit b315f45

Browse files
committed
feat(chat): a font-independent wordmark for the empty state
Replaces the pixel-art mark from #82, which shattered into disconnected bars in the editor. WHY THIS ONE SURVIVES WHERE THAT ONE DID NOT. The old mark drew letterforms out of `█`, which only works where the font FILLS the character cell — a property of the font, not the character, and one Monaco does not have. This mark asks for neither: the frame is built from box-drawing rules, which are CONNECTOR glyphs designed to join across cells in every monospace family, and the letters are real text rather than pixels. The `▁▃▅█` ramp is the only block run left, and it is a decorative ascent — "level" made literal — so a font that renders it slightly short costs nothing. Verified before shipping this time, which is the part I skipped last time: rendered in Monaco, SF Mono, Menlo, Courier New, Andale Mono, Consolas and the generic fallback, plus the real empty state in Monaco in both themes. Legible in all seven. Monaco leaves hairline gaps at the frame's corners and nothing else. The mark is 32 columns where the old one was 41, so the container-query factor moves with it: clamp(5px, 3.6cqi, 13px) -> clamp(5px, 4.6cqi, 13px). Those two numbers are halves of one value — 41 x 3.6 and 32 x 4.6 both land the mark at ~88% of its container — and the test now pins the RELATIONSHIP rather than either number, because changing the art without the factor is the way this breaks: widen it and it overflows, narrow it and it becomes a stamp floating in white space. The failure message reports the computed fill percentage. The old guard, which required full blocks only, is gone. It encoded a false belief as a rule and would have blocked this fix while waving through the bug it was written for. Guards, each bypass-verified by reverting the fix: - the cqi left at the old 41-column value (69% fill — a stamp) - the cqi raised too far (115%) and the art widened without it (127%) - sizing no longer container-relative at all - role="img" dropped; the accessible name dropped 33 tests in webviewCss, 34 suites green.
1 parent 9f4ff5d commit b315f45

2 files changed

Lines changed: 34 additions & 26 deletions

File tree

extensions/levelcode-ai/media/chat.html

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@
767767
/* font-size is sized off the CONTAINER width (cqi) — reliable in a webview where vw = the whole
768768
editor, not this panel — so the wordmark always fits and the last glyph is never clipped.
769769
line-height:1 makes the box-drawing rows connect into solid letters. */
770-
#empty .lc-ascii { font-family: var(--vscode-editor-font-family, ui-monospace, monospace); font-size: clamp(5px, 3.6cqi, 13px); line-height: 1; white-space: pre; color: var(--vscode-terminal-ansiGreen, #98c379); opacity: 1; width: max-content; max-width: 100%; margin: 2px auto 6px; overflow-x: auto; user-select: none; }
770+
#empty .lc-ascii { font-family: var(--vscode-editor-font-family, ui-monospace, monospace); font-size: clamp(5px, 4.6cqi, 13px); line-height: 1; white-space: pre; color: var(--vscode-terminal-ansiGreen, #98c379); opacity: 1; width: max-content; max-width: 100%; margin: 2px auto 6px; overflow-x: auto; user-select: none; }
771771
#empty .lc-ascii-sub { font-family: var(--vscode-editor-font-family, monospace); font-size: 11px; letter-spacing: .22em; color: var(--muted); opacity: .9; margin-bottom: 14px; }
772772
#empty .starters { display: flex; flex-wrap: wrap; gap: 7px; justify-content: center; margin-top: 14px; }
773773
#empty .starter { cursor: pointer; border: 1px solid var(--border); border-radius: 14px; padding: 4px 11px; font-size: 11.5px; background: var(--field-bg); color: var(--vscode-foreground); opacity: .95; }
@@ -1259,18 +1259,11 @@
12591259
<body>
12601260
<div id="log">
12611261
<div id="empty">
1262-
<div class="lc-ascii-wrap"><pre class="lc-ascii" role="img" aria-label="LevelCode AI">██╗ ███████╗██╗ ██╗███████╗██╗
1263-
██║ ██╔════╝██║ ██║██╔════╝██║
1264-
██║ █████╗ ██║ ██║█████╗ ██║
1265-
██║ ██╔══╝ ╚██╗ ██╔╝██╔══╝ ██║
1266-
███████╗███████╗ ╚████╔╝ ███████╗███████╗
1267-
╚══════╝╚══════╝ ╚═══╝ ╚══════╝╚══════╝
1268-
██████╗ ██████╗ ██████╗ ███████╗
1269-
██╔════╝██╔═══██╗██╔══██╗██╔════╝
1270-
██║ ██║ ██║██║ ██║█████╗
1271-
██║ ██║ ██║██║ ██║██╔══╝
1272-
╚██████╗╚██████╔╝██████╔╝███████╗
1273-
╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝</pre></div>
1262+
<div class="lc-ascii-wrap"><pre class="lc-ascii" role="img" aria-label="LevelCode AI">╭──────────────────────────────╮
1263+
│ │
1264+
│ ▁▃▅█ L E V E L C O D E │
1265+
│ │
1266+
╰──────────────────────────────╯</pre></div>
12741267
<div class="lc-ascii-sub">levelcode.ai</div>
12751268
<div class="empty-copy">Ask about your code, or describe what to build. The open file is sent as context; add a selection with <b>+</b> or ⌥⌘A.</div>
12761269
<div class="starters">

extensions/levelcode-ai/test/webviewCss.test.js

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -578,25 +578,40 @@ test('TRANSCRIPT: the user bubble hugs its content, and is capped short of the c
578578
'tint is the secondary cue and still earns its place — side alone would drop on a wrapped log');
579579
});
580580

581-
test('WORDMARK: the empty-state logo fits its container and has an accessible name', () => {
582-
// NOT asserted here: which characters it is drawn from. A previous version of this test required
583-
// full blocks only, on the theory that `█` tiles seamlessly while `▀`/`▄` can seam. Half of that is
584-
// right — `▀` above `▄` does leave a gap — but the other half is not: whether `█` FILLS its cell is
585-
// a property of the FONT, not of the character. In Monaco it does not, and a wordmark built on that
581+
test('WORDMARK: the mark and its cqi scale factor stay in agreement', () => {
582+
// NOT asserted here: which characters it is drawn from. A previous version required full blocks
583+
// only, on the theory that `█` tiles seamlessly while `▀`/`▄` can seam. Half of that is right —
584+
// `▀` above `▄` does leave a gap — but the other half is not: whether `█` FILLS its cell is a
585+
// property of the FONT, not of the character. In Monaco it does not, and a wordmark built on that
586586
// assumption shattered into disconnected bars in the editor while looking perfect in a harness
587-
// running SF Mono. The lesson is that this file cannot check the thing that actually matters, so it
588-
// should stop pretending to; ASCII art has to be looked at in the target font.
587+
// running SF Mono.
588+
//
589+
// The current mark sidesteps that entirely: box-drawing rules are CONNECTOR glyphs that join in
590+
// every monospace family, and the letters are real text rather than pixel art. Verified by
591+
// rendering it in Monaco, SF Mono, Menlo, Courier New, Andale Mono, Consolas and the generic
592+
// fallback — legible in all seven. That check cannot live in this file; ASCII art has to be looked
593+
// at in the target font.
589594
const m = /<pre class="lc-ascii"[^>]*>([\s\S]*?)<\/pre>/.exec(html);
590595
assert.ok(m, 'the empty-state wordmark is gone');
591596
const art = m[1].replace(/^\n/, '');
592-
593-
// This bound IS checkable and is not about glyphs: the logo is sized from the container
594-
// (clamp(5px, 3.6cqi, 13px)) inside #empty, which is capped at 560px. Past ~44 columns it stops
595-
// fitting and the pre grows a horizontal scrollbar under the logo.
596597
const lines = art.split('\n');
597598
const cols = Math.max(...lines.map((l) => l.length));
598-
assert.ok(cols <= 44, 'the wordmark is ' + cols + ' columns; wider than ~44 overflows #empty (max 560px)');
599-
assert.ok(lines.length <= 14, 'the wordmark is ' + lines.length + ' lines; it has to leave room for the prompt beneath it');
599+
600+
// What IS checkable, and the thing most likely to be got wrong: the mark is sized from the
601+
// container, so its WIDTH IN COLUMNS and the cqi factor are two halves of one number. Widen the art
602+
// without lowering the factor and it overflows; narrow it without raising the factor and it shrinks
603+
// to a stamp floating in white space. The 41-column mark used 3.6cqi; this 32-column one uses 4.6
604+
// precisely to land in the same place.
605+
const cqi = /\.lc-ascii[^{]*\{[^}]*font-size:\s*clamp\(\s*\d+px\s*,\s*([\d.]+)cqi/.exec(css);
606+
assert.ok(cqi, 'the wordmark is no longer sized from its container');
607+
// A monospace cell is ~0.6em wide, so the mark occupies cols * 0.6 * (cqi/100) of the container.
608+
const fill = cols * 0.6 * Number(cqi[1]) / 100;
609+
assert.ok(fill > 0.8 && fill < 0.95,
610+
'the mark would fill ' + Math.round(fill * 100) + '% of its container — ' + cols + ' columns at '
611+
+ cqi[1] + 'cqi. Below ~80% it reads as a stamp; above ~95% it touches the edges and can overflow.');
612+
613+
assert.ok(lines.length <= 14,
614+
'the wordmark is ' + lines.length + ' lines; it has to leave room for the prompt and starters beneath it');
600615

601616
// The accessible name is the whole reason a picture made of text is not a wall of noise to a
602617
// screen reader.

0 commit comments

Comments
 (0)