Skip to content

Add slides presentation mode v1 (local-only fullscreen)#240

Merged
hackerwins merged 17 commits into
mainfrom
feat/slides-presentation-mode
May 14, 2026
Merged

Add slides presentation mode v1 (local-only fullscreen)#240
hackerwins merged 17 commits into
mainfrom
feat/slides-presentation-mode

Conversation

@hackerwins

@hackerwins hackerwins commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Implements the Phase 5 presentation mode that docs/design/slides/slides.md
    has reserved a slot for. Editor already exposes onStartPresentation and the
    Cmd/Ctrl+Enter shortcut keys are wired — this PR fills in the handler and
    the underlying presenter.
  • New framework-free startPresenter(options) module in
    packages/slides/src/view/present/ (~450 LOC, 52 unit tests) covering
    fullscreen, overlay fallback, keyboard nav, click-to-advance, end-screen,
    cursor auto-hide, identity-gated fullscreenchange, ID-tracked
    remote-change handling, and clean dispose.
  • Thin React shell SlidesPresentationMode (portal-style, renders null)
    plus a <PresentButton> split-button in SiteHeader next to share /
    presence chrome. Cmd/Ctrl+Enter routes through the same handler as the
    button via a ref-wrapped onStartPresentation prop, so a fresh parent
    callback identity doesn't tear down the editor.
  • Local-only in v1: no presence broadcast and collaborators are not
    auto-snapped to the presenter's slide. Speaker-notes display and dual-
    screen presenter view remain v2 per the design doc.

Design

  • docs/design/slides/slides-presentation-mode.md — full v1 spec.
  • docs/design/slides/slides.md Phase 5 section now cross-references the
    new doc and is downscoped to local-only.

Test plan

  • pnpm verify:fast green on every commit (47 test files, 764 tests).
  • 52 presenter unit tests in
    packages/slides/src/view/present/presenter.test.ts cover navigation,
    canvas mount, end-screen text, keyboard / click, cursor auto-hide,
    fullscreen, overlay fallback, identity-gated fullscreenchange,
    dispose-during-pending-fullscreen race, stale-startSlideId fallback,
    remote-change handling, and dispose cleanup.
  • Browser smoke (pnpm dev):
    • Open slides doc with 3 slides → click Present → fullscreen,
      letterboxed, current slide visible.
    • Arrow keys / Space / PageDown / PageUp / Home / End all navigate.
      Click on canvas advances.
    • Past the last slide → black 'End of slideshow' screen → click or
      Esc exits to editor.
    • Cmd+Enter from a non-text-focused state starts present mode.
      Cmd+Enter while editing a text box does NOT enter (gated).
    • Dropdown 'Present from beginning' starts at slide 1.
    • Cursor disappears after ~3 s of no movement; reappears on move.
    • Native browser Esc / F11 exits cleanly (handler bridges
      fullscreenchangeonExit).
    • Open in two tabs: edit current slide in tab B → tab A re-renders.
      Delete current slide in tab B → tab A jumps to the slide at the
      same index (or last). Delete all slides → tab A exits with toast
      'Presentation ended (deck is empty)'.
    • Click Present then press Esc immediately (dispose-during-pending-
      fullscreen race): page should not be stuck in fullscreen.

Notes

  • Public API on @wafflebase/slides: startPresenter, Presenter,
    PresenterOptions.
  • The branch closes the Deferred: onStartPresentation wiring TODO on
    20260514-slides-keyboard-shortcuts-todo.md.
  • Implementation followed docs/tasks/active/20260514-slides-presentation-mode-todo.md
    task-by-task with per-task spec + code-quality reviews plus a final
    cross-cutting review; Status section at the bottom of that doc lists all
    16 commits.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Slides Presentation Mode with fullscreen support, keyboard navigation (arrow keys, page up/down, home/end, escape), and click-to-advance functionality.
    • Added "Present" split-button to start presentation from current or first slide.
    • Auto-hiding cursor during presentation and end-screen display.
    • Fallback overlay mode when fullscreen is unavailable.
  • Documentation

    • Added design specifications for presentation mode features and implementation architecture.

Review Change Stack

hackerwins and others added 16 commits May 15, 2026 07:14
Phase 5 of slides.md has reserved a slot for presentation mode, the
editor exposes onStartPresentation, and Cmd+Enter is already wired —
the handler and the underlying presenter just don't exist yet.

