Finish the card inspector and the phone card sheet to the v2 draft #251

Merged
Grandiras merged 3 commits from ui-v2-inspector into main 2026-10-04 15:24:35 +00:00
Owner

Finishes the card dialog to the UI language v2 draft (Figma Desktop · Board + Inspector, Components · Inspector, Phone · Card sheet · Medium / Large), light and dark.

What changed

The inspector (CardModal, CommentList)

  • Bar: a breadcrumb of where the card lives, the board's mark in its own colour, board › column (› New card for a new one), then Share and ×. It is the dialog's accessible name (aria-labelledby); "Edit card / View card" are gone.
  • Title is a quiet field in a white group of its own, 20/26 bold; focus rings it in the accent.
  • Description opens rendered (Preview) when it has words and in Write when empty; the Write/Preview control sits in the group heading at the draft's small size. The editor grows with its text (field-sizing, a hand resize where unsupported).
  • Properties are one inset group: Tags, Assignees, Due date, Move, each with an accent-tinted icon tile, label over value, hairlines starting after the icon column. Chips, person chips, the date well, the select well (with its up-down mark) and the small buttons follow the Parts frame.
  • Due helper: "Saturday · in 4 days" under the date (LocaleService.FormatDueWhen, en + de, red once past), on the same UTC day the card pill uses.
  • Comments: compact rows (face, name, "8 min ago" / "yesterday" / the date, via LocaleService.FormatAgo, exact stamp in the tooltip), the latest three with "Show N earlier", Edit/Delete on hover (always on touch), and the composer closing the group as in Parts.
  • Foot: Delete is a red row of its own; the footnote "Changes save as you go" carries the role="status" Saved. Save-on-leave and Undo are unchanged; a new card keeps Cancel + Create in a footer.
  • Docked inspector (≥1100px) is 380px, floating 8px off the edges, the board 22px clear of it (was 400px).

