EDITOR: table-of-contents rail on the note card (ticks per heading, hover title pill, active section) #636

Open
opened 2026-10-01 15:43:12 +00:00 by kayg · 14 comments
Owner

Owner decisions (2026-10-01): sliding table-of-contents rail for notes

  • One thin tick per heading on the left edge of the note card; tick length by heading level; the current section's tick is longer and brighter (follows scroll).
  • Hover a tick: a glass pill shows the heading title (one line, ellipsis), with a Copy link to heading icon in the pill.
  • Click: scroll to the heading with the shared spring; keyboard accessible (the rail is a navigation landmark; arrow keys move between ticks).
  • Hidden when the note has fewer than 3 headings.
  • Phones: no rail; a "Contents" item in the note's ⋯ menu opens a sheet list.
  • No sidebar TOC (the sidebar is the Navigator, #615).
  • Minimal and quiet: ticks use the hairline token, and the active tick uses the ink token.
  • Reuse: the existing heading extraction (outline) and Pill.
    Test: e2e: a note with 6 headings shows 6 ticks; scrolling updates the active tick; hover shows the title; click scrolls; a note with 2 headings shows no rail. Screenshots at 1440 in both themes (+ a frame strip of the hover pill).
    Do not name other products in code, commits or issues.
## Owner decisions (2026-10-01): sliding table-of-contents rail for notes - One thin tick per heading on the **left edge of the note card**; tick length by heading level; the **current section's** tick is longer and brighter (follows scroll). - **Hover** a tick: a glass pill shows the heading title (one line, ellipsis), with a **Copy link to heading** icon in the pill. - **Click:** scroll to the heading with the shared spring; keyboard accessible (the rail is a `navigation` landmark; arrow keys move between ticks). - Hidden when the note has fewer than 3 headings. - **Phones:** no rail; a "Contents" item in the note's ⋯ menu opens a sheet list. - **No sidebar TOC** (the sidebar is the Navigator, #615). - Minimal and quiet: ticks use the hairline token, and the active tick uses the ink token. - Reuse: the existing heading extraction (outline) and Pill. **Test:** e2e: a note with 6 headings shows 6 ticks; scrolling updates the active tick; hover shows the title; click scrolls; a note with 2 headings shows no rail. Screenshots at 1440 in both themes (+ a frame strip of the hover pill). Do not name other products in code, commits or issues.
Author
Owner

Starting #636 on branch job/tocrail-636, based on 3f258302a0. Reading the note UI and shared navigation components before implementation.

Starting #636 on branch job/tocrail-636, based on 3f258302a0f2d6418ff60c9ce22cbb33e008ca99. Reading the note UI and shared navigation components before implementation.
Author
Owner

Finding: the shared motion tokens zeroed every duration under data-input=keyboard, and the Settings route also skipped entrance motion when opened by shortcut. That conflicted with the 2026-10-01 owner override. I removed those modality-based skips, kept reduced-motion handling, and updated the shared checks. Web check passed; the focused pill feedback suite passed 8/8 and capsule motion suite passed 10/10. The final full web suite is still pending.

Finding: the shared motion tokens zeroed every duration under `data-input=keyboard`, and the Settings route also skipped entrance motion when opened by shortcut. That conflicted with the 2026-10-01 owner override. I removed those modality-based skips, kept reduced-motion handling, and updated the shared checks. Web check passed; the focused pill feedback suite passed 8/8 and capsule motion suite passed 10/10. The final full web suite is still pending.
Author
Owner

The Note heading link extension already extracts heading titles, levels, stable slugs and positions while maintaining its widgets. I extended that plugin state with a versioned outline snapshot, so the rail and phone sheet reuse the existing extraction and ordinary edits do not trigger a second heading scan. Focused outline tests pass (5/5); the real production browser check is in progress.

The Note heading link extension already extracts heading titles, levels, stable slugs and positions while maintaining its widgets. I extended that plugin state with a versioned outline snapshot, so the rail and phone sheet reuse the existing extraction and ordinary edits do not trigger a second heading scan. Focused outline tests pass (5/5); the real production browser check is in progress.
Author
Owner

The first production E2E pass showed no active-section change at 1440 px: percentage root margins collapsed the vertical observer band on a wide viewport. I derived pixel insets from viewport height and narrowed the band; the test now observes the fifth heading becoming current after scroll. Hash navigation also remounts the Note view before its outline is ready, so the selected fragment now restores the current tick once that outline arrives. The complete production check passes at 390, 820 and 1440 px in both themes.

The first production E2E pass showed no active-section change at 1440 px: percentage root margins collapsed the vertical observer band on a wide viewport. I derived pixel insets from viewport height and narrowed the band; the test now observes the fifth heading becoming current after scroll. Hash navigation also remounts the Note view before its outline is ready, so the selected fragment now restores the current tick once that outline arrives. The complete production check passes at 390, 820 and 1440 px in both themes.
Author
Owner

The shared-motion production browser check exposed a timing issue in its Escape-close assertion: it pressed Escape before the pointer-open transition had settled, so it observed no running exit animation. I added a 250 ms settle before Escape; the check now passes with keyboard and pointer motion, and reduced-motion assertions still pass. This changes only the test sequence, not its animation expectation. Commit: 26b65d290.

The shared-motion production browser check exposed a timing issue in its Escape-close assertion: it pressed Escape before the pointer-open transition had settled, so it observed no running exit animation. I added a 250 ms settle before Escape; the check now passes with keyboard and pointer motion, and reduced-motion assertions still pass. This changes only the test sequence, not its animation expectation. Commit: `26b65d290`.
Author
Owner

Completed: Note contents rail

Built the sliding Note contents rail and phone Contents sheet for #636. The desktop rail reuses the heading-link plugin outline. It shows one tick per heading, tracks the current section, exposes the heading title and Copy link on hover or focus, supports arrow keys, and reveals the selected heading with the shared spring. On phones, the Note menu opens the same outline in a sheet. Notes with fewer than three headings have neither the rail nor the Contents action.

The heading outline comes from the existing heading-link plugin, so the rail does not scan the Note a second time. No API route or contract changed.

Files

  • apps/web/src/lib/notes/NoteContents.svelte
  • apps/web/src/lib/notes/NoteView.svelte
  • apps/web/src/lib/notes/anchors.ts
  • apps/web/src/lib/notes/headingLinks.ts and headingLinks.svelte.test.ts
  • apps/web/e2e/tocrail-636.mjs and tocrail-636-fixtures.mjs
  • apps/web/package.json
  • bench/notes-tocrail-636.mjs
  • docs/perf/notes-tocrail-636.md
  • The owner motion override also updates CLAUDE.md, docs/DESIGN.md, shared UI motion, and related tests. A keyboard-close browser assertion now waits for the opening transition to settle before testing the close transition.

Commits

  • 63f869930 Make keyboard motion match pointer motion
  • 5fb7d8b7b Add sliding Note contents rail
  • d99e95b23 Merge origin/dev
  • 26b65d290 Stabilize keyboard close motion check
  • 6cd8a20be Record notes contents rail profile

Head: 6cd8a20bea5bc6da3e80783235b9a3d86acd3dce

Gate output

bun run check:

User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/tocrail-636/apps/web
Getting Svelte diagnostics...
svelte-check found 0 errors and 0 warnings

bun run test:

Test Files  149 passed (149)
      Tests  1013 passed (1013)
   Duration  126.95s (transform 49%, environment 20%, import 17%, tests 10%, setup 4%)

Production browser checks:

PASS keyboard and pointer actions share motion; reduced motion removes it
tocrail-636 e2e: six headings, active section, hover Copy link, arrow keys, phone sheet and two-heading threshold passed

node --check on the changed browser and benchmark scripts and git diff --check passed. No Rust source changed, so no Rust lint or test gate was needed. No endpoint or contract changed, so no endpoint adversarial probe was added.

Performance

The local profile ran on calternal-dev because the perf VM returned No route to host. Load average was 14.38, 18.12, 16.33 before and 17.77, 18.47, 16.55 after.

  • Six-heading Note open: p50/p95 1,481.3/1,766.0 ms (10 samples).
  • Heading reveal: p50/p95 631.4/1,008.9 ms (20 samples).
  • 1,000-heading Note open: 4,325.2 ms.
  • 100-arrow burst: p50/p95 46.4/141.7 ms, max 917.8 ms.
  • Browser peak RSS: 837.3 MiB average case; 967.7 MiB at 1,000 headings.

docs/perf/baseline.json has no Note editor rail baseline. Its closest entry is the 1440 px Notes Explorer route at p50/p95 1,257/1,337 ms; that is different work and is not directly comparable. The local host load was high, so this profile does not establish a regression. The 1,000-heading peak RSS is recorded as a worst-case observation; there is no direct baseline or threshold for it.

Decisions not specified in DESIGN

  • Base tick lengths are 14/12/10/8/6/4 px for H1–H6. The active tick adds 7 px and one pixel of height.
  • The current section is the heading nearest the viewport center among headings in a narrow center band. One IntersectionObserver tracks the outline; a resize rebuilds it.
  • The existing IsMobile hook and the 767 px CSS breakpoint select the phone sheet layout.

Visual evidence

The screenshots and hover frame strip are attached to this issue:

# Completed: Note contents rail Built the sliding Note contents rail and phone Contents sheet for #636. The desktop rail reuses the heading-link plugin outline. It shows one tick per heading, tracks the current section, exposes the heading title and Copy link on hover or focus, supports arrow keys, and reveals the selected heading with the shared spring. On phones, the Note menu opens the same outline in a sheet. Notes with fewer than three headings have neither the rail nor the Contents action. The heading outline comes from the existing heading-link plugin, so the rail does not scan the Note a second time. No API route or contract changed. ## Files - `apps/web/src/lib/notes/NoteContents.svelte` - `apps/web/src/lib/notes/NoteView.svelte` - `apps/web/src/lib/notes/anchors.ts` - `apps/web/src/lib/notes/headingLinks.ts` and `headingLinks.svelte.test.ts` - `apps/web/e2e/tocrail-636.mjs` and `tocrail-636-fixtures.mjs` - `apps/web/package.json` - `bench/notes-tocrail-636.mjs` - `docs/perf/notes-tocrail-636.md` - The owner motion override also updates `CLAUDE.md`, `docs/DESIGN.md`, shared UI motion, and related tests. A keyboard-close browser assertion now waits for the opening transition to settle before testing the close transition. ## Commits - `63f869930` Make keyboard motion match pointer motion - `5fb7d8b7b` Add sliding Note contents rail - `d99e95b23` Merge `origin/dev` - `26b65d290` Stabilize keyboard close motion check - `6cd8a20be` Record notes contents rail profile Head: `6cd8a20bea5bc6da3e80783235b9a3d86acd3dce` ## Gate output `bun run check`: ```text User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/tocrail-636/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test`: ```text Test Files 149 passed (149) Tests 1013 passed (1013) Duration 126.95s (transform 49%, environment 20%, import 17%, tests 10%, setup 4%) ``` Production browser checks: ```text PASS keyboard and pointer actions share motion; reduced motion removes it tocrail-636 e2e: six headings, active section, hover Copy link, arrow keys, phone sheet and two-heading threshold passed ``` `node --check` on the changed browser and benchmark scripts and `git diff --check` passed. No Rust source changed, so no Rust lint or test gate was needed. No endpoint or contract changed, so no endpoint adversarial probe was added. ## Performance The local profile ran on `calternal-dev` because the perf VM returned `No route to host`. Load average was `14.38, 18.12, 16.33` before and `17.77, 18.47, 16.55` after. - Six-heading Note open: p50/p95 `1,481.3/1,766.0 ms` (10 samples). - Heading reveal: p50/p95 `631.4/1,008.9 ms` (20 samples). - 1,000-heading Note open: `4,325.2 ms`. - 100-arrow burst: p50/p95 `46.4/141.7 ms`, max `917.8 ms`. - Browser peak RSS: `837.3 MiB` average case; `967.7 MiB` at 1,000 headings. `docs/perf/baseline.json` has no Note editor rail baseline. Its closest entry is the 1440 px Notes Explorer route at p50/p95 `1,257/1,337 ms`; that is different work and is not directly comparable. The local host load was high, so this profile does not establish a regression. The 1,000-heading peak RSS is recorded as a worst-case observation; there is no direct baseline or threshold for it. ## Decisions not specified in DESIGN - Base tick lengths are 14/12/10/8/6/4 px for H1–H6. The active tick adds 7 px and one pixel of height. - The current section is the heading nearest the viewport center among headings in a narrow center band. One IntersectionObserver tracks the outline; a resize rebuilds it. - The existing `IsMobile` hook and the 767 px CSS breakpoint select the phone sheet layout. ## Visual evidence The screenshots and hover frame strip are attached to this issue: - 1440 px light: https://git.kayg.org/attachments/3b662176-4858-48c7-b88f-465d86b4daf1 - Hover pill frame strip: https://git.kayg.org/attachments/e2fe4848-df58-42e2-a323-878d34a1e315 - 820 px light: https://git.kayg.org/attachments/71a279dc-446c-4981-b69b-889347292ff5 - 390 px light: https://git.kayg.org/attachments/b4fe044c-188d-4639-8897-61944240d082 - Phone Contents sheet, light: https://git.kayg.org/attachments/92e27031-aa01-4fad-9965-e1f09b6603fb - 1440 px dark: https://git.kayg.org/attachments/65c6b3ee-4281-4258-8310-2a053a383203 - 820 px dark: https://git.kayg.org/attachments/96006269-fed8-4e87-966d-028f9fd98474 - 390 px dark: https://git.kayg.org/attachments/94bcec35-657c-4753-bcad-9b10bf5eee1e - Phone Contents sheet, dark: https://git.kayg.org/attachments/8161d231-8ec0-4257-b26e-501261c95934
Author
Owner

Round 2 started on job/tocrail-636 from origin/dev base 687ff70313 (current head 6cd8a20bea). I’m checking the screenshot defect, Contents sheet chrome, rail contrast tokens, and the Note Card placement before final captures and gates.

Round 2 started on job/tocrail-636 from origin/dev base 687ff703136e71e89f8dfba139e93cd0788b25c1 (current head 6cd8a20bea5bc6da3e80783235b9a3d86acd3dce). I’m checking the screenshot defect, Contents sheet chrome, rail contrast tokens, and the Note Card placement before final captures and gates.
Author
Owner

Finding: the attached hover artifact is only 1640×32 px; it captures five cropped pill frames instead of the 1440×900 Note screen. The existing 390 px sheet capture shows a solid title band above the list. I’m replacing the frame strip with a full-viewport hover capture and using the shared sheet blur with a centered glass title pill.

Finding: the attached hover artifact is only 1640×32 px; it captures five cropped pill frames instead of the 1440×900 Note screen. The existing 390 px sheet capture shows a solid title band above the list. I’m replacing the frame strip with a full-viewport hover capture and using the shared sheet blur with a centered glass title pill.
Author
Owner

Correction to my finding above: reports the hover artifact is 1555×30 px (not 1640×32). It is still a cropped five-frame strip, not a full viewport.

Correction to my finding above: reports the hover artifact is 1555×30 px (not 1640×32). It is still a cropped five-frame strip, not a full viewport.
Author
Owner

Correction to my finding above: the actual hover artifact is 1555×30 px (not 1640×32). It is still a cropped five-frame strip, not a full viewport.

Correction to my finding above: the actual hover artifact is 1555×30 px (not 1640×32). It is still a cropped five-frame strip, not a full viewport.
Author
Owner

Round 2 visual fixes are committed as 790b26e03. The broken 30 px hover strip came from screenshotting the title Pill inside the rail's overflow-y:auto scroller; the Pill is now a single floating preview positioned alongside the rail, and full viewport capture verifies it is visible and inside the screen. The phone sheet now puts its visible title in the shared frosted Pill slot, leaves the shared ProgressiveBlur in place, and keeps the list in the scroll region. Inactive ticks now use the shared muted ink role (the old decorative hairline was nearly invisible in tokyo-night). bun run check reports 0 errors and 0 warnings; the targeted Playwright flow passed on repeat after one transient active-heading observer mismatch. I will merge origin/dev next and run final checks against that state.

Round 2 visual fixes are committed as `790b26e03`. The broken 30 px hover strip came from screenshotting the title Pill inside the rail's `overflow-y:auto` scroller; the Pill is now a single floating preview positioned alongside the rail, and full viewport capture verifies it is visible and inside the screen. The phone sheet now puts its visible title in the shared frosted Pill slot, leaves the shared ProgressiveBlur in place, and keeps the list in the scroll region. Inactive ticks now use the shared muted ink role (the old decorative hairline was nearly invisible in tokyo-night). `bun run check` reports 0 errors and 0 warnings; the targeted Playwright flow passed on repeat after one transient active-heading observer mismatch. I will merge `origin/dev` next and run final checks against that state.
Author
Owner

Round 2 report

Built

  • Moved the desktop heading preview outside the rail scrollport. The full viewport now shows the hover title pill and Copy link without clipping.
  • Replaced the phone sheet's opaque title band with a centered shared frosted Pill over OverlaySurface's ProgressiveBlur. The Contents rows remain in the shared sheet scroller.
  • Changed inactive rail ticks from the decorative hairline token to the shared muted ink role. The active tick keeps the ink role.
  • Added production E2E assertions for the full-screen hover capture, the sheet surface and scroll region, and token-based tick colors.

Files and commits

  • apps/web/src/lib/notes/NoteContents.svelte
  • apps/web/e2e/tocrail-636.mjs
  • docs/perf/notes-tocrail-636.md
  • 790b26e034b06aec98a0683680ac8376bb2ed69c — rail review fixes
  • 3c55268b6bda4a2c5d4e69c182b58bc60b75511b — Round 2 performance report
  • Head SHA: 3c55268b6bda4a2c5d4e69c182b58bc60b75511b

Fetched and merged origin/dev; it was already up to date at 687ff703136e71e89f8dfba139e93cd0788b25c1. The #632 Card changes are on origin/job/editor-series, not origin/dev. I kept this round on its owned files. The rail stays anchored to the current Note content inset; check its alignment against the Card stack after #632 reaches dev.

Production screenshots

All ten screenshots are attached to this issue.

Gates

bun run check after the merge:

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/tocrail-636/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run test:

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/tocrail-636/apps/web

 Test Files  149 passed (149)
      Tests  1013 passed (1013)
   Start at  21:40:59
   Duration  223.88s (transform 55%, environment 19%, import 13%, tests 10%, setup 3%)

Production build completed and wrote apps/web/build. The targeted production E2E passed:

tocrail-636 e2e: six headings, active section, hover Copy link, arrow keys, phone sheet and two-heading threshold passed

git diff --check and node --check apps/web/e2e/tocrail-636.mjs passed with no output. No Rust source or API changed, so Rust gates were not run. cargo clean removed 7,237 files (4.6 GiB), and generated web build output was deleted.

Performance profile

The run was local on calternal-dev; load averages were 36.08, 39.96, 35.34 before and 42.37, 40.82, 36.16 after. Six-heading Note open p50/p95 was 3,413.2/4,688.3 ms; heading reveal was 1,635.8/2,703.2 ms; browser CPU/RSS was 48.68 s / 70.3%, 874,395,318 mean and 1,075,417,088 peak bytes. The 1,000-heading open took 10,630.6 ms; 100 ArrowDown presses were 54.9/230.4 ms p50/p95; browser CPU/RSS was 5.70 s / 59.2%, 845,322,799 mean and 857,059,328 peak bytes.

docs/perf/baseline.json has no Note rail metric. Its nearest route is Notes at 1440 px, data-ready p50/p95 1,257/1,337 ms; that route measures different work. The earlier local rail profile in docs/perf/notes-tocrail-636.md ran at lower host load. These measurements do not establish a regression. The Round 2 numbers and load are recorded next to that baseline context in the same report.

Known gaps

  • Recheck rail alignment when the #632 Card stack reaches dev.
  • One earlier rail run had a transient active-heading observer assertion mismatch. An immediate rerun and the final post-merge production E2E both passed. The assertion timeout and expected behavior were not changed.
  • The full web test suite printed jsdom CSS/scrollTo() warnings while passing all 1,013 tests.

Decisions

  • origin/dev did not include the Card stack, so I did not import or adapt code from the separate origin/job/editor-series branch.
  • The performance VM was unavailable in the previous round, so this profile used the local shared build host and records its high load.
## Round 2 report **Built** - Moved the desktop heading preview outside the rail scrollport. The full viewport now shows the hover title pill and Copy link without clipping. - Replaced the phone sheet's opaque title band with a centered shared frosted Pill over `OverlaySurface`'s `ProgressiveBlur`. The Contents rows remain in the shared sheet scroller. - Changed inactive rail ticks from the decorative hairline token to the shared muted ink role. The active tick keeps the ink role. - Added production E2E assertions for the full-screen hover capture, the sheet surface and scroll region, and token-based tick colors. **Files and commits** - `apps/web/src/lib/notes/NoteContents.svelte` - `apps/web/e2e/tocrail-636.mjs` - `docs/perf/notes-tocrail-636.md` - `790b26e034b06aec98a0683680ac8376bb2ed69c` — rail review fixes - `3c55268b6bda4a2c5d4e69c182b58bc60b75511b` — Round 2 performance report - Head SHA: `3c55268b6bda4a2c5d4e69c182b58bc60b75511b` Fetched and merged `origin/dev`; it was already up to date at `687ff703136e71e89f8dfba139e93cd0788b25c1`. The #632 Card changes are on `origin/job/editor-series`, not `origin/dev`. I kept this round on its owned files. The rail stays anchored to the current Note content inset; check its alignment against the Card stack after #632 reaches `dev`. **Production screenshots** All ten screenshots are attached to this issue. - Phone, paper: [Note](https://git.kayg.org/attachments/23a66c10-d8a2-49a0-9641-0faced05e2a4), [Contents sheet](https://git.kayg.org/attachments/c874df4c-c9f8-484e-b50d-c6791497359b) - Phone, tokyo-night: [Note](https://git.kayg.org/attachments/82fbfb85-37a1-45c1-8b73-d1eda6c1997e), [Contents sheet](https://git.kayg.org/attachments/42ceb8bd-be83-453d-8b92-141b88187cbb) - Tablet, paper: [820 px](https://git.kayg.org/attachments/f0b1c598-bd80-45ac-8407-3900e8738f47) - Tablet, tokyo-night: [820 px](https://git.kayg.org/attachments/60d5f3cb-0bbf-4e55-8955-21effc4bf40d) - Desktop, paper: [1440 px](https://git.kayg.org/attachments/e2411e65-6ceb-47d1-a6a1-9853b97a167b), [full viewport hover](https://git.kayg.org/attachments/62a6cb31-2cf6-43a1-9c96-978b861fcefa) - Desktop, tokyo-night: [1440 px](https://git.kayg.org/attachments/6d56d5f9-e4ef-4ecf-a343-b41e527824dd), [full viewport hover](https://git.kayg.org/attachments/b44aeae2-43b8-45cb-b047-ce5b87ef9379) **Gates** `bun run check` after the merge: ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/tocrail-636/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test`: ```text RUN v5.0.1 /home/kayg/Developer/calternal-wt/tocrail-636/apps/web Test Files 149 passed (149) Tests 1013 passed (1013) Start at 21:40:59 Duration 223.88s (transform 55%, environment 19%, import 13%, tests 10%, setup 3%) ``` Production build completed and wrote `apps/web/build`. The targeted production E2E passed: ```text tocrail-636 e2e: six headings, active section, hover Copy link, arrow keys, phone sheet and two-heading threshold passed ``` `git diff --check` and `node --check apps/web/e2e/tocrail-636.mjs` passed with no output. No Rust source or API changed, so Rust gates were not run. `cargo clean` removed 7,237 files (4.6 GiB), and generated web build output was deleted. **Performance profile** The run was local on `calternal-dev`; load averages were `36.08, 39.96, 35.34` before and `42.37, 40.82, 36.16` after. Six-heading Note open p50/p95 was `3,413.2/4,688.3 ms`; heading reveal was `1,635.8/2,703.2 ms`; browser CPU/RSS was `48.68 s / 70.3%`, `874,395,318` mean and `1,075,417,088` peak bytes. The 1,000-heading open took `10,630.6 ms`; 100 ArrowDown presses were `54.9/230.4 ms` p50/p95; browser CPU/RSS was `5.70 s / 59.2%`, `845,322,799` mean and `857,059,328` peak bytes. `docs/perf/baseline.json` has no Note rail metric. Its nearest route is Notes at 1440 px, data-ready p50/p95 `1,257/1,337 ms`; that route measures different work. The earlier local rail profile in `docs/perf/notes-tocrail-636.md` ran at lower host load. These measurements do not establish a regression. The Round 2 numbers and load are recorded next to that baseline context in the same report. **Known gaps** - Recheck rail alignment when the #632 Card stack reaches `dev`. - One earlier rail run had a transient active-heading observer assertion mismatch. An immediate rerun and the final post-merge production E2E both passed. The assertion timeout and expected behavior were not changed. - The full web test suite printed jsdom CSS/`scrollTo()` warnings while passing all 1,013 tests. **Decisions** - `origin/dev` did not include the Card stack, so I did not import or adapt code from the separate `origin/job/editor-series` branch. - The performance VM was unavailable in the previous round, so this profile used the local shared build host and records its high load.
Author
Owner

Round 3 started on job/tocrail-636, based on origin/dev merge-base 687ff703136e71e89f8dfba139e93cd0788b25c1.

Finding: NoteContents.svelte renders the heading preview as a frosted Pill (the glass-chrome role), while toolbar warm tooltips use the shared floating overlay surface. In tocrail-hover-1440-tokyo-night.png, heading and body text remain readable through the preview. I am aligning the preview with the warm tooltip surface and retaining its Copy link action.

Round 3 started on `job/tocrail-636`, based on `origin/dev` merge-base `687ff703136e71e89f8dfba139e93cd0788b25c1`. Finding: `NoteContents.svelte` renders the heading preview as a frosted `Pill` (the `glass-chrome` role), while toolbar warm tooltips use the shared floating overlay surface. In `tocrail-hover-1440-tokyo-night.png`, heading and body text remain readable through the preview. I am aligning the preview with the warm tooltip surface and retaining its Copy link action.
Author
Owner

Round 3 complete. The TOC hover title now uses the shared warm tooltip fill, blur and hairline. Its fill uses the existing 94% static chrome surface; reduced-transparency mode uses the solid surface. The production regression checks run at 1440 px and 820 px in both themes, including fill opacity and Copy link styling. I inspected the four captures zoomed in: the title stays legible and the Copy link icon/text center line is within about 1 px.

Files:

  • packages/ui/src/tokens.css
  • packages/ui/src/components/tooltip/TooltipLayer.svelte
  • apps/web/src/lib/notes/NoteContents.svelte
  • apps/web/e2e/tocrail-636.mjs

Hover captures:

Gate output:

bun run check

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/tocrail-636/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run test output:

Test Files  149 passed (149)
      Tests  1013 passed (1013)
   Start at  23:22:03
   Duration  115.99s (transform 50%, environment 19%, import 17%, tests 10%, setup 4%)

Environment  |component| jsdom was created 47 times · 113.72s total, 28% of tracked time
             create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares state across files)
             learn more: https://vitest.dev/guide/improving-performance#test-environments

