port: code block language picker and syntax colours (approved mockup) #903

Closed
kayg wants to merge 0 commits from port/codeblock into dev
Owner

Ports the approved mockup's code block language picker (apps/mockup CodeLang.svelte, branch review/calendar-2) and adds syntax colours. Work in progress: the gate numbers and screenshots are added as comments below.

What it does

  • A code block's top right corner holds a button that says the language (Plain text, Rust, …). It sits in the place of the opening fence, which is never drawn. Pressing it opens the mockup's glass menu: Last used, Common, More, a filter you type into, ↑ ↓ ↵ esc, a small spring from the button for a pointer, none for the keyboard. The pick is written after the opening fence (```rust); plain text is a bare fence. Nothing is detected from the code.
  • Colours (keywords, strings, comments, numbers, calls) use the mockup's palette and tk-* classes, light and dark.
  • Mockup values win; desktop UI text stays 14 px (--text-ui). The fence lines that are not drawn are the block's 11 px top and bottom padding, as in the mockup.

Cost on the typing path

  • Colours are a lazy chunk (colours), fetched only when a note has a fenced block that names a language we colour. A tiny gate (code/gate.ts) is all that ships with the editor; typing elsewhere costs one look at the line being typed on.
  • Each language's raw lezer parser is its own lazy chunk (code-javascript (also TypeScript), code-python, code-rust, code-css, code-json), fetched only for a language that is in the note, plus code-runtime (the shared @lezer/lr). Shell and SQL use the mockup's own regular-expression rules (no grammar to fetch).
  • A change inside a code block parses and repaints that one block; a change that adds or removes a fence, or a parser arriving, repaints all code blocks once. Blocks over 40,000 characters stay plain. Text outside code costs nothing.
  • CodeMirror's language wrappers (@codemirror/lang-*) and the nested markdown parse were tried first and dropped: they pulled about 7 KB brotli of shared helpers (@codemirror/language, @lezer/common, autocomplete) into the first paint bundle. The raw parsers keep first paint at +1.5 KiB (233.2 → 234.7 KiB, budget 240).
  • Budget lines are added for the new lazy chunks only (perf/budgets.json), with the total updated to the sum.

Decisions

  • The language word is resolved at press time from the page (posAtDOM), and the menu follows its fence through changes while it is open; a stored position went stale when the server rewrote the properties after a save and a quick second pick changed the wrong line (found by --repeat-each).
  • @lezer/* parsers are direct dependencies of packages/editor (bun.lock updated).
  • No duplicate open PR found for this work (searched open PRs for code block / language / highlight).

Tests

  • packages/editor unit tests (511 pass), apps/web unit tests (96 pass), svelte-check (only the existing review4-signout errors).
  • apps/web/e2e/code-block.spec.ts (6 cases) and the perf test type.code_note_prose / type.code_block in apps/web/perf/typing.perf.ts (a note of 400 paragraphs and 80 code blocks).

Not ported

  • The mockup's read-only code file view (Who owes whom, line numbers) is the attachments port's, not this one.
  • Languages: the nine of the mockup (plain, JavaScript, TypeScript, Python, Rust, Shell, JSON, CSS, SQL). Others show their word as the label and stay plain.

🤖 Generated with Claude Code

Ports the approved mockup's code block language picker (`apps/mockup` `CodeLang.svelte`, branch `review/calendar-2`) and adds syntax colours. Work in progress: the gate numbers and screenshots are added as comments below. ## What it does - A code block's top right corner holds a button that says the language (`Plain text`, `Rust`, …). It sits in the place of the opening fence, which is never drawn. Pressing it opens the mockup's glass menu: *Last used*, *Common*, *More*, a filter you type into, ↑ ↓ ↵ esc, a small spring from the button for a pointer, none for the keyboard. The pick is written after the opening fence (` ```rust `); plain text is a bare fence. Nothing is detected from the code. - Colours (keywords, strings, comments, numbers, calls) use the mockup's palette and `tk-*` classes, light and dark. - Mockup values win; desktop UI text stays 14 px (`--text-ui`). The fence lines that are not drawn are the block's 11 px top and bottom padding, as in the mockup. ## Cost on the typing path - Colours are a **lazy chunk** (`colours`), fetched only when a note has a fenced block that names a language we colour. A tiny gate (`code/gate.ts`) is all that ships with the editor; typing elsewhere costs one look at the line being typed on. - Each language's **raw lezer parser** is its own lazy chunk (`code-javascript` (also TypeScript), `code-python`, `code-rust`, `code-css`, `code-json`), fetched only for a language that is in the note, plus `code-runtime` (the shared `@lezer/lr`). Shell and SQL use the mockup's own regular-expression rules (no grammar to fetch). - A change inside a code block parses and repaints that one block; a change that adds or removes a fence, or a parser arriving, repaints all code blocks once. Blocks over 40,000 characters stay plain. Text outside code costs nothing. - CodeMirror's language wrappers (`@codemirror/lang-*`) and the nested markdown parse were tried first and dropped: they pulled about 7 KB brotli of shared helpers (`@codemirror/language`, `@lezer/common`, `autocomplete`) into the first paint bundle. The raw parsers keep first paint at +1.5 KiB (233.2 → 234.7 KiB, budget 240). - Budget lines are added for the new lazy chunks only (`perf/budgets.json`), with the total updated to the sum. ## Decisions - The language word is resolved at press time from the page (`posAtDOM`), and the menu follows its fence through changes while it is open; a stored position went stale when the server rewrote the properties after a save and a quick second pick changed the wrong line (found by `--repeat-each`). - `@lezer/*` parsers are direct dependencies of `packages/editor` (`bun.lock` updated). - No duplicate open PR found for this work (searched open PRs for code block / language / highlight). ## Tests - `packages/editor` unit tests (511 pass), `apps/web` unit tests (96 pass), svelte-check (only the existing `review4-signout` errors). - `apps/web/e2e/code-block.spec.ts` (6 cases) and the perf test `type.code_note_prose` / `type.code_block` in `apps/web/perf/typing.perf.ts` (a note of 400 paragraphs and 80 code blocks). ## Not ported - The mockup's read-only code file view (`Who owes whom`, line numbers) is the attachments port's, not this one. - Languages: the nine of the mockup (plain, JavaScript, TypeScript, Python, Rust, Shell, JSON, CSS, SQL). Others show their word as the label and stay plain. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kayg added 17 commits 2026-11-03 18:02:25 +00:00
Lazy grammars per language, painted as tk-* classes by a field that repaints only the changed block.
The opening fence's place holds the language button, which asks the app to open its menu.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The nested markdown parse and CodeMirror's language wrappers pulled 7 KB of shared helpers into the first
paint bundle. Each block is parsed on its own instead, by the parser for its fence word.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A stored position went stale when the server rewrote the properties after a save, so a quick second pick
changed the wrong line. The menu now follows its fence through changes while it is open.
test(web): give saves and the file 15 s on a slow browser
Some checks failed
CI / server (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
CI / web (pull_request) Has been cancelled
Markdown extraction contract / markdown-native-contract (pull_request) Has been cancelled
Markdown extraction contract / markdown-editor-contract (pull_request) Has been cancelled
f4372f3c42
Author
Owner

Product (left) vs approved mockup (right), 1440x900 and 390x844, light and dark. Taken by me and checked; the mockup is review/calendar-2 on its own page, so its surroundings differ.

state product mockup
port-codeblock-corner-1440-dark
port-codeblock-corner-1440-light
port-codeblock-corner-390-dark
port-codeblock-corner-390-light
port-codeblock-menu-1440-dark
port-codeblock-menu-1440-light
port-codeblock-menu-390-dark
port-codeblock-menu-390-light
Product (left) vs approved mockup (right), 1440x900 and 390x844, light and dark. Taken by me and checked; the mockup is `review/calendar-2` on its own page, so its surroundings differ. | state | product | mockup | |---|---|---| | port-codeblock-corner-1440-dark | ![](https://git.kayg.org/attachments/3babbddc-55ed-42fb-b60b-1f5a74224b91) | ![](https://git.kayg.org/attachments/efc09868-9c91-4a3c-bfed-d2e428a39ef1) | | port-codeblock-corner-1440-light | ![](https://git.kayg.org/attachments/90bd540e-b836-4323-a73d-56fa0c14cacc) | ![](https://git.kayg.org/attachments/9b1af97f-914d-424b-a374-0ac373d37575) | | port-codeblock-corner-390-dark | ![](https://git.kayg.org/attachments/d60a5da7-ca13-4ab1-b204-2405dcd13110) | ![](https://git.kayg.org/attachments/efeb0b92-94a0-44c0-bc2e-d83cd2afa6d1) | | port-codeblock-corner-390-light | ![](https://git.kayg.org/attachments/a3382f9b-0d62-4353-af82-bc2dac77a321) | ![](https://git.kayg.org/attachments/69bd35e2-9a88-4286-b4dc-f900b2f9e5c2) | | port-codeblock-menu-1440-dark | ![](https://git.kayg.org/attachments/693ffc69-fe8c-44ba-b97b-bf6de49a6b3d) | ![](https://git.kayg.org/attachments/c8f45eb8-d1f9-4fe7-8fae-e86c9208a57b) | | port-codeblock-menu-1440-light | ![](https://git.kayg.org/attachments/be082887-df84-4bbf-814e-3b5706ce9854) | ![](https://git.kayg.org/attachments/a00145ea-b918-4800-9d4a-39e9f15ba465) | | port-codeblock-menu-390-dark | ![](https://git.kayg.org/attachments/7fecfce8-4bed-43e7-8241-5efe8b75a15d) | ![](https://git.kayg.org/attachments/e6bfabc2-b479-440c-ba24-044a789b015a) | | port-codeblock-menu-390-light | ![](https://git.kayg.org/attachments/abc9a20a-385c-4e26-be77-426559a47981) | ![](https://git.kayg.org/attachments/68f4885d-2b1c-4edc-8dc0-d3cccfa8479e) |
Author
Owner

First paint, exact bytes (brotli, index chunk; dev 41f2efa3 vs this head 78b4c77a)

  • dev: 238,797; this PR: 239,117; delta +320 B (0.31 KiB). First-paint CSS: 19,250 vs 19,250 (identical).
  • It cannot be exactly zero: something in first paint has to notice a fence. What stays there: lib/lazy-code.ts (one view plugin: a regex on the note when it opens, and a regex on typed backticks/tildes until the extension is in) plus the dynamic-import preload map entry. Everything else is lazy.
  • Moved out of first paint: the language button, fence padding, syntax colours, their stylesheet, the language menu (code-ui.svelte chunk, 4.4 KiB), and every parser (code-runtime, code-javascript, code-python, code-rust, code-css, code-json). packages/editor's editor.ts, index.ts and syntax/hidden.ts and cm.css are byte-identical to dev. The colours read the parsers' own styleTags through getStyleTags (a few hundred bytes of the highlighter's shared module) instead of highlightTree/tagHighlighter (1.5 KB).
  • A note without a fence fetches nothing for code (asserted by code-block.spec.ts); a note with a fence fetches code-ui.svelte once, then the parsers for the languages it names. Typing a fence into a note without code loads it too (asserted).
  • Known trade-off: for a note with a fence the button and the 11 px padding arrive when the lazy chunk does (a few tens of ms after the text), so the block settles once.

Gates at 78b4c77a

  • code-block.spec.ts (7 cases), --repeat-each=5, CI=1 --retries=0 --workers=1: chromium-1440 35/35, chromium-390 35/35 (dev-01), webkit-1440 35/35, webkit-390 35/35 (dev-02).
  • editor unit tests 511 pass, web 96 pass, tsc --noEmit clean, svelte-check only the existing review4-signout errors.

Typing latency on the perf VMs (ABAB, dev-vs-dev control, both locks)

Taken 11-03 on perf-01 with perf-lock held on BOTH perf VMs for the whole run. Chromium at 4x CPU slowdown, 10k Home, 4 rounds, order reversed every other round. Cases: dev (41f2efa3), dev2 (the same dist again: the dev-vs-dev control), cb (#903 head), h887 (#887 head c8afa867), hb (#904 head). Each cell is the median over the 4 rounds of that run's p50 (or p95, or CPU per key), ms:

20
metric (median over rounds of per-run value; ms)   dev dev2 cb h887 hb
type.code_block                       59.8(4)    62.7(4)    64.7(4)    65.1(4)    63.5(4)
type.code_block_cpu                   24.6(4)    27.9(4)    29.9(4)    27.5(4)    27.5(4)
type.code_block|p95                  147.4(4)   151.1(4)   155.7(4)   153.2(4)   151.2(4)
type.code_note_prose                  62.1(4)    68.1(4)    67.0(4)    67.7(4)    69.0(4)
type.code_note_prose_cpu              30.0(4)    33.3(4)    33.2(4)    33.1(4)    32.9(4)
type.code_note_prose|p95             156.1(4)   160.1(4)   161.7(4)   160.8(4)   160.2(4)
type.long_note                        61.6(4)    63.2(4)    61.9(4)    63.5(4)    65.7(4)
type.long_note_cpu                    26.9(4)    28.8(4)    28.6(4)    29.0(4)    29.2(4)
type.long_note|p95                   149.9(4)   152.4(4)   153.1(4)   153.1(4)   154.6(4)

Reading it: dev and dev2 are identical bits and differ by up to 6 ms (code_note_prose), so that is the resolution. #903 against dev2: within -1.1 to +2.0 ms p50 and -0.4 to +2.0 ms CPU per key; #904 against #887: within +0.4 to +2.2 ms p50 and about +0.2 to +0.4 ms... see the table; no row separates a PR from its base by more than the control does. The earlier +10 ms (one run, perf-01 under load 9, no control) is not reproduced. The per-keystroke work was also cut: the #903 gate now looks only at typed backticks/tildes, and #904 does nothing on a keystroke until a hidden list exists.

## First paint, exact bytes (brotli, `index` chunk; dev 41f2efa3 vs this head 78b4c77a) - dev: **238,797**; this PR: **239,117**; delta **+320 B** (0.31 KiB). First-paint CSS: 19,250 vs 19,250 (identical). - It cannot be exactly zero: something in first paint has to notice a fence. What stays there: `lib/lazy-code.ts` (one view plugin: a regex on the note when it opens, and a regex on typed backticks/tildes until the extension is in) plus the dynamic-import preload map entry. Everything else is lazy. - Moved out of first paint: the language button, fence padding, syntax colours, their stylesheet, the language menu (`code-ui.svelte` chunk, 4.4 KiB), and every parser (`code-runtime`, `code-javascript`, `code-python`, `code-rust`, `code-css`, `code-json`). `packages/editor`'s `editor.ts`, `index.ts` and `syntax/hidden.ts` and `cm.css` are byte-identical to dev. The colours read the parsers' own `styleTags` through `getStyleTags` (a few hundred bytes of the highlighter's shared module) instead of `highlightTree`/`tagHighlighter` (1.5 KB). - A note without a fence fetches nothing for code (asserted by `code-block.spec.ts`); a note with a fence fetches `code-ui.svelte` once, then the parsers for the languages it names. Typing a fence into a note without code loads it too (asserted). - Known trade-off: for a note with a fence the button and the 11 px padding arrive when the lazy chunk does (a few tens of ms after the text), so the block settles once. ## Gates at 78b4c77a - `code-block.spec.ts` (7 cases), `--repeat-each=5`, `CI=1 --retries=0 --workers=1`: chromium-1440 35/35, chromium-390 35/35 (dev-01), webkit-1440 35/35, webkit-390 35/35 (dev-02). - editor unit tests 511 pass, web 96 pass, `tsc --noEmit` clean, svelte-check only the existing `review4-signout` errors. ## Typing latency on the perf VMs (ABAB, dev-vs-dev control, both locks) Taken 11-03 on perf-01 with `perf-lock` held on BOTH perf VMs for the whole run. Chromium at 4x CPU slowdown, 10k Home, 4 rounds, order reversed every other round. Cases: `dev` (41f2efa3), `dev2` (the same dist again: the dev-vs-dev control), `cb` (#903 head), `h887` (#887 head c8afa867), `hb` (#904 head). Each cell is the median over the 4 rounds of that run's p50 (or p95, or CPU per key), ms: ``` 20 metric (median over rounds of per-run value; ms) dev dev2 cb h887 hb type.code_block 59.8(4) 62.7(4) 64.7(4) 65.1(4) 63.5(4) type.code_block_cpu 24.6(4) 27.9(4) 29.9(4) 27.5(4) 27.5(4) type.code_block|p95 147.4(4) 151.1(4) 155.7(4) 153.2(4) 151.2(4) type.code_note_prose 62.1(4) 68.1(4) 67.0(4) 67.7(4) 69.0(4) type.code_note_prose_cpu 30.0(4) 33.3(4) 33.2(4) 33.1(4) 32.9(4) type.code_note_prose|p95 156.1(4) 160.1(4) 161.7(4) 160.8(4) 160.2(4) type.long_note 61.6(4) 63.2(4) 61.9(4) 63.5(4) 65.7(4) type.long_note_cpu 26.9(4) 28.8(4) 28.6(4) 29.0(4) 29.2(4) type.long_note|p95 149.9(4) 152.4(4) 153.1(4) 153.1(4) 154.6(4) ``` Reading it: dev and dev2 are identical bits and differ by up to 6 ms (code_note_prose), so that is the resolution. #903 against dev2: within -1.1 to +2.0 ms p50 and -0.4 to +2.0 ms CPU per key; #904 against #887: within +0.4 to +2.2 ms p50 and about +0.2 to +0.4 ms... see the table; no row separates a PR from its base by more than the control does. The earlier +10 ms (one run, perf-01 under load 9, no control) is not reproduced. The per-keystroke work was also cut: the #903 gate now looks only at typed backticks/tildes, and #904 does nothing on a keystroke until a hidden list exists.
Author
Owner

Exact deltas for the table above (this PR minus the same-bits control dev2, ms): p50 code_block +2.0, code_note_prose -1.1, long_note -1.3; CPU per key +2.0, -0.1, -0.2. Minus dev: p50 +4.9, +4.9, +0.3. The control itself differs from dev by +2.9, +6.0, +1.6 p50, so none of this separates the PR from noise; the first run's +10 ms is not reproduced.

Exact deltas for the table above (this PR minus the same-bits control `dev2`, ms): p50 code_block +2.0, code_note_prose -1.1, long_note -1.3; CPU per key +2.0, -0.1, -0.2. Minus `dev`: p50 +4.9, +4.9, +0.3. The control itself differs from `dev` by +2.9, +6.0, +1.6 p50, so none of this separates the PR from noise; the first run's +10 ms is not reproduced.
Author
Owner

First-paint diet (BM15, #947). Head 4c4cdda1 (unchanged by me).

First-paint bytes are brotli q11 from apps/web/scripts/bundle-size.mjs (JS of the first-paint closure = the index chunk; CSS of the first stylesheet). Dev baseline 41f2efa3: 238,797 B JS, 19,250 B CSS. A second figure, "hash-normalised", is the same chunk with the 8-character chunk-file hashes replaced by a fixed string: the hashes are random, and they move the real brotli number by ±300 B between two builds of identical code, so a few-hundred-byte claim is only meaningful in the normalised figure (dev: 238,804). Chunk budgets: every new lazy chunk's line is taken from the first-script line or the idle-screens line so the total stays 404.2; perf/web.sh --bundle-only --gate passes on this head.

first JS first CSS lazy JS
dev 238,797 19,250 122,936
this PR 239,117 (+320; normalised 238,930 = +126) 19,250 (0) 205,936 (+83 KB: the language parsers)

The language list, menu and colours are already lazy (lazy-code.ts is the trigger). The first-screen cost is that trigger (470 raw) plus one export of @lezer/highlight (getStyleTags) that code/blocks.ts imports and the editor shares, plus the preload-list entries of the new chunks. Not ≤ 0 on its own; covered by #875's offset (running total in #947).

code-block.spec: 28 passed on all four projects. Budget: unavoidable total change. The seven code-* chunks are 83 KB of new lazy JS; the total is the sum of the chunk lines, so it cannot stay 404.2 here (it is 488.3 as the author set it). That is lazy weight, not first paint. I left the author's lines.

**First-paint diet (BM15, #947).** Head `4c4cdda1` (unchanged by me). First-paint bytes are brotli q11 from `apps/web/scripts/bundle-size.mjs` (JS of the first-paint closure = the `index` chunk; CSS of the first stylesheet). Dev baseline `41f2efa3`: **238,797 B JS, 19,250 B CSS**. A second figure, "hash-normalised", is the same chunk with the 8-character chunk-file hashes replaced by a fixed string: the hashes are random, and they move the real brotli number by ±300 B between two builds of identical code, so a few-hundred-byte claim is only meaningful in the normalised figure (dev: 238,804). Chunk budgets: every new lazy chunk's line is taken from the first-script line or the idle-screens line so the total stays 404.2; `perf/web.sh --bundle-only --gate` passes on this head. | | first JS | first CSS | lazy JS | |---|---|---|---| | dev | 238,797 | 19,250 | 122,936 | | this PR | 239,117 (**+320**; normalised 238,930 = **+126**) | 19,250 (0) | 205,936 (+83 KB: the language parsers) | The language list, menu and colours are already lazy (`lazy-code.ts` is the trigger). The first-screen cost is that trigger (470 raw) plus one export of `@lezer/highlight` (`getStyleTags`) that `code/blocks.ts` imports and the editor shares, plus the preload-list entries of the new chunks. Not ≤ 0 on its own; covered by #875's offset (running total in #947). `code-block.spec`: **28 passed** on all four projects. **Budget: unavoidable total change.** The seven `code-*` chunks are 83 KB of new lazy JS; the total is the sum of the chunk lines, so it cannot stay 404.2 here (it is 488.3 as the author set it). That is lazy weight, not first paint. I left the author's lines.
Author
Owner

Total JS budget 488.3 KB brotli approved by kayg on 11-05 (option A on #947). No other budget changes.

Total JS budget 488.3 KB brotli approved by kayg on 11-05 (option A on #947). No other budget changes.
Author
Owner

#903 per-PR gates at 3a634dd1: BLOCKED on first-paint JS (+182 B brotli)

Head = 505064a7 + merge of origin/dev b5e8dcb2 (no conflicts). Everything else passes:

gate result
lint / svelte-check / vitest pass / 2 errors, 16 warnings = dev's / 107 + 55 pass
Chromium 1440+390 and WebKit 1440+390: code-block, editor, lazy-ui, lazy-offline, lazy-styled, preload, block-no-text-sel, vim-block Chromium 96 passed, 0 failed; WebKit 88 passed, 0 failed
perf/web.sh --bundle-only --gate rc 0 against its own perf/budgets.json, but first-paint JS is bigger than dev (see below)
check-ui-primitives not on dev yet; the train found styles/code-block.css:89 (local :active { scale }) in this PR: must become the shared press (after #941)

First paint, same machine, built from each head (brotli): JS 232.862 → 233.040 KB (dev → #903), the index chunk +182 B (lazyCode ViewPlugin in Editor.svelte, lib/lazy-code.ts); first CSS 18.571 = 18.571. The new parser chunks are lazy (total JS 363.4 → 444.5 KB).
Two things for the orchestrator: (1) the +182 B needs an equal cut in the first chunk, or an explicit OK; (2) perf/budgets.json raises web.bundle.total_js_brotli 404.2 → 488.3 and adds 7 lazy-chunk lines: that is a budget raise, not approved by me.

## #903 per-PR gates at `3a634dd1`: BLOCKED on first-paint JS (+182 B brotli) Head = `505064a7` + merge of origin/dev `b5e8dcb2` (no conflicts). Everything else passes: | gate | result | |---|---| | lint / svelte-check / vitest | pass / 2 errors, 16 warnings = dev's / 107 + 55 pass | | Chromium 1440+390 and WebKit 1440+390: code-block, editor, lazy-ui, lazy-offline, lazy-styled, preload, block-no-text-sel, vim-block | Chromium 96 passed, 0 failed; WebKit 88 passed, 0 failed | | `perf/web.sh --bundle-only --gate` | rc 0 against its own `perf/budgets.json`, **but first-paint JS is bigger than dev** (see below) | | `check-ui-primitives` | not on dev yet; the train found `styles/code-block.css:89` (local `:active { scale }`) in this PR: must become the shared press (after #941) | First paint, same machine, built from each head (brotli): JS **232.862 → 233.040 KB (dev → #903)**, the `index` chunk +182 B (`lazyCode` ViewPlugin in `Editor.svelte`, `lib/lazy-code.ts`); first CSS 18.571 = 18.571. The new parser chunks are lazy (total JS 363.4 → 444.5 KB). Two things for the orchestrator: (1) the +182 B needs an equal cut in the first chunk, or an explicit OK; (2) `perf/budgets.json` raises `web.bundle.total_js_brotli` 404.2 → 488.3 and adds 7 lazy-chunk lines: that is a budget raise, not approved by me.
Author
Owner

#903 after #941: Pill/press done, specs green, first-paint bytes NOT yet ≤ dev

Head 25374a8b0, dev 850c23a9 merged in. The language button is bounce (shared press); the local :active rule is gone (its transition lists scale). perf/budgets.json total 488.3 is kayg-approved.

gate result
lint / svelte-check / vitest pass / 2 errors, 16 warnings = dev's / pass
check-ui-primitives clean
Chromium (dev-01) + WebKit (dev-02), 1440+390: code-block, editor, lazy-ui, lazy-offline, lazy-styled, preload, block-no-text-sel, vim-block, pill-press all pass (Chromium 102 passed, WebKit 94 passed, 0 failed)
first-paint JS brotli (dev 232.565 KB, first CSS 17.952 = dev) 232.794: +229 B

Not GREEN until the first-paint bytes are cut by the amount above.

## #903 after #941: Pill/press done, specs green, first-paint bytes NOT yet ≤ dev Head `25374a8b0`, dev `850c23a9` merged in. The language button is `bounce` (shared press); the local `:active` rule is gone (its transition lists `scale`). `perf/budgets.json` total 488.3 is kayg-approved. | gate | result | |---|---| | lint / svelte-check / vitest | pass / 2 errors, 16 warnings = dev's / pass | | `check-ui-primitives` | **clean** | | Chromium (dev-01) + WebKit (dev-02), 1440+390: code-block, editor, lazy-ui, lazy-offline, lazy-styled, preload, block-no-text-sel, vim-block, pill-press | all pass (Chromium 102 passed, WebKit 94 passed, 0 failed) | | first-paint JS brotli (dev 232.565 KB, first CSS 17.952 = dev) | **232.794: **+229 B**** | Not GREEN until the first-paint bytes are cut by the amount above.
Author
Owner

#903 re-gated on dev c808ef67: GREEN at 571ebdd1

Merged dev (the vite.config.ts chunk groups of #975 and #903 both kept; the shared-press fix is kept). total_js_brotli 488.3 is the kayg-approved one.

gate result
lint / svelte-check / vitest web / ui primitives / check-ui-primitives pass / 0 errors, 16 warnings (= dev) / 109 / 59 / clean
first-paint JS brotli (this PR vs dev c808ef67 225.407 KB vs prod 71827028 233.040 KB) 225.708 KB: +301 B vs dev, −7.33 KB vs prod (first CSS 17.952 = dev, prod 18.376). Option A: dev after this merge stays ≤ prod.
perf/web.sh --bundle-only --gate rc 0. idle-screens 51.147; total JS 448.3 (code chunks are lazy)
Chromium (dev-01) 1440+390: code-block, editor, lazy-ui, lazy-offline, lazy-styled, preload, block-no-text-sel, vim-block, pill-press, picker 106 passed, 0 failed
WebKit (dev-02) same 98 passed, 0 failed
Rust n/a
## #903 re-gated on dev `c808ef67`: GREEN at `571ebdd1` Merged dev (the `vite.config.ts` chunk groups of #975 and #903 both kept; the shared-press fix is kept). `total_js_brotli` 488.3 is the kayg-approved one. | gate | result | |---|---| | lint / svelte-check / vitest web / ui primitives / `check-ui-primitives` | pass / 0 errors, 16 warnings (= dev) / 109 / 59 / clean | | **first-paint JS brotli (this PR vs dev `c808ef67` 225.407 KB vs prod `71827028` 233.040 KB)** | **225.708 KB: +301 B vs dev, −7.33 KB vs prod** (first CSS 17.952 = dev, prod 18.376). Option A: dev after this merge stays ≤ prod. | | `perf/web.sh --bundle-only --gate` | rc 0. idle-screens 51.147; total JS 448.3 (code chunks are lazy) | | Chromium (dev-01) 1440+390: code-block, editor, lazy-ui, lazy-offline, lazy-styled, preload, block-no-text-sel, vim-block, pill-press, picker | 106 passed, 0 failed | | WebKit (dev-02) same | 98 passed, 0 failed | | Rust | n/a |
Author
Owner

Merged into dev by the orchestrator (U1 GREEN, credit bank).

Merged into dev by the orchestrator (U1 GREEN, credit bank).
kayg closed this pull request 2026-11-06 11:41:28 +00:00
Some checks failed
CI / server (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
CI / web (pull_request) Has been cancelled
Markdown extraction contract / markdown-native-contract (pull_request) Has been cancelled
Markdown extraction contract / markdown-editor-contract (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal!903
No description provided.