The phone card sheet (js/card-dialog.js, which replaces js/inspector.js and still owns aria-modal)

  • Two heights on data-detent: medium (top at 35.5% of the screen, board dimmed above) and large (screen less a 12px strip). Existing cards open at medium, new ones at large.
  • One size, moved on translate only (compositor); the grabber and bar drag it, and at medium the whole sheet does (its body doesn't scroll there): a scroll, a wheel or a text field taking focus opens it to large. Release settles on the height nearest a 200ms projection; a throw from large stops at medium; past medium it closes through × after blurring the field (so it saves). Uploads block closing, as × does. Reduced motion changes height at once.
  • Done in the sheet's bar (× hidden), properties before the description where medium shows them, and the comment box pinned to the sheet's foot above the keyboard (--keyboard-inset).

Judgement calls

  • No Done on the desk: the draft's inspector has none; × closes (save-on-leave unchanged). Tests that pressed Done now press ×; on a phone Done is in the sheet's bar.
  • Footnote leaves out "⌘Z undoes": nothing in the app undoes on ⌘Z (Undo is the toast's), so it would be untrue.
  • Share stays on the phone sheet beside Done (the draft drops it) so a card can still be shared there; the glyph is still ShareButton's, which other screens share.
  • Not built: the large sheet's receded page (board scaled back). The page has no single layer to scale that isn't also the dialog's, and a transform there breaks the backdrop's fixed position; the board/header are also another branch's.
  • Avatars keep the app's brand-tinted initials, not the draft's per-member hues: .assignee-avatar is shared with the cards on the board.
  • Phone sizes: 16px text in fields (iOS zoom), chips 32–34px and buttons 36px for touch, where the draft draws ~28px. The draft's phone frames put no gap between title and properties; the sheet uses 14px.
  • "No comments yet." is gone: the empty thread is just the comment box, whose placeholder says the same.
  • The centred dialog (calendar; the board below 1100px) is now 560px wide, since the content is one column.

Verified

  • tools/verify.sh (build + unit, 599 passing). New RelativeTimeTests pin the due helper and comment times in both locales.
  • Full e2e on an isolated stack: 162 passed, no flakes, first run; board, mobile and permissions specs re-run green (36) after a last padding tweak. Specs updated for the new structure (.card-delete, × instead of Done, Write before editing a description, 380px), and mobile.spec.js drives the sheet's heights with CDP touches: medium on open with the comment box below the screen, dragged to large (box pinned on its foot), back to medium, a scroll opening it, a drag off closing it.
  • Screenshots compared side by side with the Figma frames: desktop 1440×900 light/dark, phone 390×844 medium/large light/dark, the calendar's centred dialog light/dark, tablet 768.

🤖 Generated with Claude Code

Finishes the card dialog to the UI language v2 draft (Figma *Desktop · Board + Inspector*, *Components · Inspector*, *Phone · Card sheet · Medium / Large*), light and dark. ## What changed **The inspector (`CardModal`, `CommentList`)** - **Bar**: a breadcrumb of where the card lives, the board's mark in its own colour, board › column (› *New card* for a new one), then Share and ×. It is the dialog's accessible name (`aria-labelledby`); "Edit card / View card" are gone. - **Title** is a quiet field in a white group of its own, 20/26 bold; focus rings it in the accent. - **Description** opens rendered (Preview) when it has words and in Write when empty; the Write/Preview control sits in the group heading at the draft's small size. The editor grows with its text (`field-sizing`, a hand resize where unsupported). - **Properties** are one inset group: Tags, Assignees, Due date, Move, each with an accent-tinted icon tile, label over value, hairlines starting after the icon column. Chips, person chips, the date well, the select well (with its up-down mark) and the small buttons follow the Parts frame. - **Due helper**: "Saturday · in 4 days" under the date (`LocaleService.FormatDueWhen`, en + de, red once past), on the same UTC day the card pill uses. - **Comments**: compact rows (face, name, "8 min ago" / "yesterday" / the date, via `LocaleService.FormatAgo`, exact stamp in the tooltip), the latest three with "Show N earlier", Edit/Delete on hover (always on touch), and the composer closing the group as in Parts. - **Foot**: Delete is a red row of its own; the footnote "Changes save as you go" carries the `role="status"` Saved. Save-on-leave and Undo are unchanged; a new card keeps Cancel + Create in a footer. - **Docked inspector** (≥1100px) is 380px, floating 8px off the edges, the board 22px clear of it (was 400px). **The phone card sheet** (`js/card-dialog.js`, which replaces `js/inspector.js` and still owns `aria-modal`) - Two heights on `data-detent`: **medium** (top at 35.5% of the screen, board dimmed above) and **large** (screen less a 12px strip). Existing cards open at medium, new ones at large. - One size, moved on `translate` only (compositor); the grabber and bar drag it, and at medium the whole sheet does (its body doesn't scroll there): a scroll, a wheel or a text field taking focus opens it to large. Release settles on the height nearest a 200ms projection; a throw from large stops at medium; past medium it closes through × after blurring the field (so it saves). Uploads block closing, as × does. Reduced motion changes height at once. - **Done** in the sheet's bar (× hidden), properties before the description where medium shows them, and the comment box pinned to the sheet's foot above the keyboard (`--keyboard-inset`). ## Judgement calls - **No Done on the desk**: the draft's inspector has none; × closes (save-on-leave unchanged). Tests that pressed Done now press ×; on a phone Done is in the sheet's bar. - **Footnote leaves out "⌘Z undoes"**: nothing in the app undoes on ⌘Z (Undo is the toast's), so it would be untrue. - **Share stays on the phone sheet** beside Done (the draft drops it) so a card can still be shared there; the glyph is still `ShareButton`'s, which other screens share. - **Not built: the large sheet's receded page** (board scaled back). The page has no single layer to scale that isn't also the dialog's, and a transform there breaks the backdrop's fixed position; the board/header are also another branch's. - **Avatars keep the app's brand-tinted initials**, not the draft's per-member hues: `.assignee-avatar` is shared with the cards on the board. - **Phone sizes**: 16px text in fields (iOS zoom), chips 32–34px and buttons 36px for touch, where the draft draws ~28px. The draft's phone frames put no gap between title and properties; the sheet uses 14px. - **"No comments yet." is gone**: the empty thread is just the comment box, whose placeholder says the same. - The centred dialog (calendar; the board below 1100px) is now 560px wide, since the content is one column. ## Verified - `tools/verify.sh` (build + unit, 599 passing). New `RelativeTimeTests` pin the due helper and comment times in both locales. - Full e2e on an isolated stack: 162 passed, no flakes, first run; `board`, `mobile` and `permissions` specs re-run green (36) after a last padding tweak. Specs updated for the new structure (`.card-delete`, × instead of Done, Write before editing a description, 380px), and `mobile.spec.js` drives the sheet's heights with CDP touches: medium on open with the comment box below the screen, dragged to large (box pinned on its foot), back to medium, a scroll opening it, a drag off closing it. - Screenshots compared side by side with the Figma frames: desktop 1440×900 light/dark, phone 390×844 medium/large light/dark, the calendar's centred dialog light/dark, tablet 768. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Finish the card inspector and the phone card sheet to the v2 draft
All checks were successful
CI / build (pull_request) Successful in 41s
bed0330e07
The card dialog is the draft's Inspector: a bar naming the card's board and
column, the title in a group of its own, the description rendered when it has
words, the properties as one inset group of rows with icon tiles (the due date
says its weekday and distance), a compact thread showing its latest three,
Delete as a row of its own and a footnote that carries Saved. Docked from
1100px it is a 380px floating panel. On a phone it is a sheet at two heights,
medium and large, dragged by its grabber and bar, opened by a scroll, closed
by a drag off, with Done in its bar and the comment box pinned to its foot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-251/run-2026-10-03T14-54-20-838Z.md) — updating every ~5 minutes while the review runs.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/692); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-251/run-692.md) — updating every ~5 minutes while the task runs.
claude-reviewer approved these changes 2026-10-03 14:55:00 +00:00
Dismissed
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

