Add --bespoke.thumbnails option for slide thumbnails in presenter view#705
Add --bespoke.thumbnails option for slide thumbnails in presenter view#705mil-ad wants to merge 11 commits intomarp-team:mainfrom
Conversation
|
closes #464 |
|
Regarding #464, we imagine it more like a Reveal.js screen with the popup modal (shows |
|
Ah fair enough. I updated it: screen-area-capture-2026-02-27-12.30.27.mp4Is this more aligned with you have in mind? also - apologies for the potential AI slop. I had to get some help. I'm not a js guy. |
|
One question though. Do we want this command available from the normal view as well or just the presenter view? |
|
Oh yes, based on the original request marp-team/marp#326, it would be good if it is available in either mode. I’ve updated the |
Done |
|
@yhatt do you see this getting into main or is it too hacky? |
|
For maintainability, I've expected the overview mode to be splitted from regular view modes just like If so, the overview modal in both of regular mode and presenter mode only needs to display a URL with its mode rewritten to |
|
Ok I think I have changed it based on your feedback. Let me know what you think. One thing I'm not sure about is whether the overview view should open in a new window or not. It currently does open in a new windows. |
The previous presenter view loaded a full copy of the presentation in an iframe to show a single next-slide preview. Replace this with SVG cloning that copies already-rendered slide nodes from the DOM, enabling instant thumbnails of all slides with click-to-navigate. The feature is opt-in via --bespoke.thumbnails (or config file), following the existing --bespoke.osc/progress/transition pattern. When disabled (default), the original iframe-based next-slide preview is preserved.
Remove the --bespoke.thumbnails CLI option and presenter view thumbnail panel. Add a new overview plugin that displays all slides in a CSS-transform-based grid, toggled with 'o' or Escape in both normal and presenter views. Arrow keys navigate focus, Enter/click selects a slide.
Instead of an overlay within normal/presenter views, overview is now a separate view mode following the same pattern as ?view=presenter. Pressing 'o' opens a new synced window, and navigation syncs automatically via bespokeSync.
Pressing 'o' now navigates the current window to ?view=overview. From overview, clicking a slide or pressing Escape/Enter/o navigates back to normal view at that slide. Also fixes hash duplication bug in navigateBack.
| function openOverviewView(this: BespokeForOverview) { | ||
| const { max, floor } = Math | ||
| const w = max(floor(window.innerWidth * 0.85), 640) | ||
| const h = max(floor(window.innerHeight * 0.85), 360) | ||
|
|
||
| return window.open( | ||
| this.overviewUrl, | ||
| overviewPrefix + this.syncKey, | ||
| `width=${w},height=${h},menubar=no,toolbar=no` | ||
| ) |
There was a problem hiding this comment.
I think it's better to be overlay on the current view instead of opening new window.
| const applyLayout = () => { | ||
| const cols = columns() | ||
| const rows = Math.ceil(slideCount() / cols) | ||
| const parent = deck.parent as HTMLElement | ||
| const rect = parent.getBoundingClientRect() | ||
| const gap = 20 | ||
| const cellW = (rect.width - gap * (cols + 1)) / cols | ||
| const cellH = (rect.height - gap * (rows + 1)) / rows | ||
|
|
||
| const scaleX = cellW / rect.width | ||
| const scaleY = cellH / rect.height | ||
| const cellScale = Math.min(scaleX, scaleY) | ||
|
|
||
| const renderedW = rect.width * cellScale | ||
| const renderedH = rect.height * cellScale | ||
|
|
||
| slides().forEach((slide, i) => { | ||
| const col = i % cols | ||
| const row = Math.floor(i / cols) | ||
| const x = gap + col * (renderedW + gap) | ||
| const y = gap + row * (renderedH + gap) | ||
|
|
||
| slide.style.transformOrigin = '0 0' | ||
| slide.style.transform = `translate(${x}px, ${y}px) scale(${cellScale})` | ||
| }) | ||
| } |
| slide.addEventListener('click', () => { | ||
| deck.slide(i) | ||
| }) |
There was a problem hiding this comment.
a11y: Each slide should be focusable by setting tabindex="0", and navigatable by hitting Entry or Space in addition to the click event.
Amazing. We can close this one then? |
|
Yes. I'll credit you in the CHANGELOG. The feature request wouldn't have been resolved without your contribution 😎 |
|
🫡 |
|
Shipped as v4.3.0 🚢 |
|
Thank you both for working on this, I'm very happy to see it merged! :) |
Adds an optional
--bespoke.thumbnailsCLI flag for showing thumbnails of all slides in the presenter view. This allows the presenter to jump directly to any slide without cycling through intermediate slides in front of the audience:screen-area-capture-2026-02-26-18.11.25.mp4
Usage
Off by default and can be enabled via CLI:
Or via config file:
{ "bespoke": { "thumbnails": true } }I started by having iframes but that that proved too slow so moved to cloning each slide's SVG into a scrollable thumbnail strip on the right panel.