port: contacts edit fields (approved mockup, 3/4) #848

Closed
kayg wants to merge 13 commits from port/contacts-edit into dev
Owner

Slice 3 of 4 of the Contacts port. Stacked on #843 (base port/contacts-card; retarget to dev as the ones below merge).

What it is

Editing the card, with no edit mode: a line opens where it is.

  • Desktop: click copies (after a short beat, so a double-click can edit instead), double-click edits the line, hover ⋯ has the action, Copy and Edit. Phone: tap does the action, hold lists Copy, Edit and the action.
  • Enter or leaving a line keeps what was typed; Escape puts it back; an emptied line goes away, and its group with it when it was the last. While a line is open a "+ add phone" row sits under its group.
  • Add field… in the card's ⋯: phone, email, address, website, date, birthday, social, related name, nickname, pronouns, department (company and job title open the heading). Edit name: name, job title and company open in place. Add timestamp and double-click / tap on the Notes open them for typing; Notes made when there are none.
  • Pure edits in card.ts (tested, byte-exact elsewhere): set a line's label and value, remove a line and its emptied group, add to a group or start one (also in a note with no final newline or with CRLF), set/add/clear a once-only fact, rename the heading, rewrite the Notes. Only the lines an edit means to change are different afterwards (a test checks every other line of the note, for LF, CRLF and no final newline).
  • Writing (save.ts): the note is read fresh, one edit is made, and the whole text goes back through notes.replace with the version it was read at (the normal save path). A 409/412 reads again and makes the edit once more on the new text. The line to change is found again by what it says, so an edit never lands on another line.
  • The contact icons now register with the shared Icon (a three-line registerIcons hook) when the screen loads: menus that named an icon the set did not know drew the notes icon.

Decisions

  • Renaming only rewrites the heading: the note's file follows its heading (as for every note), so no contact-specific rename.
  • A line with no label whose value contains : gets its kind's usual label, so it is never read back as a label.
  • "Pending" mockup parts that need no backend but are not here: field templates, the hover "+" on a line, Mark as deceased, alternate birthday, phonetic names, name parts (prefix/middle/suffix). Listed in the final report.

Gates (CI=1 --retries=0, through ~/bin/slot, head 9c2571cc)

Same as the stack top (below): bun run check 0 errors; vitest 152 passed; contacts-edit.spec.ts (edit a line and only that line changes; Escape; empty line removes its group; Add field starts a group and a once-only line; rename; Notes with a timestamp) passes in chromium-1440/390 and webkit-1440/390 with the rest of the stack.

Budget (kayg approved, 2026-11-08 08:20 IST): kayg approved raising the lazy budget total, up to 543.9 KiB. perf/budgets.json in this PR sets each named lazy line to the largest size measured over the 17 approved ports after trimming (rounded up to 0.01 KiB) and the total to the sum of the lines (543.25 KiB). The same file is in the first PR of every stack, so whichever merges first carries it. First paint is judged separately and has not been raised.

