@bendyline/squisq-editor-react 2.13.15 → 2.13.16

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.
@@ -1806,22 +1806,28 @@ function resolveBlockVisual(doc, block, theme, viewport, position = {
1806
1806
  function buildRecorderSlideDeck(doc, theme, viewport) {
1807
1807
  const flat = flattenRenderableBlocks(doc.blocks);
1808
1808
  if (flat.length === 0) return [];
1809
- let slidesById;
1809
+ let preview;
1810
1810
  try {
1811
- const preview = buildPreviewDoc2(doc, { interleaveImages: false });
1812
- slidesById = new Map(preview.blocks.map((slide) => [slide.id, slide]));
1811
+ preview = buildPreviewDoc2(
1812
+ {
1813
+ ...doc,
1814
+ documentMedia: doc.documentMedia?.filter((clip) => clip.anchor !== "document")
1815
+ },
1816
+ { interleaveImages: false }
1817
+ );
1813
1818
  } catch {
1814
- slidesById = /* @__PURE__ */ new Map();
1819
+ preview = null;
1815
1820
  }
1821
+ if (preview?.presentationApplied) {
1822
+ return presentationDeck(doc, flat, preview.blocks, theme, viewport);
1823
+ }
1824
+ const slidesById = new Map(
1825
+ (preview?.blocks ?? []).map((slide) => [slide.id, slide])
1826
+ );
1816
1827
  return flat.map((block, index) => {
1817
- const heading = block.title?.trim() || `Slide ${index + 1}`;
1818
- let bodyText = "";
1819
- try {
1820
- bodyText = getBlockBodyText(block);
1821
- } catch {
1822
- bodyText = "";
1823
- }
1828
+ const { heading, bodyText } = blockNotes(block, index);
1824
1829
  return {
1830
+ id: block.id,
1825
1831
  blockId: block.id,
1826
1832
  heading,
1827
1833
  bodyText,
@@ -1837,6 +1843,35 @@ function buildRecorderSlideDeck(doc, theme, viewport) {
1837
1843
  };
1838
1844
  });
1839
1845
  }
1846
+ function presentationDeck(doc, flat, slides, theme, viewport) {
1847
+ const blockIndex = new Map(flat.map((block, index) => [block.id, index]));
1848
+ let previous = 0;
1849
+ return slides.map((slide, index) => {
1850
+ const found = slide.sourceBlockId ? blockIndex.get(slide.sourceBlockId) : void 0;
1851
+ const sourceIndex = found ?? previous;
1852
+ previous = sourceIndex;
1853
+ const block = flat[sourceIndex];
1854
+ const { heading, bodyText } = blockNotes(block, sourceIndex);
1855
+ return {
1856
+ id: slide.id,
1857
+ blockId: block.id,
1858
+ ...slide.sourceCharOffset !== void 0 ? { sourceCharOffset: slide.sourceCharOffset } : {},
1859
+ heading,
1860
+ bodyText,
1861
+ visual: resolveSlideVisual(doc, block, slide, theme, viewport, index, slides.length)
1862
+ };
1863
+ });
1864
+ }
1865
+ function blockNotes(block, index) {
1866
+ const heading = block.title?.trim() || `Slide ${index + 1}`;
1867
+ let bodyText = "";
1868
+ try {
1869
+ bodyText = getBlockBodyText(block);
1870
+ } catch {
1871
+ bodyText = "";
1872
+ }
1873
+ return { heading, bodyText };
1874
+ }
1840
1875
  function resolveSlideVisual(doc, block, slide, theme, viewport, index, totalBlocks) {
1841
1876
  if (slide) {
1842
1877
  try {
@@ -1855,36 +1890,15 @@ function resolveSlideVisual(doc, block, slide, theme, viewport, index, totalBloc
1855
1890
  }
1856
1891
 
1857
1892
  // src/recorder/slides/RecorderSlidesPanel.tsx
1858
- import { useBuildNavigation } from "@bendyline/squisq-react";
1893
+ import { BlockRenderer, MediaContext, useBuildNavigation } from "@bendyline/squisq-react";
1859
1894
  import { blockBuildSteps, buildSourceText } from "@bendyline/squisq/doc";
1860
- import { useCallback as useCallback3, useEffect as useEffect4 } from "react";
1861
-
1862
- // src/TimelineBlockPreview.tsx
1863
- import { memo } from "react";
1864
- import { VIEWPORT_PRESETS } from "@bendyline/squisq/schemas";
1865
- import { BlockRenderer, MediaContext } from "@bendyline/squisq-react";
1866
- import { jsx as jsx3 } from "react/jsx-runtime";
1867
- var BlockThumbnail = memo(function BlockThumbnail2({
1868
- visual,
1869
- buildPlayback = "all",
1870
- viewport = VIEWPORT_PRESETS.landscape,
1871
- basePath = "/",
1872
- mediaProvider = null
1873
- }) {
1874
- return /* @__PURE__ */ jsx3(MediaContext.Provider, { value: mediaProvider, children: /* @__PURE__ */ jsx3(
1875
- BlockRenderer,
1876
- {
1877
- buildPlayback,
1878
- block: visual,
1879
- blockTime: 0,
1880
- basePath,
1881
- viewport
1882
- }
1883
- ) });
1884
- });
1885
-
1886
- // src/recorder/slides/RecorderSlidesPanel.tsx
1887
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1895
+ import {
1896
+ useCallback as useCallback3,
1897
+ useEffect as useEffect4,
1898
+ useRef as useRef2,
1899
+ useState as useState3
1900
+ } from "react";
1901
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1888
1902
  var rootStyle = {
1889
1903
  flex: "1 1 auto",
1890
1904
  minHeight: 0,
@@ -2000,7 +2014,7 @@ function RecorderSlidesPanel({
2000
2014
  [step]
2001
2015
  );
2002
2016
  if (count === 0 || !active) {
2003
- return /* @__PURE__ */ jsx4("div", { style: { ...rootStyle, alignItems: "center", justifyContent: "center", padding: 24 }, children: /* @__PURE__ */ jsx4("p", { style: hintStyle, children: "This document has no slides yet." }) });
2017
+ return /* @__PURE__ */ jsx3("div", { style: { ...rootStyle, alignItems: "center", justifyContent: "center", padding: 24 }, children: /* @__PURE__ */ jsx3("p", { style: hintStyle, children: "This document has no slides yet." }) });
2004
2018
  }
2005
2019
  return /* @__PURE__ */ jsxs3(
2006
2020
  "div",
@@ -2019,7 +2033,7 @@ function RecorderSlidesPanel({
2019
2033
  count,
2020
2034
  builds.count > 0 ? ` \xB7 Build ${builds.cursor.revealed} of ${builds.count}` : ""
2021
2035
  ] }),
2022
- /* @__PURE__ */ jsx4("span", { style: dotsStyle, "aria-hidden": "true", children: slides.map((slide, i) => /* @__PURE__ */ jsx4(
2036
+ /* @__PURE__ */ jsx3("span", { style: dotsStyle, "aria-hidden": "true", children: slides.map((slide, i) => /* @__PURE__ */ jsx3(
2023
2037
  "span",
2024
2038
  {
2025
2039
  style: {
@@ -2029,9 +2043,9 @@ function RecorderSlidesPanel({
2029
2043
  background: i === index ? "var(--squisq-recorder-accent)" : shownBlockIds?.has(slide.blockId) ? "var(--squisq-recorder-text)" : "var(--squisq-recorder-border)"
2030
2044
  }
2031
2045
  },
2032
- slide.blockId
2046
+ slide.id
2033
2047
  )) }),
2034
- /* @__PURE__ */ jsx4(
2048
+ /* @__PURE__ */ jsx3(
2035
2049
  "button",
2036
2050
  {
2037
2051
  type: "button",
@@ -2039,10 +2053,10 @@ function RecorderSlidesPanel({
2039
2053
  onClick: () => step(-1),
2040
2054
  disabled: index <= 0 && builds.cursor.revealed === 0,
2041
2055
  "aria-label": "Previous slide",
2042
- children: /* @__PURE__ */ jsx4(Icon, { icon: "fa-solid fa-chevron-left" })
2056
+ children: /* @__PURE__ */ jsx3(Icon, { icon: "fa-solid fa-chevron-left" })
2043
2057
  }
2044
2058
  ),
2045
- /* @__PURE__ */ jsx4(
2059
+ /* @__PURE__ */ jsx3(
2046
2060
  "button",
2047
2061
  {
2048
2062
  type: "button",
@@ -2050,11 +2064,11 @@ function RecorderSlidesPanel({
2050
2064
  onClick: () => step(1),
2051
2065
  disabled: index >= count - 1 && builds.cursor.revealed >= builds.count && builds.cursor.progress >= 1,
2052
2066
  "aria-label": "Next slide",
2053
- children: /* @__PURE__ */ jsx4(Icon, { icon: "fa-solid fa-chevron-right" })
2067
+ children: /* @__PURE__ */ jsx3(Icon, { icon: "fa-solid fa-chevron-right" })
2054
2068
  }
2055
2069
  )
2056
2070
  ] }),
2057
- /* @__PURE__ */ jsx4("div", { style: stageStyle, children: /* @__PURE__ */ jsx4(
2071
+ /* @__PURE__ */ jsx3("div", { style: stageStyle, children: /* @__PURE__ */ jsx3(
2058
2072
  "div",
2059
2073
  {
2060
2074
  style: {
@@ -2064,15 +2078,17 @@ function RecorderSlidesPanel({
2064
2078
  overflow: "hidden",
2065
2079
  background: "#000"
2066
2080
  },
2067
- children: active.visual ? /* @__PURE__ */ jsx4(
2068
- BlockThumbnail,
2081
+ children: active.visual ? /* @__PURE__ */ jsx3(
2082
+ RecorderSlideStage,
2069
2083
  {
2070
2084
  visual: active.visual,
2071
2085
  buildPlayback: builds.cursor,
2072
2086
  viewport,
2073
2087
  basePath,
2074
- mediaProvider
2075
- }
2088
+ mediaProvider,
2089
+ recording
2090
+ },
2091
+ active.id
2076
2092
  ) : /* @__PURE__ */ jsxs3(
2077
2093
  "div",
2078
2094
  {
@@ -2097,14 +2113,58 @@ function RecorderSlidesPanel({
2097
2113
  }
2098
2114
  ) }),
2099
2115
  /* @__PURE__ */ jsxs3("div", { style: notesStyle, "data-testid": "recorder-slide-notes", children: [
2100
- /* @__PURE__ */ jsx4("p", { style: headingStyle, children: active.heading }),
2101
- active.bodyText ? /* @__PURE__ */ jsx4("div", { children: active.bodyText }) : /* @__PURE__ */ jsx4("p", { style: hintStyle, children: "No additional narrative for this slide." }),
2102
- /* @__PURE__ */ jsx4("p", { style: { ...hintStyle, marginTop: 8 }, children: recording ? "Recording \u2014 each slide is timed from when you first show it." : "Use the arrow keys or the buttons to move through the deck." })
2116
+ /* @__PURE__ */ jsx3("p", { style: headingStyle, children: active.heading }),
2117
+ active.bodyText ? /* @__PURE__ */ jsx3("div", { children: active.bodyText }) : /* @__PURE__ */ jsx3("p", { style: hintStyle, children: "No additional narrative for this slide." }),
2118
+ /* @__PURE__ */ jsx3("p", { style: { ...hintStyle, marginTop: 8 }, children: recording ? "Recording \u2014 each slide is timed from when you first show it." : "Use the arrow keys or the buttons to move through the deck." })
2103
2119
  ] })
2104
2120
  ]
2105
2121
  }
2106
2122
  );
2107
2123
  }
2124
+ function RecorderSlideStage({
2125
+ visual,
2126
+ buildPlayback,
2127
+ viewport,
2128
+ basePath,
2129
+ mediaProvider,
2130
+ recording
2131
+ }) {
2132
+ const blockTime = useSlideClock(readsBlockClock(visual), recording);
2133
+ return /* @__PURE__ */ jsx3(MediaContext.Provider, { value: mediaProvider, children: /* @__PURE__ */ jsx3(
2134
+ BlockRenderer,
2135
+ {
2136
+ block: visual,
2137
+ buildPlayback,
2138
+ blockTime,
2139
+ isPlaying: true,
2140
+ muted: true,
2141
+ basePath,
2142
+ viewport
2143
+ }
2144
+ ) });
2145
+ }
2146
+ function readsBlockClock(visual) {
2147
+ return (visual.layers ?? []).some(
2148
+ (layer) => layer.type === "video" || layer.animation?.type === "countUp"
2149
+ );
2150
+ }
2151
+ function useSlideClock(running, recording) {
2152
+ const [seconds, setSeconds] = useState3(0);
2153
+ const originRef = useRef2(null);
2154
+ useEffect4(() => {
2155
+ if (recording) originRef.current = null;
2156
+ }, [recording]);
2157
+ useEffect4(() => {
2158
+ if (!running) return;
2159
+ let frame = requestAnimationFrame(function tick(now) {
2160
+ const origin = originRef.current ?? (originRef.current = now);
2161
+ setSeconds((now - origin) / 1e3);
2162
+ frame = requestAnimationFrame(tick);
2163
+ });
2164
+ return () => cancelAnimationFrame(frame);
2165
+ }, [running]);
2166
+ return seconds;
2167
+ }
2108
2168
 
2109
2169
  // src/recorder/RecorderModal.tsx
2110
2170
  import { recordBuildAdvance } from "@bendyline/squisq/schemas";
@@ -2114,19 +2174,21 @@ import {
2114
2174
  useEffect as useEffect5,
2115
2175
  useId as useId2,
2116
2176
  useMemo,
2117
- useRef as useRef2,
2118
- useState as useState3
2177
+ useRef as useRef3,
2178
+ useState as useState4
2119
2179
  } from "react";
2120
- import { VIEWPORT_PRESETS as VIEWPORT_PRESETS2 } from "@bendyline/squisq/schemas";
2180
+ import { VIEWPORT_PRESETS } from "@bendyline/squisq/schemas";
2121
2181
  import {
2122
2182
  EMPTY_ADVANCE_LOG,
2183
+ EMPTY_SLIDE_MARK_LOG,
2123
2184
  advanceCoverage,
2124
2185
  buildAdvanceTimingJson,
2186
+ recordSlideMark,
2125
2187
  recordSlideShown
2126
2188
  } from "@bendyline/squisq/narration";
2127
2189
 
2128
2190
  // src/recorder/RecorderScreenSettings.tsx
2129
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2191
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2130
2192
  var field = { display: "flex", flexDirection: "column", gap: 4, minWidth: 0 };
2131
2193
  var input = {
2132
2194
  width: "100%",
@@ -2156,7 +2218,7 @@ function RecorderScreenSettings({
2156
2218
  fontSize: 13
2157
2219
  },
2158
2220
  children: [
2159
- /* @__PURE__ */ jsx5("legend", { children: "What to record" }),
2221
+ /* @__PURE__ */ jsx4("legend", { children: "What to record" }),
2160
2222
  /* @__PURE__ */ jsxs4(
2161
2223
  "div",
2162
2224
  {
@@ -2175,10 +2237,10 @@ function RecorderScreenSettings({
2175
2237
  value: value.displaySurface ?? "",
2176
2238
  onChange: (event) => onChange({ ...value, displaySurface: event.target.value || void 0 }),
2177
2239
  children: [
2178
- /* @__PURE__ */ jsx5("option", { value: "", children: "Any available surface" }),
2179
- /* @__PURE__ */ jsx5("option", { value: "monitor", children: "Entire screen" }),
2180
- /* @__PURE__ */ jsx5("option", { value: "window", children: "Application window" }),
2181
- /* @__PURE__ */ jsx5("option", { value: "browser", children: "Browser tab (when available)" })
2240
+ /* @__PURE__ */ jsx4("option", { value: "", children: "Any available surface" }),
2241
+ /* @__PURE__ */ jsx4("option", { value: "monitor", children: "Entire screen" }),
2242
+ /* @__PURE__ */ jsx4("option", { value: "window", children: "Application window" }),
2243
+ /* @__PURE__ */ jsx4("option", { value: "browser", children: "Browser tab (when available)" })
2182
2244
  ]
2183
2245
  }
2184
2246
  )
@@ -2195,13 +2257,13 @@ function RecorderScreenSettings({
2195
2257
  crop: event.target.value === "region" ? { x: 0, y: 0, width: 1280, height: 720 } : void 0
2196
2258
  }),
2197
2259
  children: [
2198
- /* @__PURE__ */ jsx5("option", { value: "full", children: "Whole selected surface" }),
2199
- /* @__PURE__ */ jsx5("option", { value: "region", children: "Region by coordinates" })
2260
+ /* @__PURE__ */ jsx4("option", { value: "full", children: "Whole selected surface" }),
2261
+ /* @__PURE__ */ jsx4("option", { value: "region", children: "Region by coordinates" })
2200
2262
  ]
2201
2263
  }
2202
2264
  )
2203
2265
  ] }),
2204
- crop && /* @__PURE__ */ jsx5(
2266
+ crop && /* @__PURE__ */ jsx4(
2205
2267
  "div",
2206
2268
  {
2207
2269
  style: {
@@ -2217,7 +2279,7 @@ function RecorderScreenSettings({
2217
2279
  width: "Region width (px)",
2218
2280
  height: "Region height (px)"
2219
2281
  }[key],
2220
- /* @__PURE__ */ jsx5(
2282
+ /* @__PURE__ */ jsx4(
2221
2283
  "input",
2222
2284
  {
2223
2285
  style: input,
@@ -2239,7 +2301,7 @@ function RecorderScreenSettings({
2239
2301
  "Choose a screen, window or tab in the picker, then check the preview. Available choices depend on your browser or app.",
2240
2302
  value.crop && " Coordinates start at the selected surface\u2019s top-left, in captured pixels. Only this region is saved; audio is unchanged."
2241
2303
  ] }),
2242
- /* @__PURE__ */ jsx5(
2304
+ /* @__PURE__ */ jsx4(
2243
2305
  "button",
2244
2306
  {
2245
2307
  type: "button",
@@ -2279,7 +2341,7 @@ function narrationCaptureSummary(withCamera) {
2279
2341
  }
2280
2342
 
2281
2343
  // src/recorder/RecorderModal.tsx
2282
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2344
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2283
2345
  var overlayStyle = {
2284
2346
  position: "fixed",
2285
2347
  inset: 0,
@@ -2637,26 +2699,26 @@ function RecorderModal({
2637
2699
  maxRecordingBytes
2638
2700
  }) {
2639
2701
  const initialToggles = toggleStateFromMode(initialMode);
2640
- const [micOn, setMicOn] = useState3(initialToggles.micOn);
2641
- const [cameraOn, setCameraOn] = useState3(initialToggles.cameraOn);
2642
- const [screenOn, setScreenOn] = useState3(initialToggles.screenOn);
2643
- const [sourceText, setSourceText] = useState3("");
2644
- const [basename, setBasename] = useState3("");
2645
- const [includeSystemAudio, setIncludeSystemAudio] = useState3(false);
2646
- const [isSaving, setIsSaving] = useState3(false);
2647
- const [saveError, setSaveError] = useState3(null);
2648
- const [playbackUrl, setPlaybackUrl] = useState3(null);
2649
- const [cameraPlaybackUrl, setCameraPlaybackUrl] = useState3(null);
2650
- const [playbackPositionMs, setPlaybackPositionMs] = useState3(0);
2651
- const [panelMode, setPanelMode] = useState3("none");
2702
+ const [micOn, setMicOn] = useState4(initialToggles.micOn);
2703
+ const [cameraOn, setCameraOn] = useState4(initialToggles.cameraOn);
2704
+ const [screenOn, setScreenOn] = useState4(initialToggles.screenOn);
2705
+ const [sourceText, setSourceText] = useState4("");
2706
+ const [basename, setBasename] = useState4("");
2707
+ const [includeSystemAudio, setIncludeSystemAudio] = useState4(false);
2708
+ const [isSaving, setIsSaving] = useState4(false);
2709
+ const [saveError, setSaveError] = useState4(null);
2710
+ const [playbackUrl, setPlaybackUrl] = useState4(null);
2711
+ const [cameraPlaybackUrl, setCameraPlaybackUrl] = useState4(null);
2712
+ const [playbackPositionMs, setPlaybackPositionMs] = useState4(0);
2713
+ const [panelMode, setPanelMode] = useState4("none");
2652
2714
  const narrationOn = panelMode === "narration" || panelMode === "both";
2653
2715
  const slidesOn = panelMode === "slides" || panelMode === "both";
2654
2716
  const narrationRecording = narrationOn && Boolean(narration?.recording);
2655
2717
  const expanded = isExpandedPanel(panelMode);
2656
- const [narrationRequesting, setNarrationRequesting] = useState3(false);
2657
- const [countingIn, setCountingIn] = useState3(false);
2658
- const [narrationPreview, setNarrationPreview] = useState3(null);
2659
- const [deviceSettings, setDeviceSettings] = useState3(() => ({
2718
+ const [narrationRequesting, setNarrationRequesting] = useState4(false);
2719
+ const [countingIn, setCountingIn] = useState4(false);
2720
+ const [narrationPreview, setNarrationPreview] = useState4(null);
2721
+ const [deviceSettings, setDeviceSettings] = useState4(() => ({
2660
2722
  ...DEFAULT_RECORDER_DEVICE_SETTINGS,
2661
2723
  audio: { ...DEFAULT_RECORDER_DEVICE_SETTINGS.audio },
2662
2724
  camera: { ...DEFAULT_RECORDER_DEVICE_SETTINGS.camera },
@@ -2664,11 +2726,11 @@ function RecorderModal({
2664
2726
  screenAudio: { ...DEFAULT_RECORDER_DEVICE_SETTINGS.screenAudio },
2665
2727
  encoding: { ...DEFAULT_RECORDER_DEVICE_SETTINGS.encoding }
2666
2728
  }));
2667
- const overlayRef = useRef2(null);
2668
- const dialogRef = useRef2(null);
2729
+ const overlayRef = useRef3(null);
2730
+ const dialogRef = useRef3(null);
2669
2731
  const headingId = useId2();
2670
- const previewRef = useRef2(null);
2671
- const cameraPreviewRef = useRef2(null);
2732
+ const previewRef = useRef3(null);
2733
+ const cameraPreviewRef = useRef3(null);
2672
2734
  const derivedSource = deriveSource({ micOn, cameraOn, screenOn });
2673
2735
  const canCapture = derivedSource !== null;
2674
2736
  const source = derivedSource ?? "mic";
@@ -2714,7 +2776,7 @@ function RecorderModal({
2714
2776
  });
2715
2777
  const filenameSeed = recordingFilenameSeed(source, recorder.stream, micOn || includeSystemAudio);
2716
2778
  const narrationAvailable = Boolean(narration?.doc);
2717
- const basenameRef = useRef2(basename);
2779
+ const basenameRef = useRef3(basename);
2718
2780
  basenameRef.current = basename;
2719
2781
  const stage = useNarrationStage({
2720
2782
  doc: narration?.doc ?? null,
@@ -2740,18 +2802,18 @@ function RecorderModal({
2740
2802
  videoKeyFrameIntervalCount: encoding.videoKeyFrameIntervalCount
2741
2803
  }
2742
2804
  });
2743
- const stageRef = useRef2(stage);
2805
+ const stageRef = useRef3(stage);
2744
2806
  stageRef.current = stage;
2745
- const slidesViewport = slides?.viewport ?? VIEWPORT_PRESETS2.landscape;
2807
+ const slidesViewport = slides?.viewport ?? VIEWPORT_PRESETS.landscape;
2746
2808
  const slideDeck = useMemo(
2747
2809
  () => slides?.doc ? buildRecorderSlideDeck(slides.doc, slides.theme, slidesViewport) : [],
2748
2810
  [slides?.doc, slides?.theme, slidesViewport]
2749
2811
  );
2750
2812
  const slidesAvailable = slideDeck.length > 0;
2751
2813
  const slidesCaptureTimings = Boolean(slides?.captureTimings && slides?.doc);
2752
- const [slideIndex, setSlideIndex] = useState3(0);
2753
- const [buildLog, setBuildLog] = useState3([]);
2754
- const buildLogRef = useRef2(buildLog);
2814
+ const [slideIndex, setSlideIndex] = useState4(0);
2815
+ const [buildLog, setBuildLog] = useState4([]);
2816
+ const buildLogRef = useRef3(buildLog);
2755
2817
  buildLogRef.current = buildLog;
2756
2818
  const handleBuildShown = useCallback4(
2757
2819
  (blockId, step, sourceText2) => {
@@ -2763,13 +2825,16 @@ function RecorderModal({
2763
2825
  },
2764
2826
  [recorder]
2765
2827
  );
2766
- const [advanceLog, setAdvanceLog] = useState3(EMPTY_ADVANCE_LOG);
2767
- const [applySlideTimings, setApplySlideTimings] = useState3(true);
2768
- const slideIndexRef = useRef2(slideIndex);
2828
+ const [advanceLog, setAdvanceLog] = useState4(EMPTY_ADVANCE_LOG);
2829
+ const [slideMarks, setSlideMarks] = useState4(EMPTY_SLIDE_MARK_LOG);
2830
+ const slideMarksRef = useRef3(slideMarks);
2831
+ slideMarksRef.current = slideMarks;
2832
+ const [applySlideTimings, setApplySlideTimings] = useState4(true);
2833
+ const slideIndexRef = useRef3(slideIndex);
2769
2834
  slideIndexRef.current = slideIndex;
2770
- const advanceLogRef = useRef2(advanceLog);
2835
+ const advanceLogRef = useRef3(advanceLog);
2771
2836
  advanceLogRef.current = advanceLog;
2772
- const slideDeckRef = useRef2(slideDeck);
2837
+ const slideDeckRef = useRef3(slideDeck);
2773
2838
  slideDeckRef.current = slideDeck;
2774
2839
  const shownBlockIds = useMemo(
2775
2840
  () => new Set(advanceLog.map((advance) => advance.blockId)),
@@ -2780,15 +2845,18 @@ function RecorderModal({
2780
2845
  const clamped = clampSlideIndex(next, slideDeckRef.current.length);
2781
2846
  setSlideIndex(clamped);
2782
2847
  const elapsedMs = recorder.getElapsedMs();
2783
- const blockId = slideDeckRef.current[clamped]?.blockId;
2784
- if (elapsedMs === null || !blockId) return;
2785
- setAdvanceLog((log) => recordSlideShown(log, blockId, elapsedMs));
2848
+ const slide = slideDeckRef.current[clamped];
2849
+ if (elapsedMs === null || !slide?.blockId) return;
2850
+ setAdvanceLog((log) => recordSlideShown(log, slide.blockId, elapsedMs));
2851
+ const offset = slide.sourceCharOffset;
2852
+ if (offset !== void 0) setSlideMarks((log) => recordSlideMark(log, offset, elapsedMs));
2786
2853
  },
2787
2854
  [recorder]
2788
2855
  );
2789
- const slideDeckKey = slideDeck.map((slide) => slide.blockId).join("\0");
2856
+ const slideDeckKey = [...new Set(slideDeck.map((slide) => slide.blockId))].join("\0");
2790
2857
  useEffect5(() => {
2791
2858
  setAdvanceLog(EMPTY_ADVANCE_LOG);
2859
+ setSlideMarks(EMPTY_SLIDE_MARK_LOG);
2792
2860
  setBuildLog([]);
2793
2861
  setSlideIndex(0);
2794
2862
  }, [slidesOn, slideDeckKey]);
@@ -2819,7 +2887,7 @@ function RecorderModal({
2819
2887
  }
2820
2888
  );
2821
2889
  }, [narrationMicDeviceId, narrationRecording]);
2822
- const prevNarrationOnRef = useRef2(narrationOn);
2890
+ const prevNarrationOnRef = useRef3(narrationOn);
2823
2891
  useEffect5(() => {
2824
2892
  const was = prevNarrationOnRef.current;
2825
2893
  prevNarrationOnRef.current = narrationOn;
@@ -2847,7 +2915,7 @@ function RecorderModal({
2847
2915
  }, [narrationOn]);
2848
2916
  const narrationMicLive = stage.controller.mic.status === "live";
2849
2917
  const narrationPreviewWanted = narrationRecording && stage.recorder.withCamera && narrationMicLive && stage.recorder.cameraStream === null && stage.recorder.state !== "processing" && stage.recorder.state !== "review" && stage.recorder.state !== "saving";
2850
- const narrationPreviewRef = useRef2(null);
2918
+ const narrationPreviewRef = useRef3(null);
2851
2919
  useEffect5(() => {
2852
2920
  const existing = narrationPreviewRef.current;
2853
2921
  if (existing) {
@@ -2935,13 +3003,14 @@ function RecorderModal({
2935
3003
  };
2936
3004
  }, [cameraBlob]);
2937
3005
  const captureKey = `${source}:${cameraOn ? micOn : ""}:${includeSystemAudio}:${JSON.stringify(deviceSettings)}`;
2938
- const previousKeyRef = useRef2(captureKey);
2939
- const dualSaveProgressRef = useRef2({});
3006
+ const previousKeyRef = useRef3(captureKey);
3007
+ const dualSaveProgressRef = useRef3({});
2940
3008
  useEffect5(() => {
2941
3009
  if (previousKeyRef.current !== captureKey) {
2942
3010
  previousKeyRef.current = captureKey;
2943
3011
  dualSaveProgressRef.current = {};
2944
3012
  setAdvanceLog(EMPTY_ADVANCE_LOG);
3013
+ setSlideMarks(EMPTY_SLIDE_MARK_LOG);
2945
3014
  recorder.cancel();
2946
3015
  }
2947
3016
  }, [captureKey, recorder]);
@@ -2995,8 +3064,13 @@ function RecorderModal({
2995
3064
  }
2996
3065
  setBuildLog([]);
2997
3066
  recorder.start();
2998
- const blockId = slideDeckRef.current[slideIndexRef.current]?.blockId;
2999
- setAdvanceLog(blockId ? recordSlideShown(EMPTY_ADVANCE_LOG, blockId, 0) : EMPTY_ADVANCE_LOG);
3067
+ const slide = slideDeckRef.current[slideIndexRef.current];
3068
+ setAdvanceLog(
3069
+ slide?.blockId ? recordSlideShown(EMPTY_ADVANCE_LOG, slide.blockId, 0) : EMPTY_ADVANCE_LOG
3070
+ );
3071
+ setSlideMarks(
3072
+ slide?.sourceCharOffset !== void 0 ? recordSlideMark(EMPTY_SLIDE_MARK_LOG, slide.sourceCharOffset, 0) : EMPTY_SLIDE_MARK_LOG
3073
+ );
3000
3074
  }, [narrationOn, recorder]);
3001
3075
  const handleStop = useCallback4(async () => {
3002
3076
  setSaveError(null);
@@ -3013,7 +3087,7 @@ function RecorderModal({
3013
3087
  const log = advanceLogRef.current;
3014
3088
  if (log.length === 0) return null;
3015
3089
  const timing = {
3016
- ...buildAdvanceTimingJson(doc, log, durationSec),
3090
+ ...buildAdvanceTimingJson(doc, log, durationSec, { slides: slideMarksRef.current }),
3017
3091
  builds: buildLogRef.current
3018
3092
  };
3019
3093
  const encoded = encodeNarrationTimingJson(timing);
@@ -3189,6 +3263,7 @@ function RecorderModal({
3189
3263
  const handleDiscard = useCallback4(() => {
3190
3264
  dualSaveProgressRef.current = {};
3191
3265
  setAdvanceLog(EMPTY_ADVANCE_LOG);
3266
+ setSlideMarks(EMPTY_SLIDE_MARK_LOG);
3192
3267
  recorder.reset();
3193
3268
  if (narrationOn) stageRef.current.controller.restart();
3194
3269
  }, [narrationOn, recorder]);
@@ -3216,7 +3291,7 @@ function RecorderModal({
3216
3291
  () => slides?.doc && showTimingCheckbox ? advanceCoverage(slides.doc, advanceLog) : null,
3217
3292
  [slides?.doc, showTimingCheckbox, advanceLog]
3218
3293
  );
3219
- const [timingDownloadUrl, setTimingDownloadUrl] = useState3(null);
3294
+ const [timingDownloadUrl, setTimingDownloadUrl] = useState4(null);
3220
3295
  const downloadFilename = useMemo(
3221
3296
  () => !recorder.blob ? "" : buildFilename(
3222
3297
  source === "mic" ? "audio" : "video",
@@ -3234,7 +3309,7 @@ function RecorderModal({
3234
3309
  }
3235
3310
  const duration = recorder.durationMs / 1e3;
3236
3311
  const encoded = slidesOn && slidesCaptureTimings && applySlideTimings && slides?.doc && advanceLog.length > 0 ? encodeNarrationTimingJson({
3237
- ...buildAdvanceTimingJson(slides.doc, advanceLog, duration),
3312
+ ...buildAdvanceTimingJson(slides.doc, advanceLog, duration, { slides: slideMarks }),
3238
3313
  builds: buildLog
3239
3314
  }) : source === "mic" ? encodeTimingJson(buildTimingJson(sourceText, duration)) : null;
3240
3315
  if (encoded === null) {
@@ -3256,6 +3331,7 @@ function RecorderModal({
3256
3331
  applySlideTimings,
3257
3332
  slides?.doc,
3258
3333
  advanceLog,
3334
+ slideMarks,
3259
3335
  source,
3260
3336
  sourceText
3261
3337
  ]);
@@ -3329,7 +3405,7 @@ function RecorderModal({
3329
3405
  };
3330
3406
  }
3331
3407
  };
3332
- return /* @__PURE__ */ jsx6(
3408
+ return /* @__PURE__ */ jsx5(
3333
3409
  "div",
3334
3410
  {
3335
3411
  ref: overlayRef,
@@ -3355,7 +3431,7 @@ function RecorderModal({
3355
3431
  "aria-labelledby": headingId,
3356
3432
  tabIndex: -1,
3357
3433
  children: [
3358
- /* @__PURE__ */ jsx6("h2", { id: headingId, style: titleStyle, children: title }),
3434
+ /* @__PURE__ */ jsx5("h2", { id: headingId, style: titleStyle, children: title }),
3359
3435
  /* @__PURE__ */ jsxs5("div", { style: expanded ? bodyRowStyle : void 0, children: [
3360
3436
  /* @__PURE__ */ jsxs5(
3361
3437
  "div",
@@ -3365,12 +3441,12 @@ function RecorderModal({
3365
3441
  ...panelMode === "both" ? { flex: "0 0 min(280px, 24vw)" } : {}
3366
3442
  } : void 0,
3367
3443
  children: [
3368
- /* @__PURE__ */ jsx6("div", { style: toggleRowStyle, role: "group", "aria-label": "Capture sources", children: TOGGLE_GROUPS.map((group, groupIndex) => /* @__PURE__ */ jsxs5(Fragment2, { children: [
3369
- groupIndex > 0 && /* @__PURE__ */ jsx6("div", { "aria-hidden": "true", style: groupDividerStyle }),
3370
- /* @__PURE__ */ jsx6("div", { style: toggleGroupStyle, children: group.toggles.map((t) => {
3444
+ /* @__PURE__ */ jsx5("div", { style: toggleRowStyle, role: "group", "aria-label": "Capture sources", children: TOGGLE_GROUPS.map((group, groupIndex) => /* @__PURE__ */ jsxs5(Fragment2, { children: [
3445
+ groupIndex > 0 && /* @__PURE__ */ jsx5("div", { "aria-hidden": "true", style: groupDividerStyle }),
3446
+ /* @__PURE__ */ jsx5("div", { style: toggleGroupStyle, children: group.toggles.map((t) => {
3371
3447
  if (t.key === "systemAudio" && !canIncludeSystemAudio) return null;
3372
3448
  const props = narrationRecording ? narrationToggleFor(t.key) : simpleToggleProps(t.key);
3373
- return /* @__PURE__ */ jsx6(
3449
+ return /* @__PURE__ */ jsx5(
3374
3450
  "button",
3375
3451
  {
3376
3452
  type: "button",
@@ -3385,10 +3461,10 @@ function RecorderModal({
3385
3461
  );
3386
3462
  }) })
3387
3463
  ] }, group.label)) }),
3388
- /* @__PURE__ */ jsx6("p", { style: summaryStyle, children: narrationRecording ? narrationCaptureSummary(stage.recorder.withCamera) : captureSummary(micOn, cameraOn, screenOn, includeSystemAudio) }),
3464
+ /* @__PURE__ */ jsx5("p", { style: summaryStyle, children: narrationRecording ? narrationCaptureSummary(stage.recorder.withCamera) : captureSummary(micOn, cameraOn, screenOn, includeSystemAudio) }),
3389
3465
  /* @__PURE__ */ jsxs5("div", { style: modeTogglesStyle, children: [
3390
3466
  narrationAvailable && /* @__PURE__ */ jsxs5("label", { style: checkboxRowStyle, children: [
3391
- /* @__PURE__ */ jsx6(
3467
+ /* @__PURE__ */ jsx5(
3392
3468
  "input",
3393
3469
  {
3394
3470
  type: "checkbox",
@@ -3402,7 +3478,7 @@ function RecorderModal({
3402
3478
  "Show narration mode"
3403
3479
  ] }),
3404
3480
  slidesAvailable && /* @__PURE__ */ jsxs5("label", { style: checkboxRowStyle, children: [
3405
- /* @__PURE__ */ jsx6(
3481
+ /* @__PURE__ */ jsx5(
3406
3482
  "input",
3407
3483
  {
3408
3484
  type: "checkbox",
@@ -3416,7 +3492,7 @@ function RecorderModal({
3416
3492
  "Show slides mode"
3417
3493
  ] })
3418
3494
  ] }),
3419
- /* @__PURE__ */ jsx6(
3495
+ /* @__PURE__ */ jsx5(
3420
3496
  RecorderDeviceQuickPicks,
3421
3497
  {
3422
3498
  devices: mediaDevices,
@@ -3435,7 +3511,7 @@ function RecorderModal({
3435
3511
  disabled: deviceSettingsLocked
3436
3512
  }
3437
3513
  ),
3438
- !narrationRecording && screenOn && /* @__PURE__ */ jsx6(
3514
+ !narrationRecording && screenOn && /* @__PURE__ */ jsx5(
3439
3515
  RecorderScreenSettings,
3440
3516
  {
3441
3517
  value: deviceSettings.screen,
@@ -3448,7 +3524,7 @@ function RecorderModal({
3448
3524
  }
3449
3525
  }
3450
3526
  ),
3451
- /* @__PURE__ */ jsx6(
3527
+ /* @__PURE__ */ jsx5(
3452
3528
  RecorderDeviceSettingsPanel,
3453
3529
  {
3454
3530
  value: deviceSettings,
@@ -3463,10 +3539,10 @@ function RecorderModal({
3463
3539
  cameraStream: activeCameraStream
3464
3540
  }
3465
3541
  ),
3466
- !narrationRecording && recorder.error && /* @__PURE__ */ jsx6("div", { style: errorStyle, children: recorder.error.message }),
3542
+ !narrationRecording && recorder.error && /* @__PURE__ */ jsx5("div", { style: errorStyle, children: recorder.error.message }),
3467
3543
  !narrationRecording && saveError && /* @__PURE__ */ jsxs5("div", { role: "alert", style: errorStyle, children: [
3468
3544
  saveError,
3469
- /* @__PURE__ */ jsx6("p", { style: { marginBottom: 0 }, children: "Your recording is still available below. Download a copy before closing, or try saving again." })
3545
+ /* @__PURE__ */ jsx5("p", { style: { marginBottom: 0 }, children: "Your recording is still available below. Download a copy before closing, or try saving again." })
3470
3546
  ] }),
3471
3547
  /* @__PURE__ */ jsxs5("p", { style: summaryStyle, children: [
3472
3548
  "Recording size:",
@@ -3482,9 +3558,9 @@ function RecorderModal({
3482
3558
  " ",
3483
3559
  "total. Final encoding may add more data."
3484
3560
  ] }),
3485
- !narrationRecording && recorder.limitReached && /* @__PURE__ */ jsx6("p", { role: "status", style: recordingStatusStyle, children: "Recording stopped at the size limit. Your complete recording is retained; download a copy or save it to the document." }),
3486
- narrationOn && (stage.recorder.error ?? stage.controller.mic.error) && /* @__PURE__ */ jsx6("div", { style: errorStyle, children: (stage.recorder.error ?? stage.controller.mic.error)?.message }),
3487
- narrationRecording && narrationCameraStream && /* @__PURE__ */ jsx6("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx6(
3561
+ !narrationRecording && recorder.limitReached && /* @__PURE__ */ jsx5("p", { role: "status", style: recordingStatusStyle, children: "Recording stopped at the size limit. Your complete recording is retained; download a copy or save it to the document." }),
3562
+ narrationOn && (stage.recorder.error ?? stage.controller.mic.error) && /* @__PURE__ */ jsx5("div", { style: errorStyle, children: (stage.recorder.error ?? stage.controller.mic.error)?.message }),
3563
+ narrationRecording && narrationCameraStream && /* @__PURE__ */ jsx5("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx5(
3488
3564
  "video",
3489
3565
  {
3490
3566
  ref: previewRef,
@@ -3494,10 +3570,10 @@ function RecorderModal({
3494
3570
  style: { width: "100%", height: "100%", objectFit: "contain" }
3495
3571
  }
3496
3572
  ) }),
3497
- narrationRecording && !narrationCameraStream && narrationTakeDone && /* @__PURE__ */ jsx6("div", { style: audioMeterStyle, children: stage.recorder.take ? `\u2713 Recorded ${formatDurationMs(stage.recorder.take.durationSec * 1e3)}` : "\u25CF Processing take\u2026" }),
3498
- narrationRecording && !narrationCameraStream && !narrationTakeDone && stage.recorder.withCamera && /* @__PURE__ */ jsx6("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx6("span", { children: narrationPreviewWanted ? "Camera starting\u2026" : "Click Start preview to turn on your camera." }) }),
3573
+ narrationRecording && !narrationCameraStream && narrationTakeDone && /* @__PURE__ */ jsx5("div", { style: audioMeterStyle, children: stage.recorder.take ? `\u2713 Recorded ${formatDurationMs(stage.recorder.take.durationSec * 1e3)}` : "\u25CF Processing take\u2026" }),
3574
+ narrationRecording && !narrationCameraStream && !narrationTakeDone && stage.recorder.withCamera && /* @__PURE__ */ jsx5("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx5("span", { children: narrationPreviewWanted ? "Camera starting\u2026" : "Click Start preview to turn on your camera." }) }),
3499
3575
  narrationRecording && !narrationCameraStream && !narrationTakeDone && !stage.recorder.withCamera && /* @__PURE__ */ jsxs5("div", { style: { ...audioMeterStyle, position: "relative", overflow: "hidden" }, children: [
3500
- /* @__PURE__ */ jsx6(
3576
+ /* @__PURE__ */ jsx5(
3501
3577
  "div",
3502
3578
  {
3503
3579
  "aria-hidden": "true",
@@ -3507,11 +3583,11 @@ function RecorderModal({
3507
3583
  }
3508
3584
  }
3509
3585
  ),
3510
- /* @__PURE__ */ jsx6("span", { style: { position: "relative" }, children: stage.recorder.state === "recording" ? "\u25CF Recording narration" : narrationMicLive ? stage.controller.voiceActive ? "Voice detected" : "Microphone ready" : "Click Start preview to check your mic." })
3586
+ /* @__PURE__ */ jsx5("span", { style: { position: "relative" }, children: stage.recorder.state === "recording" ? "\u25CF Recording narration" : narrationMicLive ? stage.controller.voiceActive ? "Voice detected" : "Microphone ready" : "Click Start preview to check your mic." })
3511
3587
  ] }),
3512
- !narrationRecording && !showPreview && /* @__PURE__ */ jsx6("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx6("span", { children: screenOn ? "Choose a screen or window to preview what will be recorded." : "Click Start preview to prepare a recording." }) }),
3588
+ !narrationRecording && !showPreview && /* @__PURE__ */ jsx5("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx5("span", { children: screenOn ? "Choose a screen or window to preview what will be recorded." : "Click Start preview to prepare a recording." }) }),
3513
3589
  !narrationRecording && showPreview && recorder.state !== "stopped" && !isAudioOnly && /* @__PURE__ */ jsxs5("div", { style: isDual ? { ...previewBoxStyle, position: "relative" } : previewBoxStyle, children: [
3514
- /* @__PURE__ */ jsx6(
3590
+ /* @__PURE__ */ jsx5(
3515
3591
  "video",
3516
3592
  {
3517
3593
  ref: previewRef,
@@ -3521,7 +3597,7 @@ function RecorderModal({
3521
3597
  style: { width: "100%", height: "100%", objectFit: "contain" }
3522
3598
  }
3523
3599
  ),
3524
- isDual && /* @__PURE__ */ jsx6(
3600
+ isDual && /* @__PURE__ */ jsx5(
3525
3601
  "video",
3526
3602
  {
3527
3603
  ref: cameraPreviewRef,
@@ -3542,12 +3618,12 @@ function RecorderModal({
3542
3618
  }
3543
3619
  )
3544
3620
  ] }),
3545
- !narrationRecording && showPreview && recorder.state !== "stopped" && isAudioOnly && /* @__PURE__ */ jsx6("div", { style: audioMeterStyle, children: recorder.state === "recording" ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
3621
+ !narrationRecording && showPreview && recorder.state !== "stopped" && isAudioOnly && /* @__PURE__ */ jsx5("div", { style: audioMeterStyle, children: recorder.state === "recording" ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
3546
3622
  "\u25CF Recording ",
3547
3623
  formatDurationMs(recorder.durationMs)
3548
- ] }) : /* @__PURE__ */ jsx6(Fragment3, { children: "Microphone ready" }) }),
3624
+ ] }) : /* @__PURE__ */ jsx5(Fragment3, { children: "Microphone ready" }) }),
3549
3625
  !narrationRecording && recorder.state === "stopped" && playbackUrl && !isAudioOnly && /* @__PURE__ */ jsxs5("div", { style: { ...previewBoxStyle, position: "relative" }, children: [
3550
- /* @__PURE__ */ jsx6(
3626
+ /* @__PURE__ */ jsx5(
3551
3627
  "video",
3552
3628
  {
3553
3629
  src: playbackUrl,
@@ -3574,8 +3650,8 @@ function RecorderModal({
3574
3650
  )
3575
3651
  ] }),
3576
3652
  !narrationRecording && recorder.state === "stopped" && isDual && cameraPlaybackUrl && /* @__PURE__ */ jsxs5("div", { style: { marginBottom: 12 }, children: [
3577
- /* @__PURE__ */ jsx6("div", { style: summaryStyle, children: "Camera (picture-in-picture)" }),
3578
- /* @__PURE__ */ jsx6(
3653
+ /* @__PURE__ */ jsx5("div", { style: summaryStyle, children: "Camera (picture-in-picture)" }),
3654
+ /* @__PURE__ */ jsx5(
3579
3655
  "video",
3580
3656
  {
3581
3657
  src: cameraPlaybackUrl,
@@ -3596,7 +3672,7 @@ function RecorderModal({
3596
3672
  "\u2713 Recorded ",
3597
3673
  formatDurationMs(recorder.durationMs)
3598
3674
  ] }),
3599
- /* @__PURE__ */ jsx6("audio", { src: playbackUrl, controls: true, style: { width: "100%" } })
3675
+ /* @__PURE__ */ jsx5("audio", { src: playbackUrl, controls: true, style: { width: "100%" } })
3600
3676
  ] }),
3601
3677
  !narrationRecording && canSave && playbackUrl && /* @__PURE__ */ jsxs5(
3602
3678
  "div",
@@ -3608,8 +3684,8 @@ function RecorderModal({
3608
3684
  marginBottom: 12
3609
3685
  },
3610
3686
  children: [
3611
- /* @__PURE__ */ jsx6("a", { href: playbackUrl, download: downloadFilename, style: btnSecondary, children: isDual ? "Download screen recording" : "Download recording" }),
3612
- isDual && cameraPlaybackUrl && /* @__PURE__ */ jsx6(
3687
+ /* @__PURE__ */ jsx5("a", { href: playbackUrl, download: downloadFilename, style: btnSecondary, children: isDual ? "Download screen recording" : "Download recording" }),
3688
+ isDual && cameraPlaybackUrl && /* @__PURE__ */ jsx5(
3613
3689
  "a",
3614
3690
  {
3615
3691
  href: cameraPlaybackUrl,
@@ -3623,7 +3699,7 @@ function RecorderModal({
3623
3699
  children: "Download camera recording"
3624
3700
  }
3625
3701
  ),
3626
- timingDownloadUrl && /* @__PURE__ */ jsx6(
3702
+ timingDownloadUrl && /* @__PURE__ */ jsx5(
3627
3703
  "a",
3628
3704
  {
3629
3705
  href: timingDownloadUrl,
@@ -3636,8 +3712,8 @@ function RecorderModal({
3636
3712
  }
3637
3713
  ),
3638
3714
  !narrationOn && !slidesTimingCapture && source === "mic" && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3639
- /* @__PURE__ */ jsx6("label", { style: labelStyle, htmlFor: "recorder-source-text", children: "Script (used to auto-match this narration to a block)" }),
3640
- /* @__PURE__ */ jsx6(
3715
+ /* @__PURE__ */ jsx5("label", { style: labelStyle, htmlFor: "recorder-source-text", children: "Script (used to auto-match this narration to a block)" }),
3716
+ /* @__PURE__ */ jsx5(
3641
3717
  "textarea",
3642
3718
  {
3643
3719
  id: "recorder-source-text",
@@ -3649,8 +3725,8 @@ function RecorderModal({
3649
3725
  }
3650
3726
  )
3651
3727
  ] }),
3652
- /* @__PURE__ */ jsx6("label", { style: labelStyle, htmlFor: "recorder-basename", children: "Filename (optional)" }),
3653
- /* @__PURE__ */ jsx6(
3728
+ /* @__PURE__ */ jsx5("label", { style: labelStyle, htmlFor: "recorder-basename", children: "Filename (optional)" }),
3729
+ /* @__PURE__ */ jsx5(
3654
3730
  "input",
3655
3731
  {
3656
3732
  id: "recorder-basename",
@@ -3668,7 +3744,7 @@ function RecorderModal({
3668
3744
  ] }),
3669
3745
  showTimingCheckbox && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3670
3746
  /* @__PURE__ */ jsxs5("label", { style: checkboxRowStyle, children: [
3671
- /* @__PURE__ */ jsx6(
3747
+ /* @__PURE__ */ jsx5(
3672
3748
  "input",
3673
3749
  {
3674
3750
  type: "checkbox",
@@ -3680,10 +3756,10 @@ function RecorderModal({
3680
3756
  ),
3681
3757
  SLIDE_TIMING_CHECKBOX_LABEL
3682
3758
  ] }),
3683
- applySlideTimings && slideCoverageWarning && /* @__PURE__ */ jsx6("p", { style: summaryStyle, children: slideCoverageWarning })
3759
+ applySlideTimings && slideCoverageWarning && /* @__PURE__ */ jsx5("p", { style: summaryStyle, children: slideCoverageWarning })
3684
3760
  ] }),
3685
3761
  /* @__PURE__ */ jsxs5("div", { style: buttonRowStyle, children: [
3686
- /* @__PURE__ */ jsx6(
3762
+ /* @__PURE__ */ jsx5(
3687
3763
  "button",
3688
3764
  {
3689
3765
  type: "button",
@@ -3694,7 +3770,7 @@ function RecorderModal({
3694
3770
  }
3695
3771
  ),
3696
3772
  narrationRecording && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3697
- narrationRecorderIdle && !narrationMicLive && /* @__PURE__ */ jsx6(
3773
+ narrationRecorderIdle && !narrationMicLive && /* @__PURE__ */ jsx5(
3698
3774
  "button",
3699
3775
  {
3700
3776
  type: "button",
@@ -3705,13 +3781,13 @@ function RecorderModal({
3705
3781
  }
3706
3782
  ),
3707
3783
  narrationRecorderIdle && narrationMicLive && /* @__PURE__ */ jsxs5("button", { type: "button", style: btnRecord, onClick: handleNarrationRecord, children: [
3708
- /* @__PURE__ */ jsx6(
3784
+ /* @__PURE__ */ jsx5(
3709
3785
  "span",
3710
3786
  {
3711
3787
  className: "squisq-recorder-record-dot",
3712
3788
  style: recordDotFrameStyle,
3713
3789
  "aria-hidden": "true",
3714
- children: /* @__PURE__ */ jsx6(
3790
+ children: /* @__PURE__ */ jsx5(
3715
3791
  "span",
3716
3792
  {
3717
3793
  className: "squisq-recorder-record-dot-center",
@@ -3722,7 +3798,7 @@ function RecorderModal({
3722
3798
  ),
3723
3799
  "Record"
3724
3800
  ] }),
3725
- (stage.recorder.state === "recording" || stage.recorder.state === "starting") && /* @__PURE__ */ jsx6(
3801
+ (stage.recorder.state === "recording" || stage.recorder.state === "starting") && /* @__PURE__ */ jsx5(
3726
3802
  "button",
3727
3803
  {
3728
3804
  type: "button",
@@ -3731,11 +3807,11 @@ function RecorderModal({
3731
3807
  children: "Stop"
3732
3808
  }
3733
3809
  ),
3734
- stage.recorder.state === "processing" && /* @__PURE__ */ jsx6("span", { style: recordingStatusStyle, children: "Aligning take\u2026" }),
3735
- stage.recorder.state === "saving" && /* @__PURE__ */ jsx6("span", { style: recordingStatusStyle, children: "Saving\u2026" }),
3810
+ stage.recorder.state === "processing" && /* @__PURE__ */ jsx5("span", { style: recordingStatusStyle, children: "Aligning take\u2026" }),
3811
+ stage.recorder.state === "saving" && /* @__PURE__ */ jsx5("span", { style: recordingStatusStyle, children: "Saving\u2026" }),
3736
3812
  stage.recorder.state === "review" && stage.recorder.take && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3737
- /* @__PURE__ */ jsx6("button", { type: "button", style: btnSecondary, onClick: stage.handleRetake, children: "Discard & re-record" }),
3738
- /* @__PURE__ */ jsx6(
3813
+ /* @__PURE__ */ jsx5("button", { type: "button", style: btnSecondary, onClick: stage.handleRetake, children: "Discard & re-record" }),
3814
+ /* @__PURE__ */ jsx5(
3739
3815
  "button",
3740
3816
  {
3741
3817
  type: "button",
@@ -3747,7 +3823,7 @@ function RecorderModal({
3747
3823
  ] })
3748
3824
  ] }),
3749
3825
  !narrationRecording && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3750
- (recorder.state === "idle" || recorder.state === "error" || recorder.state === "requesting") && /* @__PURE__ */ jsx6(
3826
+ (recorder.state === "idle" || recorder.state === "error" || recorder.state === "requesting") && /* @__PURE__ */ jsx5(
3751
3827
  "button",
3752
3828
  {
3753
3829
  type: "button",
@@ -3765,13 +3841,13 @@ function RecorderModal({
3765
3841
  onClick: () => void handleStart(),
3766
3842
  disabled: isBusy,
3767
3843
  children: [
3768
- /* @__PURE__ */ jsx6(
3844
+ /* @__PURE__ */ jsx5(
3769
3845
  "span",
3770
3846
  {
3771
3847
  className: "squisq-recorder-record-dot",
3772
3848
  style: recordDotFrameStyle,
3773
3849
  "aria-hidden": "true",
3774
- children: /* @__PURE__ */ jsx6(
3850
+ children: /* @__PURE__ */ jsx5(
3775
3851
  "span",
3776
3852
  {
3777
3853
  className: "squisq-recorder-record-dot-center",
@@ -3784,9 +3860,9 @@ function RecorderModal({
3784
3860
  ]
3785
3861
  }
3786
3862
  ),
3787
- canStop && /* @__PURE__ */ jsx6("button", { type: "button", style: btnDanger, onClick: handleStop, disabled: isBusy, children: "Stop" }),
3863
+ canStop && /* @__PURE__ */ jsx5("button", { type: "button", style: btnDanger, onClick: handleStop, disabled: isBusy, children: "Stop" }),
3788
3864
  canSave && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3789
- /* @__PURE__ */ jsx6(
3865
+ /* @__PURE__ */ jsx5(
3790
3866
  "button",
3791
3867
  {
3792
3868
  type: "button",
@@ -3796,7 +3872,7 @@ function RecorderModal({
3796
3872
  children: "Discard & re-record"
3797
3873
  }
3798
3874
  ),
3799
- /* @__PURE__ */ jsx6(
3875
+ /* @__PURE__ */ jsx5(
3800
3876
  "button",
3801
3877
  {
3802
3878
  type: "button",
@@ -3812,7 +3888,7 @@ function RecorderModal({
3812
3888
  ]
3813
3889
  }
3814
3890
  ),
3815
- narrationOn && narration && /* @__PURE__ */ jsx6("div", { style: { ...rightColStyle, flex: "1 1 0" }, children: /* @__PURE__ */ jsx6(
3891
+ narrationOn && narration && /* @__PURE__ */ jsx5("div", { style: { ...rightColStyle, flex: "1 1 0" }, children: /* @__PURE__ */ jsx5(
3816
3892
  NarrationStage,
3817
3893
  {
3818
3894
  stage,
@@ -3824,7 +3900,7 @@ function RecorderModal({
3824
3900
  showReviewActions: false
3825
3901
  }
3826
3902
  ) }),
3827
- slidesOn && /* @__PURE__ */ jsx6("div", { style: { ...rightColStyle, flex: "1 1 0" }, children: /* @__PURE__ */ jsx6(
3903
+ slidesOn && /* @__PURE__ */ jsx5("div", { style: { ...rightColStyle, flex: "1 1 0" }, children: /* @__PURE__ */ jsx5(
3828
3904
  RecorderSlidesPanel,
3829
3905
  {
3830
3906
  slides: slideDeck,
@@ -3847,9 +3923,9 @@ function RecorderModal({
3847
3923
  }
3848
3924
 
3849
3925
  // src/recorder/RecorderPanel.tsx
3850
- import { useCallback as useCallback5, useState as useState4 } from "react";
3926
+ import { useCallback as useCallback5, useState as useState5 } from "react";
3851
3927
  import { createPortal } from "react-dom";
3852
- import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3928
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
3853
3929
  function RecorderPanel({
3854
3930
  mediaProvider,
3855
3931
  container = null,
@@ -3865,7 +3941,7 @@ function RecorderPanel({
3865
3941
  onOpenChange,
3866
3942
  showTrigger = true
3867
3943
  }) {
3868
- const [uncontrolledOpen, setUncontrolledOpen] = useState4(false);
3944
+ const [uncontrolledOpen, setUncontrolledOpen] = useState5(false);
3869
3945
  const open = controlledOpen ?? uncontrolledOpen;
3870
3946
  const setOpen = useCallback5(
3871
3947
  (nextOpen) => {
@@ -3876,7 +3952,7 @@ function RecorderPanel({
3876
3952
  );
3877
3953
  const handleClose = useCallback5(() => setOpen(false), [setOpen]);
3878
3954
  return /* @__PURE__ */ jsxs6(Fragment4, { children: [
3879
- showTrigger && /* @__PURE__ */ jsx7(
3955
+ showTrigger && /* @__PURE__ */ jsx6(
3880
3956
  "button",
3881
3957
  {
3882
3958
  type: "button",
@@ -3885,11 +3961,11 @@ function RecorderPanel({
3885
3961
  "aria-label": tooltip,
3886
3962
  "aria-expanded": open,
3887
3963
  onClick: () => setOpen(!open),
3888
- children: /* @__PURE__ */ jsx7(Icon, { icon: "fa-solid fa-microphone" })
3964
+ children: /* @__PURE__ */ jsx6(Icon, { icon: "fa-solid fa-microphone" })
3889
3965
  }
3890
3966
  ),
3891
3967
  open && typeof document !== "undefined" && createPortal(
3892
- /* @__PURE__ */ jsx7(
3968
+ /* @__PURE__ */ jsx6(
3893
3969
  RecorderModal,
3894
3970
  {
3895
3971
  mediaProvider,
@@ -3932,7 +4008,6 @@ export {
3932
4008
  documentTitleFromFileName,
3933
4009
  resolveBlockVisual,
3934
4010
  buildRecorderSlideDeck,
3935
- BlockThumbnail,
3936
4011
  RecorderSlidesPanel,
3937
4012
  useModalDialog,
3938
4013
  RecorderModal,