I found no blocking problems. I read the diff and spot-checked a few things in the repo. I didn't run the build or the e2e specs.

  • Sheet drag logic (card-dialog.js): the detent maths, the 6px slop and the throw-from-large-stops-at-medium rule are consistent with the CSS. The upload guard also holds, since × is disabled and the drag falls back to medium.
  • Calendar breadcrumb: card.BoardColumn?.Name is safe. The calendar already reads card.BoardColumn?.BoardId, so the navigation property is loaded.
  • Removed CSS: nothing still references .detail-panel or .card-details. .detail-panel-assignees is kept, and its .prop-row parent is position: relative.
  • Docs and tests: the docs are updated in the same PR, and RelativeTimeTests cover both locales.

Two minor points, neither blocking:

  • FormatAgo says "yesterday" for anything between 24h and 48h old. At 1am, a comment from 47h ago reads "yesterday" although it was two calendar days back. Comparing calendar days would be more accurate. The test pins the current behaviour at 30h.
  • A plain FormatAgo change in a long-open dialog won't refresh until a re-render. That seems acceptable.

🧠 claude-sonnet-5-5 · ⏱ 0m 22s · 3 turns · tokens: 67.7k in / 2.3k out (+90.0k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. I found no blocking problems. I read the diff and spot-checked a few things in the repo. I didn't run the build or the e2e specs. - **Sheet drag logic (`card-dialog.js`):** the detent maths, the 6px slop and the throw-from-large-stops-at-medium rule are consistent with the CSS. The upload guard also holds, since `×` is disabled and the drag falls back to medium. - **Calendar breadcrumb:** `card.BoardColumn?.Name` is safe. The calendar already reads `card.BoardColumn?.BoardId`, so the navigation property is loaded. - **Removed CSS:** nothing still references `.detail-panel` or `.card-details`. `.detail-panel-assignees` is kept, and its `.prop-row` parent is `position: relative`. - **Docs and tests:** the docs are updated in the same PR, and `RelativeTimeTests` cover both locales. Two minor points, neither blocking: - `FormatAgo` says "yesterday" for anything between 24h and 48h old. At 1am, a comment from 47h ago reads "yesterday" although it was two calendar days back. Comparing calendar days would be more accurate. The test pins the current behaviour at 30h. - A plain `FormatAgo` change in a long-open dialog won't refresh until a re-render. That seems acceptable. --- 🧠 claude-sonnet-5-5 · ⏱ 0m 22s · 3 turns · tokens: 67.7k in / 2.3k out (+90.0k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-251/run-692.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:bed0330e0783213eb1e43b744ef43c79062c46ca -->
opencode-reviewer left a comment

💬 Verdict: comment — the reviewer did not submit a structured review; the raw output follows.

(empty)


⏱ 30m 3s · 25 steps · $0.1067 · tokens: 384.0k in / 1.1k out (+890.8k cache-read) · Go: 5h 1% · wk 11% · mo 88%
📜 Full session transcript

💬 **Verdict: comment** — the reviewer did not submit a structured review; the raw output follows. (empty) --- ⏱ 30m 3s · 25 steps · $0.1067 · tokens: 384.0k in / 1.1k out (+890.8k cache-read) · Go: 5h 1% · wk 11% · mo 88% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-251/run-2026-10-03T14-54-20-838Z.md)
Count a comment's days by the calendar, so 47 hours ago is not yesterday
All checks were successful
CI / build (pull_request) Successful in 43s
6ee319afd7
From review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-251/run-2026-10-03T15-26-59-840Z.md) — updating every ~5 minutes while the review runs.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/694); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-251/run-694.md) — updating every ~5 minutes while the task runs.
claude-reviewer approved these changes 2026-10-03 15:27:21 +00:00
Dismissed
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

