mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-24 19:32:10 +00:00
a8b158829b
* feat(item-detail): gate write affordances on canEditItem (TASK-1105)
Item detail page hides title-edit, content editing, FieldEditor inputs,
delete button, and assignment dropdowns when the user lacks edit on this
specific item. Mirrors the server's per-item permission cascade so the UI
cannot show affordances the server would 403.
Per-item gate via workspaceStore.canEditItem(item) — owner → item grant →
collection grant → role + visibility → deny. Handles guests with single
ItemGrant.edit (full edit on that one item, read-only on siblings) and
the precedence regression where ItemGrant.view + CollectionGrant.edit on
the same item resolves to read-only (item grant wins per server cascade).
Changes:
- FieldEditor: new `readonly?: boolean` prop. When true, renders a unified
display block per field type (select / checkbox / date / number / url /
text) — same visual language as the editor's idle state, no inputs, no
dropdowns, no mutation handlers. Documented in the component header.
- RawMarkdownEditor: new `readonly?: boolean` prop, applied to the
underlying textarea.
- [slug]/+page.svelte: derived canEdit predicate. Title swaps from
click-to-edit button to plain h1 when read-only. Editor passes
editable=canEdit; EditorBubbleMenu / EditorLinkPopover only mount when
editable. RawMarkdownEditor passes readonly. Delete button hidden.
FieldEditor receives readonly={!canEdit}. Assignment + role dropdowns
swap to read-only display spans.
- New CSS: .title-readonly (no hover, default cursor),
.assignment-readonly (matches assignment-select height for layout
stability when the user gains/loses edit permission).
Parent: PLAN-1100.
* fix(item-detail): gate Editor toolbars + ?new=1 title bypass per Codex review (round 2)
Two read-only escape hatches found by Codex re-review:
1. Editor.svelte mobile toolbar (line 818) and table toolbar (line 846)
rendered without checking the `editable` prop. tiptap's editor instance
correctly refuses commands when editable=false, so the buttons would
no-op, but they still rendered and were visually misleading. Both
toolbars now gated on `editable`.
2. The slug page's auto-start-title-edit path for ?new=1 didn't check
canEdit. A read-only user appending ?new=1 would land on the title
textarea (which the visible-branch gate now hides). Added canEdit to
the auto-start condition AND to startEditTitle() itself as a defensive
second line.
Round 1 disagreements stand: Move-to / item-links / ChildItems are
explicitly TASK-1108 sweep scope and intentionally not addressed here.
Parent: PLAN-1100. Refs TASK-1105 PR #419.
* fix(item-detail): exclude BlockDragHandle in read-only + gate Move-to / links per Codex review (round 3)
Three findings from round 3:
1. Editor's BlockDragHandle ProseMirror plugin (registered in Editor's
extensions list) is not gated by tiptap's `editable` flag — its drag
handle is injected into the view DOM regardless. A read-only user
could drag blocks to dispatch transactions through onUpdate. Fix:
conditionally include the plugin in the extensions array based on
`editable`.
2 + 3. Move-to button and item-links add/delete affordances. These were
originally TASK-1108 sweep scope, but Codex re-flagged them in
round 3 despite the round-2 deferral. Absorbed into TASK-1105
rather than burn more review rounds — the gating is mechanical
(a few {#if canEdit} wrappers). TASK-1108 sweep will still grep
for any remaining open-coded patterns elsewhere.
Parent: PLAN-1100. Refs TASK-1105 PR #419.
* fix(item-detail): re-key Editor on canEdit change so BlockDragHandle reattaches per Codex review (round 4)
Round 3 excluded BlockDragHandle from the editor's extensions array when
editable=false. Round 4 caught the construction-time-only nature of that
gate: on cold/direct navigation /me resolves after the editor mounts, so
canEdit starts false → editor created without BlockDragHandle → /me
resolves → canEdit flips true but the existing $effect only calls
editor.setEditable(true) and does not re-register extensions.
Fix: add canEdit to the {#key} value so the editor is reconstructed when
permission flips. Cost is a brief loss of cursor/scroll position on the
flip — acceptable since the only path that flips canEdit mid-session is
a grant change while the page is open, which is rare.
Same approach is appropriate for any future extension whose registration
is gated on `editable`.
Parent: PLAN-1100. Refs TASK-1105 PR #419.
* fix(item-detail): handle ?new=1 auto-edit reactively for slow /me per Codex review (round 5)
* fix(item-detail): always reassign pendingNewItemEdit per Codex review (round 6)