Skip to content

slides: add read-only mobile view (<768px)#255

Merged
hackerwins merged 11 commits into
mainfrom
slides-mobile-view
May 17, 2026
Merged

slides: add read-only mobile view (<768px)#255
hackerwins merged 11 commits into
mainfrom
slides-mobile-view

Conversation

@hackerwins

@hackerwins hackerwins commented May 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a dedicated read-only MobileSlidesView shell — thin header (back / title / Present), full-width slide canvas, footer indicator, and left/right swipe nav — mounted by SlidesLayout when useIsMobile() is true.
  • Editor module is intentionally not mounted on mobile, so read-only is enforced by construction rather than by a feature flag. SlideRenderer is reused directly the same way view/present/presenter.ts does.
  • Adds a usePointerSwipe / attachPointerSwipe pair; the pure-DOM core is unit-tested against a fake element (the frontend test runner is node:test and stubs .tsx, so the React wrapper itself is covered only by manual smoke).
  • Design doc: docs/design/slides/slides-mobile-view.md. Implementation lessons (plan deviations) captured in docs/tasks/active/20260517-slides-mobile-view-lessons.md.

Test plan

  • pnpm verify:fast green (792 tests)
  • pnpm verify:self green (sheets/docs/slides/frontend/backend/cli builds + entropy)
  • Manual smoke: mobile emulation at 360, 390, 430 viewport widths — header / canvas / footer layout, swipe navigation, Present button (fullscreen API + iOS overlay fallback), back nav
  • Manual smoke: cross-breakpoint resize at the 768px boundary swaps the two layouts cleanly without losing the Yorkie attachment or remounting DocumentProvider
  • Manual smoke: desktop editor at ≥768px is byte-for-byte identical to main (no regression to the existing slide editor path)

Known limitations

  • Visual baseline scenario for the mobile chrome is deferred — the visual harness has no Yorkie wiring and MobileSlidesView is tightly coupled to useDocument. Detail in the lessons file.
  • iOS edge-anchored swipe-back gestures cannot be intercepted (system constraint); footer arrow buttons are the documented fallback.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added mobile-optimized read-only slides viewer with swipe gesture navigation for devices under 768px.
    • Enabled presentation mode on mobile devices.
    • Mobile UI includes header navigation, footer controls, and intuitive touch gestures.
  • Documentation

    • Added implementation specifications and design guidelines for mobile slides experience.
  • Tests

    • Added gesture detection and navigation test suite.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds a complete mobile slides viewer experience for viewports under 768px, featuring pointer-swipe gesture navigation, canvas-based rendering, and responsive layout branching, alongside comprehensive design documentation and task planning spanning six implementation steps.

Changes

Mobile Slides View Feature

Layer / File(s) Summary
Pointer swipe gesture detection hook and tests
packages/frontend/src/hooks/use-pointer-swipe.ts, packages/frontend/tests/hooks/use-pointer-swipe.test.ts
attachPointerSwipe implements a pointer state machine to classify horizontal vs. vertical motion, trigger callbacks only when displacement exceeds threshold and duration is within bounds, and handle multi-pointer isolation. usePointerSwipe wraps it as a React hook. 14 unit tests using a lightweight fake-DOM harness verify threshold behavior, cancellation, cleanup, and pointer lifecycle correctness without JSDOM.
Mobile slides view component
packages/frontend/src/app/slides/mobile-slides-view.tsx
MobileSlidesView loads a Yorkie slides document, manages YorkieSlidesStore, derives slide snapshot and current index, wires swipe/button navigation via usePointerSwipe, renders canvas using SlideRenderer with DPR-aware sizing and RAF-coalesced ResizeObserver, subscribes to remote changes, supports presentation mode entry, and renders loader/error states.
Responsive layout branching
packages/frontend/src/app/slides/slides-detail.tsx
SlidesLayout fetches document/workspace data and guards against document-not-found errors before rendering. It conditionally mounts MobileSlidesView via useIsMobile() for mobile viewports or DesktopSlidesLayout for desktop, preserving desktop editor behavior. DesktopSlidesLayout reuses the parent's react-query cache and removed its local redirect guard.
Design specification and task planning
docs/design/README.md, docs/design/slides/slides-mobile-view.md, docs/tasks/README.md, docs/tasks/active/20260517-slides-mobile-view-todo.md, docs/tasks/active/20260517-slides-mobile-view-lessons.md
Design document specifies mobile architecture, Yorkie integration, gesture/button navigation, and testing strategy. Task breakdown lists six implementation steps with embedded code examples. Lessons document captures decisions on read-only enforcement, duplicated utilities, React hooks constraints, and test runner adjustments.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A mobile view hops into the frame,
Swipe left, swipe right—gestures stake their claim,
Canvas rendered sharp, store in sync,
Layout branches swift in a responsive blink,
Docs and tests ensure all's well,
The rabbit's mobile tale to tell! 🎪

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% 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 title clearly and concisely summarizes the main change: adding a read-only mobile slides view for viewports under 768px, which is the primary objective across all modified files.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch slides-mobile-view

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