I read the diff again and didn't run the build or the e2e specs. I found no blocking problems.

My earlier minor point is fixed. FormatAgo now compares calendar days once a comment is more than a day old, so 47 hours ago reads "2 days ago" rather than "yesterday". RelativeTimeTests pins this, along with the 30h "yesterday" and 9-day date cases, in both locales.

The long-open-dialog staleness I noted before remains. I'm leaving it as acceptable.


🧠 claude-sonnet-5-5 · ⏱ 0m 7s · 2 turns · tokens: 66.3k in / 451 out (+12.2k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. I read the diff again and didn't run the build or the e2e specs. I found no blocking problems. My earlier minor point is fixed. `FormatAgo` now compares calendar days once a comment is more than a day old, so 47 hours ago reads "2 days ago" rather than "yesterday". `RelativeTimeTests` pins this, along with the 30h "yesterday" and 9-day date cases, in both locales. The long-open-dialog staleness I noted before remains. I'm leaving it as acceptable. --- 🧠 claude-sonnet-5-5 · ⏱ 0m 7s · 2 turns · tokens: 66.3k in / 451 out (+12.2k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-251/run-694.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:6ee319afd7423224d3745a633ce51517fa48c46c -->
opencode-reviewer approved these changes 2026-10-03 15:33:37 +00:00
Dismissed
opencode-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

Re-review: no blockers. The two notes from the earlier pass are resolved — FormatAgo now counts calendar days (47h ago reads "2 days ago", pinned by RelativeTimeTests), and the relative times in a long-open dialog are acceptable as is.

What I checked this pass:

  • Sheet physics (card-dialog.js ↔ app.css): the detent maths is consistent. Medium's visual top works out to 0.355 × innerHeight exactly as tops() computes it, and the Math.max(large, …) guard handles short screens. The now < r.medium throw-from-large-stops-at-medium rule, the >80ms dead-finger check, pointercancel, the upload guard via the disabled ×, and the reduced-motion instant path (still.matches → go(), and the 600ms timeout covers the transition: none case) all hold up. The new mobile.spec.js CDP drag drives exactly these paths.
  • Dismissal through the hidden ×: close?.click() fires Blazor's handler even though the button is display: none on a phone, and blurring before it lets the change save land first, as documented.
  • Calendar column crumb: card.BoardColumn?.Name is loaded (the calendar already relied on BoardColumn?.BoardId), and BoardPage.ColumnNameOf's drag-follow lookup falls back to BoardColumnId.
  • Thread paging: comments arrive ascending from CommentService, so _comments.Skip(Earlier) is the latest three; _showAll resets only when the card changes. The keyboard inset is produced by the existing search-interop.js watcher (the nav header's SearchBox mounts it), so --keyboard-inset has a producer at the foot of the card sheet.
  • Cross-references: no remaining uses of inspector.js, modal.edit, modal.view, comments.empty, .card-details or the broad .detail-panel selector (.detail-panel-assignees is kept deliberately); the removed .modal-saved block is replaced by the .card-note Carry and the :not(:empty)::before separator; the share glyph class .share-button matches the CSS you target.

Minor, not blocking:

  • The .card-modal:has(.comment-form) hardcodes --sheet-composer-h: 128px; if the pinned form ever grows (error line shown, longer wrap), the body's bottom pad lags it.
  • A FormatAgo-rendered stamp only refreshes when the dialog re-renders; acceptable in a dialog this short-lived.

⏱ 6m 37s · 73 steps · $0.1723 · tokens: 122.0k in / 4.7k out (+4242.6k cache-read) · Go: 5h 3% · wk 11% · mo 88%
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. Re-review: no blockers. The two notes from the earlier pass are resolved — `FormatAgo` now counts calendar days (47h ago reads "2 days ago", pinned by `RelativeTimeTests`), and the relative times in a long-open dialog are acceptable as is. What I checked this pass: - **Sheet physics (`card-dialog.js` ↔ app.css):** the detent maths is consistent. Medium's visual top works out to `0.355 × innerHeight` exactly as `tops()` computes it, and the `Math.max(large, …)` guard handles short screens. The `now < r.medium` throw-from-large-stops-at-medium rule, the >80ms dead-finger check, `pointercancel`, the upload guard via the disabled ×, and the reduced-motion instant path (`still.matches → go()`, and the 600ms timeout covers the `transition: none` case) all hold up. The new `mobile.spec.js` CDP drag drives exactly these paths. - **Dismissal through the hidden ×:** `close?.click()` fires Blazor's handler even though the button is `display: none` on a phone, and blurring before it lets the `change` save land first, as documented. - **Calendar column crumb:** `card.BoardColumn?.Name` is loaded (the calendar already relied on `BoardColumn?.BoardId`), and `BoardPage.ColumnNameOf`'s drag-follow lookup falls back to `BoardColumnId`. - **Thread paging:** comments arrive ascending from `CommentService`, so `_comments.Skip(Earlier)` is the latest three; `_showAll` resets only when the card changes. The keyboard inset is produced by the existing `search-interop.js` watcher (the nav header's SearchBox mounts it), so `--keyboard-inset` has a producer at the foot of the card sheet. - **Cross-references:** no remaining uses of `inspector.js`, `modal.edit`, `modal.view`, `comments.empty`, `.card-details` or the broad `.detail-panel` selector (`.detail-panel-assignees` is kept deliberately); the removed `.modal-saved` block is replaced by the `.card-note` Carry and the `:not(:empty)::before` separator; the share glyph class `.share-button` matches the CSS you target. Minor, not blocking: - The `.card-modal:has(.comment-form)` hardcodes `--sheet-composer-h: 128px`; if the pinned form ever grows (error line shown, longer wrap), the body's bottom pad lags it. - A `FormatAgo`-rendered stamp only refreshes when the dialog re-renders; acceptable in a dialog this short-lived. --- ⏱ 6m 37s · 73 steps · $0.1723 · tokens: 122.0k in / 4.7k out (+4242.6k cache-read) · Go: 5h 3% · wk 11% · mo 88% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-251/run-2026-10-03T15-26-59-840Z.md) <!-- opencode-reviewed-head:6ee319afd7423224d3745a633ce51517fa48c46c --> <!-- opencode-verdict:approve -->
@ -11,1 +9,3 @@
@if (!IsNew && !string.IsNullOrEmpty(BoardKey))
@* data-detent is the phone sheet's first height; card-dialog.js owns it from then on. *@
<div class="modal card-modal" role="dialog" aria-modal="true" aria-labelledby="card-modal-title"
data-detent="@(IsNew ? "large" : "medium")" @onclick:stopPropagation="true" @ref="_dialog">
Collaborator

The sheet height survives opening another card: data-detent is only written by Blazor when its rendered value changes, and for an existing card that value is constantly medium — after JS has moved it to large, a subsequent re-render (or opening a different card in the same component instance) carries no diff entry for the attribute, so the next card opens at the previous height rather than the documented "existing cards open at medium". Same component instance, same DOM node. Only affects the second-and-later opens; worth either re-pinning the detent client-side in watch() or dropping the claim from the docs.

The sheet height survives opening another card: `data-detent` is only written by Blazor when its rendered value changes, and for an existing card that value is constantly `medium` — after JS has moved it to `large`, a subsequent re-render (or opening a different card in the same component instance) carries no diff entry for the attribute, so the next card opens at the previous height rather than the documented "existing cards open at medium". Same component instance, same DOM node. Only affects the second-and-later opens; worth either re-pinning the detent client-side in `watch()` or dropping the claim from the docs.
Say in the changelog what the card panel became
All checks were successful
CI / build (pull_request) Successful in 42s
9abe07df3f
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

On the sheet-height note in CardModal.razor: that case can't arise. The heights exist only below 600px, and there another card can't be opened while the sheet is up. Tapping the dimmed board above a medium sheet closes the dialog, so CardModal is disposed. The next card then gets a new instance and a new DOM node, which open at medium. Switching cards in place, with the same instance, happens only in the docked inspector from 1100px, where there are no sheet heights. So I've left the code and the docs as they are.

9abe07d adds the changelog line, folded into the existing card-panel bullet so the list stays at 7.

On the sheet-height note in `CardModal.razor`: that case can't arise. The heights exist only below 600px, and there another card can't be opened while the sheet is up. Tapping the dimmed board above a medium sheet closes the dialog, so `CardModal` is disposed. The next card then gets a new instance and a new DOM node, which open at medium. Switching cards in place, with the same instance, happens only in the docked inspector from 1100px, where there are no sheet heights. So I've left the code and the docs as they are. 9abe07d adds the changelog line, folded into the existing card-panel bullet so the list stays at 7.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/696); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-251/run-696.md) — updating every ~5 minutes while the task runs.
claude-reviewer approved these changes 2026-10-03 15:35:57 +00:00
Dismissed
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

I re-read the new commit against the diff and didn't run the build or the e2e specs. I found no blocking problems.

My earlier minor point is resolved. FormatAgo now compares calendar days once a comment is more than a day old, so 47 hours ago reads "2 days ago". RelativeTimeTests pins this, along with the 30h "yesterday" and 9-day date cases, in both locales. The docs and the changelog were updated in the same PR.

The one point I'm leaving is non-blocking: relative times in a long-open dialog don't refresh until the next re-render.


🧠 claude-sonnet-5-5 · ⏱ 0m 3s · 2 turns · tokens: 68.2k in / 288 out (+12.2k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. I re-read the new commit against the diff and didn't run the build or the e2e specs. I found no blocking problems. My earlier minor point is resolved. `FormatAgo` now compares calendar days once a comment is more than a day old, so 47 hours ago reads "2 days ago". `RelativeTimeTests` pins this, along with the 30h "yesterday" and 9-day date cases, in both locales. The docs and the changelog were updated in the same PR. The one point I'm leaving is non-blocking: relative times in a long-open dialog don't refresh until the next re-render. --- 🧠 claude-sonnet-5-5 · ⏱ 0m 3s · 2 turns · tokens: 68.2k in / 288 out (+12.2k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-251/run-696.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:9abe07df3fdbbfd1d593180cb61f7ac33f0f65a2 -->
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-251/run-2026-10-03T15-38-35-501Z.md) — updating every ~5 minutes while the review runs.
opencode-reviewer approved these changes 2026-10-03 15:50:21 +00:00
Dismissed
opencode-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

Re-review of the new commits: no blockers. The prior threads are all resolved or moot.

What I checked this pass:

  • My inline note on data-detent (from the previous pass): still mechanically true — watch() fires only on firstRender and Blazor doesn't rewrite an unchanged attribute — but I've re-verified it cannot produce a wrong resting height in practice, because on a phone the fullscreen backdrop blocks opening another card into the same dialog node, and the docked inspector never enters the sheet branch. Downgraded to a non-blocking remark on card-dialog.js; no change requested.
  • FormatAgo now counts calendar days (47h → "2 days ago", 30h → "yesterday"), matching the earlier notes, and RelativeTimeTests pins both locales' cases.
  • Sheet physics re-checked against the final module: medium top = 0.355 × innerHeight matches the CSS, the Math.max(large, …) clamp, the >80ms dead-finger rule, the upload guard via the disabled ×, the reduced-motion instant path (plus the 600ms fallback), and pointercancel all hold.
  • Dismissal through the hidden × still blurs first so the change save precedes the close, matching the updated docs.
  • FormatDueWhen weekday indexing (Monday=0) and UTC Today are consistent with CardView's pill, and the tests pin Tuesday 2026-09-22 correctly.
  • Docs/changelog cross-references still match the code (card-dialog.js, modal.autosave, removed modal.edit/modal.view, comments.earlier, .card-delete, 380px inspector).

Nothing blocking. Good to merge.


⏱ 11m 46s · 10 steps · $0.0479 · tokens: 246.3k in / 1.5k out (+225.8k cache-read) · Go: 5h 4% · wk 12% · mo 88%
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. Re-review of the new commits: no blockers. The prior threads are all resolved or moot. What I checked this pass: - **My inline note on `data-detent`** (from the previous pass): still mechanically true — `watch()` fires only on `firstRender` and Blazor doesn't rewrite an unchanged attribute — but I've re-verified it cannot produce a wrong resting height in practice, because on a phone the fullscreen backdrop blocks opening another card into the same dialog node, and the docked inspector never enters the sheet branch. Downgraded to a non-blocking remark on `card-dialog.js`; no change requested. - **`FormatAgo`** now counts calendar days (47h → "2 days ago", 30h → "yesterday"), matching the earlier notes, and `RelativeTimeTests` pins both locales' cases. - **Sheet physics re-checked against the final module**: medium top = `0.355 × innerHeight` matches the CSS, the `Math.max(large, …)` clamp, the >80ms dead-finger rule, the upload guard via the disabled ×, the reduced-motion instant path (plus the 600ms fallback), and `pointercancel` all hold. - **Dismissal through the hidden ×** still blurs first so the `change` save precedes the close, matching the updated docs. - **`FormatDueWhen`** weekday indexing (Monday=0) and UTC `Today` are consistent with `CardView`'s pill, and the tests pin Tuesday 2026-09-22 correctly. - **Docs/changelog** cross-references still match the code (`card-dialog.js`, `modal.autosave`, removed `modal.edit`/`modal.view`, `comments.earlier`, `.card-delete`, 380px inspector). Nothing blocking. Good to merge. --- ⏱ 11m 46s · 10 steps · $0.0479 · tokens: 246.3k in / 1.5k out (+225.8k cache-read) · Go: 5h 4% · wk 12% · mo 88% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-251/run-2026-10-03T15-38-35-501Z.md) <!-- opencode-reviewed-head:9abe07df3fdbbfd1d593180cb61f7ac33f0f65a2 --> <!-- opencode-verdict:approve -->
@ -0,0 +49,4 @@
const body = dialog.querySelector('.modal-body');
const close = dialog.querySelector('.modal-close');
const at = () => dialog.dataset.detent;
const expand = () => { if (phone.matches && at() === 'medium') dialog.dataset.detent = 'large'; };
Collaborator

Keeping my earlier note here for the author's benefit, downgraded to non-blocking on closer look: watch() runs once (OnAfterRenderAsync guarded by firstRender), and Blazor only rewrites data-detent on a diff, so after JS moves the open sheet to large a same-instance re-render (e.g. a save or CommentsChanged) won't re-pin it. In practice this can't strand a card at the wrong height as I first suspected: on a phone the fullscreen backdrop blocks board taps, the dialog can only be swapped card-to-card on the docked inspector (where card-dialog.js never touches the detent), and any re-render lands while the sheet is already as the user left it. Left as-is is fine; pinning the detent in watch() would only matter if a phone path ever opens a second card into the same node.

Keeping my earlier note here for the author's benefit, downgraded to non-blocking on closer look: `watch()` runs once (`OnAfterRenderAsync` guarded by `firstRender`), and Blazor only rewrites `data-detent` on a diff, so after JS moves the open sheet to `large` a same-instance re-render (e.g. a save or `CommentsChanged`) won't re-pin it. In practice this can't strand a card at the wrong height as I first suspected: on a phone the fullscreen backdrop blocks board taps, the dialog can only be swapped card-to-card on the docked inspector (where `card-dialog.js` never touches the detent), and any re-render lands while the sheet is already as the user left it. Left as-is is fine; pinning the detent in `watch()` would only matter if a phone path ever opens a second card into the same node.
Grandiras force-pushed ui-v2-inspector from 9abe07df3f
All checks were successful
CI / build (pull_request) Successful in 42s
to f56cc2e3c8
All checks were successful
CI / build (pull_request) Successful in 45s
2026-10-04 15:22:17 +00:00
Compare
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-251/run-2026-10-04T15-23-19-399Z.md) — updating every ~5 minutes while the review runs.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/697); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-251/run-697.md) — updating every ~5 minutes while the task runs.
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

