Skip to content

Mount toolbar on slides share + enforce viewer read-only#282

Merged
hackerwins merged 3 commits into
mainfrom
slides-share-toolbar-readonly
May 23, 2026
Merged

Mount toolbar on slides share + enforce viewer read-only#282
hackerwins merged 3 commits into
mainfrom
slides-share-toolbar-readonly

Conversation

@hackerwins

@hackerwins hackerwins commented May 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • SharedSlidesLayout never mounted SlidesToolbar, so editor-role share-link visitors saw a canvas with no editing controls. The layout now lazy-mounts SlidesToolbar alongside SlidesView (gated on !readOnly), mirroring SharedDocsLayout. Image upload from the shared toolbar surfaces an info toast — the workspace-scoped upload API needs JWT that share-link visitors don't carry.
  • SlidesView accepted readOnly only for API parity; the underlying editor still bound every pointer + keyboard handler, so viewer-role shares could mutate the deck. Adds an opt-in readOnly flag on initializeEditor, mountThumbnailPanel, and mountNotesPanel, and threads it through SlidesViewattachInteractions() short-circuits, thumbnail drag/contextmenu handlers are skipped, the notes textarea is readOnly. The empty-deck seed is also suppressed in read-only mounts.
  • Chunk budget bumped 90 → 93 to absorb the lazy SlidesToolbar subsections (idle / object / text-edit / mobile); only slides share-link visitors download these chunks.

Test plan

  • pnpm verify:fast green.
  • pnpm verify:frontend:chunks green (91 / 93).
  • New vitest cases in editor.test.ts, thumbnail-panel.test.ts, notes-panel.test.ts exercise the readOnly gate against the parallel writeable assertions.
  • Manual smoke (editor-role share link): toolbar appears, all controls work, image-pick shows the info toast.
  • Manual smoke (viewer-role share link): canvas paints, slide switching via click/arrow works, but element clicks / Delete / drag-reorder / notes typing all no-op.
  • CI green.

Known limitations (out of scope)

  • ensureSlidesRoot may still run a doc.update() migration on viewer mounts of unmigrated pre-v0.5 decks — covered by an in-source comment and owned by the doc-migration workstream.
  • MobileSlidesView does not forward readOnly yet (only mounted from the owner route today); a TODO marks the spot for when SharedSlidesLayout grows a phone-width branch.
  • Server-side Yorkie ACLs that would block a viewer who bypasses the UI and writes directly are not part of this PR.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Implemented read-only mode for shared slide links to prevent viewer edits while preserving navigation and viewing.
    • Thumbnail panel in read-only mode disables drag-reordering and context menus; click navigation remains functional.
    • Speaker notes rendered as non-editable for shared read-only viewers.
  • Documentation

    • Added design and implementation documentation for read-only slides and toolbar support.

Review Change Stack

SharedSlidesLayout never mounted SlidesToolbar, so editor-role share
links had a canvas with no editing controls. The layout also forwarded
`readOnly` to SlidesView only for API parity — the slides editor had
no read-only mode, so viewer-role shares could still mutate the deck
via clicks, keyboard, thumbnail drag-reorder, and notes typing.

Mount SlidesToolbar lazily alongside SlidesView when the share role
is editor, matching SharedDocsLayout's pattern. Image upload from the
shared toolbar surfaces a toast — the workspace-scoped upload API
needs JWT that share-link visitors don't carry.

Add an opt-in `readOnly` flag on the three public mount entry points
(initializeEditor, mountThumbnailPanel, mountNotesPanel) and thread
it through SlidesView. The editor flag short-circuits
attachInteractions() so every pointer + document keydown handler is
skipped at the bind site; the thumbnail flag clears item.draggable
and skips drag/contextmenu handlers; the notes flag marks the
textarea readOnly and skips the input handler. SlidesView also skips
the empty-deck seed when read-only so a viewer arriving before the
owner has saved the first slide never mutates the doc.
The previous commit lazy-loads SlidesToolbar alongside SlidesView in
shared-document.tsx for editor-role slides shares. Vite splits the
toolbar's contextual subsections (idle / object / text-edit / mobile)
into their own dynamic-import chunks, so the build emits ~3 chunks
beyond the lazy SlidesView entry — pushing the count from 88 to 91
against the 90 limit.

Bump the budget to 93 with a matching note in harness.config.json so
the chunk gate stays the canonical record of why each lift happened.
Only slides share-link visitors download these chunks; sheets/docs
share routes are unaffected.
@coderabbitai