🤖 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/design/slides/slides-mobile-view.md`:
- Around line 94-110: Update the fenced code block that contains the
JSX/HTML-like DOM snippet to include a language identifier (use "html") so
markdown linting and syntax highlighting pass; locate the block showing the <div
class="mobile-slides"> ... <canvas /> ... </div> (references: mobile-slides,
canvasHostRef, canvas) and change the opening triple backticks from ``` to
```html.

In `@packages/frontend/src/app/slides/slides-detail.tsx`:
- Around line 66-70: The early return in SlidesLayout bypasses the shared
document fetching and not-found/permission redirect logic currently used inside
DesktopSlidesLayout; move the document query and any redirect/validation (the
fetchDocument or equivalent logic that produces the resolved title and not-found
behavior) up into SlidesLayout before checking useIsMobile(), then branch to
MobileSlidesView and DesktopSlidesLayout passing the resolved title (e.g., prop
name title) into MobileSlidesView (and keep passing it into DesktopSlidesLayout
if needed) so both mobile and desktop share the same existence/permission
guardrails and API title source.

In `@packages/frontend/src/hooks/use-pointer-swipe.ts`:
- Around line 52-67: The onDown handler currently resets
startX/startY/startTime/pointerId even if a gesture is active; modify onDown
(function onDown) to ignore additional pointerdown events by returning early
when a tracked pointer is already active (e.g., if pointerId != null or phase
=== "pending" or "active"), so only the first down initializes startX, startY,
startTime and pointerId and only that pointer attempts setPointerCapture; ensure
you clear pointerId when the pointer ends/cancels elsewhere so new downs are
accepted.
🪄 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: 16d0ecf6-01eb-4b32-97db-e52a35a54a11

📥 Commits

Reviewing files that changed from the base of the PR and between 860c4ba and 6fb937b.

📒 Files selected for processing (10)
  • docs/design/README.md
  • docs/design/slides/slides-mobile-view.md
  • docs/tasks/README.md
  • docs/tasks/active/20260517-slides-mobile-view-lessons.md
  • docs/tasks/active/20260517-slides-mobile-view-todo.md
  • docs/tasks/archive/README.md
  • packages/frontend/src/app/slides/mobile-slides-view.tsx
  • packages/frontend/src/app/slides/slides-detail.tsx
  • packages/frontend/src/hooks/use-pointer-swipe.ts
  • packages/frontend/tests/hooks/use-pointer-swipe.test.ts

Comment thread docs/design/slides/slides-mobile-view.md Outdated
Comment thread packages/frontend/src/app/slides/slides-detail.tsx
Comment thread packages/frontend/src/hooks/use-pointer-swipe.ts
hackerwins added a commit that referenced this pull request May 17, 2026
A second pointerdown landing mid-swipe (common iOS/Android
two-finger scenario) was silently overwriting startX/startY/
pointerId, so the first finger's gesture either misfired or
produced no swipe at all. The fix is a one-line active-pointer
guard in onDown; both regression cases (mid-gesture second
finger, fresh gesture after pointercancel) are covered by new
unit tests. Also tags the markdown DOM-tree code block in
docs/design/slides/slides-mobile-view.md with `html` so linters
syntax-highlight it correctly. Both issues raised by CodeRabbit
on PR #255.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
hackerwins added a commit that referenced this pull request May 17, 2026
The useIsMobile branch in SlidesLayout previously skipped past
DesktopSlidesLayout's document fetchDocument + not-found redirect,
so navigating to /p/<bad-id> on mobile would attach to an empty
Yorkie doc instead of redirecting away — a real access-control
gap, not just a UX miss, since Yorkie does not consult the
backend's document existence check. Moves both the document and
workspaces queries plus the toast + redirect up to SlidesLayout
(parent), drops the now-duplicated redirect from DesktopSlidesLayout
(react-query's cache means its inner useQuery is free), and passes
the resolved title down to MobileSlidesView so the mobile header
matches the document API rather than relying on the Yorkie meta
title. Raised by CodeRabbit on PR #255.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
hackerwins and others added 10 commits May 17, 2026 21:51
Captures the design for a read-only mobile experience: SlidesView
branches to a dedicated MobileSlidesView component below 768px,
the editor module is not mounted (read-only by construction), and
SlideRenderer is reused directly the way presenter.ts does. Adds
the matching todo + lessons scaffold so the implementation can
land as small commits on this branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Encapsulates horizontal-swipe classification so the upcoming mobile
slides view can hand off gesture detection. Locks horizontal intent
once |dx| > 10px (to suppress iOS swipe-back where allowed), falls
back to no-op below a 50px / 600ms threshold, and is split into a
pure attach helper plus a thin React wrapper because the frontend
test runner is node:test and cannot drive React components.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Read-only mobile shell for slide decks: thin header (back / title /
Present), an empty canvas host that's wired to usePointerSwipe for
left/right slide navigation, footer indicator with explicit arrow
buttons as the screen-reader fallback, and an inline
SlidesPresentationMode mount so the Present button launches the
same fullscreen presenter the desktop view uses. Canvas painting
lands in the next commit; this commit keeps the React shell
reviewable in isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Wires a SlideRenderer to MobileSlidesView's canvas. A
RAF-coalesced ResizeObserver rebuilds the renderer on host-size
changes (cheap constructor, same pattern as presenter.ts), and the
store's onChange callback triggers a fresh read+paint when peers
edit the deck. Re-uses the same computeFitSize math as
slides-view.tsx / presenter.ts — intentional ~10-line duplicate
per the design doc since the slides package must stay
frontend-agnostic.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Adds a useIsMobile branch at the top of SlidesLayout. When the
viewport is narrower than 768px the layout swaps the entire
desktop chrome (SidebarProvider, AppSidebar, SiteHeader,
SlidesToolbar, SlidesView, ThemePanel) for MobileSlidesView,
which is read-only by construction since the editor module is
never mounted. Crossing the breakpoint at runtime unmounts one
tree and mounts the other; the surrounding DocumentProvider keeps
the Yorkie attachment alive across the swap.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Capture the three plan deviations made during implementation so the
next mobile-related task starts with the right priors:
node:test (not Vitest) and the .tsx stub policy, the need to branch
in SlidesLayout (not SlidesView) so the desktop chrome is fully
replaced, and the deferral of the visual baseline scenario for the
mobile chrome.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The previous docstring read "canvas painting lands in the next
commit", which was accurate when the scaffold landed but is
misleading now that the renderer is wired up. Rewrites the JSDoc
to point at the SlideRenderer reuse instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
A second pointerdown landing mid-swipe (common iOS/Android
two-finger scenario) was silently overwriting startX/startY/
pointerId, so the first finger's gesture either misfired or
produced no swipe at all. The fix is a one-line active-pointer
guard in onDown; both regression cases (mid-gesture second
finger, fresh gesture after pointercancel) are covered by new
unit tests. Also tags the markdown DOM-tree code block in
docs/design/slides/slides-mobile-view.md with `html` so linters
syntax-highlight it correctly. Both issues raised by CodeRabbit
on PR #255.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The useIsMobile branch in SlidesLayout previously skipped past
DesktopSlidesLayout's document fetchDocument + not-found redirect,
so navigating to /p/<bad-id> on mobile would attach to an empty
Yorkie doc instead of redirecting away — a real access-control
gap, not just a UX miss, since Yorkie does not consult the
backend's document existence check. Moves both the document and
workspaces queries plus the toast + redirect up to SlidesLayout
(parent), drops the now-duplicated redirect from DesktopSlidesLayout
(react-query's cache means its inner useQuery is free), and passes
the resolved title down to MobileSlidesView so the mobile header
matches the document API rather than relying on the Yorkie meta
title. Raised by CodeRabbit on PR #255.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Picks up two entries that landed on main while this branch was in
review (docs-comments-followup and slides-textbox-click-scale) and
bumps the archive count from 181 to 184. Auto-generated by
pnpm tasks:index.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@hackerwins
hackerwins force-pushed the slides-mobile-view branch from e6fae82 to f68a9f1 Compare May 17, 2026 12:56
@github-actions

github-actions Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 209.6s

Lane Status Duration
sheets:build ✅ pass 12.8s
docs:build ✅ pass 12.5s
slides:build ✅ pass 13.1s
verify:fast ✅ pass 124.4s
frontend:build ✅ pass 18.6s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 5.0s
cli:build ✅ pass 2.2s
verify:entropy ✅ pass 20.7s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented May 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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

🧹 Nitpick comments (1)
docs/design/slides/slides-mobile-view.md (1)

67-78: ⚡ Quick win

Design document inconsistencies with implementation.

The review stack context indicates the implementation diverged from this design specification in two areas:

  1. Component location (lines 67, 224): The document states branching occurs in SlidesView (slides-view.tsx), but the actual implementation places this logic in SlidesLayout (slides-detail.tsx). Per the review stack context: "Updates SlidesLayout to own document fetch and redirect-on-error guard for both mobile and desktop."

  2. Data flow approach (lines 119-131): The document specifies using doc.subscribe directly while avoiding YorkieSlidesStore, but the implementation actually "instantiates and syncs YorkieSlidesStore" according to the stack context for the mobile slides view component layer.

📝 Suggested alignment

Update the design document to reflect the as-built implementation:

Section "Detection and branching" (line 67):

-`SlidesView` (`packages/frontend/src/app/slides/slides-view.tsx`)
+`SlidesLayout` (`packages/frontend/src/app/slides/slides-detail.tsx`)
 branches at the top of its render based on the existing

Section "Yorkie data flow" (lines 119-131):

-- The deck's slides, theme, and meta are read from the Yorkie root
-  into React state. A `doc.subscribe((e) => ...)` listener triggers
-  a re-snapshot on `remote-change` events; this mirrors the pattern
-  used by `yorkie-slides-store.ts` and avoids a heavier
-  `YorkieSlidesStore` instance, which the mobile view does not
-  need. No writes are issued.
+- A `YorkieSlidesStore` instance is instantiated and synchronized with
+  the Yorkie document. The store's snapshot is subscribed to track
+  remote changes, providing slides, theme, and metadata to the React
+  component. No mutation operations are called on the store.

File change summary table (line 224):

-| `packages/frontend/src/app/slides/slides-view.tsx`              | Add `useIsMobile()` branch at the top of render; delegate to `MobileSlidesView` when true.           |
+| `packages/frontend/src/app/slides/slides-detail.tsx`            | Add `useIsMobile()` branch in `SlidesLayout`; mount `MobileSlidesView` for mobile, `DesktopSlidesLayout` otherwise. |

Also applies to: 119-131, 224-224

🤖 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-mobile-view.md` around lines 67 - 78, The design
doc is out-of-sync with the implementation: the branching for mobile/desktop
lives in SlidesLayout (slides-detail.tsx) not SlidesView (slides-view.tsx), and
the data flow uses YorkieSlidesStore (instantiated/synced) instead of directly
calling doc.subscribe; update the "Detection and branching" section to state
that SlidesLayout performs the useIsMobile-based branch and mention useIsMobile,
and update the "Yorkie data flow" section to describe instantiation and sync
behavior of YorkieSlidesStore (and where it is used) rather than doc.subscribe;
also update the file change summary to reflect SlidesLayout owning document
fetch and redirect-on-error guard and that YorkieSlidesStore is used for mobile
slides.
🤖 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 77-113: The document view is mounting before the document query
settles, risking Yorkie attachment for invalid IDs; update the component to gate
rendering by using the document query's isLoading and isError state: add
isLoading from the useQuery that fetches document (alongside isDocumentError),
and if isLoading return null (or a placeholder) so neither MobileSlidesView nor
DesktopSlidesLayout mounts; additionally, if isDocumentError return null (so
nothing mounts) while the useEffect handles toast+navigate; reference the
document fetch useQuery, isDocumentError/isLoading, documentData, fallbackSlug,
isMobile, MobileSlidesView and DesktopSlidesLayout when making the change.

