port: attachments (approved mockup) 2/3: embeds (strip, galleries, files, video, audio) #860

Open
kayg wants to merge 38 commits from port/port-attachments-2 into port/port-attachments
Owner

port: attachments, slice 2 of 3 (embeds: name strip, galleries, files, video, audio) · stacked on slice 1

Replaces the picture-only widget (one version, not two) with the mockup's embed set. Markdown stays plain: ![alt](<../Photos/…> "layout"), several on one line (spaces) are a gallery.

  • Name strip on pictures: the name on the lower edge over a fading blur, with w × h · 152 KB on the right (size from attachments.meta; the facts line drops the pixels under 420 px, as the mockup).
  • Galleries: two or more pictures on one line. Stack (default): bounce cards, same fan/timing/curves as the mockup (reimplemented from sveltebits' bounce-cards, no GSAP), hover pushes the others aside, tap picks and opens; the fan is sized from the line's width at the first frame. Grid: "grid" after the first picture's address. The viewer goes through the gallery with ← → ("2 of 5").
  • Files and documents: a coloured tile with the type, name, size and kind in words; a PDF shows its page count (attachments.meta, read from the file) in a card. A tap opens it in its own tab (downloads for non-showable types).
  • Video: a card with a play button; the <video> element (and its download) exists only after the first tap. Recordings: round play button, name and a row of bars that fill as it plays, tap to jump; the real waveform is decoded when first played.
  • Attach with several photos writes one gallery line; files and videos keep their own lines. Documents are ![name](<../Documents/…>) embeds now (were plain links in slice 1).
  • Layout via the context menu (right-click): Card/Large for pictures and video, Stack/Grid for galleries, File/Card for PDFs; written as the quoted word after the address; Open, Download, Remove. (The mini toolbar is slice 3.)
  • A playing video or recording is not torn down when the file's facts (size) arrive (updateDOM + a signature of the line); a picture that cannot be drawn here (HEIC outside Safari) falls back to its file row.

Gates (head cd58ab26; Chromium dev-01, WebKit dev-02, CI=1 --retries=0, ~/bin/slot)

  • editor vitest 521 pass (embeds.test.ts, 10), tsc ok; web vitest 87 pass; svelte-check 0 errors.
  • e2e/embeds.spec.ts (strip with size; gallery stack → grid via menu + viewer arrows; Attach with two photos = one line; PDF pages + file row + open; video card/player + recording; Enter opens) + pictures + bar-stable + phone-bars: chromium 262 pass; webkit 262 pass. Linux WebKit has no media backend (a <video> element crashes the page), so the play click runs on Chromium only.

Bundle

Slice 2 adds +5.9 KB first paint / +6.3 KB total over slice 1 (323,395 (+9.2 KB) first paint at this slice).

Screenshots

Control host: ~/Developer/Worktrees/shots/port-attachments/ (product-* ↔ mockup-*, 1440x900 and 390x844). Pairs here: picture, gallery stack, video, audio, pdf/file, viewer; gallery grid and large are product-only (the mockup's grid/large are the same states, other notes).

Not ported yet, and why

  • A PDF's first-page picture (the mockup shows a rendered page): needs a server-side renderer or pdf.js; the card shows its tile, name and page count.
  • Video poster / dimensions: the server reads dimensions for pictures only; the video box is 16:9.
  • Mockup's HEIC conversion; code and CSV previews (kind: code|csv); map and web-link cards.

🤖 Generated with Claude Code

## port: attachments, slice 2 of 3 (embeds: name strip, galleries, files, video, audio) · stacked on slice 1 Replaces the picture-only widget (one version, not two) with the mockup's embed set. Markdown stays plain: `![alt](<../Photos/…> "layout")`, several on one line (spaces) are a gallery. - **Name strip** on pictures: the name on the lower edge over a fading blur, with `w × h · 152 KB` on the right (size from `attachments.meta`; the facts line drops the pixels under 420 px, as the mockup). - **Galleries**: two or more pictures on one line. Stack (default): bounce cards, same fan/timing/curves as the mockup (reimplemented from sveltebits' bounce-cards, no GSAP), hover pushes the others aside, tap picks and opens; the fan is sized from the line's width at the first frame. Grid: `"grid"` after the first picture's address. The viewer goes through the gallery with ← → ("2 of 5"). - **Files and documents**: a coloured tile with the type, name, size and kind in words; a PDF shows its **page count** (`attachments.meta`, read from the file) in a card. A tap opens it in its own tab (downloads for non-showable types). - **Video**: a card with a play button; the `<video>` element (and its download) exists only after the first tap. **Recordings**: round play button, name and a row of bars that fill as it plays, tap to jump; the real waveform is decoded when first played. - **Attach with several photos** writes one gallery line; files and videos keep their own lines. Documents are `![name](<../Documents/…>)` embeds now (were plain links in slice 1). - **Layout via the context menu** (right-click): Card/Large for pictures and video, Stack/Grid for galleries, File/Card for PDFs; written as the quoted word after the address; Open, Download, Remove. (The mini toolbar is slice 3.) - A playing video or recording is not torn down when the file's facts (size) arrive (`updateDOM` + a signature of the line); a picture that cannot be drawn here (HEIC outside Safari) falls back to its file row. ### Gates (head `cd58ab26`; Chromium dev-01, WebKit dev-02, `CI=1 --retries=0`, `~/bin/slot`) - editor `vitest` 521 pass (`embeds.test.ts`, 10), `tsc` ok; web `vitest` 87 pass; `svelte-check` 0 errors. - `e2e/embeds.spec.ts` (strip with size; gallery stack → grid via menu + viewer arrows; Attach with two photos = one line; PDF pages + file row + open; video card/player + recording; Enter opens) + `pictures` + `bar-stable` + `phone-bars`: chromium 262 pass; webkit 262 pass. Linux WebKit has no media backend (a `<video>` element crashes the page), so the play click runs on Chromium only. ### Bundle Slice 2 adds +5.9 KB first paint / +6.3 KB total over slice 1 (323,395 (+9.2 KB) first paint at this slice). ### Screenshots Control host: `~/Developer/Worktrees/shots/port-attachments/` (`product-*` ↔ `mockup-*`, 1440x900 and 390x844). Pairs here: picture, gallery stack, video, audio, pdf/file, viewer; gallery grid and large are product-only (the mockup's grid/large are the same states, other notes). ### Not ported yet, and why - A PDF's **first-page picture** (the mockup shows a rendered page): needs a server-side renderer or pdf.js; the card shows its tile, name and page count. - Video **poster** / dimensions: the server reads dimensions for pictures only; the video box is 16:9. - Mockup's HEIC conversion; code and CSV previews (`kind: code|csv`); map and web-link cards. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kayg added 13 commits 2026-11-02 13:16:25 +00:00
Author
Owner

First-paint budget (#866) at head e492b8d6: first_js_brotli 239.775 KB (limit 240, ok). The embed widgets, galleries, players and file rows are in the same lazy attachments chunk (7.04 KB brotli, needs a web.bundle.chunk.attachments budget line); the editor reaches them through a subpath export (@calternal/editor/embeds), so the first paint never does. Over: web.bundle.chunk.PictureViewer 1.161 KB > 1.1 (gallery stepping and the count line; the only change to that chunk since #846's 0.996). Not in kayg's #840–#853 allotment: this PR is new, so its chunk lines need his call. Gates: Chromium 262 and WebKit 262 pass (embeds, pictures, bar-stable, phone-bars), editor 521 / web 87 unit tests.

**First-paint budget (#866) at head `e492b8d6`:** first_js_brotli **239.775 KB** (limit 240, ok). The embed widgets, galleries, players and file rows are in the same lazy `attachments` chunk (7.04 KB brotli, needs a `web.bundle.chunk.attachments` budget line); the editor reaches them through a subpath export (`@calternal/editor/embeds`), so the first paint never does. Over: `web.bundle.chunk.PictureViewer` 1.161 KB > 1.1 (gallery stepping and the count line; the only change to that chunk since #846's 0.996). Not in kayg's #840–#853 allotment: this PR is new, so its chunk lines need his call. Gates: Chromium 262 and WebKit 262 pass (embeds, pictures, bar-stable, phone-bars), editor 521 / web 87 unit tests.
Author
Owner

housekeeping #1009: rebased+failing, left open for review.

restacked embed slice two on #846. preserved final lazy embed modules alongside dev covers, frontmatter filter, picker styles and lazy code. restored final lazy embed facet test harness.

head: 1bed8a81d8a0cc742d82bd0a51845834af00904f; based on dev cd7f84432febfc63b788fbb9b9f9950452982d74, pushed with the original exact force-with-lease.

frontend frozen install, check, lint, build and unit tests passed. bundle gate failed. first-paint brotli vs dev: JS 231011 B (+234 B), CSS 18386 B (+0 B).

Rust gate results (Mac VM):

exit command
0 cargo fmt -p calternal-attachments -- --check
0 cargo check -j4 --locked -p calternal-attachments --all-targets
0 cargo clippy -j4 --locked -p calternal-attachments --all-targets -- -D warnings
0 cargo test -j4 --locked -p calternal-attachments

targeted Chromium and WebKit specs at 1440 and 390 never started. the audit waited more than three hours behind the live daily-train-1107 locks (first queue at 23:45 IST, 7 November; final queue cleanup after 02:46 IST, 8 November). browser validation and UI captures are unrun. only this audit’s exact queued units were stopped; the train locks and owners were left intact. hot-path perf qualification is unrun.

all executed gates used the VM Nix environment, shared Mac lock and slot. exact commands, source heads, logs and dev baseline. frontend inputs are identical to the tested frontend tree when a Rust-only repair changed the head.

original-head cherry/patch inventory, pr-860.md; unmatched entries are missing-patch candidates, with semantic overlap/replacements noted above.

final queue, cleanup and source-verification evidence.

housekeeping #1009: rebased+failing, left open for review. restacked embed slice two on #846. preserved final lazy embed modules alongside dev covers, frontmatter filter, picker styles and lazy code. restored final lazy embed facet test harness. head: `1bed8a81d8a0cc742d82bd0a51845834af00904f`; based on dev `cd7f84432febfc63b788fbb9b9f9950452982d74`, pushed with the original exact force-with-lease. frontend frozen install, check, lint, build and unit tests passed. bundle gate failed. first-paint brotli vs dev: JS 231011 B (+234 B), CSS 18386 B (+0 B). Rust gate results (Mac VM): | exit | command | |---|---| | 0 | `cargo fmt -p calternal-attachments -- --check` | | 0 | `cargo check -j4 --locked -p calternal-attachments --all-targets` | | 0 | `cargo clippy -j4 --locked -p calternal-attachments --all-targets -- -D warnings` | | 0 | `cargo test -j4 --locked -p calternal-attachments` | targeted Chromium and WebKit specs at 1440 and 390 never started. the audit waited more than three hours behind the live daily-train-1107 locks (first queue at 23:45 IST, 7 November; final queue cleanup after 02:46 IST, 8 November). browser validation and UI captures are unrun. only this audit’s exact queued units were stopped; the train locks and owners were left intact. hot-path perf qualification is unrun. all executed gates used the VM Nix environment, shared Mac lock and slot. [exact commands, source heads, logs and dev baseline](https://git.kayg.org/attachments/870a0fef-2e31-425c-aea6-eb87a66d365a). frontend inputs are identical to the tested frontend tree when a Rust-only repair changed the head. [original-head cherry/patch inventory](https://git.kayg.org/attachments/e05a38dc-c028-4d77-8655-efa9d5c8ea50), `pr-860.md`; unmatched entries are missing-patch candidates, with semantic overlap/replacements noted above. [final queue, cleanup and source-verification evidence](https://git.kayg.org/attachments/7ab315a5-4b6c-4af1-971b-cdae2637adae).
Author
Owner

Final-port test pass (issue #1009), READY: no: 1 real failure at 1440; bundle gate fails.

head 1bed8a81d8a0cc742d82bd0a51845834af00904f

Targeted specs, CI=1 --retries=0 --workers=1, dev-01 Chromium and dev-02 WebKit (through ~/bin/slot), against a dev-head backend (cd7f84432).

spec Chromium 1440 Chromium 390 WebKit 1440 WebKit 390
embeds 5 pass, 1 fail 6/6 5 pass, 1 fail 6/6

Failing: embeds.spec.ts:101 Attach with several pictures writes one gallery line: no 'Attach' button in .bar-wrap on desktop.

Bundle gate: perf/web.sh --bundle-only --gate (the 17 failures from the housekeeping audit). Per the gate's own rule (total_js_brotli budget must equal the sum of the named chunk budgets) a cap can only move between named chunks; there is no donor chunk inside this PR, so a fix is a code cut or a decision by kayg. I raised nothing and moved nothing.

  • Dev's toolbar shows Attach on the phone only (and its Photos row is disabled on dev). The PR enables the rows but the spec expects a desktop Attach button that no longer exists. Needs a design decision for the desktop entry (Insert sheet?).
  • Gate: attachments 6.69 KB has no budget, PictureViewer 1.167 vs 1.1.

Not done: Not run yet: Mac VM Safari blur/frame check and perf ABAB x5 on the perf VMs (they need a ready PR first); light/dark 2x screenshots against the mockup are only done for #888 (see its comment).

Not marked READY FOR MERGE.

**Final-port test pass (issue #1009), READY: no: 1 real failure at 1440; bundle gate fails.** head `1bed8a81d8a0cc742d82bd0a51845834af00904f` Targeted specs, `CI=1 --retries=0 --workers=1`, dev-01 Chromium and dev-02 WebKit (through `~/bin/slot`), against a dev-head backend (`cd7f84432`). | spec | Chromium 1440 | Chromium 390 | WebKit 1440 | WebKit 390 | |---|---|---|---|---| | embeds | 5 pass, 1 fail | 6/6 | 5 pass, 1 fail | 6/6 | Failing: `embeds.spec.ts:101 Attach with several pictures writes one gallery line`: no 'Attach' button in `.bar-wrap` on desktop. **Bundle gate:** `perf/web.sh --bundle-only --gate` (the 17 failures from the housekeeping audit). Per the gate's own rule (`total_js_brotli` budget must equal the sum of the named chunk budgets) a cap can only move between named chunks; there is no donor chunk inside this PR, so a fix is a code cut or a decision by kayg. I raised nothing and moved nothing. - Dev's toolbar shows Attach on the phone only (and its Photos row is disabled on dev). The PR enables the rows but the spec expects a desktop Attach button that no longer exists. Needs a design decision for the desktop entry (Insert sheet?). - Gate: `attachments` 6.69 KB has no budget, `PictureViewer` 1.167 vs 1.1. **Not done:** Not run yet: Mac VM Safari blur/frame check and perf ABAB x5 on the perf VMs (they need a ready PR first); light/dark 2x screenshots against the mockup are only done for #888 (see its comment). Not marked READY FOR MERGE.
Author
Owner

READY FOR MERGE: attachments 2, embeds (head 860e3647d, on #846)

Merged dev fe2efbf11 (#1044) into the branch (clean; it also merges cleanly with the newer 4274dafb4), budgets regenerated from a fresh build, bundle gate clean, specs on dev-02 with calternal-h861, chromium and webkit at 1440 and 390. Dev baseline: 228023 B plain / 227378 B normalised / CSS 18236.

first paint plain / normalised CSS budget total
228110 / 227742 B (+87 / +364 B; accepted) 18236 538.67 KiB

Specs (+ embeds): 152 passed, 36 skipped, 0 failed.

## READY FOR MERGE: attachments 2, embeds (head `860e3647d`, on #846) Merged dev `fe2efbf11` (#1044) into the branch (clean; it also merges cleanly with the newer `4274dafb4`), budgets regenerated from a fresh build, bundle gate clean, specs on dev-02 with `calternal-h861`, chromium and webkit at 1440 and 390. Dev baseline: 228023 B plain / 227378 B normalised / CSS 18236. | first paint plain / normalised | CSS | budget total | |---|---|---| | 228110 / 227742 B (**+87 / +364 B**; accepted) | 18236 | 538.67 KiB | Specs (+ embeds): 152 passed, 36 skipped, 0 failed.
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
This pull request is broken due to missing fork information.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin port/port-attachments-2:port/port-attachments-2
git switch port/port-attachments-2

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch port/port-attachments
git merge --no-ff port/port-attachments-2
git switch port/port-attachments-2
git rebase port/port-attachments
git switch port/port-attachments
git merge --ff-only port/port-attachments-2
git switch port/port-attachments-2
git rebase port/port-attachments
git switch port/port-attachments
git merge --no-ff port/port-attachments-2
git switch port/port-attachments
git merge --squash port/port-attachments-2
git switch port/port-attachments
git merge --ff-only port/port-attachments-2
git switch port/port-attachments
git merge port/port-attachments-2
git push origin port/port-attachments
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!860
No description provided.