Repository navigation
feat(playground): line-number gutter, Hack font, string literals - #23
Conversation
The CSS already asked for JetBrains Mono and Fira Code but loaded neither, so every visitor saw a fallback. Ship Hack woff2 (regular + bold, MIT license included) with @font-face and put it first in the mono stacks for editor, output, and autocomplete.
buildEditorFragment now splits quoted spans before emote substitution and wraps them in a .str span. The quote pairing mirrors the lexer's readString: no escapes, newlines allowed, unterminated runs to EOF. Emote names still render as images inside strings, as before. editorSource/forEachPiece/setCaretOffset already recurse into element nodes, so the span wrapper needs no caret-math changes.
The editor gets a real text-editor gutter: numbers derived from the source (never the DOM), vertical scroll mirrored from the editor, horizontal scrolls left alone, and clicking a number places the caret at that line's first character. applySnapshot updates the gutter too, so undo/redo keep the count honest. Two layout fixes make internal scrolling possible at all: main's grid row is capped with minmax(0,1fr) and the wide layout gives body a definite height, so long source scrolls inside the pane instead of stretching the page and dragging the gutter away. The editor switches from pre-wrap to pre for the same reason - a wrapped line would span several rows and desynchronize the numbers.
|
Self-review of the stacked diff, same drill as #20. Verified against a local server in a real browser: gutter row one and text line one share an exact y-coordinate; line counts track typing, undo/redo round trips, and example loads; click-to-line lands on exact source offsets; source extraction round-trips through string spans and emote images unchanged. Found four things. All are small, so they're fixed in this PR rather than deferred:
Known limits, left alone on purpose: verification ran in one Chromium engine, no Firefox/Safari pass; the gutter width jumps one notch when the count crosses 9 -> 10 lines; there's no active-line marker yet; dragging from the gutter to select a range isn't supported. |
- pair strings the way the lexer does: #-to-EOL comment regions are carved out first, so a quote in a comment can no longer open a phantom string across real code; comments render muted, emotes inside them still render as images - ignore gutter clicks on empty space below the last number instead of teleporting the caret to line 1 - resync gutter scroll after undo/redo restores, for engines that skip the scroll event when clamping scrollTop - drop hack-bold.woff2: no stylesheet requests bold monospace Found via the self-review posted on #23.
* feat(playground): manual undo/redo for the editor
normalize() rebuilds the DOM after every input event, so the browser's
native undo history never survives; ctrl+z did nothing. Keep our own
stack instead of pulling in an editor component:
- snapshot commits land 400ms after the last mutation and store the
previous {source, caretOffset}, so one step rewinds a typed burst
- ctrl/cmd+z undoes, ctrl/cmd+shift+z and ctrl+y redo; the combos are
always swallowed since native undo would corrupt the rebuilt DOM
- programmatic replaces (autocomplete accept, example load) reset the
stack, as sketched in #18
- depth capped at 200 snapshots
Closes #18
* fix(playground): guard undo commit against a missing baseline
Self-review found committed is only initialized because loadExample
runs synchronously at startup; make commitUndoPoint adopt the current
state instead of throwing if that init ever moves.
* feat(playground): line-number gutter, Hack font, string literals (#23)
* feat(playground): vendor Hack as the editor font
The CSS already asked for JetBrains Mono and Fira Code but loaded
neither, so every visitor saw a fallback. Ship Hack woff2 (regular +
bold, MIT license included) with @font-face and put it first in the
mono stacks for editor, output, and autocomplete.
* feat(playground): highlight string literals in the editor (#21)
buildEditorFragment now splits quoted spans before emote substitution
and wraps them in a .str span. The quote pairing mirrors the lexer's
readString: no escapes, newlines allowed, unterminated runs to EOF.
Emote names still render as images inside strings, as before.
editorSource/forEachPiece/setCaretOffset already recurse into element
nodes, so the span wrapper needs no caret-math changes.
* feat(playground): line-number gutter with click-to-line
The editor gets a real text-editor gutter: numbers derived from the
source (never the DOM), vertical scroll mirrored from the editor,
horizontal scrolls left alone, and clicking a number places the caret
at that line's first character. applySnapshot updates the gutter too,
so undo/redo keep the count honest.
Two layout fixes make internal scrolling possible at all: main's grid
row is capped with minmax(0,1fr) and the wide layout gives body a
definite height, so long source scrolls inside the pane instead of
stretching the page and dragging the gutter away. The editor switches
from pre-wrap to pre for the same reason - a wrapped line would span
several rows and desynchronize the numbers.
* fix(playground): address self-review findings on the editor work
- pair strings the way the lexer does: #-to-EOL comment regions are
carved out first, so a quote in a comment can no longer open a
phantom string across real code; comments render muted, emotes
inside them still render as images
- ignore gutter clicks on empty space below the last number instead
of teleporting the caret to line 1
- resync gutter scroll after undo/redo restores, for engines that
skip the scroll event when clamping scrollTop
- drop hack-bold.woff2: no stylesheet requests bold monospace
Found via the self-review posted on #23.
…releases (#25) * feat(playground): manual undo/redo for the editor normalize() rebuilds the DOM after every input event, so the browser's native undo history never survives; ctrl+z did nothing. Keep our own stack instead of pulling in an editor component: - snapshot commits land 400ms after the last mutation and store the previous {source, caretOffset}, so one step rewinds a typed burst - ctrl/cmd+z undoes, ctrl/cmd+shift+z and ctrl+y redo; the combos are always swallowed since native undo would corrupt the rebuilt DOM - programmatic replaces (autocomplete accept, example load) reset the stack, as sketched in #18 - depth capped at 200 snapshots Closes #18 * fix(playground): guard undo commit against a missing baseline Self-review found committed is only initialized because loadExample runs synchronously at startup; make commitUndoPoint adopt the current state instead of throwing if that init ever moves. * feat(playground): vendor Hack as the editor font The CSS already asked for JetBrains Mono and Fira Code but loaded neither, so every visitor saw a fallback. Ship Hack woff2 (regular + bold, MIT license included) with @font-face and put it first in the mono stacks for editor, output, and autocomplete. * feat(playground): highlight string literals in the editor (#21) buildEditorFragment now splits quoted spans before emote substitution and wraps them in a .str span. The quote pairing mirrors the lexer's readString: no escapes, newlines allowed, unterminated runs to EOF. Emote names still render as images inside strings, as before. editorSource/forEachPiece/setCaretOffset already recurse into element nodes, so the span wrapper needs no caret-math changes. * feat(playground): line-number gutter with click-to-line The editor gets a real text-editor gutter: numbers derived from the source (never the DOM), vertical scroll mirrored from the editor, horizontal scrolls left alone, and clicking a number places the caret at that line's first character. applySnapshot updates the gutter too, so undo/redo keep the count honest. Two layout fixes make internal scrolling possible at all: main's grid row is capped with minmax(0,1fr) and the wide layout gives body a definite height, so long source scrolls inside the pane instead of stretching the page and dragging the gutter away. The editor switches from pre-wrap to pre for the same reason - a wrapped line would span several rows and desynchronize the numbers. * fix(playground): address self-review findings on the editor work - pair strings the way the lexer does: #-to-EOL comment regions are carved out first, so a quote in a comment can no longer open a phantom string across real code; comments render muted, emotes inside them still render as images - ignore gutter clicks on empty space below the last number instead of teleporting the caret to line 1 - resync gutter scroll after undo/redo restores, for engines that skip the scroll event when clamping scrollTop - drop hack-bold.woff2: no stylesheet requests bold monospace Found via the self-review posted on #23. * fix(ci): only rebuild the pages site when its inputs change * fix(ci): stop cutting releases on docs-only pushes to main * fix(ci): skip tests on docs-only changes and add gofmt/vet job
Stacked on #20; merge order is #20 then this. Three related editor upgrades:
Line-number gutter. Numbers are derived from the source text, never from the DOM, and the gutter mirrors only the editor's vertical scroll (wide lines scroll horizontally under fixed numbers). Clicking a number places the caret at that line's start. Getting here surfaced two layout bugs worth knowing about: main's auto-sized grid row let long source stretch the whole page instead of scrolling inside the pane, and body's min-height gave flex nothing definite to hand down. The wide layout now caps both, so the pane scrolls internally and the gutter stays pinned. The editor also switches from pre-wrap to pre, since a wrapped line would occupy several rows and desynchronize the numbers.
Hack font. The CSS has been asking for JetBrains Mono since it existed but loaded neither it nor Fira Code, so every visitor saw an arbitrary fallback. Hack woff2 (regular + bold) is vendored under web/fonts with its MIT license, same offline-first approach as the vendored emotes.
String literals (#21). buildEditorFragment splits quoted spans before emote substitution and wraps them in .str (amber). Quote pairing mirrors the lexer's readString exactly: no escapes, newlines allowed, unterminated quotes stay lit to EOF because that is what the lexer will consume as ILLEGAL. Emote names still render as images inside strings, as before. No changes were needed in editorSource/forEachPiece/setCaretOffset - they already recurse into element nodes.
Verified in a real browser against a local server: Hack loads via document.fonts; gutter row 1 and text line 1 share the same y-coordinate; line count tracks typing, undo (11 -> 15 -> 11 rows across an undo/redo round trip), and example loading; click-to-line lands on exact source offsets; source extraction round-trips through spans and emote images unchanged; autocomplete acceptance still resets undo history as designed in #20.
Closes #21
Related: #20, #22