---

Nitpick comments:
In `@docs/design/slides/slides-mobile-view.md`:
- Around line 67-78: The design doc is out-of-sync with the implementation: the
branching for mobile/desktop lives in SlidesLayout (slides-detail.tsx) not
SlidesView (slides-view.tsx), and the data flow uses YorkieSlidesStore
(instantiated/synced) instead of directly calling doc.subscribe; update the
"Detection and branching" section to state that SlidesLayout performs the
useIsMobile-based branch and mention useIsMobile, and update the "Yorkie data
flow" section to describe instantiation and sync behavior of YorkieSlidesStore
(and where it is used) rather than doc.subscribe; also update the file change
summary to reflect SlidesLayout owning document fetch and redirect-on-error
guard and that YorkieSlidesStore is used for mobile slides.
🪄 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: 9b41aced-cd8f-4cdb-91e0-ce3ccf591124

📥 Commits

Reviewing files that changed from the base of the PR and between 6fb937b and f68a9f1.

📒 Files selected for processing (9)
  • docs/design/README.md
  • docs/design/slides/slides-mobile-view.md
  • docs/tasks/README.md
  • docs/tasks/active/20260517-slides-mobile-view-lessons.md
  • docs/tasks/active/20260517-slides-mobile-view-todo.md
  • packages/frontend/src/app/slides/mobile-slides-view.tsx
  • packages/frontend/src/app/slides/slides-detail.tsx
  • packages/frontend/src/hooks/use-pointer-swipe.ts
  • packages/frontend/tests/hooks/use-pointer-swipe.test.ts