Slice 3 of 4 of the Contacts port. **Stacked on #843** (base `port/contacts-card`; retarget to `dev` as the ones below merge). ## What it is Editing the card, with no edit mode: a line opens where it is. - **Desktop**: click copies (after a short beat, so a double-click can edit instead), double-click edits the line, hover ⋯ has the action, Copy and Edit. **Phone**: tap does the action, hold lists Copy, Edit and the action. - Enter or leaving a line keeps what was typed; Escape puts it back; an emptied line goes away, and its group with it when it was the last. While a line is open a "+ add phone" row sits under its group. - **Add field…** in the card's ⋯: phone, email, address, website, date, birthday, social, related name, nickname, pronouns, department (company and job title open the heading). **Edit name**: name, job title and company open in place. **Add timestamp** and double-click / tap on the Notes open them for typing; Notes made when there are none. - **Pure edits** in `card.ts` (tested, byte-exact elsewhere): set a line's label and value, remove a line and its emptied group, add to a group or start one (also in a note with no final newline or with CRLF), set/add/clear a once-only fact, rename the heading, rewrite the Notes. Only the lines an edit means to change are different afterwards (a test checks every other line of the note, for LF, CRLF and no final newline). - **Writing** (`save.ts`): the note is read fresh, one edit is made, and the whole text goes back through `notes.replace` with the version it was read at (the normal save path). A 409/412 reads again and makes the edit once more on the new text. The line to change is found again by what it says, so an edit never lands on another line. - The contact icons now register with the shared `Icon` (a three-line `registerIcons` hook) when the screen loads: menus that named an icon the set did not know drew the notes icon. ## Decisions - Renaming only rewrites the heading: the note's file follows its heading (as for every note), so no contact-specific rename. - A line with no label whose value contains `: ` gets its kind's usual label, so it is never read back as a label. - "Pending" mockup parts that need no backend but are not here: field templates, the hover "+" on a line, Mark as deceased, alternate birthday, phonetic names, name parts (prefix/middle/suffix). Listed in the final report. ## Gates (CI=1 --retries=0, through ~/bin/slot, head `9c2571cc`) Same as the stack top (below): `bun run check` 0 errors; vitest 152 passed; `contacts-edit.spec.ts` (edit a line and only that line changes; Escape; empty line removes its group; Add field starts a group and a once-only line; rename; Notes with a timestamp) passes in chromium-1440/390 and webkit-1440/390 with the rest of the stack. **Budget (kayg approved, 2026-11-08 08:20 IST):** kayg approved raising the lazy budget total, up to 543.9 KiB. `perf/budgets.json` in this PR sets each named lazy line to the largest size measured over the 17 approved ports after trimming (rounded up to 0.01 KiB) and the total to the sum of the lines (543.25 KiB). The same file is in the first PR of every stack, so whichever merges first carries it. First paint is judged separately and has not been raised.
Pure edits in card.ts (set a line, remove a line and its emptied group, add to a group or start one, once-only facts, heading, Notes),
written back through notes.replace with the version read (save.ts); the card's menu adds lines; the contact icons register with the
shared Icon when the screen loads.

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>
Contacts edit spec: blur the Notes box itself (WebKit keeps focus on a click of plain text)
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
9c2571cc5e
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
kayg changed target branch from port/contacts-card to dev 2026-11-07 20:33:23 +00:00
Author
Owner

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

rebased only the six still-missing contacts edit commits; earlier contact slices have semantic equivalents in dev. kept current icon registry. expanded icon test to include existing lazy Contacts registrations.

