@marimo-team/islands 0.25.3-dev182 → 0.25.3-dev186

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.25.3-dev182",
3
+ "version": "0.25.3-dev186",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -216,7 +216,7 @@
216
216
  "msw": "^2.15.0",
217
217
  "npm-run-all2": "^6.2.6",
218
218
  "oxc-transform-react": "0.145.0",
219
- "oxfmt": "^0.73.0",
219
+ "oxfmt": "^0.74.0",
220
220
  "oxlint": "^1.88.0",
221
221
  "postcss": "^8.5.29",
222
222
  "postcss-prefix-selector": "^2.2.1",
@@ -195,4 +195,41 @@ describe("SlideScrollContainer", () => {
195
195
 
196
196
  expect(screen.queryByTestId("slide-scroll-hint")).toBeNull();
197
197
  });
198
+
199
+ it.each([
200
+ { scale: 0.5, fragmentOffset: 1000, expected: true },
201
+ { scale: 2, fragmentOffset: 500, expected: false },
202
+ ])(
203
+ "measures revealed content in canvas coordinates at scale $scale",
204
+ ({ scale, fragmentOffset, expected }) => {
205
+ render(
206
+ <SlideScrollContainer>
207
+ <div>revealed content</div>
208
+ <div className="fragment">unrevealed content</div>
209
+ </SlideScrollContainer>,
210
+ );
211
+
212
+ const container = screen.getByTestId("slide-scroll-container");
213
+ const fragment = container.querySelector<HTMLElement>(".fragment");
214
+ if (!fragment) {
215
+ throw new Error("Missing fragment");
216
+ }
217
+ stubOverflow(container, { scrollHeight: 1200, clientHeight: 720 });
218
+ Object.defineProperty(container, "offsetHeight", { value: 720 });
219
+ vi.spyOn(container, "getBoundingClientRect").mockReturnValue(
220
+ DOMRect.fromRect({ y: 100, height: 720 * scale }),
221
+ );
222
+ vi.spyOn(fragment, "getBoundingClientRect").mockReturnValue(
223
+ DOMRect.fromRect({ y: 100 + fragmentOffset * scale }),
224
+ );
225
+
226
+ act(() => {
227
+ for (const observer of resizeObservers) {
228
+ observer.callback([], observer as unknown as ResizeObserver);
229
+ }
230
+ });
231
+
232
+ expect(screen.queryByTestId("slide-scroll-hint") !== null).toBe(expected);
233
+ },
234
+ );
198
235
  });
@@ -69,6 +69,8 @@ import {
69
69
  } from "./speaker-view";
70
70
 
71
71
  const ASPECT_RATIO = 16 / 9;
72
+ const SLIDE_WIDTH = 1280;
73
+ const SLIDE_HEIGHT = SLIDE_WIDTH / ASPECT_RATIO;
72
74
  const CODE_TOGGLE_KEY_CODE = 67;
73
75
 
74
76
  // Reveal retains custom key bindings after initialization. Replace the `C`
@@ -133,8 +135,11 @@ function classifyNavKey(event: KeyboardEvent): 1 | -1 | 0 {
133
135
  return 0;
134
136
  }
135
137
 
