Add the Paper board card inspector and compact density - #2097
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d344125558
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| v-if="selectedCard" | ||
| :card="selectedCard" | ||
| :is-open="Boolean(selectedCard)" | ||
| :labels="boardStore.currentBoardLabels" | ||
| :presentation="cardPresentation" |
There was a problem hiding this comment.
Reinitialize state when switching inspector cards
On desktop, the non-modal inspector leaves the board's other card buttons usable, so selecting card B changes this prop while the same CardModal remains open. The card watcher overwrites card A's form fields without invoking the discard confirmation, while the open-state watcher does not rerun to clear comment drafts, fetch B's comments, update editingCardId, or replace A's expectedUpdatedAt; this can silently lose A's edits, post A's comment draft to B, or make B's save fail concurrency validation. Guard card switching when dirty and fully reinitialize the editor session when the card ID changes.
Useful? React with 👍 / 👎.
| top: 1rem; | ||
| flex: 0 0 min(420px, 36vw); | ||
| height: calc(100vh - 2rem); | ||
| min-width: 340px; |
There was a problem hiding this comment.
Prevent the desktop rule from overriding inspector bounds
At every desktop inspector width, the later @media (min-width: 768px) .card-modal-viewport rule has equal specificity and overrides this top with inset: 0 and this explicit height with height: auto. Consequently the inspector scroll region's percentage max-height has no definite viewport-sized containing height, so long cards expand beyond the viewport instead of scrolling inside the sticky side panel, moving the editor actions and board out of the intended persistent layout. Scope the later rule to modal presentation or reassert the inspector bounds after it.
Useful? React with 👍 / 👎.
| <PaperHLBtn | ||
| label="Compact density" | ||
| :aria-pressed="density === 'compact'" | ||
| data-testid="paper-board-density-toggle" | ||
| @keydown.enter.stop |
There was a problem hiding this comment.
Keep the density control inside the mobile viewport
On phone-sized boards, this additional max-content button is placed in the existing non-wrapping, flex: none action row alongside Settings, Capture here, and Review. The four labelled controls plus their gaps are wider than the available 358px content area at the tested 390px viewport, and the header has no mobile wrapping or overflow treatment, so the action row creates horizontal overflow and pushes controls outside the visible board. Wrap or collapse the header actions, or move the density preference into a responsive menu.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eb8772d974
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| watch(boardId, () => { | ||
| selectedCard.value = null | ||
| pendingCard.value = null | ||
| cardEditorDirty.value = false |
There was a problem hiding this comment.
Prompt before discarding dirty state on route changes
When a card contains unsaved form or comment edits, navigating through the desktop inspector's still-available Review/Capture controls, sidebar, command palette, or another board proceeds without the discard confirmation: leaving the route unmounts the editor, while a reused board route reaches this watcher and clears both the selection and dirty flag. The new protection only covers closing the editor and selecting another card, and a repo-wide search finds no route-leave or beforeunload guard, so these navigation paths silently lose the draft; route navigation should pass through the same confirmation.
Useful? React with 👍 / 👎.
| function discardAndSwitchCard() { | ||
| if (!pendingCard.value) return | ||
| selectedCard.value = pendingCard.value | ||
| pendingCard.value = null | ||
| cardEditorDirty.value = false |
There was a problem hiding this comment.
Restore focus to the newly selected card
After confirming a dirty switch from card A to card B, this replaces the prop while preserving the same CardModal instance and keeping isOpen true. Its open-state watcher therefore retains card A as previouslyFocusedElement; when B is later closed, focus jumps back to A rather than B, which breaks keyboard users' board position and can move focus to an off-screen lane. Refresh the stored opener when the card ID changes or remount the editor keyed by card ID.
Useful? React with 👍 / 👎.
| role="dialog" | ||
| aria-label="Edit Card" | ||
| aria-modal="true" | ||
| :aria-modal="isInspector ? undefined : 'true'" |
There was a problem hiding this comment.
Move focus inside when the inspector becomes modal
If an open desktop inspector becomes a tablet/phone modal after a window resize or orientation change while focus is on the still-interactive board, aria-modal and the backdrop appear but the isOpen watcher does not rerun because the editor remained open. Focus consequently stays behind the overlay, and the dialog's Tab handler cannot trap it because key events from that external element never bubble through the dialog; watch the presentation transition and move focus into the editor when it becomes modal.
Useful? React with 👍 / 👎.
| boardStore.setEditingCard(null) | ||
| } | ||
| boardStore.setEditingCard(newCard.id) | ||
| void boardStore.fetchCardComments(newCard.boardId, newCard.id) |
There was a problem hiding this comment.
Handle rejected comment loads during card switches
When the comments request fails while switching inspector cards, fetchCardComments displays the store error/toast and then rethrows, but this new fire-and-forget call has no rejection handler. The rejection therefore reaches the application's global unhandledrejection listener and is reported to Sentry as an unhandled application failure in addition to the expected API error; attach a catch handler or await the request inside an error-handled transition.
Useful? React with 👍 / 👎.
| :labels="boardStore.currentBoardLabels" | ||
| :presentation="cardPresentation" | ||
| @close="closeCard" | ||
| @updated="closeCard" |
There was a problem hiding this comment.
Keep stale save completions from closing the next card
On a slow save, the desktop board remains interactive and the editor has no saving-state switch guard: a user can start saving card A, choose card B, confirm the switch, and begin editing B before A's request resolves. When A finally completes, handleSave emits updated, and this unconditional handler closes whichever card is currently selected, bypassing B's dirty-close confirmation and silently losing B's new draft. Disable switching while the originating mutation is pending or scope completion callbacks to the card/session that initiated them.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b8a004f497
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| function discardAndSwitchCard() { | ||
| if (!pendingCard.value) return | ||
| selectedCard.value = pendingCard.value |
There was a problem hiding this comment.
Scope comment completions to the originating card
When an add/edit-comment request for card A is still pending, this confirmed switch allows the user to open card B and start another draft before A resolves. handleAddComment then clears the shared newCommentContent ref, while handleSaveEditComment calls handleCancelEditComment(), so A's late completion can silently erase B's new draft or editing session. Disable switching during comment mutations or guard completion-side state updates with the originating card/session ID.
AGENTS.md reference: frontend/AGENTS.md:L5-L5
Useful? React with 👍 / 👎.
Summary
Implementation
td.paper.board-density.v1as a local preference fallback with no API or schema changeVerification
npm.cmd exec vitest -- run: 342 files, 4,906 tests passednpm.cmd run build: passed, including typecheck and production buildgit diff --check: passednpm.cmd run build: passedorigin/mainat013ed764before final verificationDocs
No docs changed. This implements the narrowed v0.2 M1 issue without changing product or API contracts.
Risks
Closes #2086