Tasks UI slice 4: task card and property pickers #889

Closed
kayg wants to merge 1 commit from port/tasks-3 into port/tasks-3-s3
Owner

Stacked on #888 (slice 3) → #832. Slice 4 of the Tasks UI port plan: the task card and the property pickers.

What it does

  • Opening a task (#/tasks/<list>/<id>): right pane on desktop (with a Close), third screen on a phone (back goes to the list; the status bar takes the paper's colour). The row is a button, selected while open.
  • The card (TaskCard): box + title, the description, and the properties as one row of pills at the bottom (When, Deadline, Repeat, Priority, Tags, +), with the picker of the pill just tapped growing out of it (mockup TaskCard motion and values).
  • Pickers, each a real tasks.update (null clears): When and Deadline (type plain words with the Composer's recogniser, or a month grid), Repeat (Daily/Weekly/Monthly/Custom → RRULE), Priority (Urgent red, High amber, Medium blue, Low grey, from the chips' own AA tokens), Tags (chips to remove + the shared lazy TagPicker to add), More (Someday through tasks.set-status, Pinned).
  • The row and the card show a change in the next frame (edits) and let go of it once the list and the card have been asked again. A refusal puts it back and says so once ("Edit this one in source view" for hand-written odd YAML, W5).

Not ported, and why

  • Description is read-only here (plain paragraphs; the whole body opens Notes on double-click or Enter). The Notes editor is one instance with global state (lib/editing.svelte, and Notes stays mounted behind Tasks), so a second editor in this pane would share it. The card shows the task note's body (notes.get) Inline editing needs the editor made multi-instance: its own slice.
  • Title is not editable: no action renames a task. Missing server action: tasks.update { title } (rename sync both ways, TQ11).
  • Waiting on…, Duplicate, Delete, Copy link: Duplicate and Delete have no server action yet (tasks.duplicate, tasks.delete are not on dev).
  • The When field itself (the typed sentence, read-back, chips, mic) is slice 6; here When is the date picker in the capsule's sheet.
  • No Undo toast for property changes (the pill shows the old value one tap away); tasks.update returns no job.

Gates: first comment.

🤖 Generated with Claude Code

Stacked on #888 (slice 3) → #832. Slice 4 of the Tasks UI port plan: the task card and the property pickers. **What it does** - Opening a task (`#/tasks/<list>/<id>`): right pane on desktop (with a Close), third screen on a phone (back goes to the list; the status bar takes the paper's colour). The row is a button, selected while open. - The card (`TaskCard`): box + title, the description, and the properties as one row of pills at the bottom (When, Deadline, Repeat, Priority, Tags, +), with the picker of the pill just tapped growing out of it (mockup `TaskCard` motion and values). - Pickers, each a real `tasks.update` (`null` clears): When and Deadline (type plain words with the Composer's recogniser, or a month grid), Repeat (Daily/Weekly/Monthly/Custom → RRULE), Priority (Urgent red, High amber, Medium blue, Low grey, from the chips' own AA tokens), Tags (chips to remove + the shared lazy TagPicker to add), More (Someday through `tasks.set-status`, Pinned). - The row and the card show a change in the next frame (`edits`) and let go of it once the list and the card have been asked again. A refusal puts it back and says so once ("Edit this one in source view" for hand-written odd YAML, W5). **Not ported, and why** - **Description is read-only here** (plain paragraphs; the whole body opens Notes on double-click or Enter). The Notes editor is one instance with global state (`lib/editing.svelte`, and Notes stays mounted behind Tasks), so a second editor in this pane would share it. The card shows the task note's body (`notes.get`) Inline editing needs the editor made multi-instance: its own slice. - **Title is not editable**: no action renames a task. Missing server action: `tasks.update { title }` (rename sync both ways, TQ11). - Waiting on…, Duplicate, Delete, Copy link: Duplicate and Delete have no server action yet (`tasks.duplicate`, `tasks.delete` are not on dev). - The When field itself (the typed sentence, read-back, chips, mic) is slice 6; here When is the date picker in the capsule's sheet. - No Undo toast for property changes (the pill shows the old value one tap away); `tasks.update` returns no job. Gates: first comment. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Tasks UI slice 4: the task card (title, notes, properties row) and the pickers (When, Deadline, Repeat, Priority, Tags, More)
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
2b079b27a5
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Author
Owner

Gates for slice 4 (head 080b8a7e, includes #888 at 3b7c3e33)

dev-02 (aarch64), CI=1 --retries=0 --workers=1, through ~/bin/slot. Specs: tasks-tick, tasks-lists, tasks-lazy, tasks-no-hscroll, task-check, tasks-card.

Project Result
chromium-1440 29 passed
chromium-390 28 passed
webkit-1440 29 passed
webkit-390 28 passed
vitest (web) 96 passed
svelte-check 0 errors in src/tasks (same 2 pre-existing errors in e2e/review4-signout.spec.ts)

dev-01 Chromium on the earlier head a88ce286: 29 (1440) and 28 (390) passed; its /tmp ran out of inodes afterwards, so the final heads ran on dev-02.

tasks-card proves: a row opens its card (#/tasks/today/<id>), Close/Back leave it; Priority, Deadline, Repeat, When (typed words and Clear) and Someday each write through the real action and tasks.get shows it; Tags opens the add button. Bugs found and fixed by the specs: the open picker closed itself whenever the same task was asked for again (the effect read the row object, not its id); a picker opened again showed the words typed the last time; the compose dock covered the properties on a phone.

Bundle (brotli, KB)

head first-paint JS all JS Tasks chunk JS / CSS
#888 233.38 321.87 5.95 / 1.97
this PR 233.84 326.75 10.23 / 3.24

First paint stays ≤ 240 (the +0.46 is the shared bundle's, not Tasks'; Tasks is still one lazy chunk). TasksScreen for budgets.json: 10.23 JS + 3.24 CSS (cap now 20).

Screenshots

~/Developer/Worktrees/shots/port-tasks-3/: s4-card-*, s4-priority-*, s4-when-* (product), m-s4-card-*, m-s4-more-* (mockup). Attached. Known differences from the mockup, shown honestly: the mockup's properties are the floating bottom-bar capsule (slice 6 builds that morph; here they are a dock at the foot of the card); the mockup description is editable in place (here it is read only, see the PR body); the mockup shows the Views section in the sidebar.

Not measured

No Mac VM frame numbers (card open, picker spring); the ?pick=when hand-off from the swipe's Date lives in #893.

s4-card-1440x900.png

s4-card-1440x900.png

m-s4-card-1440x900.png

m-s4-card-1440x900.png

s4-priority-1440x900.png

s4-priority-1440x900.png

s4-when-1440x900.png

s4-when-1440x900.png

m-s4-more-1440x900.png

m-s4-more-1440x900.png

s4-card-390x844.png

s4-card-390x844.png

m-s4-card-390x844.png

m-s4-card-390x844.png

s4-priority-390x844.png

s4-priority-390x844.png

s4-when-390x844.png

s4-when-390x844.png

m-s4-more-390x844.png

m-s4-more-390x844.png

## Gates for slice 4 (head `080b8a7e`, includes #888 at `3b7c3e33`) dev-02 (aarch64), `CI=1 --retries=0 --workers=1`, through `~/bin/slot`. Specs: `tasks-tick`, `tasks-lists`, `tasks-lazy`, `tasks-no-hscroll`, `task-check`, `tasks-card`. | Project | Result | |---|---| | chromium-1440 | 29 passed | | chromium-390 | 28 passed | | webkit-1440 | 29 passed | | webkit-390 | 28 passed | | vitest (web) | 96 passed | | svelte-check | 0 errors in `src/tasks` (same 2 pre-existing errors in `e2e/review4-signout.spec.ts`) | dev-01 Chromium on the earlier head `a88ce286`: 29 (1440) and 28 (390) passed; its `/tmp` ran out of inodes afterwards, so the final heads ran on dev-02. `tasks-card` proves: a row opens its card (`#/tasks/today/<id>`), Close/Back leave it; Priority, Deadline, Repeat, When (typed words and Clear) and Someday each write through the real action and `tasks.get` shows it; Tags opens the add button. Bugs found and fixed by the specs: the open picker closed itself whenever the same task was asked for again (the effect read the row object, not its id); a picker opened again showed the words typed the last time; the compose dock covered the properties on a phone. ## Bundle (brotli, KB) | head | first-paint JS | all JS | Tasks chunk JS / CSS | |---|---|---|---| | #888 | 233.38 | 321.87 | 5.95 / 1.97 | | this PR | 233.84 | 326.75 | 10.23 / 3.24 | First paint stays ≤ 240 (the +0.46 is the shared bundle's, not Tasks'; Tasks is still one lazy chunk). `TasksScreen` for budgets.json: 10.23 JS + 3.24 CSS (cap now 20). ## Screenshots `~/Developer/Worktrees/shots/port-tasks-3/`: `s4-card-*`, `s4-priority-*`, `s4-when-*` (product), `m-s4-card-*`, `m-s4-more-*` (mockup). Attached. Known differences from the mockup, shown honestly: the mockup's properties are the floating bottom-bar capsule (slice 6 builds that morph; here they are a dock at the foot of the card); the mockup description is editable in place (here it is read only, see the PR body); the mockup shows the Views section in the sidebar. ## Not measured No Mac VM frame numbers (card open, picker spring); the `?pick=when` hand-off from the swipe's Date lives in #893. **s4-card-1440x900.png** ![s4-card-1440x900.png](https://git.kayg.org/attachments/d394bd61-04de-42c5-9a47-b1f790abcd08) **m-s4-card-1440x900.png** ![m-s4-card-1440x900.png](https://git.kayg.org/attachments/5e16590c-c3fb-4114-b01b-a02ab0bcfd2d) **s4-priority-1440x900.png** ![s4-priority-1440x900.png](https://git.kayg.org/attachments/5c54f095-9649-42b3-92ac-f44ba00d3c2a) **s4-when-1440x900.png** ![s4-when-1440x900.png](https://git.kayg.org/attachments/34571abe-85e1-447a-8853-2af4128d6da4) **m-s4-more-1440x900.png** ![m-s4-more-1440x900.png](https://git.kayg.org/attachments/0fac325f-e20f-4e1e-b79b-6c56b041591a) **s4-card-390x844.png** ![s4-card-390x844.png](https://git.kayg.org/attachments/8a3408ba-3626-4bb3-8641-ce2a15d91a30) **m-s4-card-390x844.png** ![m-s4-card-390x844.png](https://git.kayg.org/attachments/1b0efc0e-f78e-4404-ad24-0b1cffdfe898) **s4-priority-390x844.png** ![s4-priority-390x844.png](https://git.kayg.org/attachments/21fdbc7d-ee59-4142-94b0-3e6b5cba53ce) **s4-when-390x844.png** ![s4-when-390x844.png](https://git.kayg.org/attachments/dbade2ba-66a7-4c09-a272-9bf0e66f411b) **m-s4-more-390x844.png** ![m-s4-more-390x844.png](https://git.kayg.org/attachments/e58d6ccd-6441-4722-9a59-28b3ba8f0da3)
kayg changed title from WIP: Tasks UI slice 4: task card and property pickers to Tasks UI slice 4: task card and property pickers 2026-11-03 10:54:19 +00:00
Author
Owner

Round 2 (head f74294cd): mockup values on the card

  1. Card page is the mockup's opaque paper in a rim of glass (8 px), not see-through glass. (An earlier set of shots predated this; the new ones show it. Spec: the page's background is opaque.)
  2. Description: the task note's body as plain paragraphs, no heading and no button. The whole body is the link into Notes: double-click or Enter opens it there (role=link, focusable). When the note has no body the muted placeholder "Notes" shows, as in the mockup. Spec: text shows, no "Edit in Notes" button, Enter opens #/note/<id>.
  3. List width: the middle list is exactly 380 px when a task is open (spec measures it).
  4. Properties are the mockup's floating capsule centred at the card's foot, the shared Bar (@calternal/ui/bar) that Notes uses, and each picker is the capsule morphing into its sheet (SheetBody): tap outside, Esc or drag down puts it away. No docked bar, no divider (spec: centred within 2 px, 30 px from the foot, .dock gone). Toolbar/Pill do not exist in packages/ui; the shared bar is the capsule.
  5. The pills' icons take --glyph-bar (20 px at 1.9 on a desktop, 22 at 2.1 on a phone, as the mockup's icon-bar.css), and the words start 60 px in as the mockup.

Gates, dev-02, CI=1 --retries=0 --workers=1, tick / lists / lazy / no-hscroll / task-check / card: chromium-1440 33 passed, chromium-390 31, webkit-1440 33, webkit-390 31. vitest 96 passed; svelte-check 0 errors in src/tasks (same 2 pre-existing e2e errors). Bundle (brotli KB): first-paint JS 233.53, all JS 326.57, TasksScreen 10.33 JS + 3.18 CSS.
Shots (product, mockup m-): ~/Developer/Worktrees/shots/port-tasks-3/s4-*, m-s4-*, attached. Remaining differences: the product capsule has two more pills (Priority, Tags) because the server has those properties; the mockup's has Views in the sidebar.

s4-card-1440x900.png

s4-card-1440x900.png

m-s4-card-1440x900.png

m-s4-card-1440x900.png

s4-priority-1440x900.png

s4-priority-1440x900.png

s4-when-1440x900.png

s4-when-1440x900.png

m-s4-more-1440x900.png

m-s4-more-1440x900.png

s4-card-390x844.png

s4-card-390x844.png

m-s4-card-390x844.png

m-s4-card-390x844.png

s4-priority-390x844.png

s4-priority-390x844.png

s4-when-390x844.png

s4-when-390x844.png

m-s4-more-390x844.png

m-s4-more-390x844.png

## Round 2 (head `f74294cd`): mockup values on the card 1. **Card page is the mockup's opaque paper** in a rim of glass (8 px), not see-through glass. (An earlier set of shots predated this; the new ones show it. Spec: the page's background is opaque.) 2. **Description**: the task note's body as plain paragraphs, no heading and no button. The whole body is the link into Notes: double-click or Enter opens it there (`role=link`, focusable). When the note has no body the muted placeholder "Notes" shows, as in the mockup. Spec: text shows, no "Edit in Notes" button, Enter opens `#/note/<id>`. 3. **List width**: the middle list is exactly 380 px when a task is open (spec measures it). 4. **Properties** are the mockup's floating capsule centred at the card's foot, the shared `Bar` (`@calternal/ui/bar`) that Notes uses, and each picker is the capsule morphing into its sheet (`SheetBody`): tap outside, Esc or drag down puts it away. No docked bar, no divider (spec: centred within 2 px, 30 px from the foot, `.dock` gone). `Toolbar`/`Pill` do not exist in `packages/ui`; the shared bar is the capsule. 5. The pills' icons take `--glyph-bar` (20 px at 1.9 on a desktop, 22 at 2.1 on a phone, as the mockup's icon-bar.css), and the words start 60 px in as the mockup. Gates, dev-02, `CI=1 --retries=0 --workers=1`, tick / lists / lazy / no-hscroll / task-check / card: chromium-1440 33 passed, chromium-390 31, webkit-1440 33, webkit-390 31. vitest 96 passed; svelte-check 0 errors in `src/tasks` (same 2 pre-existing e2e errors). Bundle (brotli KB): first-paint JS 233.53, all JS 326.57, `TasksScreen` 10.33 JS + 3.18 CSS. Shots (product, mockup `m-`): `~/Developer/Worktrees/shots/port-tasks-3/s4-*`, `m-s4-*`, attached. Remaining differences: the product capsule has two more pills (Priority, Tags) because the server has those properties; the mockup's has Views in the sidebar. **s4-card-1440x900.png** ![s4-card-1440x900.png](https://git.kayg.org/attachments/ccf21753-97c5-4d12-a4a2-0c5542ce33ea) **m-s4-card-1440x900.png** ![m-s4-card-1440x900.png](https://git.kayg.org/attachments/87090bb7-156f-43f2-9e1c-937394558506) **s4-priority-1440x900.png** ![s4-priority-1440x900.png](https://git.kayg.org/attachments/d5de8081-c01c-4026-b89e-faa01026c34b) **s4-when-1440x900.png** ![s4-when-1440x900.png](https://git.kayg.org/attachments/b0492f6b-ee07-4f91-8e15-bbb9633d7c35) **m-s4-more-1440x900.png** ![m-s4-more-1440x900.png](https://git.kayg.org/attachments/f1b13cfd-8514-4b7d-805d-f4013561bb78) **s4-card-390x844.png** ![s4-card-390x844.png](https://git.kayg.org/attachments/12043784-fd5d-47d9-8d67-4721286aaa7f) **m-s4-card-390x844.png** ![m-s4-card-390x844.png](https://git.kayg.org/attachments/acaa2849-3759-4edf-87f2-12bd32f3d738) **s4-priority-390x844.png** ![s4-priority-390x844.png](https://git.kayg.org/attachments/27318360-4086-4437-a740-93e0b4a477a1) **s4-when-390x844.png** ![s4-when-390x844.png](https://git.kayg.org/attachments/fd47639f-7b38-47e2-a182-7cde61edd193) **m-s4-more-390x844.png** ![m-s4-more-390x844.png](https://git.kayg.org/attachments/42046129-7225-4a32-b335-c0b7b6e5d20a)
Author
Owner

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

restacked on #888. kept shared Pill and current other-client retry behavior; resolved phone navigation at depth two. reused shared h32 Pill for close; removed duplicate stale TaskCard panel effect.

head: 718f479d70cbacc63f1f642c73a3ac1f8526b223; 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 230735 B (-42 B), CSS 18386 B (+0 B).

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-889.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 on #888. kept shared Pill and current other-client retry behavior; resolved phone navigation at depth two. reused shared h32 Pill for close; removed duplicate stale TaskCard panel effect. head: `718f479d70cbacc63f1f642c73a3ac1f8526b223`; 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 230735 B (-42 B), CSS 18386 B (+0 B). 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-889.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: specs pass; bundle gate fails.

head 718f479d70cbacc63f1f642c73a3ac1f8526b223

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
tasks-card 6/6 5/5 6/6 5/5

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.

  • TasksScreen 10.30 KB vs 5.1 cap, recognise chunk has no budget (1.48 KB). First paint JS -42 B, CSS +0 B vs dev. Stacked on #888, which has the same cap problem.

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: specs pass; bundle gate fails.** head `718f479d70cbacc63f1f642c73a3ac1f8526b223` 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 | |---|---|---|---|---| | tasks-card | 6/6 | 5/5 | 6/6 | 5/5 | **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. - `TasksScreen` 10.30 KB vs 5.1 cap, `recognise` chunk has no budget (1.48 KB). First paint JS -42 B, CSS +0 B vs dev. Stacked on #888, which has the same cap problem. **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

Final gates (issue #1009), 63d80a2682

head 63d80a26827a99c8653e688f1ad6f718b5b6fe26, on dev 2fa9e18fb (#888 merged).

READY FOR MERGE by the plain-build measure (-35 B). By the hash-normalised measure it is +50 B; see below.

gate result
first paint JS, plain build, median of 3 builds (the three are byte-identical) 231120 B vs dev 231155 B: -35 B
first paint JS, hash-normalised 230672 B vs 230622 B: +50 B
first paint CSS 18371 B vs 18371 B: +0 B
specs, Chromium 1440 + 390 (dev-01) tasks-card: 11 passed (1 skipped by design)
specs, WebKit 1440 + 390 (dev-02) 11 passed (1 skipped)
perf/web.sh --bundle-only --gate passes
perf ABAB x5 + control below (taken on the previous dev; this PR's hot paths are unchanged since)
screenshots, 2x light and dark, 1440 and 390; real Safari on #957 below

First paint, two ways. Plain: the build as shipped, brotli of the first-screen files (dev 231155 B JS, 18371 B CSS). Hash-normalised: the same files with the random chunk hashes replaced by a constant, which removes the 100 to 250 B that any change adds through the hashes (dev 230622 B). This PR touches the entry only in App.svelte (it hands the card the bar and the tag picker loader, so the first screen exports nothing new to the Tasks chunk: the entry's export list is identical to dev's) and it makes Tasks and Contacts load through one effect, which is shorter than before. The normalised +50 B is what is left after that; a 50-character change moves the compressed size by 100 to 400 B either way, so I tried 15 variants and kept the smallest.

Typing and open-note, perf-02, Chromium with 4x CPU slowdown, 10k Home, release server (dev head, 20eab8d3a) for every arm, rounds rotated, 5 rounds. devA and devB are the same dev build (the control). Median of the 5 per-run medians [min-max], ms:

metric devA devB (control) #957 (stack tip)
typing, long note p50 22.3 [22.1-22.6] 22.4 [21.8-23.4] 22.5 [21.8-26.2]
typing, long note CPU/key 26.9 [26.5-27.3] 27.4 [26.7-27.7] 27.2 [26.7-27.9]
typing, nested list p50 42.2 [42.1-42.9] 42.4 [42.1-43.1] 42.5 [41.9-43.5]
typing, nested list CPU/key 49.1 [48.8-50.9] 49.8 [49.0-50.4] 50.2 [49.1-51.0]
open note (blaze), shown p50 35.0 [34.3-35.8] 35.3 [34.0-36.4] 35.2 [33.5-35.3]
open note, back p50 30.9 [30.1-31.5] 31.2 [30.8-31.8] 31.7 [31.1-32.0]
flick through 50 notes, CPU 70.8 [70.5-77.1] 75.4 [72.2-82.7] 73.1 [69.4-77.3]
longest task in the flick 143 [133-151] 151 [150-184] 142 [129-173]

Every metric of the tip sits inside the dev-vs-dev control (the control itself differs by 4.6 ms in the flick's CPU). Two variants of the press registration (one effect per row; the press engine asked for on pointer intent) were built and measured in the same session (v1 76.4, v2 74.8 in the flick's CPU): neither beats the control, so neither is in the PRs. The earlier session (which showed +1.4 to +3.3 % on the tip) was one that the control spread covers as well.

Screenshots (2x; light and dark; 1440x900 and 390x844). Left: the approved mockup (review/calendar-2). Right: this PR.

list

mockup this PR
1440 light
1440 dark
390 light
390 dark

list-ticked

mockup this PR
1440 light a tick is interactive; not in the static mockup
1440 dark a tick is interactive; not in the static mockup
390 light a tick is interactive; not in the static mockup
390 dark a tick is interactive; not in the static mockup

card

mockup this PR
1440 light
1440 dark
390 light
390 dark

Differences from the mockup that are later slices or not ported: the view switcher beyond List and Board, the Views section, the big title, the share and more pills on the card, the mic in the When field; the product also shows its own sky backdrop and the seeded tasks above.

**Final gates (issue #1009), 63d80a26827a99c8653e688f1ad6f718b5b6fe26** head `63d80a26827a99c8653e688f1ad6f718b5b6fe26`, on dev `2fa9e18fb` (#888 merged). **READY FOR MERGE** by the plain-build measure (-35 B). By the hash-normalised measure it is +50 B; see below. | gate | result | |---|---| | first paint JS, plain build, median of 3 builds (the three are byte-identical) | **231120 B vs dev 231155 B: -35 B** | | first paint JS, hash-normalised | 230672 B vs 230622 B: **+50 B** | | first paint CSS | 18371 B vs 18371 B: +0 B | | specs, Chromium 1440 + 390 (dev-01) | tasks-card: 11 passed (1 skipped by design) | | specs, WebKit 1440 + 390 (dev-02) | 11 passed (1 skipped) | | `perf/web.sh --bundle-only --gate` | passes | | perf ABAB x5 + control | below (taken on the previous dev; this PR's hot paths are unchanged since) | | screenshots, 2x light and dark, 1440 and 390; real Safari on #957 | below | **First paint, two ways.** Plain: the build as shipped, brotli of the first-screen files (dev 231155 B JS, 18371 B CSS). Hash-normalised: the same files with the random chunk hashes replaced by a constant, which removes the 100 to 250 B that any change adds through the hashes (dev 230622 B). This PR touches the entry only in App.svelte (it hands the card the bar and the tag picker loader, so the first screen exports nothing new to the Tasks chunk: the entry's export list is identical to dev's) and it makes Tasks and Contacts load through one effect, which is shorter than before. The normalised +50 B is what is left after that; a 50-character change moves the compressed size by 100 to 400 B either way, so I tried 15 variants and kept the smallest. Typing and open-note, perf-02, Chromium with 4x CPU slowdown, 10k Home, release server (dev head, 20eab8d3a) for every arm, rounds rotated, 5 rounds. `devA` and `devB` are the same dev build (the control). Median of the 5 per-run medians [min-max], ms: | metric | devA | devB (control) | #957 (stack tip) | |---|---|---|---| | typing, long note p50 | 22.3 [22.1-22.6] | 22.4 [21.8-23.4] | 22.5 [21.8-26.2] | | typing, long note CPU/key | 26.9 [26.5-27.3] | 27.4 [26.7-27.7] | 27.2 [26.7-27.9] | | typing, nested list p50 | 42.2 [42.1-42.9] | 42.4 [42.1-43.1] | 42.5 [41.9-43.5] | | typing, nested list CPU/key | 49.1 [48.8-50.9] | 49.8 [49.0-50.4] | 50.2 [49.1-51.0] | | open note (blaze), shown p50 | 35.0 [34.3-35.8] | 35.3 [34.0-36.4] | 35.2 [33.5-35.3] | | open note, back p50 | 30.9 [30.1-31.5] | 31.2 [30.8-31.8] | 31.7 [31.1-32.0] | | flick through 50 notes, CPU | 70.8 [70.5-77.1] | 75.4 [72.2-82.7] | 73.1 [69.4-77.3] | | longest task in the flick | 143 [133-151] | 151 [150-184] | 142 [129-173] | Every metric of the tip sits inside the dev-vs-dev control (the control itself differs by 4.6 ms in the flick's CPU). Two variants of the press registration (one effect per row; the press engine asked for on pointer intent) were built and measured in the same session (v1 76.4, v2 74.8 in the flick's CPU): neither beats the control, so neither is in the PRs. The earlier session (which showed +1.4 to +3.3 % on the tip) was one that the control spread covers as well. ### Screenshots (2x; light and dark; 1440x900 and 390x844). Left: the approved mockup (review/calendar-2). Right: this PR. **list** | | mockup | this PR | |---|---|---| | 1440 light | ![](https://git.kayg.org/attachments/9392d208-d8c7-42ec-89f6-9fbdf26e8c2a) | ![](https://git.kayg.org/attachments/d65ed75f-762c-4e05-9fd5-1462dfa2735a) | | 1440 dark | ![](https://git.kayg.org/attachments/82f65563-4ca9-4feb-bfdd-41bbe63d8079) | ![](https://git.kayg.org/attachments/c58b81bf-c10b-4cb2-a640-b8e8123f79bf) | | 390 light | ![](https://git.kayg.org/attachments/1e921c19-718e-4d08-a98a-081ddc5db7b2) | ![](https://git.kayg.org/attachments/854ccbc2-c7c8-476c-a755-a4126ddfbf86) | | 390 dark | ![](https://git.kayg.org/attachments/c39a289b-5744-45b0-a8b4-957ff6ba0419) | ![](https://git.kayg.org/attachments/92fc2dbe-b879-4ac3-ae48-d68cc030aba6) | **list-ticked** | | mockup | this PR | |---|---|---| | 1440 light | a tick is interactive; not in the static mockup | ![](https://git.kayg.org/attachments/c7354556-b651-4f8f-b28a-22853cf1ce9b) | | 1440 dark | a tick is interactive; not in the static mockup | ![](https://git.kayg.org/attachments/d8ea65dc-c553-4227-be2f-8304479f86b6) | | 390 light | a tick is interactive; not in the static mockup | ![](https://git.kayg.org/attachments/a574e5d7-715e-4bba-a7f4-3489b9f6da62) | | 390 dark | a tick is interactive; not in the static mockup | ![](https://git.kayg.org/attachments/41476fd2-7242-4c43-83d9-81a0444a12e9) | **card** | | mockup | this PR | |---|---|---| | 1440 light | ![](https://git.kayg.org/attachments/2123141f-df5f-4fc8-badf-5b591a7562a7) | ![](https://git.kayg.org/attachments/443ac198-c844-403c-9445-a5a6570e68c8) | | 1440 dark | ![](https://git.kayg.org/attachments/b6bc9646-4538-4f45-9330-e53097ff6abf) | ![](https://git.kayg.org/attachments/d9383ebf-c699-4abe-b16d-b86f48058e4f) | | 390 light | ![](https://git.kayg.org/attachments/c095a878-cf3f-4997-8c53-a7ab9794a71f) | ![](https://git.kayg.org/attachments/68378b8b-8f54-4d7e-ad31-6c3d04ea8815) | | 390 dark | ![](https://git.kayg.org/attachments/540fa13f-1b4c-4e6e-a16d-b8b8f9118666) | ![](https://git.kayg.org/attachments/d4f1c95f-2f6b-45f0-81b2-8d9772f5ddda) | Differences from the mockup that are later slices or not ported: the view switcher beyond List and Board, the Views section, the big title, the share and more pills on the card, the mic in the When field; the product also shows its own sky backdrop and the seeded tasks above.
Author
Owner

Task card with a property picker (issue #1009), 63d80a2682

Product (Chromium, 2x) with the Priority picker open on the task card, next to the approved mockup's task card (review/calendar-2, 2x; the static mockup shows the card without a picker open).

Card with a picker open

mockup this PR
1440 light
1440 dark
390 light
390 dark
**Task card with a property picker (issue #1009), 63d80a26827a99c8653e688f1ad6f718b5b6fe26** Product (Chromium, 2x) with the Priority picker open on the task card, next to the approved mockup's task card (review/calendar-2, 2x; the static mockup shows the card without a picker open). **Card with a picker open** | | mockup | this PR | |---|---|---| | 1440 light | ![](https://git.kayg.org/attachments/5bfeddfd-9c69-4522-a020-9c60d9be090a) | ![](https://git.kayg.org/attachments/e58fedc9-9945-4aa8-b40c-e26523323180) | | 1440 dark | ![](https://git.kayg.org/attachments/b265b1bc-7ebc-47a5-b431-b0a4a8dbd916) | ![](https://git.kayg.org/attachments/17e67214-e660-4118-a4fb-aa0f2172ecf1) | | 390 light | ![](https://git.kayg.org/attachments/d1db081c-56bd-40ba-96a3-a7f4a8460bd3) | ![](https://git.kayg.org/attachments/75622e20-2163-4932-bed8-bf49cc7880cd) | | 390 dark | ![](https://git.kayg.org/attachments/e396477e-609b-424c-8da7-b4d987a7786f) | ![](https://git.kayg.org/attachments/dddb9f47-52e9-46bc-93cc-683e7c9012a0) |
Author
Owner

Merged to dev (head 63d80a268; merge chain 24b3d75db → 0635d3de7 → d4b32af94). Checked by the orchestrator: first paint gate, specs in Chromium and WebKit at 1440/390, typing/open-note ABAB inside the control, 2× crops vs mockup (switcher icons fixed). Matrix view not ported yet (separate follow-up). Ships with the next daily train.

Merged to dev (head 63d80a268; merge chain 24b3d75db → 0635d3de7 → d4b32af94). Checked by the orchestrator: first paint gate, specs in Chromium and WebKit at 1440/390, typing/open-note ABAB inside the control, 2× crops vs mockup (switcher icons fixed). Matrix view not ported yet (separate follow-up). Ships with the next daily train.
kayg closed this pull request 2026-11-09 04:54:46 +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!889
No description provided.