This adds the design (single-canvas fullscreen player with keyboard
nav, click-to-advance, end screen, overlay fallback) and the 10-task
implementation plan. Local-only in v1; presence broadcast and
speaker-notes display stay in v2 per the spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The presenter is the pure state-machine half of v1 presentation mode:
slide-by-slide navigation by ID, end-screen flag, and the public
Presenter / PresenterOptions surface. Rendering, input wiring, and
fullscreen come in later tasks.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
goToFirst() / goToLast() indexed state.slides without checking for
an empty array. next() / prev() are safe via their currentSlideId
== null short-circuit, but the jump methods are not. A remote
collaborator can empty the deck via Task 5's setDocument once it
lands, so the guard ships now while the surface area is tiny.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Fills in paint(): mount a canvas in the container, fit it to the
viewport with devicePixelRatio applied to the backing store, and
re-render through SlideRenderer on every slide change. The end-screen
state paints a black canvas with centered exit text via the raw 2D
context — no SlideRenderer involved, since it's not a real slide.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Two small follow-ups from the Task 2 review. Match the
thumbnail-panel idiom by feature-detecting ResizeObserver before
instantiating, so the presenter degrades gracefully outside browser
environments. Hoist the end-screen copy and font-size ratio to
module constants so a future i18n pass has a single point to touch.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Capture-phase document keydown listener with a KEY_ACTIONS map for
Arrow / Space / PageDown / n / N (next), Arrow / PageUp / Backspace
/ p / P (prev), Home / End (jump), and Escape (onExit). Every key
is swallowed (stopImmediatePropagation + preventDefault) so editor
shortcuts can't fire under the presenter — e.g. Cmd+Z won't undo
out from under the live deck. Canvas clicks advance, except on the
end-screen where they trigger onExit instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
requestFullscreen on the container on mount; if denied, fall back
to a fixed-position overlay (inset 0, z-index 9999) so iframe-
sandboxed or permission-blocked environments still get the same UX
minus the native chrome. A document fullscreenchange handler bridges
the browser's own Esc back to options.onExit when mounted in
fullscreen. The container's cursor hides after 3 s of mousemove
inactivity and restores on the next move.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The previous handler called onExit whenever fullscreenchange reported
fullscreenElement === null and mountMode was still 'fullscreen', which
fires too eagerly in two cases — a stray fullscreenchange before our
own requestFullscreen has resolved, and a sibling element on the page
exiting fullscreen. Track an enteredFullscreen flag set inside .then,
and gate onExit on `document.fullscreenElement === container` so only
the presenter's own transitions count.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Concurrent edits on the Yorkie store arrive via setDocument while the
presentation runs. Three branches matter: an empty deck triggers
onExit so the shell can toast and unmount; an unchanged current-slide
id just re-renders so theme and element edits show up; a deleted
current slide falls back to the slide at its original index, clamped
to the new tail. The end-screen state survives any structural change
short of emptying the deck — the presentation is still 'over'.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
dispose() now reverses every install Tasks 2-4 added — canvas, four
listeners, ResizeObserver, cursor timeout, fullscreen exit, and the
container's prior cssText. The fullscreenchange listener detaches
first to avoid the exitFullscreen→handler→onExit→dispose loop. The
top-level package index now re-exports startPresenter so the React
shell in Task 7 can import it from @wafflebase/slides.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Wraps startPresenter from @wafflebase/slides in a portal-style React
component: imperatively creates a div on document.body, mounts the
presenter on it, forwards store snapshots into setDocument so remote
edits propagate during the presentation, and disposes on unmount.
The component renders null itself — its host element is decoupled
from React's reconciliation so the presenter can own canvas mount /
fullscreen / styles without strict-mode double-mounts splitting them.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Adds the prop to SlidesViewProps and threads it into initializeEditor
so the Cmd/Ctrl+Enter shortcuts and the new toolbar Present button
(Task 9) route through the same handler in the parent layout. Uses
a ref to avoid the editor remounting when the parent re-renders with
a fresh callback reference, matching the pattern the React shell
landed in Task 7. The "present mode is a separate phase" comment is
no longer accurate and is removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
SlidesLayout now owns the present-mode state machine: presentingFrom
('current' | 'first' | null) flips when the user clicks the Present
split-button or hits Cmd/Ctrl+Enter, drives a conditional mount of
SlidesPresentationMode, and resets via the presenter's onExit. The
handleStart empty-deck guard covers both the button path and the
shortcut path. The split-button itself sits in the header next to
share and presence chrome, with a dropdown for "Present from
beginning" mirroring Google Slides' UX.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Tick the deferred onStartPresentation item on the keyboard-shortcuts
task with a back-reference, and fill the Status section on the
presentation-mode task doc with the commit log and the remaining
human-driven close-out steps (smoke, lessons, archive).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The end-of-branch review found two real seams. If dispose runs while
requestFullscreen is still pending, the browser still honors the
request and the page sits in fullscreen with our teardown already
done — the .then continuation now exits fullscreen when it sees
the disposed flag. If a peer deletes the editor's current slide
between the host computing startSlideId and startPresenter running,
the presenter would mount on a phantom id; startPresenter now falls
back to the first slide. Also: the empty-deck onExit path surfaces
a toast, and the design doc drops two stale references (a frontend
test file that never landed and the wrong icon library name).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@hackerwins has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 44 minutes and 22 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, 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 the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78816687-596c-4339-a89c-0613eb3c21a5