coderabbitai Bot commented May 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@hackerwins, we couldn't start this review because you've used your available PR reviews for now.

Your plan currently allows 1 review/hour. Refill in 47 minutes and 27 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 439f8f55-1f39-4fcb-847e-cf2659f59af4

📥 Commits

Reviewing files that changed from the base of the PR and between 6a7b7ed and e7ab5fc.

📒 Files selected for processing (2)
  • docs/tasks/archive/2026/05/20260523-slides-share-toolbar-lessons.md
  • docs/tasks/archive/2026/05/20260523-slides-share-toolbar-todo.md
📝 Walkthrough

Walkthrough

This PR implements read-only mode for viewer-role shared slide links. It gates pointer/keyboard interactions and mutating actions (drag-reorder, context menus, note editing) via optional readOnly flags on editor initialization and panel mounting, while preserving slide navigation and rendering. The shared layout conditionally mounts the editor toolbar only for editors.

Changes

Read-Only Shared Slides

Layer / File(s) Summary
Editor core read-only infrastructure
packages/slides/src/view/editor/editor.ts, packages/slides/src/view/editor/notes-panel.ts, packages/slides/src/view/editor/thumbnail-panel.ts, packages/slides/src/index.ts, packages/slides/test/view/editor/*
SlidesEditorOptions gains readOnly?: boolean, and when true, attachInteractions() is skipped so all pointer/keyboard bindings remain unbound. MountThumbnailPanelOptions and MountNotesPanelOptions introduce parallel readOnly flags that disable dragging/context-menu and make textarea native read-only with input listener skipped. Public re-exports expanded to include these new option types. Tests verify no selection/deletion occurs and context menus don't mount in read-only mode.
SlidesView read-only support
packages/frontend/src/app/slides/slides-view.tsx, packages/frontend/src/app/slides/mobile-slides-view.tsx
SlidesView now accepts readOnly prop, derives readOnlyMount flag, and threads it into editor and panel initialization. Empty-deck seeding is skipped for read-only viewers to prevent store mutations on first mount. Comments document a known gap where ensureSlidesRoot may perform doc migrations for unmigrated decks. Mobile view gains a TODO noting planned readOnly support for share-link routes.
Shared layout and toolbar
packages/frontend/src/app/shared/shared-document.tsx, harness.config.json
SharedSlidesLayout now tracks editor and Yorkie store state via onEditorReady/onStoreReady callbacks, derives active theme from the store, and conditionally mounts SlidesToolbar only for editor-role shares. Image picking shows a toast indicating upload is unavailable. Frontend chunk budget increased from 90 to 93 to account for lazy-loaded toolbar dependencies.
Documentation and task tracking
docs/design/slides/slides.md, docs/tasks/*
Design document updated with "Read-only mounts" section describing the feature. Archived task docs capture implementation findings (interaction binding patterns, jsdom drag limitations, Suspense boundary sharing) and process notes (verification steps, API coordination). Task indices incremented.

Sequence Diagram

sequenceDiagram
  participant Viewer as Viewer (readOnly=true)
  participant Editor as SlidesEditorImpl
  participant Thumbs as ThumbnailPanel
  participant Notes as NotesPanel
  participant Store as SlidesStore
  Viewer->>Editor: initialize (readOnly: true)
  Editor->>Editor: skip attachInteractions()
  Viewer->>Thumbs: mountThumbnailPanel ({readOnly: true})
  Thumbs->>Thumbs: set draggable=false, skip drag/context handlers
  Viewer->>Notes: mountNotesPanel ({readOnly: true})
  Notes->>Notes: textarea.readOnly=true, skip input listener
  Viewer->>Thumbs: click thumbnail to navigate
  Thumbs->>Editor: update currentSlideIndex
  Viewer->>Notes: view notes (read-only)
  Note over Thumbs,Notes: No mutations reach Store
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • wafflebase/wafflebase#261: Both PRs modify slides editor interaction wiring—main PR gates mutating handlers via readOnly in thumbnail-panel and editor attachInteractions, while PR #261 migrates those bindings to Pointer Events.
  • wafflebase/wafflebase#267: Main PR's SlidesEditorOptions.readOnly suppresses attachInteractions(), directly suppressing the move-cursor and ghost-preview drag interactions added in PR #267.
  • wafflebase/wafflebase#271: Both PRs modify SlidesView and mountThumbnailPanel logic in packages/frontend/src/app/slides/slides-view.tsx and packages/slides/src/view/editor/thumbnail-panel.ts, so read-only mounting intersects with thumbnail rendering refactors.

Poem

A viewer now sees the slides so bright,
But cannot move them left or right.
The notes display, the thumbs align,
Yet all mutations are confined.
Safe read-only, the deck won't bend—
🐰 A feature fit to share and send!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'Mount toolbar on slides share + enforce viewer read-only' accurately summarizes the two main changes: mounting the toolbar for editor-role share links and implementing read-only enforcement for viewer-role shares.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch slides-share-toolbar-readonly

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/tasks/archive/2026/05/20260523-slides-share-toolbar-lessons.md`:
- Line 36: Update the stale chunk-gate numbers in the markdown line that
currently reads "surface is a small JS-only chunk (chunk gate went 87 → 88 /
90)" to reflect the PR’s final verification values "91 / 93" (with budget 93);
locate the exact sentence in
docs/tasks/archive/2026/05/20260523-slides-share-toolbar-lessons.md and replace
the old "87 → 88 / 90" sequence with "91 / 93" so the recorded verification
matches the PR results.

In `@docs/tasks/archive/2026/05/20260523-slides-share-toolbar-todo.md`:
- Around line 56-57: The sentence "Yorkie write enforcement on the server side
(a viewer that bypasses the UI today would still hit Yorkie ACLs — out of scope
for this PR)." incorrectly claims server-side ACL enforcement; update that line
to remove the security guarantee and clearly state server-side write enforcement
is out of scope for this PR (e.g., reword to indicate only UI-level controls are
addressed and that bypassing the UI may not be blocked by server-side ACLs in
this work). Locate the exact sentence in the document (the line starting "Yorkie
write enforcement on the server side") and replace it with the clarified
phrasing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f6b38645-edb8-496d-adf8-ba4076521938

📥 Commits

Reviewing files that changed from the base of the PR and between 1ba8f33 and 6a7b7ed.

📒 Files selected for processing (16)
  • docs/design/slides/slides.md
  • docs/tasks/README.md
  • docs/tasks/archive/2026/05/20260523-slides-share-toolbar-lessons.md
  • docs/tasks/archive/2026/05/20260523-slides-share-toolbar-todo.md
  • docs/tasks/archive/README.md
  • harness.config.json
  • packages/frontend/src/app/shared/shared-document.tsx
  • packages/frontend/src/app/slides/mobile-slides-view.tsx
  • packages/frontend/src/app/slides/slides-view.tsx
  • packages/slides/src/index.ts
  • packages/slides/src/view/editor/editor.ts
  • packages/slides/src/view/editor/notes-panel.ts
  • packages/slides/src/view/editor/thumbnail-panel.ts
  • packages/slides/test/view/editor/editor.test.ts
  • packages/slides/test/view/editor/notes-panel.test.ts
  • packages/slides/test/view/editor/thumbnail-panel.test.ts

Comment thread docs/tasks/archive/2026/05/20260523-slides-share-toolbar-lessons.md Outdated
Comment thread docs/tasks/archive/2026/05/20260523-slides-share-toolbar-todo.md Outdated
@github-actions

github-actions Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 216.7s

Lane Status Duration
sheets:build ✅ pass 13.0s
docs:build ✅ pass 12.5s
slides:build ✅ pass 14.1s
verify:fast ✅ pass 130.0s
frontend:build ✅ pass 18.5s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.9s
cli:build ✅ pass 2.2s
verify:entropy ✅ pass 21.2s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented May 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

CodeRabbit flagged two factual drifts in the archived task files:

- The lessons file recorded a stale "87 → 88 / 90" chunk gate transition.
  The actual final state after the lazy SlidesToolbar split is 91 chunks
  against the new budget of 93, because the toolbar's contextual
  subsections each emit their own dynamic-import chunk.
- The non-goals list claimed "a viewer that bypasses the UI today would
  still hit Yorkie ACLs," implying server-side enforcement we never
  verified. Reword to make clear UI-level gating is all this PR adds.
@hackerwins
hackerwins merged commit 0c6e295 into main May 23, 2026
1 check passed
@hackerwins
hackerwins deleted the slides-share-toolbar-readonly branch May 23, 2026 01:31
@hackerwins hackerwins mentioned this pull request May 24, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant