Skip to content

Polish slides chrome: thumbnails, picker, notes, toolbar add-slide#198

Merged
hackerwins merged 5 commits into
mainfrom
20260509-slides-thumb-responsive
May 9, 2026
Merged

Polish slides chrome: thumbnails, picker, notes, toolbar add-slide#198
hackerwins merged 5 commits into
mainfrom
20260509-slides-thumb-responsive

Conversation

@hackerwins

@hackerwins hackerwins commented May 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Thumbnails fit the panel. The left thumbnail panel is drag-resizable, but each thumbnail was pinned to 192×108. Compute the size from the panel's measured width (clamp 80–320px), with the inner canvas derived to be exactly 16:9 so the slide-renderer's Math.min(scaleX, scaleY) letterbox doesn't leave the slide background short of the right/bottom border. Re-render on a ResizeObserver tick when the user drags the column resizer.
  • Crisp on Retina. Both the thumbnail canvas and the layout-picker preview hardcoded dpr: 1 while the main slide canvas already multiplied by window.devicePixelRatio. Pass the real dpr through and size the backing store at inner × dpr. The layout-preview cache key now includes dpr so a window dragged between Retina and a non-Retina monitor gets a freshly rendered preview.
  • Border, not outline. Switch the thumbnail item ring from outline to a constant-thickness border inside a border-box. Outline pixels sit at the negative edge of the item and were getting clipped by the panel's overflow box on left/top, making the slide look like its left + top edges were missing. Color-only state change, so the inner canvas size doesn't jump on selection.
  • Add-slide moved to toolbar. "+ Add slide" lives left of Shapes in the formatting toolbar (split-button: primary adds blank, chevron opens layout picker). The in-panel split button is removed; the layout-picker grid column track is expanded by 2 × CELL_PAD so the preview canvas no longer overflows the cell's outline.
  • Light-mode aware. The layout picker, speaker-notes textarea, and thumbnail item border switched from hardcoded #2a2a2a / #444 / #ddd / #3a7 to shadcn tokens (--popover, --popover-foreground, --background, --foreground, --border, --primary) with the previous hex values kept as fallbacks for jsdom and theme-less hosts.

Test plan

  • pnpm verify:fast green (47 docs files / 764 tests + 35 slides files / 264 tests + lint + arch + cli + backend + sheets)
  • Slides built fresh (pnpm --filter @wafflebase/slides build) so frontend tests resolve the new showLayoutPicker export from dist
  • Self code review skill ran across the full branch diff; doc-drift findings folded into a third polish commit
  • Browser smoke (pnpm dev):
    • Drag the left panel handle wide → narrow → wide; thumbnails follow the panel width without overflow or whitespace
    • On Retina, thumbnail glyphs and shapes render crisp; layout-picker preview likewise
    • Toggle the current slide and confirm no inner-canvas size jump
    • Toolbar + Slide adds a blank slide; chevron opens the layout grid; pick a layout → slide appears
    • Layout-picker preview canvas sits inside its cell border (no right-side overflow)
    • Switch the workspace theme between light and dark; layout dropdown, speaker-notes textarea, and thumbnail item border all follow the host theme
  • No regression on existing thumbnail behaviors: shift-click multi-select, click-to-switch, drag-and-drop reorder

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Thumbnails now resize responsively with re-render on sidebar width changes.
    • Slide insertion moved to a split "Add slide" control in the formatting toolbar with a layout picker that provides a stable close/cleanup API.
  • Bug Fixes

    • Thumbnails render crisply on high‑DPI displays; sizing clamps and fallback sizes handled.
    • Thumbnail panel properly disposes resize listeners/observers.
  • Tests

    • Expanded tests for responsive/DPR behavior and picker close/idempotency.

Review Change Stack