✅ Files skipped from review due to trivial changes (3)
  • docs/design/README.md
  • docs/tasks/active/20260517-slides-mobile-view-lessons.md
  • docs/tasks/active/20260517-slides-mobile-view-todo.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/frontend/src/hooks/use-pointer-swipe.ts

Comment thread packages/frontend/src/app/slides/slides-detail.tsx Outdated
The hoist in 8c028b8 moved the not-found redirect into
SlidesLayout, but the useEffect that fires the toast + navigate
runs only after the first paint. In that window both branches
would mount and trigger a Yorkie attach for the requested id
before the backend access check had landed — a peer with the
deck already open would briefly leak contents through the Yorkie
subscription. Holds both branches behind a Loader while the
document query is in-flight and returns null once the error is
known so the redirect's race window closes. Raised by CodeRabbit
on PR #255.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@hackerwins
hackerwins merged commit 0df8c44 into main May 17, 2026
4 checks passed
@hackerwins
hackerwins deleted the slides-mobile-view branch May 17, 2026 13:18
hackerwins added a commit that referenced this pull request May 17, 2026
Below 768px the desktop chrome (sidebar + site header + toolbar) ate
the viewport and left the slide canvas unreadable. This PR adds a
dedicated MobileSlidesView — thin header (back / title / Present),
full-width canvas, footer indicator with arrow buttons and swipe
navigation — that SlidesLayout mounts in place of the desktop tree
when useIsMobile() reports a narrow viewport. Read-only is enforced
by construction; the editor module is never mounted on mobile, so
no mutation pathway exists.

SlideRenderer is reused directly the same way the presenter does, so
the canvas pipeline stays in one place. usePointerSwipe wraps a
pure-DOM gesture core (unit-tested against a fake element — the
frontend test runner is node:test and stubs .tsx files, so the
React wrapper is covered by manual smoke).

Code review surfaced three correctness items handled on the same
branch: the document existence/permission check is hoisted up to
SlidesLayout so the mobile branch is no longer an access-control
bypass; the mount is gated on the document query settling so a peer
with the deck already open cannot briefly leak contents through the
Yorkie subscription; and a multi-finger guard in usePointerSwipe
prevents a second pointerdown from overwriting an in-flight
gesture's start coordinates.

Design doc: docs/design/slides/slides-mobile-view.md
Lessons:    docs/tasks/active/20260517-slides-mobile-view-lessons.md
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