📥 Commits

Reviewing files that changed from the base of the PR and between a1ce23e and 2643693.

📒 Files selected for processing (4)
  • docs/design/slides/slides-presentation-mode.md
  • packages/frontend/src/app/slides/slides-detail.tsx
  • packages/slides/src/view/present/presenter.test.ts
  • packages/slides/src/view/present/presenter.ts
📝 Walkthrough

Walkthrough

This PR implements a complete presentation mode for slides. It adds a framework-free presenter in @wafflebase/slides that mounts a fullscreen canvas, handles keyboard/click navigation, auto-hides the cursor, manages fullscreen with overlay fallback, and responds to remote document updates. A React shell in frontend bridges the presenter to the Yorkie store. The editor UI includes a split-button for starting presentation and orchestrates the entry/exit flow. Comprehensive tests validate all presenter behaviors.

Changes

Slides Presentation Mode v1

Layer / File(s) Summary
Design specification and API contract
docs/design/README.md, docs/design/slides/slides-presentation-mode.md, docs/design/slides/slides.md, docs/tasks/active/20260514-slides-presentation-mode-todo.md, docs/tasks/active/20260514-slides-keyboard-shortcuts-todo.md
Design documents establish the presentation-mode v1 scope (local fullscreen player, keyboard/click navigation, cursor auto-hide, fullscreen with overlay fallback), define the public presenter API (PresenterOptions, Presenter, startPresenter), outline rendering/input/fullscreen/remote-update behaviors, and document testing and risk-mitigation strategy.
Core presenter: state machine, canvas rendering, input handling, and cleanup
packages/slides/src/view/present/presenter.ts, packages/slides/src/view/present/index.ts, packages/slides/src/index.ts
Presenter implementation establishes slide state (current id, end-screen flag), mounts a letterboxed 16:9 canvas, renders slides or end-screen text, handles keyboard/click navigation, auto-hides cursor after 3s, manages fullscreen entry with overlay fallback, updates document via setDocument with index-preserving fallback on deletion, and performs thorough dispose cleanup. Public exports via barrel files make the presenter API available to frontend.
Presenter comprehensive test coverage
packages/slides/src/view/present/presenter.test.ts
Test suite (1230+ lines) validates initialization fallback, navigation transitions (next/prev/first/last and end-screen boundaries), canvas mount and paint branches (slide vs end-screen), dispose() idempotency and listener/timer/fullscreen cleanup, keyboard capture-phase handling and key mapping, click-to-advance and end-screen exit, cursor auto-hide timer and re-arming, fullscreen entry/rejection/exit with overlay fallback, and setDocument remote updates including index preservation on slide deletion and empty-deck exit.
React shell and UI components
packages/frontend/src/app/slides/slides-presentation-mode.tsx, packages/frontend/src/app/slides/slides-present-button.tsx, packages/frontend/src/app/slides/slides-view.tsx
SlidesPresentationMode component wraps startPresenter imperatively: creates a host <div>, blurs focused elements, starts the presenter with store snapshot, subscribes to store changes and forwards them via setDocument, and cleans up on unmount. PresentButton renders a split-button UI (primary click for "current", dropdown for "beginning") with platform-appropriate keyboard hints. SlidesView accepts onStartPresentation callback and forwards it to the editor's initialization config via a ref to avoid identity-based remounts.
Editor state and presentation-mode orchestration
packages/frontend/src/app/slides/slides-detail.tsx
SlidesLayout introduces presentingFrom state to track presentation mode entry ("current" vs "first"), slideCount to mirror deck size for button enablement, and handleStartPresentation callback that validates non-empty deck before entering presentation. resolveStartSlideId helper computes the starting slide from editor state and requested mode. Conditional mount of SlidesPresentationMode when presentation is active, with exit handler resetting state and showing a toast if deck is empty at exit time. PresentButton renders in the header, wired to trigger handleStartPresentation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • wafflebase/wafflebase#190: Extends the existing Slides UI chrome components introduced in #190 by adding the new PresentButton and presentation-mode wiring that mounts SlidesPresentationMode and starts the presenter API.
  • wafflebase/wafflebase#238: Connected to #238's addition of onStartPresentation keyboard shortcut plumbing in the slides editor; this PR completes the wiring from editor to presenter via the React shell.