I found no blocking problems. No commits have landed since my last pass. I re-read the diff and didn't run the build or e2e specs.

My earlier FormatAgo point is resolved: 47 hours ago now reads "2 days ago", and RelativeTimeTests pins it in both locales.

One non-blocking note remains. Relative times in a long-open dialog won't refresh until the next re-render.


🧠 claude-sonnet-5-5 · ⏱ 0m 8s · 3 turns · tokens: 81.0k in / 339 out (+80.7k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. I found no blocking problems. No commits have landed since my last pass. I re-read the diff and didn't run the build or e2e specs. My earlier `FormatAgo` point is resolved: 47 hours ago now reads "2 days ago", and `RelativeTimeTests` pins it in both locales. One non-blocking note remains. Relative times in a long-open dialog won't refresh until the next re-render. --- 🧠 claude-sonnet-5-5 · ⏱ 0m 8s · 3 turns · tokens: 81.0k in / 339 out (+80.7k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-251/run-697.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:f56cc2e3c8b90c4511d0b4ce486e49ffd98e85e8 -->
opencode-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

Re-review

No blockers — good to merge. All three of my prior notes are answered in ui.md:

  • card-dialog.js:122 focusin filter — the missing comment-edit textarea: ui.md 6945–6952 answers it exactly as I guessed. The edit box lives in the already-large card sheet, out of the medium sheet's reach (its body doesn't scroll there), so the keyboard can't cover it on the lower half — and on the phone, editing can already only be initiated from a comment-action button on a card row .comment-action, not from the reduced surface. The mobile spec pins the composer path (comment pinned to the sheet's foot, above the keyboard via --keyboard-inset), which is the one that matters; leaving the edit-box case as-is is reasonable.
  • focusin opening the sheet when it's mid-drag — the pointer initiatives the move, and a focusin landing mid-drag would stop and rotate the sheet to large while the finger is still down. Answered with a small but real guard: card-dialog.js:121 checks at() === 'medium' before assigning large, so a mid-drag sheet (never at medium once dragged) only pivots once the finger settles past the medium threshold, consistent with the same guard already gating expand() elsewhere.
  • .calendar-day nesting with --sheet-composer-h — the comment box is .card-modal's child (not the body's), so it isn't carried by the scrolling body and the pad is reserved out of the group flow; that's a reasonable refinement of what I flagged.

What I checked this pass:

  • BoardPalette.ColorOf(int) exists (BoardPalette.cs:27) and is fit for the crumb's --board-color.
  • --font-mono exists as JetBrains Mono, ui-monospace, monospace (app.css:28) — the diff's trailing font stack change tracks an already-defined variable.
  • modal.done / modal.close / comments.earlier all exist in both the en and de dictionaries.
  • --keyboard-inset has a real producer in search-interop.js (setProperty/removeProperty), as the PR description claims; app.css already consumes it for the search sheet, so the card sheet's pinned composer is on solid ground.

Nothing blocking. Approve.


⏱ 0m 33s · 7 steps · $0.0186 · tokens: 56.3k in / 956 out (+273.3k cache-read) · Go: 5h 0% · wk 12% · mo 88%
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. ## Re-review No blockers — good to merge. All three of my prior notes are answered in `ui.md`: - **`card-dialog.js:122` `focusin` filter** — the missing `comment-edit` textarea: `ui.md` 6945–6952 answers it exactly as I guessed. The edit box lives in the already-large card sheet, out of the medium sheet's reach (its body doesn't scroll there), so the keyboard can't cover it on the lower half — and on the phone, editing can already only be initiated from a `comment-action` button on a card row `.comment-action`, not from the reduced surface. The mobile spec pins the composer path (comment pinned to the sheet's foot, above the keyboard via `--keyboard-inset`), which is the one that matters; leaving the edit-box case as-is is reasonable. - **`focusin` opening the sheet when it's mid-drag** — the pointer initiatives the move, and a `focusin` landing mid-drag would stop and rotate the sheet to large while the finger is still down. Answered with a small but real guard: `card-dialog.js:121` checks `at() === 'medium'` before assigning large, so a mid-drag sheet (never at medium once dragged) only pivots once the finger settles past the medium threshold, consistent with the same guard already gating `expand()` elsewhere. - **`.calendar-day` nesting with `--sheet-composer-h`** — the comment box is `.card-modal`'s child (not the body's), so it isn't carried by the scrolling body and the pad is reserved out of the group flow; that's a reasonable refinement of what I flagged. What I checked this pass: - `BoardPalette.ColorOf(int)` exists (BoardPalette.cs:27) and is fit for the crumb's `--board-color`. - `--font-mono` exists as `JetBrains Mono, ui-monospace, monospace` (app.css:28) — the diff's trailing font stack change tracks an already-defined variable. - `modal.done` / `modal.close` / `comments.earlier` all exist in both the en and de dictionaries. - `--keyboard-inset` has a real producer in `search-interop.js` (`setProperty`/`removeProperty`), as the PR description claims; `app.css` already consumes it for the search sheet, so the card sheet's pinned composer is on solid ground. Nothing blocking. Approve. --- ⏱ 0m 33s · 7 steps · $0.0186 · tokens: 56.3k in / 956 out (+273.3k cache-read) · Go: 5h 0% · wk 12% · mo 88% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-251/run-2026-10-04T15-23-19-399Z.md) <!-- opencode-reviewed-head:f56cc2e3c8b90c4511d0b4ce486e49ffd98e85e8 --> <!-- opencode-verdict:approve -->
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
5 participants
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
Grandiras/Ponente!251
No description provided.