Files
pad/web/e2e
xarmian 29e49e4c63 fix(web): make the master/pane freeze invisible to the user (BUG-2263) (#987)
On the full-page item host, opening a detail pane froze the non-active
side by DEGRADING its DOM — fields became plaintext, buttons vanished,
the title turned read-only. Under the focus-follows-editing model
(PLAN-2179) the freeze is transient and one-click-reversible, so that
degradation was pure user-visible friction: you'd click a plaintext
field, the click would flip activePane, the field would re-render into a
live control, and you'd have to click again.

The freeze exists ONLY to keep exactly one TYPEABLE collab content editor
(single-owner of the editorStore/activeItem/tab-title singletons). It is
NOT a data-collision barrier: master and pane are always DIFFERENT items,
whose collab state is fully itemID-keyed / instance-local, and most REST
surfaces (fields, title, assign/role, tags, move, delete, share,
relationships, children, comments, reactions, archived restore, star) are
single-item, server-gated, side-independent writes.

So drop the `!peeking` term from those REST surfaces — gate them on
`canEdit` alone (their pre-freeze contract) — and keep `peeking` ONLY on
the content editor and its chrome (rich + raw editors, bubble/link
popover, provider-lifecycle mode toggle + retry). The content editor is
already invisible: the host's pointerdown-capture flips activePane before
the click's caret placement (TASK-2180 no-remount reactive editable), so
one gesture activates the side and lands the edit. Now the whole side is:
click anywhere -> edit it, no visible mode.

Two surfaces are NOT side-independent and stay confined to the active side
(the two documented exceptions, found by Codex review):
 - Version restore REST-writes this item's `items.content` directly, which
   collides with the retained Y.Doc on a peeking side. Kept frozen via a
   new ItemTimeline `restoreFrozen={peeking}` prop; comments/reactions
   (separate REST entities) stay live.
 - The quick-actions "Manage/New" controls rewrite the whole collection
   `settings` from a per-item snapshot (last-write-wins across two items in
   one collection), so they gate on `isOwner && !peeking` and recheck
   canEdit at dispatch; the read-only prompt-copy actions stay visible on
   both sides.

Scope: full-page host only. The collection route never passes peeking, so
`mutationsEnabled === canEdit`, `frozen={peeking}` is inert, and
`restoreFrozen` defaults false there — every change is byte-identical on
that route. `mutationsEnabled` survives but now scopes to content-editor
chrome only.

Tests: rewrote the masterFreeze unit probe + both full-page e2e specs
(host + capstone) to assert the new contract — the frozen side keeps its
editable title/fields/buttons; only its content editor flips
contenteditable=false. The freeze signal moved from `h1.title-readonly`
to the ProseMirror `contenteditable` attribute. Added a runtime-mutation
e2e assertion (a field edit on the frozen master PATCHes the correct item),
a real-QuickActionsMenu integration test, and unit coverage for the two
exceptions.

Two PRE-EXISTING concurrency issues were surfaced by the review (version-
restore gating + collection-settings write-exposure are byte-identical to
main, so this PR neither introduces nor worsens them); filed as BUG-2264
(restore <-> Y.Doc reconciliation) and BUG-2265 (collection-settings
optimistic concurrency).

Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra
2026-07-20 18:55:25 -04:00
..