Production E2E output:

tocrail-636 e2e: six headings, active section, hover Copy link, arrow keys, phone sheet and two-heading threshold passed

git diff --check passed with no output. The production server build used for E2E completed:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 13m 48s

No Rust source or API contract changed, so Rust clippy/test gates were not applicable. cargo clean removed 7237 files (4.6 GiB), and web build output was deleted.

Known gap: Vitest emitted existing jsdom CSS parse and Window.scrollTo() not-implemented warnings; all 149 test files and 1013 tests passed.

Decision not specified in DESIGN: use the existing 94% static chrome fill for both compact warm tooltips and the TOC hover title, and use --surface when reduced transparency is requested.

Head SHA: 144f0316a7ed6eb9f1ed495d21918e3e78656365.

Round 3 complete. The TOC hover title now uses the shared warm tooltip fill, blur and hairline. Its fill uses the existing 94% static chrome surface; reduced-transparency mode uses the solid surface. The production regression checks run at 1440 px and 820 px in both themes, including fill opacity and Copy link styling. I inspected the four captures zoomed in: the title stays legible and the Copy link icon/text center line is within about 1 px. Files: - `packages/ui/src/tokens.css` - `packages/ui/src/components/tooltip/TooltipLayer.svelte` - `apps/web/src/lib/notes/NoteContents.svelte` - `apps/web/e2e/tocrail-636.mjs` Hover captures: - [1440 px · Paper](https://git.kayg.org/attachments/8423a2bd-ac0d-4f93-9718-0685a0f830c2) - [1440 px · Tokyo Night](https://git.kayg.org/attachments/7153197e-029a-47c9-beb9-64036630a0a4) - [820 px · Paper](https://git.kayg.org/attachments/5aba6777-98c6-4216-bcf3-03f9ba700790) - [820 px · Tokyo Night](https://git.kayg.org/attachments/06e1f67b-663f-4af8-bc2f-b145e523ad43) Gate output: `bun run check` ``` $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/tocrail-636/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test` output: ``` Test Files 149 passed (149) Tests 1013 passed (1013) Start at 23:22:03 Duration 115.99s (transform 50%, environment 19%, import 17%, tests 10%, setup 4%) Environment |component| jsdom was created 47 times · 113.72s total, 28% of tracked time create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares state across files) learn more: https://vitest.dev/guide/improving-performance#test-environments ``` Production E2E output: ``` tocrail-636 e2e: six headings, active section, hover Copy link, arrow keys, phone sheet and two-heading threshold passed ``` `git diff --check` passed with no output. The production server build used for E2E completed: ``` Finished `dev` profile [unoptimized + debuginfo] target(s) in 13m 48s ``` No Rust source or API contract changed, so Rust clippy/test gates were not applicable. `cargo clean` removed 7237 files (4.6 GiB), and web build output was deleted. Known gap: Vitest emitted existing jsdom CSS parse and `Window.scrollTo()` not-implemented warnings; all 149 test files and 1013 tests passed. Decision not specified in DESIGN: use the existing 94% static chrome fill for both compact warm tooltips and the TOC hover title, and use `--surface` when reduced transparency is requested. Head SHA: `144f0316a7ed6eb9f1ed495d21918e3e78656365`.
Sign in to join this conversation.
No labels
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#636
No description provided.