Reset/confirm/deletion tokens (kinds 1-3) are now stored as a SHA-256 hash and
looked up by hash, so the e2e factory must seed the hash while returning the
plaintext to the test. Kind 4 (caldav/bcrypt) is seeded verbatim.
role=toolbar with a roving tabindex: Arrow keys move between buttons,
one Tab stop, Tab leaves the toolbar, and the tab stop stays valid when
buttons become disabled. Covered by an e2e keyboard-navigation spec.
Also fixes the table-edit :disabled bindings, which referenced
editor.can().X as a property (always truthy) so those buttons never
actually disabled — surfaced by review while testing the roving focus.
Claude-Session: https://claude.ai/code/session_018prq5WqRL38EGhPNQKqZUP
Opens the existing alt-text prompt right after an image is inserted
(upload, paste and URL flows) so every new image gets a description.
Cancelling leaves the image without alt text, and the node is deselected
afterwards so the next insert appends instead of replacing.
Claude-Session: https://claude.ai/code/session_018prq5WqRL38EGhPNQKqZUP
Each attachment row was a button containing the download, copy-URL,
delete and set-cover buttons — interactive elements nested inside an
interactive element, which is invalid and confuses assistive-technology
focus (axe: nested-interactive). The row is now a non-interactive
container: the filename (and thumbnail) is the trigger that opens or
downloads the attachment, and the action buttons are its siblings.
Claude-Session: https://claude.ai/code/session_018prq5WqRL38EGhPNQKqZUP
Bumps independent dev-dependency majors split out of the bundled
renovate PR #3046:
- otplib 12.0.1 -> 13.4.1 (v13 is a rewrite: the `authenticator`
singleton was removed in favour of the functional API; the only
consumer is the TOTP e2e test, migrated to `await generate({secret})`)
- rollup-plugin-visualizer 6.0.11 -> 7.0.1
- stylelint-config-property-sort-order-smacss 10.0.0 -> 11.2.0
- @sentry/vite-plugin 3.6.1 -> 5.3.0 (no peer-dep coupling to vite)
Claude-Session: https://claude.ai/code/session_01AHD9BmNcUfgBZdPveZRa8z
Review point (#2950, comment 3444116036): when the surrounding task
<dialog> closed while the link prompt was open, the prompt was orphaned
and cleanup() never ran, leaking listeners and an unresolved promise.
Treat the prompt as a sub-modal of the task dialog: pressing Escape while
it is open now preventDefault()/stopPropagation()s the keydown so the
native modal <dialog> does not close on Escape, resolves the prompt with
'' (cancel) and runs cleanup() — only the prompt is dismissed, the task
dialog stays open. A one-shot 'cancel' listener on the enclosing dialog
backs this up in case the keydown handling is insufficient in some browser.
Tighten cleanup() so the prompt fully tears down regardless of how it
closes (Enter / Escape / click-outside): it now removes the scroll
listener, the document click listener and the dialog cancel listener, and
removes the element. handleClickOutside was hoisted so cleanup() can
remove it, closing the leaked-listener gap directly.
Adds an e2e asserting Escape cancels the prompt while the task dialog
stays open; the existing 'Enter creates the link' case still passes.
The Kanban task popup renders the description editor inside a native
<dialog> opened via showModal(), which lives in the browser's top-layer.
inputPrompt appended its URL <input> to document.body, so it was painted
behind the top-layer dialog (z-index cannot beat the top-layer) and could
not be focused through the dialog's focus trap. As a result clicking "Link"
in the popup did nothing, while it worked on the full task page (no modal).
Thread the TipTap editor through inputPrompt and append the prompt to
getPopupContainer(editor) — the open dialog ancestor when present, falling
back to document.body otherwise, so non-modal usage is unchanged. This is
the same helper the slash menu and mentions already use to escape the
top-layer (#1746).
Fixes#2940
Drives the reply flow through the browser: existing comment is
quoted via the Reply action, the prefilled blockquote round-trips
to the saved reply, the chevron jumps back to the original and
applies the brief highlight.
Route the create flow through taskStore.createNewTask so titles typed
into the related-task input get parsed for labels, priority, assignees,
due dates and cross-project targets - matching the main add-task input.
Also surface the quick-add-magic hint next to the field.
CI shard 4 hit a ~996ms skew between the JS-constructed originalDue and
the backend's advanced due date, enough to bust the <500ms precision
bound. Bump precision to -4 (<5s) — still tight enough to confirm the
regeneration advanced by ~1 day, loose enough to absorb sub-second
round-tripping through Date → ISO → Go time.Time → JSON.