hackerwins and others added 3 commits May 9, 2026 20:12
The left-side thumbnail panel was drag-resizable but each thumbnail
was pinned to 192x108px. Narrow panel → horizontal overflow; wide
panel → wasted whitespace. The thumbnail canvas also hardcoded
dpr: 1 while the main slide canvas already multiplied by
window.devicePixelRatio, so on Retina the thumbnail bitmap rendered
at half resolution and was stretched, looking blurry.

Compute thumbnail size from the panel's measured width (clamp
80-320px), derive the inner canvas to be EXACTLY 16:9 so the
slide-renderer's Math.min(scaleX, scaleY) letterbox does not leave
the slide background short of the right/bottom border, and re-render
on a ResizeObserver tick when the user drags the column resizer.
Backing store is sized at innerW/H * window.devicePixelRatio.

Switch the item ring from outline to a constant-thickness border
inside a border-box: outline pixels sit at the negative edge of the
item and were getting clipped by the panel's overflow box on
left/top, making the slide look like its left + top edges were
missing. Color-only state change avoids any inner-canvas size jump
when the current slide changes.

Move the in-panel "+ Add slide" split button onto the formatting
toolbar (left of Shapes, with a separator), matching where users
expect it from Google Slides; export showLayoutPicker so the
toolbar can reuse the existing layout grid for the chevron.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Three connected papercuts in the slides UI surfaces:

The layout picker's grid column track was set to PREVIEW_W (160px)
but each cell wraps the canvas in 4px padding — the canvas got
pushed past the cell's right outline by exactly that padding and
the slide background visibly spilled outside the border. Expand the
column to PREVIEW_W + 2*CELL_PAD and pin box-sizing: border-box on
the cell so future padding tweaks behave.

renderLayoutPreview hardcoded dpr: 1, mirroring the same Retina
half-resolution bug the thumbnail canvas already had. Multiply the
backing store by window.devicePixelRatio and add dpr to the cache
key so a window dragged between Retina and a non-Retina monitor
gets a freshly rendered preview instead of the previous display's
bitmap stretched up.

The popover, the speaker-notes textarea, and the thumbnail item
borders all hardcoded #2a2a2a / #444 / #ddd / #3a7. In Wafflebase's
light mode that reads as a dark island floating in a light surface
and the borders disappear into surrounding chrome. Switch to the
same shadcn tokens the rest of the frontend uses (--popover,
--popover-foreground, --background, --foreground, --border,
--primary) with the previous hex values kept as fallbacks for jsdom
and theme-less hosts (Storybook, isolated demos).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The "+ Add slide" UI moved from the thumbnail panel to the toolbar
in 3d964be, but mountThumbnailPanel's class JSDoc still described
the in-panel "+" button — it would mislead readers grepping for how
new slides get added. Update the doc to point at the toolbar.

renderLayoutPreview's cache-key tuple in the JSDoc lagged behind
8dc28d1's actual key, which now also includes dpr; correct the
tuple so the GC-eligibility argument still reads consistently.

Trim the toolbar's split-button JSX comment to drop the "before
this refactor" history clause — git blame already covers that and
trailing-history phrases rot when the next refactor lands.

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

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR implements responsive slide thumbnails with device pixel ratio (DPR) support. Thumbnails now scale to container width (16:9 aspect ratio with min/max bounds) and render crisp on high-DPI displays by scaling canvas backing stores. The "Add Slide" button moves from the thumbnail panel to the formatting toolbar as a split-button. The panel re-renders on width changes via ResizeObserver. Theme-aware CSS variables replace hardcoded colors throughout layout-picker and notes-panel.

Changes

Responsive Thumbnails & DPR Crispness