136
- function useSlideDimensions(ref: React.RefObject<HTMLDivElement | null>) {
137
- const [dims, setDims] = useState({ width: 960, height: 540 });
138
+ function useSlideViewportSize(ref: React.RefObject<HTMLDivElement | null>) {
139
+ const [dims, setDims] = useState({
140
+ width: SLIDE_WIDTH,
141
+ height: SLIDE_HEIGHT,
142
+ });
138
143
 
139
144
  useEffect(() => {
140
145
  const el = ref.current;
@@ -482,8 +487,21 @@ const RevealSlidesComponent = ({
482
487
  }) => {
483
488
  const containerRef = useRef<HTMLDivElement>(null);
484
489
  const deckRef = useRef<RevealApi | null>(null);
485
- const { width, height } = useSlideDimensions(containerRef);
490
+ const { width, height } = useSlideViewportSize(containerRef);
486
491
  const isFullscreen = useFullScreenElement() != null;
492
+ const [slideScale, setSlideScale] = useState(1);
493
+
494
+ useEffect(() => {
495
+ // Embedded decks also resize when panels change, without a window resize.
496
+ const frame = requestAnimationFrame(() => {
497
+ const deck = deckRef.current;
498
+ if (deck) {
499
+ deck.layout();
500
+ setSlideScale(deck.getScale());
501
+ }
502
+ });
503
+ return () => cancelAnimationFrame(frame);
504
+ }, [width, height, isFullscreen]);
487
505
 
488
506
  // A receiver frame cannot spawn another speaker view. WebAssembly exports
489
507
  // also omit the plugin because each receiver would start another runtime.
@@ -589,10 +607,10 @@ const RevealSlidesComponent = ({
589
607
  const revealConfig: RevealConfig = useMemo(
590
608
  () => ({
591
609
  embedded: true,
592
- width,
593
- height,
610
+ width: SLIDE_WIDTH,
611
+ height: SLIDE_HEIGHT,
594
612
  center: false,
595
- minScale: 0.2,
613
+ minScale: 0,
596
614
  maxScale: 2,
597
615
  transition: deckTransition,
598
616
  keyboardCondition: (event: KeyboardEvent) => !Events.fromInput(event),
@@ -602,7 +620,7 @@ const RevealSlidesComponent = ({
602
620
  // `@revealjs/react` owns, which crashes on `reveal.sync()` re-renders.
603
621
  scrollActivationWidth: 0,
604
622
  }),
605
- [width, height, deckTransition, kioskUrl],
623
+ [deckTransition, kioskUrl],
606
624
  );
607
625
 
608
626
  const navigateDeckToActiveCell = useEvent((deck: RevealApi) => {
@@ -703,6 +721,7 @@ const RevealSlidesComponent = ({
703
721
  });
704
722
 
705
723
  const handleDeckReady = useEvent((deck: RevealApi) => {
724
+ setSlideScale(deck.getScale());
706
725
  navigateDeckToActiveCell(deck);
707
726
  if (canResolveInitialHash) {
708
727
  reportDeckCell(deck);
@@ -829,22 +848,33 @@ const RevealSlidesComponent = ({
829
848
  <span>{parkedPreviewLabel}</span>
830
849
  </div>
831
850
  )}
832
- <div className="flex-1 overflow-auto flex">
833
- <div
834
- className={
835
- parkedShowsSource
836
- ? "mo-slide-content flex flex-col gap-3"
837
- : "mo-slide-content"
838
- }
839
- style={slideContentStyle}
840
- >
841
- <ParkedPreviewContent
842
- cell={parkedPreviewCell}
843
- isNoOutputPreview={isNoOutputPreview}
844
- isEditable={isEditable}
845
- showCode={parkedShowCode}
846
- />
847
- </div>
851
+ <div
852
+ className="reveal mo-slides-theme prose-slides absolute left-1/2 top-1/2"
853
+ data-testid="slide-preview-canvas"
854
+ style={{
855
+ position: "absolute",
856
+ width: SLIDE_WIDTH,
857
+ height: SLIDE_HEIGHT,
858
+ transform: `translate(-50%, -50%) scale(${slideScale})`,
859
+ }}
860
+ >
861
+ <SlideScrollContainer>
862
+ <div
863
+ className={
864
+ parkedShowsSource
865
+ ? "mo-slide-content flex flex-col gap-3"
866
+ : "mo-slide-content"
867
+ }
868
+ style={slideContentStyle}
869
+ >
870
+ <ParkedPreviewContent
871
+ cell={parkedPreviewCell}
872
+ isNoOutputPreview={isNoOutputPreview}
873
+ isEditable={isEditable}
874
+ showCode={parkedShowCode}
875
+ />
876
+ </div>
877
+ </SlideScrollContainer>
848
878
  </div>
849
879
  </div>
850
880
  )}
@@ -1,5 +1,10 @@
1
1
  @import "reveal.js/reveal.css";
2
2
 
3
+ /* Reveal scales the canvas; responsive rem sizes would reflow its content. */
4
+ html:has(.reveal.mo-slides-theme) {
5
+ font-size: 100%;
6
+ }
7
+
3
8
  /* Neutralize reveal-viewport styles so the slides inherit marimo's defaults */
4
9
  .reveal-viewport {
5
10
  background-color: transparent;
@@ -11,10 +16,6 @@
11
16
  .reveal-viewport:fullscreen {
12
17
  background-color: var(--background);
13
18
  }
14
- .reveal-viewport:fullscreen .mo-slide-content {
15
- padding-left: 2rem;
16
- padding-right: 2rem;
17
- }
18
19
  .reveal-viewport::backdrop {
19
20
  background-color: var(--background);
20
21
  }
@@ -41,8 +41,10 @@ function measureRevealedHeight(el: HTMLElement): number {
41
41
  if (!firstUnrevealed) {
42
42
  return el.scrollHeight;
43
43
  }
44
+ const bounds = el.getBoundingClientRect();
45
+ const scale = el.offsetHeight > 0 ? bounds.height / el.offsetHeight : 1;
44
46
  const fragmentTop = firstUnrevealed.getBoundingClientRect().top;
45
- return fragmentTop - el.getBoundingClientRect().top + el.scrollTop;
47
+ return (fragmentTop - bounds.top) / (scale || 1) + el.scrollTop;
46
48
  }
47
49
 
48
50
  /**