Poem

🎬 Slides Take the Stage

A canvas rises, fullscreen and bright,
With arrow keys to guide your flight.
Click or press—the slides advance,
Then fade to black, the final stance.
Framework-free, yet Yorkie-bound,
Presentation mode comes around! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add slides presentation mode v1 (local-only fullscreen)' directly summarizes the main change: implementation of presentation mode for slides as a local-only v1 with fullscreen support.
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.

✏️ 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 feat/slides-presentation-mode

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.

@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 178.2s

Lane Status Duration
sheets:build ✅ pass 13.0s
docs:build ✅ pass 11.5s
slides:build ✅ pass 10.1s
verify:fast ✅ pass 102.5s
frontend:build ✅ pass 16.5s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.6s
cli:build ✅ pass 1.9s
verify:entropy ✅ pass 17.8s

Verification: verify:integration

Result: ✅ PASS

@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: 3

🧹 Nitpick comments (2)
docs/design/slides/slides-presentation-mode.md (1)

167-173: 💤 Low value

Add language identifier to the code fence.

The code block starting at line 167 is missing a language identifier. While it's pseudo-code illustrating the fullscreen fallback pattern, adding an identifier (e.g., typescript or javascript) improves syntax highlighting and tooling support.

📝 Suggested fix
-```
+```typescript
 try {
   await container.requestFullscreen();
 } catch {

As per coding guidelines, the static analysis tool markdownlint-cli2 flagged this as MD040, fenced-code-language.

🤖 Prompt for 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.

In `@docs/design/slides/slides-presentation-mode.md` around lines 167 - 173, The
fenced code block demonstrating the fullscreen fallback is missing a language
identifier which triggers markdownlint MD040; update the code fence to include a
language (e.g., add "```typescript" or "```javascript") for the block containing
the try { await container.requestFullscreen(); } catch {
applyOverlayStyles(container); } so syntax highlighting and tooling will
recognize it.
packages/frontend/src/app/slides/slides-present-button.tsx (1)

22-26: 💤 Low value

Consider future-proofing platform detection.

navigator.platform is deprecated in favor of navigator.userAgentData.platform. The current approach works and is widely compatible, but you may want to add a fallback chain for future-proofing:

♻️ Proposed enhancement
  const modKey =
    typeof navigator !== "undefined" &&
-   navigator.platform.toLowerCase().includes("mac")
+   ((navigator.userAgentData?.platform?.toLowerCase().includes("mac")) ?? 
+    navigator.platform.toLowerCase().includes("mac"))
      ? "⌘"
      : "Ctrl";
🤖 Prompt for 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.

In `@packages/frontend/src/app/slides/slides-present-button.tsx` around lines 22 -
26, Update the platform detection used to derive modKey to use
navigator.userAgentData.platform when available, falling back to
navigator.userAgent and then navigator.platform (and finally a safe default);
modify the logic around the modKey constant (variable name: modKey) to first
check typeof navigator !== "undefined" && navigator.userAgentData &&
navigator.userAgentData.platform, then test that value for "mac"
(case-insensitive), else fall back to navigator.userAgent (searching for "Mac" /
"Macintosh") and then navigator.platform as currently implemented, so mac
detection remains correct and future-proof.
🤖 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 `@packages/frontend/src/app/slides/slides-detail.tsx`:
- Around line 245-260: The code passes a non-null assertion to
SlidesPresentationMode using resolveStartSlideId which can return undefined
during a race (e.g., remote delete); fix by guarding the call: compute const
startSlideId = resolveStartSlideId(store, presentingFrom, editor) and only
render <SlidesPresentationMode ... startSlideId={startSlideId} /> when
startSlideId is a string (or alternatively make resolveStartSlideId throw when
the deck is empty and catch/avoid rendering); reference resolveStartSlideId,
SlidesPresentationMode, presentingFrom, store, editor, and setPresentingFrom
when applying the guard.

In `@packages/slides/src/view/present/presenter.ts`:
- Around line 67-77: The computed resolvedStartId can crash when
options.doc.slides is empty; update the logic that computes resolvedStartId to
first check that options.doc.slides.length > 0 before accessing slides[0].id
(use options.startSlideId when it exists and matches a slide, otherwise if
slides is non-empty use slides[0].id, and if slides is empty return a safe
fallback such as undefined or throw a clear error). Change the code around
resolvedStartId to reference options.doc.slides.length and options.startSlideId
explicitly so you never index slides[0] when the array may be empty.
- Around line 366-379: The optional chaining on container.requestFullscreen
followed by .then() can call .then on undefined; fix by guarding the call: check
if container.requestFullscreen exists before invoking it and attach .then/.catch
only when it does; if it doesn't exist, run the fallback that sets mountMode =
'overlay' and calls applyOverlayStyles() (the same behavior currently in the
catch branch). Ensure the existing logic that checks disposed, sets
enteredFullscreen = true, and calls document.exitFullscreen?.().catch(...)
remains inside the success handler for requestFullscreen() so those symbols
(container.requestFullscreen, enteredFullscreen, disposed,
document.exitFullscreen, mountMode, applyOverlayStyles) are used correctly.

---

Nitpick comments:
In `@docs/design/slides/slides-presentation-mode.md`:
- Around line 167-173: The fenced code block demonstrating the fullscreen
fallback is missing a language identifier which triggers markdownlint MD040;
update the code fence to include a language (e.g., add "```typescript" or
"```javascript") for the block containing the try { await
container.requestFullscreen(); } catch { applyOverlayStyles(container); } so
syntax highlighting and tooling will recognize it.

In `@packages/frontend/src/app/slides/slides-present-button.tsx`:
- Around line 22-26: Update the platform detection used to derive modKey to use
navigator.userAgentData.platform when available, falling back to
navigator.userAgent and then navigator.platform (and finally a safe default);
modify the logic around the modKey constant (variable name: modKey) to first
check typeof navigator !== "undefined" && navigator.userAgentData &&
navigator.userAgentData.platform, then test that value for "mac"
(case-insensitive), else fall back to navigator.userAgent (searching for "Mac" /
"Macintosh") and then navigator.platform as currently implemented, so mac
detection remains correct and future-proof.
🪄 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: 67278fe2-d120-48e9-a1a9-698746ea1d9b

📥 Commits

Reviewing files that changed from the base of the PR and between a8a8488 and a1ce23e.

📒 Files selected for processing (13)
  • docs/design/README.md
  • docs/design/slides/slides-presentation-mode.md
  • docs/design/slides/slides.md
  • docs/tasks/active/20260514-slides-keyboard-shortcuts-todo.md
  • docs/tasks/active/20260514-slides-presentation-mode-todo.md
  • packages/frontend/src/app/slides/slides-detail.tsx
  • packages/frontend/src/app/slides/slides-present-button.tsx
  • packages/frontend/src/app/slides/slides-presentation-mode.tsx
  • packages/frontend/src/app/slides/slides-view.tsx
  • packages/slides/src/index.ts
  • packages/slides/src/view/present/index.ts
  • packages/slides/src/view/present/presenter.test.ts
  • packages/slides/src/view/present/presenter.ts

Comment thread packages/frontend/src/app/slides/slides-detail.tsx Outdated
Comment thread packages/slides/src/view/present/presenter.ts
Comment thread packages/slides/src/view/present/presenter.ts Outdated
@codecov

codecov Bot commented May 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Three real seams plus a doc-fence nit. The optional-chain bug on
container.requestFullscreen meant a missing Fullscreen API would
crash with "Cannot read .then of undefined" rather than fall through
to the overlay path — explicit feature-detect now bridges that.
startPresenter now throws fail-fast when handed an empty deck, since
the host empty-deck guard is the only thing standing between the
public API and an array-index crash, and a future caller may bypass
the React shell. The "!" non-null assertion in the layout's
conditional mount is replaced with an explicit guard so a remote
empty-deck race between setPresentingFrom and the next render no
longer fabricates an undefined startSlideId. Design-doc fenced block
gets a typescript language identifier.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@hackerwins
hackerwins merged commit 10e6165 into main May 14, 2026
4 checks passed
@hackerwins
hackerwins deleted the feat/slides-presentation-mode branch May 14, 2026 22:42
hackerwins added a commit that referenced this pull request May 18, 2026
These seven slides + PPTX tasks shipped (PRs #238, #240, #241, #255,
#259, #260, #261) but their checklist boxes were never ticked during
execution, so the archive script left them in active/. Sweep the
checkboxes to reflect what actually landed, add lessons files for the
two that were missing (keyboard-shortcuts, presentation-mode), and
convert the keyboard-shortcuts onLinkRequest popover bullet from a
checkbox to a "Deferred (follow-up)" note so the gate stops blocking on
work that is intentionally out of scope.

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
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