Layer / File(s) Summary
Planning & Public API
docs/tasks/active/20260509-slides-thumb-responsive-todo.md, packages/slides/src/index.ts
Task document outlines responsive thumbnail and DPR implementation plan; re-export showLayoutPicker as public editor API.
DPR Canvas Rendering
packages/slides/src/view/canvas/layout-preview.ts
Layout preview canvas now reads window.devicePixelRatio per render, includes DPR in cache key, and scales backing store size while maintaining logical CSS pixel dimensions.
Responsive Sizing Strategy
packages/slides/src/view/editor/thumbnail-panel.ts
Introduce computeThumbDims(containerWidth) helper to derive thumbnail outer/inner dimensions preserving 16:9 aspect ratio with min/max bounds.
Thumbnail Panel Rendering
packages/slides/src/view/editor/thumbnail-panel.ts
Render thumbnails using computed dimensions; apply DPR per render for crisp canvas backing store; switch selection highlight from outline to constant-width border with theme-variable colors; remove in-panel "Add Slide" button.
Responsive Reflow & Cleanup
packages/slides/src/view/editor/thumbnail-panel.ts
Add ResizeObserver to re-render when container width changes, skip no-op ticks; disconnect observer and unsubscribe listeners on dispose().
Toolbar Split-button for Slide Insertion
packages/frontend/src/app/slides/slides-formatting-toolbar.tsx
Move "Add Slide" UI to formatting toolbar as split-button: left portion adds blank slide, right chevron opens layout picker anchored to button ref.
Theme-aware Styling with CSS Variables
packages/slides/src/view/editor/layout-picker.ts, packages/slides/src/view/editor/notes-panel.ts
Apply theme-aware CSS variables with fallbacks for layout-picker popover, grid columns, cell outlines, and notes-panel textarea; adjust layout-picker grid column width via CELL_PAD constant to accommodate per-cell padding.
Layout Picker API & Safety
packages/slides/src/view/editor/layout-picker.ts
LayoutPickerOptions gains optional trigger field; showLayoutPicker now returns an idempotent close function and guards outside-click dismissal against the trigger.
Tests & Verification
packages/slides/src/view/editor/thumbnail-panel.test.ts, packages/slides/src/view/editor/layout-picker.test.ts
Simplify imports; add dispose cleanup test; introduce comprehensive responsive/DPR test suite covering container width derivation, 16:9 aspect ratio, DPR-backed canvas sizing, refresh() behavior, and fallback sizing; assert no in-panel add-slide UI; add layout-picker close/trigger tests; remove prior in-panel UI tests.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • wafflebase/wafflebase#190: Directly related — modifies the same slides thumbnail, layout-picker, thumbnail-panel, toolbar, and export files with overlapping responsive and DPR rendering changes.
  • wafflebase/wafflebase#197: Both PRs modify slides layout-picker, preview cache, and thumbnail-panel; this PR adds DPR/responsive rendering while that PR introduced the layout picker and preview caching infrastructure.
  • wafflebase/wafflebase#191: Both PRs modify the slides formatting toolbar and showLayoutPicker re-export, adding split-button UI and wiring layout-picker integration.

Poem

🐰 I hopped to fix thumbnails, crisp on every screen,

Measured width, set DPR — no blurry in-between.
ResizeObserver listens, the panel stays in tune,
Add-slide lives in toolbar, chevron summons the moon,
Theme vars keep colors neat — a rabbit's tidy swoon.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% 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.
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.
Title check ✅ Passed The title captures the main focus areas (thumbnails, picker, notes, toolbar add-slide) but uses 'Polish' which is somewhat vague about the specific improvements (responsiveness, DPR/Retina fixes, theming).

✏️ 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 20260509-slides-thumb-responsive

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 9, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 165.3s

Lane Status Duration
sheets:build ✅ pass 13.2s
docs:build ✅ pass 12.1s
slides:build ✅ pass 8.8s
verify:fast ✅ pass 87.7s
frontend:build ✅ pass 17.8s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.8s
cli:build ✅ pass 2.0s
verify:entropy ✅ pass 18.6s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented May 9, 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