head: 0a1e931fbea199b75b09a740f350ae1c4cb82389; 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 230781 B (+4 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-848.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. rebased only the six still-missing contacts edit commits; earlier contact slices have semantic equivalents in dev. kept current icon registry. expanded icon test to include existing lazy Contacts registrations. head: `0a1e931fbea199b75b09a740f350ae1c4cb82389`; 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 230781 B (+4 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-848.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 0a1e931fbea199b75b09a740f350ae1c4cb82389

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
contacts-edit 5/5 5/5 5/5 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.

  • Chunks with no budget: cardmenu 0.54, edits 1.23, logic 1.71, names 0.62, Timeline 0.79 KB. Adding budgets means taking them from somewhere.

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 `0a1e931fbea199b75b09a740f350ae1c4cb82389` 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 | |---|---|---|---|---| | contacts-edit | 5/5 | 5/5 | 5/5 | 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. - Chunks with no budget: `cardmenu` 0.54, `edits` 1.23, `logic` 1.71, `names` 0.62, `Timeline` 0.79 KB. Adding budgets means taking them from somewhere. **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: Contacts edit (head c49435750)

Rebased on dev 3f079de39 (#909, #1025, #1029 in). The old icons test commit is gone: dev's icon test already reads every screen's own icons.ts, so Contacts' icons are covered. Budget lines regenerated from a fresh build, at measured size (kayg's Contacts ceiling), total = sum of the lines.

check dev 3f079de39 this PR
first paint, plain brotli 227240 B 227156 B (−84 B)
first paint, normalised 226769 B 226687 B (−82 B)
budget total 506.34 KiB 512.03 KiB (≤ 543.9)
bundle gate passes

Specs (chromium and webkit, 1440 and 390; contacts-list, contacts-card, contacts-edit, contacts-merge, preload, lazy-ui, lazy-offline, glass): 148 passed, 10 failed, 26 skipped. All 10 are dev's own failures (glass:76, lazy-ui:70, preload:89 at 390). All contacts specs pass.

Shots from the earlier head still apply (the rebase changed no Contacts code or CSS). Perf for the Contacts stack is on #849's comment.

## READY FOR MERGE: Contacts edit (head `c49435750`) Rebased on dev `3f079de39` (#909, #1025, #1029 in). The old icons test commit is gone: dev's icon test already reads every screen's own `icons.ts`, so Contacts' icons are covered. Budget lines regenerated from a fresh build, at measured size (kayg's Contacts ceiling), total = sum of the lines. | check | dev `3f079de39` | this PR | |---|---|---| | first paint, plain brotli | 227240 B | 227156 B (−84 B) | | first paint, normalised | 226769 B | 226687 B (−82 B) | | budget total | 506.34 KiB | 512.03 KiB (≤ 543.9) | | bundle gate | | passes | Specs (chromium and webkit, 1440 and 390; contacts-list, contacts-card, contacts-edit, contacts-merge, preload, lazy-ui, lazy-offline, glass): 148 passed, 10 failed, 26 skipped. All 10 are dev's own failures (glass:76, lazy-ui:70, preload:89 at 390). All contacts specs pass. Shots from the earlier head still apply (the rebase changed no Contacts code or CSS). Perf for the Contacts stack is on #849's comment.
Author
Owner

READY FOR MERGE: Contacts edit (head f09390a14)

Rebased on dev 5a4e2dd0f (#1022 in); it also merges cleanly with 7c50123c3 (#1042; git merge-tree, no conflicts). My old per-stack budget commit conflicted on perf/budgets.json, so it is regenerated: only the Contacts lines and what they moved changed, every other line is dev's own. Builds, gate and specs ran on dev-02 (idle host; builds are deterministic, the dev baseline measured there equals dev-01's byte for byte).

check dev 5a4e2dd0f this PR
first paint, plain brotli 227848 B 227535 B (−313 B)
first paint, hash-normalised 227107 B 227260 B (+153 B; relettering noise, plain is under)
first-screen CSS 18234 B 18234 B
budget total 525.01 KiB 530.74 KiB (≤ 543.9): ContactsScreen 13.68 → 14.31, new Timeline 0.83, cardmenu 0.58, edits 1.26, logic 1.74, names 0.65
bundle gate passes

Specs, chromium and webkit at 1440 and 390 (contacts-list, contacts-card, contacts-edit, contacts-merge, preload, lazy-ui, lazy-offline, glass, shortcuts): 178 passed, 46 skipped, 0 failed. The failures dev used to have (glass:76, lazy-ui:70, preload:89/118, lazy-offline:3) are gone. Backend: calternal-h861 (aarch64 on dev-02).

Shots from the earlier head still apply (the rebase changed no Contacts code or CSS). Perf for the Contacts stack is on #849.

## READY FOR MERGE: Contacts edit (head `f09390a14`) Rebased on dev `5a4e2dd0f` (#1022 in); it also merges cleanly with `7c50123c3` (#1042; `git merge-tree`, no conflicts). My old per-stack budget commit conflicted on `perf/budgets.json`, so it is regenerated: only the Contacts lines and what they moved changed, every other line is dev's own. Builds, gate and specs ran on dev-02 (idle host; builds are deterministic, the dev baseline measured there equals dev-01's byte for byte). | check | dev `5a4e2dd0f` | this PR | |---|---|---| | first paint, plain brotli | 227848 B | 227535 B (−313 B) | | first paint, hash-normalised | 227107 B | 227260 B (+153 B; relettering noise, plain is under) | | first-screen CSS | 18234 B | 18234 B | | budget total | 525.01 KiB | 530.74 KiB (≤ 543.9): ContactsScreen 13.68 → 14.31, new Timeline 0.83, cardmenu 0.58, edits 1.26, logic 1.74, names 0.65 | | bundle gate | | passes | Specs, chromium and webkit at 1440 and 390 (contacts-list, contacts-card, contacts-edit, contacts-merge, preload, lazy-ui, lazy-offline, glass, shortcuts): **178 passed, 46 skipped, 0 failed**. The failures dev used to have (glass:76, lazy-ui:70, preload:89/118, lazy-offline:3) are gone. Backend: `calternal-h861` (aarch64 on dev-02). Shots from the earlier head still apply (the rebase changed no Contacts code or CSS). Perf for the Contacts stack is on #849.
Author
Owner

Merged into dev at 1907fe876; closing.

Merged into dev at `1907fe876`; closing.
kayg closed this pull request 2026-11-09 19:53:02 +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!848
No description provided.