🤖 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-formatting-toolbar.tsx`:
- Around line 200-213: The chevron handler opens the popover via
showLayoutPicker but never toggles it closed and leaks listeners on unmount;
change showLayoutPicker to return a close function (or accept a cancel signal)
and update onOpenLayoutPicker to store that close handle (e.g., closeRef) so
clicking the chevron can toggle by calling closeRef.current and clearing it, and
add a useEffect cleanup that calls closeRef.current on unmount; also handle the
capture-phase mousedown race by tracking wasOpenAtMouseDownRef in the chevron's
onMouseDown and early-return from onClick when that flag indicates the popover
was torn down by the outside-click handler so onClick does not immediately
re-open (referencing onOpenLayoutPicker, layoutChevronRef, showLayoutPicker, and
store).
🪄 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: c826406f-08b1-4ca9-bdbd-76c634d3a468

📥 Commits

Reviewing files that changed from the base of the PR and between 008d8b5 and 9d57eeb.

📒 Files selected for processing (8)
  • docs/tasks/active/20260509-slides-thumb-responsive-todo.md
  • packages/frontend/src/app/slides/slides-formatting-toolbar.tsx
  • packages/slides/src/index.ts
  • packages/slides/src/view/canvas/layout-preview.ts
  • packages/slides/src/view/editor/layout-picker.ts
  • packages/slides/src/view/editor/notes-panel.ts
  • packages/slides/src/view/editor/thumbnail-panel.test.ts
  • packages/slides/src/view/editor/thumbnail-panel.ts

Comment thread packages/frontend/src/app/slides/slides-formatting-toolbar.tsx
hackerwins and others added 2 commits May 9, 2026 21:32
The split-button "+ Slide" added to the formatting toolbar in
3d964be changed the slides-toolbar.* fixtures, and the new
border-instead-of-outline + themed fallback colors on the
thumbnail items shifted the surrounding harness fixtures by a few
pixels in both light and dark variants. CI flagged exactly these
8 mismatches; regenerate them in the same Docker container CI uses
so the byte-level baseline matches what verify-browser sees.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The toolbar chevron opened the layout picker but had two pre-existing
papercuts inherited from showLayoutPicker's fire-and-forget shape:
clicking the chevron a second time never closed the popover (the
capture-phase outside-click handler tore it down, then React's onClick
immediately re-opened it), and unmounting the toolbar while the picker
was open leaked the popover element + its document keydown/mousedown
listeners.

Make showLayoutPicker return its internal close function and accept
an optional `trigger` element. The trigger is exempted from the
outside-click close path, so the chevron's own onClick can toggle via
the returned handle without fighting the mousedown race. Guard close
with a closed flag so the returned handle is idempotent — a
useEffect cleanup that races a real Escape/outside-click no longer
fires onClose twice.

In the toolbar, store the close handle in a ref, toggle on a second
chevron click, clear the ref via onClose, and add a useEffect cleanup
so the popover dies with the toolbar.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@hackerwins hackerwins changed the title Slides: responsive thumbnails, toolbar add-slide, theme + DPR fixes Polish slides chrome: thumbnails, picker, notes, toolbar add-slide May 9, 2026
@hackerwins
hackerwins merged commit a68897d into main May 9, 2026
4 checks passed
@hackerwins
hackerwins deleted the 20260509-slides-thumb-responsive branch May 9, 2026 12:49
@hackerwins hackerwins mentioned this pull request May 11, 2026
6 tasks
hackerwins added a commit that referenced this pull request May 11, 2026
Adds @wafflebase/slides as a third surface alongside Sheets and Docs,
plus 53-shape library (Phase 1+2), adjustment handles for 9 pilot
shapes (P3-A.1), live snap guides, align/distribute toolbar, themed
authoring, and layout-change UI. Also ships Sheets cell comments
(Phase B), docs peer-avatar caret jump, yorkie-js-sdk 0.7.8 upgrade,
and CLI/REST API improvements (docs export imageFetcher, expired
session refresh, REST docs split). Minor bump because slides is a
new top-level package.

Highlights: #184 #185 #186 #187 #188 #189 #190 #191 #192 #197 #198
#201 #202 #203 #204 #205 #206 #207 #209 #210
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