@bendyline/squisq-react 2.8.0 → 2.9.0

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BlockRenderer,
3
3
  LinearDocView
4
- } from "./chunk-IQGW6SA6.js";
4
+ } from "./chunk-NGQQKH7J.js";
5
5
  import {
6
6
  useAudioSync,
7
7
  useDocPlayback,
@@ -11,6 +11,9 @@ import {
11
11
  import {
12
12
  useAutoSurface
13
13
  } from "./chunk-TT6ENR6T.js";
14
+ import {
15
+ MarkdownRenderer
16
+ } from "./chunk-ESI3P77P.js";
14
17
  import {
15
18
  useMediaProvider,
16
19
  useMediaUrl,
@@ -1514,10 +1517,525 @@ function DashboardView({
1514
1517
  );
1515
1518
  }
1516
1519
 
1520
+ // src/FlashcardView.tsx
1521
+ import {
1522
+ useCallback as useCallback3,
1523
+ useEffect as useEffect5,
1524
+ useMemo as useMemo4,
1525
+ useRef as useRef5,
1526
+ useState as useState4
1527
+ } from "react";
1528
+ import { resolveFontFamily as resolveFontFamily2, VIEWPORT_PRESETS as VIEWPORT_PRESETS2 } from "@bendyline/squisq/schemas";
1529
+ import {
1530
+ materializeBlockLayers,
1531
+ materializeFlashcards,
1532
+ resolvePageBlock,
1533
+ resolveThemeForDoc as resolveThemeForDoc2
1534
+ } from "@bendyline/squisq/doc";
1535
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1536
+ var VISUAL_TEMPLATES = /* @__PURE__ */ new Set([
1537
+ "diagram",
1538
+ "tree",
1539
+ "timeline",
1540
+ "map",
1541
+ "drawing",
1542
+ "layout",
1543
+ "barChart",
1544
+ "columnChart",
1545
+ "pieChart",
1546
+ "donutChart",
1547
+ "lineChart",
1548
+ "areaChart",
1549
+ "scatterChart"
1550
+ ]);
1551
+ function resolveVisualBlock(block, theme, viewport, customTemplates) {
1552
+ const resolved = resolvePageBlock(block);
1553
+ const visualTemplate = !!resolved.templateName && (VISUAL_TEMPLATES.has(resolved.templateName) || !!customTemplates?.some((definition) => definition.name === resolved.templateName));
1554
+ if (!visualTemplate) return null;
1555
+ const materializationBlock = resolved.templateBlock ?? block;
1556
+ const { layers, source } = materializeBlockLayers(materializationBlock, {
1557
+ theme,
1558
+ viewport,
1559
+ customTemplates,
1560
+ persistentLayers: false
1561
+ });
1562
+ if (layers.length === 0 || source === "fallback") return null;
1563
+ return { ...materializationBlock, layers };
1564
+ }
1565
+ function FaceBlock({
1566
+ block,
1567
+ depth,
1568
+ theme,
1569
+ basePath,
1570
+ showCodeCopyButton,
1571
+ onCopyCode,
1572
+ fenceRenderers,
1573
+ viewport,
1574
+ customTemplates
1575
+ }) {
1576
+ const Heading = depth === 0 ? "h2" : depth === 1 ? "h3" : "h4";
1577
+ const visualBlock = useMemo4(
1578
+ () => resolveVisualBlock(block, theme, viewport, customTemplates),
1579
+ [block, customTemplates, theme, viewport]
1580
+ );
1581
+ return /* @__PURE__ */ jsxs5("section", { className: "squisq-flashcards__content-block", "data-source-block-id": block.id, children: [
1582
+ !visualBlock && block.title && /* @__PURE__ */ jsx8(Heading, { className: "squisq-flashcards__content-title", children: block.title }),
1583
+ !visualBlock && block.contents && block.contents.length > 0 && /* @__PURE__ */ jsx8(
1584
+ MarkdownRenderer,
1585
+ {
1586
+ nodes: block.contents,
1587
+ theme,
1588
+ showCodeCopyButton,
1589
+ onCopyCode,
1590
+ fenceRenderers
1591
+ }
1592
+ ),
1593
+ (visualBlock || block.layers && block.layers.length > 0) && /* @__PURE__ */ jsx8(
1594
+ "div",
1595
+ {
1596
+ className: "squisq-flashcards__canvas",
1597
+ style: { aspectRatio: `${viewport.width} / ${viewport.height}` },
1598
+ children: /* @__PURE__ */ jsx8(
1599
+ BlockRenderer,
1600
+ {
1601
+ block: visualBlock ?? block,
1602
+ blockTime: Math.max(0, block.duration ?? 0),
1603
+ basePath,
1604
+ viewport,
1605
+ animationsEnabled: false,
1606
+ theme,
1607
+ muted: true
1608
+ }
1609
+ )
1610
+ }
1611
+ ),
1612
+ !visualBlock && block.children?.map((child) => /* @__PURE__ */ jsx8(
1613
+ FaceBlock,
1614
+ {
1615
+ block: child,
1616
+ depth: depth + 1,
1617
+ theme,
1618
+ basePath,
1619
+ showCodeCopyButton,
1620
+ onCopyCode,
1621
+ fenceRenderers,
1622
+ viewport,
1623
+ customTemplates
1624
+ },
1625
+ child.id
1626
+ ))
1627
+ ] });
1628
+ }
1629
+ function FaceContent(props) {
1630
+ return /* @__PURE__ */ jsx8("div", { className: "squisq-flashcards__face-content", children: props.face.blocks.map((block) => /* @__PURE__ */ jsx8(FaceBlock, { block, depth: 0, ...props }, block.id)) });
1631
+ }
1632
+ function FlashcardFaceView(props) {
1633
+ return /* @__PURE__ */ jsx8(FaceContent, { ...props, viewport: props.viewport ?? VIEWPORT_PRESETS2.landscape });
1634
+ }
1635
+ function hashSeed(value) {
1636
+ let hash = 2166136261;
1637
+ for (let index = 0; index < value.length; index++) {
1638
+ hash ^= value.charCodeAt(index);
1639
+ hash = Math.imul(hash, 16777619);
1640
+ }
1641
+ return hash >>> 0;
1642
+ }
1643
+ function shuffled(values, seed) {
1644
+ const result = [...values];
1645
+ let state = hashSeed(seed) || 1;
1646
+ const random = () => {
1647
+ state += 1831565813;
1648
+ let next = state;
1649
+ next = Math.imul(next ^ next >>> 15, next | 1);
1650
+ next ^= next + Math.imul(next ^ next >>> 7, next | 61);
1651
+ return ((next ^ next >>> 14) >>> 0) / 4294967296;
1652
+ };
1653
+ for (let index = result.length - 1; index > 0; index--) {
1654
+ const other = Math.floor(random() * (index + 1));
1655
+ [result[index], result[other]] = [result[other], result[index]];
1656
+ }
1657
+ return result;
1658
+ }
1659
+ function ChoiceContent(props) {
1660
+ return /* @__PURE__ */ jsx8(FaceContent, { ...props, face: props.choice.content });
1661
+ }
1662
+ function isInteractiveTarget(target) {
1663
+ return target instanceof Element && !!target.closest('button, a, input, select, textarea, [contenteditable="true"]');
1664
+ }
1665
+ function FlashcardView({
1666
+ doc,
1667
+ theme,
1668
+ basePath = ".",
1669
+ source = "auto",
1670
+ shuffle: initialShuffle = false,
1671
+ globalKeyboardShortcuts = false,
1672
+ showCodeCopyButton = false,
1673
+ onCopyCode,
1674
+ fenceRenderers,
1675
+ viewport = VIEWPORT_PRESETS2.landscape,
1676
+ className
1677
+ }) {
1678
+ const activeTheme = useMemo4(() => theme ?? resolveThemeForDoc2(doc), [doc, theme]);
1679
+ const deck = useMemo4(() => materializeFlashcards(doc, { source }), [doc, source]);
1680
+ const [shuffleEnabled, setShuffleEnabled] = useState4(initialShuffle);
1681
+ const [sessionSeed, setSessionSeed] = useState4(1);
1682
+ const [cardIndex, setCardIndex] = useState4(0);
1683
+ const [revealed, setRevealed] = useState4(false);
1684
+ const [selectedChoiceId, setSelectedChoiceId] = useState4(null);
1685
+ const [ratings, setRatings] = useState4({});
1686
+ const [retryIds, setRetryIds] = useState4(null);
1687
+ const cardViewportRef = useRef5(null);
1688
+ const orderedCards = useMemo4(
1689
+ () => shuffleEnabled ? shuffled(deck.cards, `${doc.articleId}:${sessionSeed}`) : [...deck.cards],
1690
+ [deck.cards, doc.articleId, sessionSeed, shuffleEnabled]
1691
+ );
1692
+ const sessionCards = useMemo4(
1693
+ () => retryIds ? retryIds.map((id) => orderedCards.find((card) => card.id === id)).filter((card) => !!card) : orderedCards,
1694
+ [orderedCards, retryIds]
1695
+ );
1696
+ const currentCard = sessionCards[cardIndex];
1697
+ const finished = sessionCards.length > 0 && cardIndex >= sessionCards.length;
1698
+ const resetPosition = useCallback3(() => {
1699
+ setCardIndex(0);
1700
+ setRevealed(false);
1701
+ setSelectedChoiceId(null);
1702
+ }, []);
1703
+ const restart = useCallback3(() => {
1704
+ setRatings({});
1705
+ setRetryIds(null);
1706
+ resetPosition();
1707
+ setSessionSeed((seed) => seed + 1);
1708
+ }, [resetPosition]);
1709
+ useEffect5(() => {
1710
+ setRatings({});
1711
+ setRetryIds(null);
1712
+ resetPosition();
1713
+ }, [deck, resetPosition]);
1714
+ useEffect5(() => {
1715
+ if (cardViewportRef.current) cardViewportRef.current.scrollTop = 0;
1716
+ }, [cardIndex, retryIds]);
1717
+ const revealAnswer = useCallback3(() => {
1718
+ if (!currentCard) return;
1719
+ setRevealed(true);
1720
+ if (currentCard.kind === "multiple-choice" && selectedChoiceId === null) {
1721
+ setRatings((current) => ({ ...current, [currentCard.id]: "again" }));
1722
+ }
1723
+ }, [currentCard, selectedChoiceId]);
1724
+ const nextCard = useCallback3(() => {
1725
+ if (!currentCard) return;
1726
+ if (!revealed) {
1727
+ revealAnswer();
1728
+ return;
1729
+ }
1730
+ setCardIndex((index) => index + 1);
1731
+ setRevealed(false);
1732
+ setSelectedChoiceId(null);
1733
+ }, [currentCard, revealAnswer, revealed]);
1734
+ const previousCard = useCallback3(() => {
1735
+ if (finished) {
1736
+ setCardIndex(Math.max(0, sessionCards.length - 1));
1737
+ setRevealed(true);
1738
+ return;
1739
+ }
1740
+ if (revealed) {
1741
+ setRevealed(false);
1742
+ setSelectedChoiceId(null);
1743
+ return;
1744
+ }
1745
+ if (cardIndex > 0) {
1746
+ setCardIndex((index) => index - 1);
1747
+ setRevealed(true);
1748
+ setSelectedChoiceId(null);
1749
+ }
1750
+ }, [cardIndex, finished, revealed, sessionCards.length]);
1751
+ const choose = useCallback3(
1752
+ (choice) => {
1753
+ if (!currentCard || revealed) return;
1754
+ setSelectedChoiceId(choice.id);
1755
+ setRevealed(true);
1756
+ setRatings((current) => ({
1757
+ ...current,
1758
+ [currentCard.id]: choice.correct ? "got-it" : "again"
1759
+ }));
1760
+ },
1761
+ [currentCard, revealed]
1762
+ );
1763
+ const rate = useCallback3(
1764
+ (rating) => {
1765
+ if (!currentCard) return;
1766
+ setRatings((current) => ({ ...current, [currentCard.id]: rating }));
1767
+ setCardIndex((index) => index + 1);
1768
+ setRevealed(false);
1769
+ setSelectedChoiceId(null);
1770
+ },
1771
+ [currentCard]
1772
+ );
1773
+ const handleShortcut = useCallback3(
1774
+ (event) => {
1775
+ if (event.defaultPrevented || isInteractiveTarget(event.target)) return;
1776
+ if (/^[1-9]$/.test(event.key) && currentCard?.kind === "multiple-choice" && !revealed) {
1777
+ const choice = currentCard.choices?.[Number(event.key) - 1];
1778
+ if (choice) {
1779
+ event.preventDefault();
1780
+ choose(choice);
1781
+ }
1782
+ return;
1783
+ }
1784
+ if (event.key === "ArrowLeft") {
1785
+ event.preventDefault();
1786
+ previousCard();
1787
+ } else if (event.key === "ArrowRight" || event.key === " " || event.key === "Enter") {
1788
+ event.preventDefault();
1789
+ nextCard();
1790
+ }
1791
+ },
1792
+ [choose, currentCard, nextCard, previousCard, revealed]
1793
+ );
1794
+ useEffect5(() => {
1795
+ if (!globalKeyboardShortcuts) return;
1796
+ const listener = (event) => handleShortcut(event);
1797
+ document.addEventListener("keydown", listener);
1798
+ return () => document.removeEventListener("keydown", listener);
1799
+ }, [globalKeyboardShortcuts, handleShortcut]);
1800
+ const faceProps = {
1801
+ theme: activeTheme,
1802
+ basePath,
1803
+ showCodeCopyButton,
1804
+ onCopyCode,
1805
+ fenceRenderers,
1806
+ viewport,
1807
+ customTemplates: doc.customTemplates
1808
+ };
1809
+ const titleFont = resolveFontFamily2(activeTheme.typography.titleFont, "Georgia, serif");
1810
+ const bodyFont = resolveFontFamily2(activeTheme.typography.bodyFont, "system-ui, sans-serif");
1811
+ const rootStyle = {
1812
+ "--squisq-flashcards-bg": activeTheme.colors.background,
1813
+ "--squisq-flashcards-surface": activeTheme.colors.backgroundLight,
1814
+ "--squisq-flashcards-text": activeTheme.colors.text,
1815
+ "--squisq-flashcards-muted": activeTheme.colors.textMuted,
1816
+ "--squisq-flashcards-primary": activeTheme.colors.primary,
1817
+ "--squisq-flashcards-secondary": activeTheme.colors.secondary,
1818
+ "--squisq-flashcards-highlight": activeTheme.colors.highlight,
1819
+ "--squisq-flashcards-warning": activeTheme.colors.warning,
1820
+ "--squisq-flashcards-radius": `${activeTheme.style.borderRadius ?? 18}px`,
1821
+ "--squisq-flashcards-title-font": titleFont,
1822
+ "--squisq-flashcards-body-font": bodyFont
1823
+ };
1824
+ const missedIds = deck.cards.filter((card) => ratings[card.id] === "again").map((card) => card.id);
1825
+ const gotItCount = deck.cards.filter((card) => ratings[card.id] === "got-it").length;
1826
+ if (deck.cards.length === 0) {
1827
+ return /* @__PURE__ */ jsx8(
1828
+ "div",
1829
+ {
1830
+ className: `squisq-flashcards squisq-flashcards--empty${className ? ` ${className}` : ""}`,
1831
+ style: rootStyle,
1832
+ role: "region",
1833
+ "aria-label": "Flashcards",
1834
+ children: /* @__PURE__ */ jsxs5("div", { className: "squisq-flashcards__empty-card", children: [
1835
+ /* @__PURE__ */ jsx8("h2", { children: "No complete flashcards yet" }),
1836
+ /* @__PURE__ */ jsx8("p", { children: "Add an answer beneath a heading, or nest an answer block under a question." }),
1837
+ deck.diagnostics.length > 0 && /* @__PURE__ */ jsx8("ul", { children: deck.diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsx8("li", { children: diagnostic.message }, `${diagnostic.blockId}-${diagnostic.code}-${index}`)) })
1838
+ ] })
1839
+ }
1840
+ );
1841
+ }
1842
+ if (finished) {
1843
+ return /* @__PURE__ */ jsx8(
1844
+ "div",
1845
+ {
1846
+ className: `squisq-flashcards${className ? ` ${className}` : ""}`,
1847
+ style: rootStyle,
1848
+ role: "region",
1849
+ "aria-label": "Flashcard session summary",
1850
+ tabIndex: 0,
1851
+ onKeyDown: globalKeyboardShortcuts ? void 0 : handleShortcut,
1852
+ children: /* @__PURE__ */ jsxs5("div", { className: "squisq-flashcards__summary", children: [
1853
+ /* @__PURE__ */ jsx8("span", { className: "squisq-flashcards__eyebrow", children: "Session complete" }),
1854
+ /* @__PURE__ */ jsxs5("h2", { children: [
1855
+ gotItCount,
1856
+ " remembered"
1857
+ ] }),
1858
+ /* @__PURE__ */ jsx8("p", { children: missedIds.length === 0 ? `You completed all ${deck.cards.length} cards.` : `${missedIds.length} ${missedIds.length === 1 ? "card needs" : "cards need"} another look.` }),
1859
+ /* @__PURE__ */ jsxs5("div", { className: "squisq-flashcards__summary-actions", children: [
1860
+ missedIds.length > 0 && /* @__PURE__ */ jsx8(
1861
+ "button",
1862
+ {
1863
+ type: "button",
1864
+ className: "squisq-flashcards__button squisq-flashcards__button--primary",
1865
+ onClick: () => {
1866
+ setRetryIds(missedIds);
1867
+ resetPosition();
1868
+ },
1869
+ children: "Retry missed"
1870
+ }
1871
+ ),
1872
+ /* @__PURE__ */ jsx8("button", { type: "button", className: "squisq-flashcards__button", onClick: restart, children: "Study again" }),
1873
+ /* @__PURE__ */ jsx8("button", { type: "button", className: "squisq-flashcards__button", onClick: previousCard, children: "Previous card" })
1874
+ ] })
1875
+ ] })
1876
+ }
1877
+ );
1878
+ }
1879
+ const choices = currentCard?.choices ? shuffled(currentCard.choices, `${currentCard.id}:${sessionSeed}`) : [];
1880
+ const selectedChoice = choices.find((choice) => choice.id === selectedChoiceId);
1881
+ return /* @__PURE__ */ jsxs5(
1882
+ "div",
1883
+ {
1884
+ className: `squisq-flashcards${className ? ` ${className}` : ""}`,
1885
+ style: rootStyle,
1886
+ role: "region",
1887
+ "aria-label": "Flashcards",
1888
+ tabIndex: 0,
1889
+ onKeyDown: globalKeyboardShortcuts ? void 0 : handleShortcut,
1890
+ children: [
1891
+ /* @__PURE__ */ jsxs5("header", { className: "squisq-flashcards__header", children: [
1892
+ /* @__PURE__ */ jsxs5("div", { className: "squisq-flashcards__deck-copy", children: [
1893
+ /* @__PURE__ */ jsx8("span", { className: "squisq-flashcards__eyebrow", children: "Flashcards" }),
1894
+ deck.title && /* @__PURE__ */ jsx8("h1", { children: deck.title })
1895
+ ] }),
1896
+ /* @__PURE__ */ jsxs5("div", { className: "squisq-flashcards__tools", children: [
1897
+ /* @__PURE__ */ jsx8(
1898
+ "button",
1899
+ {
1900
+ type: "button",
1901
+ className: "squisq-flashcards__tool",
1902
+ "aria-pressed": shuffleEnabled,
1903
+ onClick: () => {
1904
+ setShuffleEnabled((enabled) => !enabled);
1905
+ restart();
1906
+ },
1907
+ children: "Shuffle"
1908
+ }
1909
+ ),
1910
+ /* @__PURE__ */ jsx8("button", { type: "button", className: "squisq-flashcards__tool", onClick: restart, children: "Restart" })
1911
+ ] })
1912
+ ] }),
1913
+ /* @__PURE__ */ jsxs5("div", { className: "squisq-flashcards__progress-row", children: [
1914
+ /* @__PURE__ */ jsxs5("span", { children: [
1915
+ "Card ",
1916
+ cardIndex + 1,
1917
+ " of ",
1918
+ sessionCards.length
1919
+ ] }),
1920
+ /* @__PURE__ */ jsx8("span", { children: revealed ? "Answer revealed" : "Question" })
1921
+ ] }),
1922
+ /* @__PURE__ */ jsx8("div", { className: "squisq-flashcards__progress", "aria-hidden": "true", children: /* @__PURE__ */ jsx8(
1923
+ "span",
1924
+ {
1925
+ style: { width: `${(cardIndex + (revealed ? 1 : 0)) / sessionCards.length * 100}%` }
1926
+ }
1927
+ ) }),
1928
+ /* @__PURE__ */ jsxs5(
1929
+ "main",
1930
+ {
1931
+ ref: cardViewportRef,
1932
+ className: "squisq-flashcards__card",
1933
+ "data-card-kind": currentCard.kind,
1934
+ "aria-label": "Scrollable flashcard content",
1935
+ tabIndex: 0,
1936
+ children: [
1937
+ currentCard.label && /* @__PURE__ */ jsx8("div", { className: "squisq-flashcards__label", children: currentCard.label }),
1938
+ /* @__PURE__ */ jsx8("section", { className: "squisq-flashcards__front", "aria-label": "Question", children: /* @__PURE__ */ jsx8(FaceContent, { ...faceProps, face: currentCard.front }) }),
1939
+ currentCard.kind === "multiple-choice" && /* @__PURE__ */ jsx8("div", { className: "squisq-flashcards__choices", role: "group", "aria-label": "Answer choices", children: choices.map((choice, index) => {
1940
+ const selected = selectedChoiceId === choice.id;
1941
+ const status = revealed ? choice.correct ? "correct" : selected ? "incorrect" : "idle" : "idle";
1942
+ return /* @__PURE__ */ jsxs5(
1943
+ "button",
1944
+ {
1945
+ type: "button",
1946
+ className: "squisq-flashcards__choice",
1947
+ "data-choice-status": status,
1948
+ "aria-pressed": selected,
1949
+ disabled: revealed,
1950
+ onClick: () => choose(choice),
1951
+ children: [
1952
+ /* @__PURE__ */ jsx8("span", { className: "squisq-flashcards__choice-key", "aria-hidden": "true", children: index + 1 }),
1953
+ /* @__PURE__ */ jsx8(ChoiceContent, { ...faceProps, choice })
1954
+ ]
1955
+ },
1956
+ choice.id
1957
+ );
1958
+ }) }),
1959
+ revealed && currentCard.kind === "basic" && /* @__PURE__ */ jsxs5("section", { className: "squisq-flashcards__answer", "aria-label": "Answer", children: [
1960
+ /* @__PURE__ */ jsx8("span", { className: "squisq-flashcards__answer-label", children: "Answer" }),
1961
+ /* @__PURE__ */ jsx8(FaceContent, { ...faceProps, face: currentCard.back })
1962
+ ] }),
1963
+ revealed && currentCard.kind === "multiple-choice" && /* @__PURE__ */ jsxs5(
1964
+ "div",
1965
+ {
1966
+ className: "squisq-flashcards__feedback",
1967
+ "data-correct": selectedChoice?.correct === true,
1968
+ "aria-live": "polite",
1969
+ children: [
1970
+ /* @__PURE__ */ jsx8("strong", { children: selectedChoice === void 0 ? "Answer revealed" : selectedChoice.correct ? "Correct" : "Not quite" }),
1971
+ !selectedChoice?.correct && /* @__PURE__ */ jsxs5("div", { className: "squisq-flashcards__correct-answer", children: [
1972
+ /* @__PURE__ */ jsx8("span", { children: "The correct answer is" }),
1973
+ /* @__PURE__ */ jsx8(FaceContent, { ...faceProps, face: currentCard.back })
1974
+ ] })
1975
+ ]
1976
+ }
1977
+ ),
1978
+ revealed && currentCard.explanation && /* @__PURE__ */ jsxs5("section", { className: "squisq-flashcards__explanation", "aria-label": "Explanation", children: [
1979
+ /* @__PURE__ */ jsx8("span", { className: "squisq-flashcards__answer-label", children: "Explanation" }),
1980
+ /* @__PURE__ */ jsx8(FaceContent, { ...faceProps, face: currentCard.explanation })
1981
+ ] })
1982
+ ]
1983
+ }
1984
+ ),
1985
+ /* @__PURE__ */ jsxs5("footer", { className: "squisq-flashcards__footer", children: [
1986
+ /* @__PURE__ */ jsx8(
1987
+ "button",
1988
+ {
1989
+ type: "button",
1990
+ className: "squisq-flashcards__button",
1991
+ disabled: cardIndex === 0 && !revealed,
1992
+ onClick: previousCard,
1993
+ children: "Previous"
1994
+ }
1995
+ ),
1996
+ /* @__PURE__ */ jsx8("div", { className: "squisq-flashcards__primary-actions", children: revealed && currentCard.kind === "basic" ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
1997
+ /* @__PURE__ */ jsx8(
1998
+ "button",
1999
+ {
2000
+ type: "button",
2001
+ className: "squisq-flashcards__button",
2002
+ onClick: () => rate("again"),
2003
+ children: "Again"
2004
+ }
2005
+ ),
2006
+ /* @__PURE__ */ jsx8(
2007
+ "button",
2008
+ {
2009
+ type: "button",
2010
+ className: "squisq-flashcards__button squisq-flashcards__button--primary",
2011
+ onClick: () => rate("got-it"),
2012
+ children: "Got it"
2013
+ }
2014
+ )
2015
+ ] }) : /* @__PURE__ */ jsx8(
2016
+ "button",
2017
+ {
2018
+ type: "button",
2019
+ className: "squisq-flashcards__button squisq-flashcards__button--primary",
2020
+ onClick: nextCard,
2021
+ children: revealed ? cardIndex === sessionCards.length - 1 ? "Finish" : "Next card" : "Reveal answer"
2022
+ }
2023
+ ) })
2024
+ ] }),
2025
+ /* @__PURE__ */ jsxs5("p", { className: "squisq-flashcards__shortcut-hint", children: [
2026
+ "Use \u2190 and \u2192 to navigate, Space to reveal",
2027
+ currentCard.kind === "multiple-choice" ? ", or 1\u20139 to answer" : "",
2028
+ "."
2029
+ ] })
2030
+ ]
2031
+ }
2032
+ );
2033
+ }
2034
+
1517
2035
  // src/docPlayer/playerAppearance.ts
1518
2036
  import {
1519
2037
  resolveCoverSlideSettings,
1520
- resolveThemeForDoc as resolveThemeForDoc2
2038
+ resolveThemeForDoc as resolveThemeForDoc3
1521
2039
  } from "@bendyline/squisq/doc";
1522
2040
  function readFrontmatterSetting(frontmatter, canonical, legacy) {
1523
2041
  if (!frontmatter) return void 0;
@@ -1575,7 +2093,7 @@ function resolveDocPlayerAppearance(doc, overrides = {}) {
1575
2093
  ...overrides.coverSlidePlayback !== void 0 ? { playback: overrides.coverSlidePlayback } : {}
1576
2094
  });
1577
2095
  return {
1578
- theme: overrides.theme ?? resolveThemeForDoc2(doc),
2096
+ theme: overrides.theme ?? resolveThemeForDoc3(doc),
1579
2097
  videoPresentation: overrides.videoPresentation ?? resolveVideoPresentation(
1580
2098
  readFrontmatterSetting(frontmatter, "squisq-video-presentation", "video-presentation")
1581
2099
  ) ?? "background",
@@ -1593,14 +2111,14 @@ function resolveDocPlayerAppearance(doc, overrides = {}) {
1593
2111
 
1594
2112
  // src/DocPlayer.tsx
1595
2113
  import {
1596
- Fragment as Fragment3,
2114
+ Fragment as Fragment4,
1597
2115
  useId as useId2,
1598
- useRef as useRef6,
1599
- useState as useState5,
1600
- useEffect as useEffect6,
2116
+ useRef as useRef7,
2117
+ useState as useState6,
2118
+ useEffect as useEffect7,
1601
2119
  useLayoutEffect as useLayoutEffect2,
1602
- useCallback as useCallback4,
1603
- useMemo as useMemo4
2120
+ useCallback as useCallback5,
2121
+ useMemo as useMemo5
1604
2122
  } from "react";
1605
2123
  import { flushSync } from "react-dom";
1606
2124
  import {
@@ -1612,7 +2130,7 @@ import {
1612
2130
  import { applySurface, pipStyleVars } from "@bendyline/squisq/schemas";
1613
2131
 
1614
2132
  // src/hooks/useSlideSwipe.ts
1615
- import { useCallback as useCallback3, useEffect as useEffect5, useRef as useRef5, useState as useState4 } from "react";
2133
+ import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef6, useState as useState5 } from "react";
1616
2134
  var DISTANCE_RATIO = 0.3;
1617
2135
  var FLICK_VELOCITY = 0.5;
1618
2136
  var MIN_FLICK_DISTANCE = 12;
@@ -1636,16 +2154,16 @@ function decideSwipe({
1636
2154
  }
1637
2155
  function useSlideSwipe(opts) {
1638
2156
  const settleMs = opts.settleMs ?? DEFAULT_SETTLE_MS;
1639
- const [offsetPx, setOffsetPx] = useState4(0);
1640
- const [phase, setPhase] = useState4("idle");
1641
- const optsRef = useRef5(opts);
2157
+ const [offsetPx, setOffsetPx] = useState5(0);
2158
+ const [phase, setPhase] = useState5("idle");
2159
+ const optsRef = useRef6(opts);
1642
2160
  optsRef.current = opts;
1643
- const dragRef = useRef5(null);
1644
- const phaseRef = useRef5("idle");
2161
+ const dragRef = useRef6(null);
2162
+ const phaseRef = useRef6("idle");
1645
2163
  phaseRef.current = phase;
1646
- const settleTimer = useRef5(null);
1647
- const settleRaf = useRef5(null);
1648
- const clearPending = useCallback3(() => {
2164
+ const settleTimer = useRef6(null);
2165
+ const settleRaf = useRef6(null);
2166
+ const clearPending = useCallback4(() => {
1649
2167
  if (settleTimer.current != null) {
1650
2168
  clearTimeout(settleTimer.current);
1651
2169
  settleTimer.current = null;
@@ -1655,7 +2173,7 @@ function useSlideSwipe(opts) {
1655
2173
  settleRaf.current = null;
1656
2174
  }
1657
2175
  }, []);
1658
- const onPointerDown = useCallback3((e) => {
2176
+ const onPointerDown = useCallback4((e) => {
1659
2177
  const o = optsRef.current;
1660
2178
  if (!o.enabled) return;
1661
2179
  if (phaseRef.current === "settling") return;
@@ -1675,7 +2193,7 @@ function useSlideSwipe(opts) {
1675
2193
  setPhase("dragging");
1676
2194
  setOffsetPx(0);
1677
2195
  }, []);
1678
- useEffect5(() => {
2196
+ useEffect6(() => {
1679
2197
  function currentWidth() {
1680
2198
  return optsRef.current.containerRef.current?.getBoundingClientRect().width ?? 0;
1681
2199
  }
@@ -1745,7 +2263,7 @@ function useSlideSwipe(opts) {
1745
2263
  window.removeEventListener("pointercancel", onCancel);
1746
2264
  };
1747
2265
  }, [settleMs]);
1748
- useEffect5(() => {
2266
+ useEffect6(() => {
1749
2267
  if (!opts.enabled) {
1750
2268
  dragRef.current = null;
1751
2269
  clearPending();
@@ -1753,7 +2271,7 @@ function useSlideSwipe(opts) {
1753
2271
  setOffsetPx(0);
1754
2272
  }
1755
2273
  }, [opts.enabled, clearPending]);
1756
- useEffect5(() => clearPending, [clearPending]);
2274
+ useEffect6(() => clearPending, [clearPending]);
1757
2275
  return { offsetPx, phase, onPointerDown };
1758
2276
  }
1759
2277
 
@@ -1762,7 +2280,7 @@ import {
1762
2280
  expandCoverBlock,
1763
2281
  createTemplateContext,
1764
2282
  markdownToDoc,
1765
- VIEWPORT_PRESETS as VIEWPORT_PRESETS2
2283
+ VIEWPORT_PRESETS as VIEWPORT_PRESETS3
1766
2284
  } from "@bendyline/squisq/doc";
1767
2285
  import { parseMarkdown } from "@bendyline/squisq/markdown";
1768
2286
 
@@ -2052,7 +2570,7 @@ function seekVideoToFrame(video, targetTime, timeoutMs = DEFAULT_VIDEO_FRAME_TIM
2052
2570
  }
2053
2571
 
2054
2572
  // src/DocPlayer.tsx
2055
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
2573
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2056
2574
  function isDevEnvironment() {
2057
2575
  try {
2058
2576
  return typeof process !== "undefined" && process.env.NODE_ENV !== "production";
@@ -2084,15 +2602,30 @@ function waitForVisualUpdate() {
2084
2602
  }
2085
2603
  function DocPlayer(props) {
2086
2604
  const { doc, markdown } = props;
2087
- const markdownDoc = useMemo4(
2605
+ const markdownDoc = useMemo5(
2088
2606
  () => !doc && markdown !== void 0 ? markdownToDoc(parseMarkdown(markdown)) : void 0,
2089
2607
  [doc, markdown]
2090
2608
  );
2091
2609
  const resolvedDoc = doc ?? markdownDoc;
2092
2610
  if (!resolvedDoc) {
2093
- return /* @__PURE__ */ jsx8("div", { className: "doc-player doc-player--empty" });
2611
+ return /* @__PURE__ */ jsx9("div", { className: "doc-player doc-player--empty" });
2094
2612
  }
2095
- return /* @__PURE__ */ jsx8(DocPlayerContent, { ...props, doc: resolvedDoc });
2613
+ if (props.displayMode === "flashcards") {
2614
+ return /* @__PURE__ */ jsx9("div", { className: "doc-player doc-player--flashcards", children: /* @__PURE__ */ jsx9(
2615
+ FlashcardView,
2616
+ {
2617
+ doc: resolvedDoc,
2618
+ basePath: props.basePath,
2619
+ theme: props.theme,
2620
+ globalKeyboardShortcuts: props.globalKeyboardShortcuts,
2621
+ showCodeCopyButton: props.showCodeCopyButton,
2622
+ onCopyCode: props.onCopyCode,
2623
+ fenceRenderers: props.fenceRenderers,
2624
+ viewport: props.forceViewport
2625
+ }
2626
+ ) });
2627
+ }
2628
+ return /* @__PURE__ */ jsx9(DocPlayerContent, { ...props, doc: resolvedDoc });
2096
2629
  }
2097
2630
  function DocPlayerContent({
2098
2631
  doc,
@@ -2143,20 +2676,20 @@ function DocPlayerContent({
2143
2676
  const isSlideshowMode = displayMode === "slideshow";
2144
2677
  const isLinearMode = displayMode === "linear";
2145
2678
  const isDashboardMode = displayMode === "dashboard";
2146
- const audioRef = useRef6(null);
2147
- const containerRef = useRef6(null);
2679
+ const audioRef = useRef7(null);
2680
+ const containerRef = useRef7(null);
2148
2681
  const playerId = `squisq-player-${useId2().replace(/:/g, "")}`;
2149
- const [tapFeedback, setTapFeedback] = useState5(null);
2150
- const tapFeedbackTimer = useRef6();
2682
+ const [tapFeedback, setTapFeedback] = useState6(null);
2683
+ const tapFeedbackTimer = useRef7();
2151
2684
  const { viewport } = useViewportOrientation();
2152
- const activeViewport = forceViewport || (renderMode ? VIEWPORT_PRESETS2.landscape : viewport);
2685
+ const activeViewport = forceViewport || (renderMode ? VIEWPORT_PRESETS3.landscape : viewport);
2153
2686
  const activeOrientation = activeViewport.height > activeViewport.width ? "portrait" : "landscape";
2154
- const isDebugMode = useMemo4(() => {
2687
+ const isDebugMode = useMemo5(() => {
2155
2688
  if (typeof window === "undefined") return false;
2156
2689
  const params = new URLSearchParams(window.location.search);
2157
2690
  return params.get("debug") === "true";
2158
2691
  }, []);
2159
- const syntheticDuration = useMemo4(
2692
+ const syntheticDuration = useMemo5(
2160
2693
  () => audioMode === "synthetic" ? getDocPlaybackDuration(doc) : 0,
2161
2694
  [audioMode, doc]
2162
2695
  );
@@ -2169,7 +2702,7 @@ function DocPlayerContent({
2169
2702
  syntheticDuration
2170
2703
  );
2171
2704
  const audio = externalAudioController || internalAudio;
2172
- useEffect6(() => {
2705
+ useEffect7(() => {
2173
2706
  if (warnedMissingStyles || !isDevEnvironment()) return;
2174
2707
  const el = containerRef.current;
2175
2708
  if (!el || typeof getComputedStyle !== "function") return;
@@ -2197,16 +2730,16 @@ function DocPlayerContent({
2197
2730
  skipToSegment: _skipToSegment,
2198
2731
  restart
2199
2732
  } = audio;
2200
- const [renderClock, setRenderClock] = useState5(null);
2733
+ const [renderClock, setRenderClock] = useState6(null);
2201
2734
  const renderTimeOverride = renderClock?.doc === doc ? renderClock.time : null;
2202
2735
  const currentTime = renderMode ? renderTimeOverride ?? audioCurrentTime : audioCurrentTime;
2203
- const rawSchedule = useMemo4(() => resolveMediaSchedule(doc), [doc]);
2736
+ const rawSchedule = useMemo5(() => resolveMediaSchedule(doc), [doc]);
2204
2737
  const clipDurations = useMediaClipDurations(rawSchedule, basePath);
2205
- const mediaSchedule = useMemo4(
2738
+ const mediaSchedule = useMemo5(
2206
2739
  () => resolveMediaSchedule(doc, { intrinsicDuration: (clip) => clipDurations.get(clip.src) }),
2207
2740
  [doc, clipDurations]
2208
2741
  );
2209
- const startActiveTimelineMedia = useCallback4(() => {
2742
+ const startActiveTimelineMedia = useCallback5(() => {
2210
2743
  if (renderMode) return;
2211
2744
  const root = containerRef.current;
2212
2745
  if (!root) return;
@@ -2219,21 +2752,21 @@ function DocPlayerContent({
2219
2752
  });
2220
2753
  });
2221
2754
  }, [renderMode]);
2222
- const playWithTimelineMedia = useCallback4(() => {
2755
+ const playWithTimelineMedia = useCallback5(() => {
2223
2756
  startActiveTimelineMedia();
2224
2757
  return play();
2225
2758
  }, [play, startActiveTimelineMedia]);
2226
- const toggleWithTimelineMedia = useCallback4(() => {
2759
+ const toggleWithTimelineMedia = useCallback5(() => {
2227
2760
  if (!isPlaying) startActiveTimelineMedia();
2228
2761
  return toggle();
2229
2762
  }, [isPlaying, startActiveTimelineMedia, toggle]);
2230
- const currentTimeRef = useRef6(currentTime);
2763
+ const currentTimeRef = useRef7(currentTime);
2231
2764
  currentTimeRef.current = currentTime;
2232
- const committedRenderTimeRef = useRef6(currentTime);
2233
- const renderCommitWaitersRef = useRef6([]);
2234
- const totalDurationRef = useRef6(totalDuration);
2765
+ const committedRenderTimeRef = useRef7(currentTime);
2766
+ const renderCommitWaitersRef = useRef7([]);
2767
+ const totalDurationRef = useRef7(totalDuration);
2235
2768
  totalDurationRef.current = totalDuration;
2236
- const expandedBlocksLenRef = useRef6(0);
2769
+ const expandedBlocksLenRef = useRef7(0);
2237
2770
  useLayoutEffect2(() => {
2238
2771
  committedRenderTimeRef.current = currentTime;
2239
2772
  const pending = renderCommitWaitersRef.current;
@@ -2243,14 +2776,14 @@ function DocPlayerContent({
2243
2776
  return false;
2244
2777
  });
2245
2778
  }, [currentTime]);
2246
- useEffect6(
2779
+ useEffect7(
2247
2780
  () => () => {
2248
2781
  const error = new Error("DocPlayer unmounted before the requested frame committed.");
2249
2782
  renderCommitWaitersRef.current.splice(0).forEach((waiter) => waiter.reject(error));
2250
2783
  },
2251
2784
  []
2252
2785
  );
2253
- const waitForRenderCommit = useCallback4((time) => {
2786
+ const waitForRenderCommit = useCallback5((time) => {
2254
2787
  if (Math.abs(committedRenderTimeRef.current - time) <= RENDER_TIME_EPSILON_SECONDS) {
2255
2788
  return Promise.resolve();
2256
2789
  }
@@ -2258,7 +2791,7 @@ function DocPlayerContent({
2258
2791
  renderCommitWaitersRef.current.push({ time, resolve, reject });
2259
2792
  });
2260
2793
  }, []);
2261
- const handleContainerClick = useCallback4(
2794
+ const handleContainerClick = useCallback5(
2262
2795
  (e) => {
2263
2796
  if (renderMode || isLinearMode) return;
2264
2797
  const target = e.target;
@@ -2283,7 +2816,7 @@ function DocPlayerContent({
2283
2816
  );
2284
2817
  const autoSurface = useAutoSurface(surface === "auto");
2285
2818
  const resolvedSurface = surface === "auto" ? autoSurface : surface;
2286
- const appearance = useMemo4(
2819
+ const appearance = useMemo5(
2287
2820
  () => resolveDocPlayerAppearance(doc, {
2288
2821
  theme,
2289
2822
  videoPresentation,
@@ -2308,13 +2841,13 @@ function DocPlayerContent({
2308
2841
  coverSlidePlayback
2309
2842
  ]
2310
2843
  );
2311
- const effectiveTheme = useMemo4(() => {
2844
+ const effectiveTheme = useMemo5(() => {
2312
2845
  const base = appearance.theme;
2313
2846
  return resolvedSurface ? applySurface(base, resolvedSurface) : base;
2314
2847
  }, [appearance.theme, resolvedSurface]);
2315
- const resolvedPipStyle = useMemo4(() => pipStyleVars(effectiveTheme), [effectiveTheme]);
2848
+ const resolvedPipStyle = useMemo5(() => pipStyleVars(effectiveTheme), [effectiveTheme]);
2316
2849
  const pipVars = resolvedPipStyle;
2317
- const pipFrameStyle = useMemo4(
2850
+ const pipFrameStyle = useMemo5(
2318
2851
  () => ({
2319
2852
  border: resolvedPipStyle["--squisq-pip-border"],
2320
2853
  borderRadius: resolvedPipStyle["--squisq-pip-radius"],
@@ -2344,7 +2877,7 @@ function DocPlayerContent({
2344
2877
  // remove authored slides from the default loss-averse projection.
2345
2878
  useAudioSegmentTiming: audioMode !== "synthetic"
2346
2879
  });
2347
- const coverBlock = useMemo4(() => {
2880
+ const coverBlock = useMemo5(() => {
2348
2881
  const startBlockConfig = doc.startBlock;
2349
2882
  if (!appearance.showCoverSlide) return null;
2350
2883
  if (!startBlockConfig) return null;
@@ -2367,13 +2900,13 @@ function DocPlayerContent({
2367
2900
  appearance.coverSlideTemplate
2368
2901
  ]);
2369
2902
  const hasManagedCover = !!coverBlock;
2370
- const [slideshowCoverVisible, setSlideshowCoverVisible] = useState5(false);
2371
- const [isSlideshowPickerOpen, setIsSlideshowPickerOpen] = useState5(false);
2372
- const slideshowCoverInitKeyRef = useRef6("");
2373
- useEffect6(() => {
2903
+ const [slideshowCoverVisible, setSlideshowCoverVisible] = useState6(false);
2904
+ const [isSlideshowPickerOpen, setIsSlideshowPickerOpen] = useState6(false);
2905
+ const slideshowCoverInitKeyRef = useRef7("");
2906
+ useEffect7(() => {
2374
2907
  slideshowCoverInitKeyRef.current = "";
2375
2908
  }, [doc]);
2376
- useEffect6(() => {
2909
+ useEffect7(() => {
2377
2910
  const initKey = `${isSlideshowMode}:${hasManagedCover}:${renderMode}`;
2378
2911
  if (slideshowCoverInitKeyRef.current === initKey) return;
2379
2912
  slideshowCoverInitKeyRef.current = initKey;
@@ -2384,12 +2917,12 @@ function DocPlayerContent({
2384
2917
  setSlideshowCoverVisible(false);
2385
2918
  }
2386
2919
  }, [isSlideshowMode, hasManagedCover, renderMode, pause]);
2387
- const [coverForced, setCoverForced] = useState5(false);
2388
- const [coverGraceActive, setCoverGraceActive] = useState5(false);
2389
- const coverGraceTimer = useRef6();
2390
- const coverWasShowing = useRef6(false);
2391
- const hasPlayedOnce = useRef6(false);
2392
- useEffect6(() => {
2920
+ const [coverForced, setCoverForced] = useState6(false);
2921
+ const [coverGraceActive, setCoverGraceActive] = useState6(false);
2922
+ const coverGraceTimer = useRef7();
2923
+ const coverWasShowing = useRef7(false);
2924
+ const hasPlayedOnce = useRef7(false);
2925
+ useEffect7(() => {
2393
2926
  hasPlayedOnce.current = false;
2394
2927
  coverWasShowing.current = false;
2395
2928
  clearTimeout(coverGraceTimer.current);
@@ -2398,7 +2931,7 @@ function DocPlayerContent({
2398
2931
  }, [doc]);
2399
2932
  const atRest = !!(coverBlock && !isSlideshowMode && !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
2400
2933
  if (atRest) coverWasShowing.current = true;
2401
- useEffect6(() => {
2934
+ useEffect7(() => {
2402
2935
  if (isPlaying && coverWasShowing.current && coverBlock && !renderMode && !isSlideshowMode) {
2403
2936
  coverWasShowing.current = false;
2404
2937
  hasPlayedOnce.current = true;
@@ -2409,8 +2942,8 @@ function DocPlayerContent({
2409
2942
  );
2410
2943
  }
2411
2944
  }, [isPlaying, coverBlock, renderMode, isSlideshowMode, appearance.coverSlideDuration]);
2412
- useEffect6(() => () => clearTimeout(coverGraceTimer.current), []);
2413
- useEffect6(() => () => clearTimeout(tapFeedbackTimer.current), []);
2945
+ useEffect7(() => () => clearTimeout(coverGraceTimer.current), []);
2946
+ useEffect7(() => () => clearTimeout(tapFeedbackTimer.current), []);
2414
2947
  const showVideoCoverBlock = !isSlideshowMode && !isLinearMode && !!coverBlock && (coverForced || coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
2415
2948
  const effectiveSlideshowCoverVisible = coverVisible ?? slideshowCoverVisible;
2416
2949
  const showSlideshowCover = !!(isSlideshowMode && !isLinearMode && !renderMode && coverBlock && effectiveSlideshowCoverVisible);
@@ -2418,20 +2951,20 @@ function DocPlayerContent({
2418
2951
  const slideshowHasCover = !!(isSlideshowMode && !renderMode && coverBlock);
2419
2952
  const slideshowSlideIndex = slideshowHasCover ? effectiveSlideshowCoverVisible ? 0 : currentBlockIndex + 1 : currentBlockIndex;
2420
2953
  const slideshowTotalSlides = slideshowHasCover ? expandedBlocks.length + 1 : expandedBlocks.length;
2421
- const hasAutoPlayed = useRef6(false);
2422
- useEffect6(() => {
2954
+ const hasAutoPlayed = useRef7(false);
2955
+ useEffect7(() => {
2423
2956
  hasAutoPlayed.current = false;
2424
2957
  }, [doc]);
2425
- useEffect6(() => {
2958
+ useEffect7(() => {
2426
2959
  if (isAudioReady && autoPlay && !hasAutoPlayed.current) {
2427
2960
  hasAutoPlayed.current = true;
2428
2961
  playWithTimelineMedia();
2429
2962
  }
2430
2963
  }, [isAudioReady, autoPlay, playWithTimelineMedia]);
2431
- useEffect6(() => {
2964
+ useEffect7(() => {
2432
2965
  onTimeUpdate?.(currentTime);
2433
2966
  }, [currentTime, onTimeUpdate]);
2434
- useEffect6(() => {
2967
+ useEffect7(() => {
2435
2968
  if (isEnded) {
2436
2969
  onEnded?.();
2437
2970
  if (loop && !isSlideshowMode && !isLinearMode && !isDashboardMode) {
@@ -2439,8 +2972,8 @@ function DocPlayerContent({
2439
2972
  }
2440
2973
  }
2441
2974
  }, [isEnded, isDashboardMode, isLinearMode, isSlideshowMode, loop, onEnded, restart]);
2442
- const liveRenderAPIRef = useRef6(null);
2443
- const stableRenderAPIRef = useRef6(null);
2975
+ const liveRenderAPIRef = useRef7(null);
2976
+ const stableRenderAPIRef = useRef7(null);
2444
2977
  if (!stableRenderAPIRef.current) {
2445
2978
  const current = () => {
2446
2979
  const api = liveRenderAPIRef.current;
@@ -2461,7 +2994,7 @@ function DocPlayerContent({
2461
2994
  };
2462
2995
  }
2463
2996
  const stableRenderAPI = stableRenderAPIRef.current;
2464
- useEffect6(() => {
2997
+ useEffect7(() => {
2465
2998
  if (isDashboardMode || !renderMode && !isDebugMode) {
2466
2999
  liveRenderAPIRef.current = null;
2467
3000
  return;
@@ -2591,7 +3124,7 @@ function DocPlayerContent({
2591
3124
  waitForRenderCommit,
2592
3125
  isDashboardMode
2593
3126
  ]);
2594
- useEffect6(() => {
3127
+ useEffect7(() => {
2595
3128
  if (isDashboardMode) return;
2596
3129
  if (!renderMode && !isDebugMode || !containerRef.current) {
2597
3130
  onRenderAPIReady?.(null);
@@ -2601,20 +3134,20 @@ function DocPlayerContent({
2601
3134
  return () => onRenderAPIReady?.(null);
2602
3135
  }, [renderMode, isDebugMode, isDashboardMode, onRenderAPIReady, stableRenderAPI]);
2603
3136
  const defaultMode = captionsEnabledProp === false ? "off" : captionStyle || "standard";
2604
- const [captionMode, setCaptionMode] = useState5(defaultMode);
2605
- useEffect6(() => {
3137
+ const [captionMode, setCaptionMode] = useState6(defaultMode);
3138
+ useEffect7(() => {
2606
3139
  setCaptionMode(defaultMode);
2607
3140
  }, [defaultMode]);
2608
3141
  const captionsEnabled = captionMode !== "off";
2609
3142
  const activeCaptionStyle = captionMode === "social" ? "social" : "standard";
2610
- const setCaptionsEnabled = useCallback4(
3143
+ const setCaptionsEnabled = useCallback5(
2611
3144
  (enabled) => {
2612
3145
  setCaptionMode(enabled ? captionStyle || "standard" : "off");
2613
3146
  onCaptionsToggle?.(enabled);
2614
3147
  },
2615
3148
  [onCaptionsToggle, captionStyle]
2616
3149
  );
2617
- const cycleCaptionMode = useCallback4(() => {
3150
+ const cycleCaptionMode = useCallback5(() => {
2618
3151
  setCaptionMode((prev) => {
2619
3152
  const next = prev === "off" ? "standard" : prev === "standard" ? "social" : "off";
2620
3153
  onCaptionsToggle?.(next !== "off");
@@ -2622,8 +3155,8 @@ function DocPlayerContent({
2622
3155
  });
2623
3156
  }, [onCaptionsToggle]);
2624
3157
  const hasCaptions = doc.captions && doc.captions.phrases.length > 0;
2625
- const segmentTitleMap = useMemo4(() => buildSegmentTitleMap(doc), [doc]);
2626
- const playbackState = useMemo4(
3158
+ const segmentTitleMap = useMemo5(() => buildSegmentTitleMap(doc), [doc]);
3159
+ const playbackState = useMemo5(
2627
3160
  () => ({
2628
3161
  isPlaying,
2629
3162
  currentTime,
@@ -2666,7 +3199,7 @@ function DocPlayerContent({
2666
3199
  expandedBlocks.length
2667
3200
  ]
2668
3201
  );
2669
- const playbackActions = useMemo4(
3202
+ const playbackActions = useMemo5(
2670
3203
  () => ({
2671
3204
  toggle: toggleWithTimelineMedia,
2672
3205
  restart,
@@ -2684,7 +3217,7 @@ function DocPlayerContent({
2684
3217
  onFullscreenToggle
2685
3218
  ]
2686
3219
  );
2687
- const slideNavActions = useMemo4(
3220
+ const slideNavActions = useMemo5(
2688
3221
  () => ({
2689
3222
  nextSlide: () => {
2690
3223
  if (slideshowHasCover && slideshowCoverVisible) {
@@ -2749,7 +3282,7 @@ function DocPlayerContent({
2749
3282
  [currentBlockIndex, expandedBlocks, seekTo, pause, slideshowHasCover, slideshowCoverVisible]
2750
3283
  );
2751
3284
  const swipeEnabled = isSlideshowMode && !isLinearMode && !renderMode && enableSwipe;
2752
- const armContextFreeSwipeEntry = useCallback4(
3285
+ const armContextFreeSwipeEntry = useCallback5(
2753
3286
  (destinationSlideIndex) => {
2754
3287
  const destinationBlockIndex = destinationSlideIndex - (slideshowHasCover ? 1 : 0);
2755
3288
  const destinationBlock = expandedBlocks[destinationBlockIndex];
@@ -2757,11 +3290,11 @@ function DocPlayerContent({
2757
3290
  },
2758
3291
  [expandedBlocks, slideshowHasCover, suppressOutgoingForNextBlock]
2759
3292
  );
2760
- const handleSwipeNext = useCallback4(() => {
3293
+ const handleSwipeNext = useCallback5(() => {
2761
3294
  armContextFreeSwipeEntry(slideshowSlideIndex + 1);
2762
3295
  slideNavActions.nextSlide();
2763
3296
  }, [armContextFreeSwipeEntry, slideshowSlideIndex, slideNavActions]);
2764
- const handleSwipePrev = useCallback4(() => {
3297
+ const handleSwipePrev = useCallback5(() => {
2765
3298
  armContextFreeSwipeEntry(slideshowSlideIndex - 1);
2766
3299
  slideNavActions.prevSlide();
2767
3300
  }, [armContextFreeSwipeEntry, slideshowSlideIndex, slideNavActions]);
@@ -2773,13 +3306,13 @@ function DocPlayerContent({
2773
3306
  onNext: handleSwipeNext,
2774
3307
  onPrev: handleSwipePrev
2775
3308
  });
2776
- useEffect6(() => {
3309
+ useEffect7(() => {
2777
3310
  onPlaybackStateChange?.(playbackState);
2778
3311
  }, [playbackState, onPlaybackStateChange]);
2779
- useEffect6(() => {
3312
+ useEffect7(() => {
2780
3313
  onControlsReady?.({ play: playWithTimelineMedia, pause, ...playbackActions });
2781
3314
  }, [playWithTimelineMedia, pause, playbackActions, onControlsReady]);
2782
- const getBlockTitle = useCallback4((block) => {
3315
+ const getBlockTitle = useCallback5((block) => {
2783
3316
  const docBlock = block;
2784
3317
  if (isTemplateBlock2(docBlock)) {
2785
3318
  const props = docBlock;
@@ -2803,7 +3336,7 @@ function DocPlayerContent({
2803
3336
  }
2804
3337
  return block.id.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
2805
3338
  }, []);
2806
- const slideshowPickerItems = useMemo4(() => {
3339
+ const slideshowPickerItems = useMemo5(() => {
2807
3340
  const blockItems = expandedBlocks.map((block, index) => ({
2808
3341
  id: block.id,
2809
3342
  label: String(index + 1),
@@ -2819,7 +3352,7 @@ function DocPlayerContent({
2819
3352
  ...blockItems
2820
3353
  ];
2821
3354
  }, [coverBlock, expandedBlocks, getBlockTitle, slideshowHasCover]);
2822
- const blockMarkers = useMemo4(() => {
3355
+ const blockMarkers = useMemo5(() => {
2823
3356
  if (!totalDuration || !expandedBlocks.length) return [];
2824
3357
  let prevSegment = -1;
2825
3358
  return expandedBlocks.map((block, index) => {
@@ -2834,13 +3367,13 @@ function DocPlayerContent({
2834
3367
  };
2835
3368
  });
2836
3369
  }, [expandedBlocks, totalDuration, getBlockTitle]);
2837
- useEffect6(() => {
3370
+ useEffect7(() => {
2838
3371
  if (blockMarkers.length > 0) {
2839
3372
  onBlockMarkers?.(blockMarkers);
2840
3373
  }
2841
3374
  }, [blockMarkers, onBlockMarkers]);
2842
3375
  expandedBlocksLenRef.current = isSlideshowMode ? slideshowTotalSlides : expandedBlocks.length;
2843
- const handleKeyboardShortcut = useCallback4(
3376
+ const handleKeyboardShortcut = useCallback5(
2844
3377
  (e, global) => {
2845
3378
  if (e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
2846
3379
  const target = e.target instanceof Element ? e.target : null;
@@ -2915,11 +3448,11 @@ function DocPlayerContent({
2915
3448
  onFullscreenToggle
2916
3449
  ]
2917
3450
  );
2918
- const handleKeyDown = useCallback4(
3451
+ const handleKeyDown = useCallback5(
2919
3452
  (e) => handleKeyboardShortcut(e, false),
2920
3453
  [handleKeyboardShortcut]
2921
3454
  );
2922
- useEffect6(() => {
3455
+ useEffect7(() => {
2923
3456
  if (!globalKeyboardShortcuts || renderMode || isLinearMode || isDashboardMode) return;
2924
3457
  const handleDocumentKeyDown = (event) => {
2925
3458
  handleKeyboardShortcut(event, true);
@@ -2928,7 +3461,7 @@ function DocPlayerContent({
2928
3461
  return () => document.removeEventListener("keydown", handleDocumentKeyDown);
2929
3462
  }, [globalKeyboardShortcuts, handleKeyboardShortcut, isDashboardMode, isLinearMode, renderMode]);
2930
3463
  if (isLinearMode) {
2931
- return /* @__PURE__ */ jsx8(
3464
+ return /* @__PURE__ */ jsx9(
2932
3465
  "div",
2933
3466
  {
2934
3467
  ref: containerRef,
@@ -2940,7 +3473,7 @@ function DocPlayerContent({
2940
3473
  height: "100%",
2941
3474
  overflow: "hidden"
2942
3475
  },
2943
- children: /* @__PURE__ */ jsx8(
3476
+ children: /* @__PURE__ */ jsx9(
2944
3477
  LinearDocView,
2945
3478
  {
2946
3479
  doc,
@@ -2958,7 +3491,7 @@ function DocPlayerContent({
2958
3491
  );
2959
3492
  }
2960
3493
  if (isDashboardMode) {
2961
- return /* @__PURE__ */ jsx8(
3494
+ return /* @__PURE__ */ jsx9(
2962
3495
  "div",
2963
3496
  {
2964
3497
  ref: containerRef,
@@ -2971,7 +3504,7 @@ function DocPlayerContent({
2971
3504
  margin: "0 auto",
2972
3505
  overflow: "hidden"
2973
3506
  },
2974
- children: /* @__PURE__ */ jsx8(
3507
+ children: /* @__PURE__ */ jsx9(
2975
3508
  DashboardView,
2976
3509
  {
2977
3510
  doc,
@@ -2991,7 +3524,7 @@ function DocPlayerContent({
2991
3524
  }
2992
3525
  );
2993
3526
  }
2994
- return /* @__PURE__ */ jsxs5(
3527
+ return /* @__PURE__ */ jsxs6(
2995
3528
  "div",
2996
3529
  {
2997
3530
  ref: containerRef,
@@ -3019,8 +3552,8 @@ function DocPlayerContent({
3019
3552
  touchAction: swipeEnabled ? "pan-y" : void 0
3020
3553
  },
3021
3554
  children: [
3022
- /* @__PURE__ */ jsx8("audio", { ref: audioRef, preload: "auto", muted }),
3023
- /* @__PURE__ */ jsx8(
3555
+ /* @__PURE__ */ jsx9("audio", { ref: audioRef, preload: "auto", muted }),
3556
+ /* @__PURE__ */ jsx9(
3024
3557
  MediaClipLayer,
3025
3558
  {
3026
3559
  schedule: mediaSchedule,
@@ -3037,8 +3570,8 @@ function DocPlayerContent({
3037
3570
  pipFrameStyle
3038
3571
  }
3039
3572
  ),
3040
- /* @__PURE__ */ jsxs5("div", { className: "doc-player__viewport", children: [
3041
- showCoverBlock && coverBlock && /* @__PURE__ */ jsx8("div", { className: "doc-player__block doc-player__block--cover", children: /* @__PURE__ */ jsx8(
3573
+ /* @__PURE__ */ jsxs6("div", { className: "doc-player__viewport", children: [
3574
+ showCoverBlock && coverBlock && /* @__PURE__ */ jsx9("div", { className: "doc-player__block doc-player__block--cover", children: /* @__PURE__ */ jsx9(
3042
3575
  BlockRenderer,
3043
3576
  {
3044
3577
  block: coverBlock,
@@ -3055,7 +3588,7 @@ function DocPlayerContent({
3055
3588
  // reconciles one block's layers onto another's (templates reuse layer
3056
3589
  // ids like `title`/`background`), which would otherwise reuse stale
3057
3590
  // DOM / skip entrance animations mid-transition.
3058
- /* @__PURE__ */ jsx8("div", { className: "doc-player__block doc-player__block--previous", children: /* @__PURE__ */ jsx8(
3591
+ /* @__PURE__ */ jsx9("div", { className: "doc-player__block doc-player__block--previous", children: /* @__PURE__ */ jsx9(
3059
3592
  BlockRenderer,
3060
3593
  {
3061
3594
  block: previousBlock,
@@ -3069,7 +3602,7 @@ function DocPlayerContent({
3069
3602
  theme: effectiveTheme
3070
3603
  }
3071
3604
  ) }, previousBlock.id),
3072
- currentBlock && /* @__PURE__ */ jsx8(
3605
+ currentBlock && /* @__PURE__ */ jsx9(
3073
3606
  "div",
3074
3607
  {
3075
3608
  className: `doc-player__block doc-player__block--active${swipe.phase !== "idle" ? ` doc-player__block--${swipe.phase}` : ""}`,
@@ -3078,7 +3611,7 @@ function DocPlayerContent({
3078
3611
  ...swipe.phase !== "idle" ? { transform: `translateX(${swipe.offsetPx}px)` } : {},
3079
3612
  ...showCoverBlock ? { opacity: 0, pointerEvents: "none" } : {}
3080
3613
  } : void 0,
3081
- children: /* @__PURE__ */ jsx8(
3614
+ children: /* @__PURE__ */ jsx9(
3082
3615
  BlockRenderer,
3083
3616
  {
3084
3617
  block: currentBlock,
@@ -3095,7 +3628,7 @@ function DocPlayerContent({
3095
3628
  },
3096
3629
  currentBlock.id
3097
3630
  ),
3098
- hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */ jsx8(
3631
+ hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */ jsx9(
3099
3632
  CaptionOverlay,
3100
3633
  {
3101
3634
  captions: doc.captions,
@@ -3107,7 +3640,7 @@ function DocPlayerContent({
3107
3640
  viewport: activeViewport
3108
3641
  }
3109
3642
  ),
3110
- isDebugMode && /* @__PURE__ */ jsxs5(
3643
+ isDebugMode && /* @__PURE__ */ jsxs6(
3111
3644
  "div",
3112
3645
  {
3113
3646
  className: "doc-player__debug",
@@ -3128,27 +3661,27 @@ function DocPlayerContent({
3128
3661
  textAlign: "left"
3129
3662
  },
3130
3663
  children: [
3131
- /* @__PURE__ */ jsx8("div", { style: { color: "#ffcc00", fontWeight: "bold", marginBottom: "4px" }, children: "DEBUG MODE" }),
3132
- /* @__PURE__ */ jsxs5("div", { children: [
3133
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "template:" }),
3664
+ /* @__PURE__ */ jsx9("div", { style: { color: "#ffcc00", fontWeight: "bold", marginBottom: "4px" }, children: "DEBUG MODE" }),
3665
+ /* @__PURE__ */ jsxs6("div", { children: [
3666
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "template:" }),
3134
3667
  " ",
3135
- /* @__PURE__ */ jsx8("span", { style: { color: "#ff6b6b" }, children: currentBlock?.template ?? "raw" })
3668
+ /* @__PURE__ */ jsx9("span", { style: { color: "#ff6b6b" }, children: currentBlock?.template ?? "raw" })
3136
3669
  ] }),
3137
- /* @__PURE__ */ jsxs5("div", { children: [
3138
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "block:" }),
3670
+ /* @__PURE__ */ jsxs6("div", { children: [
3671
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "block:" }),
3139
3672
  " ",
3140
3673
  currentBlockIndex + 1,
3141
3674
  "/",
3142
3675
  expandedBlocks.length,
3143
3676
  " ",
3144
- /* @__PURE__ */ jsxs5("span", { style: { color: "#666" }, children: [
3677
+ /* @__PURE__ */ jsxs6("span", { style: { color: "#666" }, children: [
3145
3678
  "(",
3146
3679
  currentBlock?.id || "none",
3147
3680
  ")"
3148
3681
  ] })
3149
3682
  ] }),
3150
- /* @__PURE__ */ jsxs5("div", { children: [
3151
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "time:" }),
3683
+ /* @__PURE__ */ jsxs6("div", { children: [
3684
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "time:" }),
3152
3685
  " ",
3153
3686
  currentTime.toFixed(2),
3154
3687
  "s /",
@@ -3156,7 +3689,7 @@ function DocPlayerContent({
3156
3689
  totalDuration.toFixed(1),
3157
3690
  "s",
3158
3691
  " ",
3159
- /* @__PURE__ */ jsxs5("span", { style: { color: "#666" }, children: [
3692
+ /* @__PURE__ */ jsxs6("span", { style: { color: "#666" }, children: [
3160
3693
  "(progress: ",
3161
3694
  (docProgress * 100).toFixed(1),
3162
3695
  "%, scriptDur: ",
@@ -3164,8 +3697,8 @@ function DocPlayerContent({
3164
3697
  ")"
3165
3698
  ] })
3166
3699
  ] }),
3167
- /* @__PURE__ */ jsxs5("div", { children: [
3168
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "blockTime:" }),
3700
+ /* @__PURE__ */ jsxs6("div", { children: [
3701
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "blockTime:" }),
3169
3702
  " ",
3170
3703
  blockTime.toFixed(2),
3171
3704
  "s /",
@@ -3173,58 +3706,58 @@ function DocPlayerContent({
3173
3706
  (currentBlock?.duration || 0).toFixed(1),
3174
3707
  "s"
3175
3708
  ] }),
3176
- /* @__PURE__ */ jsxs5("div", { children: [
3177
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "segment:" }),
3709
+ /* @__PURE__ */ jsxs6("div", { children: [
3710
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "segment:" }),
3178
3711
  " ",
3179
3712
  currentSegment,
3180
3713
  "/",
3181
3714
  doc.audio.segments.length - 1,
3182
3715
  " ",
3183
- /* @__PURE__ */ jsxs5("span", { style: { color: "#666" }, children: [
3716
+ /* @__PURE__ */ jsxs6("span", { style: { color: "#666" }, children: [
3184
3717
  "(",
3185
3718
  doc.audio.segments[currentSegment]?.name || "none",
3186
3719
  ")"
3187
3720
  ] })
3188
3721
  ] }),
3189
- /* @__PURE__ */ jsxs5("div", { children: [
3190
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "viewport:" }),
3722
+ /* @__PURE__ */ jsxs6("div", { children: [
3723
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "viewport:" }),
3191
3724
  " ",
3192
3725
  activeViewport.name || `${activeViewport.width}x${activeViewport.height}`,
3193
3726
  " ",
3194
- /* @__PURE__ */ jsxs5("span", { style: { color: "#666" }, children: [
3727
+ /* @__PURE__ */ jsxs6("span", { style: { color: "#666" }, children: [
3195
3728
  "(",
3196
3729
  activeOrientation,
3197
3730
  ")"
3198
3731
  ] })
3199
3732
  ] }),
3200
- /* @__PURE__ */ jsxs5("div", { children: [
3201
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "playing:" }),
3733
+ /* @__PURE__ */ jsxs6("div", { children: [
3734
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "playing:" }),
3202
3735
  " ",
3203
- /* @__PURE__ */ jsx8("span", { style: { color: isPlaying ? "#4ade80" : "#f87171" }, children: isPlaying ? "yes" : "no" }),
3204
- showCoverBlock && /* @__PURE__ */ jsx8("span", { style: { color: "#60a5fa" }, children: " (cover)" })
3736
+ /* @__PURE__ */ jsx9("span", { style: { color: isPlaying ? "#4ade80" : "#f87171" }, children: isPlaying ? "yes" : "no" }),
3737
+ showCoverBlock && /* @__PURE__ */ jsx9("span", { style: { color: "#60a5fa" }, children: " (cover)" })
3205
3738
  ] }),
3206
3739
  hasCaptions && (() => {
3207
3740
  const debugPhrase = getCaptionAtTime2(doc.captions, currentTime);
3208
3741
  const debugEnabled = captionsEnabled && (isPlaying || currentTime > 0);
3209
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
3210
- /* @__PURE__ */ jsxs5("div", { children: [
3211
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "captions:" }),
3742
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
3743
+ /* @__PURE__ */ jsxs6("div", { children: [
3744
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "captions:" }),
3212
3745
  " ",
3213
3746
  doc.captions?.phrases.length || 0,
3214
3747
  " phrases",
3215
3748
  " ",
3216
- /* @__PURE__ */ jsxs5("span", { style: { color: captionsEnabled ? "#4ade80" : "#666" }, children: [
3749
+ /* @__PURE__ */ jsxs6("span", { style: { color: captionsEnabled ? "#4ade80" : "#666" }, children: [
3217
3750
  "(",
3218
3751
  captionsEnabled ? "on" : "off",
3219
3752
  ")"
3220
3753
  ] })
3221
3754
  ] }),
3222
- /* @__PURE__ */ jsxs5("div", { children: [
3223
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "cc.enabled:" }),
3755
+ /* @__PURE__ */ jsxs6("div", { children: [
3756
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "cc.enabled:" }),
3224
3757
  " ",
3225
- /* @__PURE__ */ jsx8("span", { style: { color: debugEnabled ? "#4ade80" : "#f87171" }, children: String(debugEnabled) }),
3758
+ /* @__PURE__ */ jsx9("span", { style: { color: debugEnabled ? "#4ade80" : "#f87171" }, children: String(debugEnabled) }),
3226
3759
  " ",
3227
- /* @__PURE__ */ jsxs5("span", { style: { color: "#666" }, children: [
3760
+ /* @__PURE__ */ jsxs6("span", { style: { color: "#666" }, children: [
3228
3761
  "(playing=",
3229
3762
  String(isPlaying),
3230
3763
  " t>0=",
@@ -3232,15 +3765,15 @@ function DocPlayerContent({
3232
3765
  ")"
3233
3766
  ] })
3234
3767
  ] }),
3235
- /* @__PURE__ */ jsxs5("div", { children: [
3236
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "cc.phrase:" }),
3768
+ /* @__PURE__ */ jsxs6("div", { children: [
3769
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "cc.phrase:" }),
3237
3770
  " ",
3238
- /* @__PURE__ */ jsx8("span", { style: { color: debugPhrase ? "#4ade80" : "#f87171" }, children: debugPhrase ? `"${debugPhrase.text.slice(0, 30)}..."` : "null" })
3771
+ /* @__PURE__ */ jsx9("span", { style: { color: debugPhrase ? "#4ade80" : "#f87171" }, children: debugPhrase ? `"${debugPhrase.text.slice(0, 30)}..."` : "null" })
3239
3772
  ] }),
3240
- debugPhrase && /* @__PURE__ */ jsxs5("div", { children: [
3241
- /* @__PURE__ */ jsx8("span", { style: { color: "#888" }, children: "cc.range:" }),
3773
+ debugPhrase && /* @__PURE__ */ jsxs6("div", { children: [
3774
+ /* @__PURE__ */ jsx9("span", { style: { color: "#888" }, children: "cc.range:" }),
3242
3775
  " ",
3243
- /* @__PURE__ */ jsxs5("span", { style: { color: "#60a5fa" }, children: [
3776
+ /* @__PURE__ */ jsxs6("span", { style: { color: "#60a5fa" }, children: [
3244
3777
  debugPhrase.startTime.toFixed(2),
3245
3778
  "-",
3246
3779
  debugPhrase.endTime.toFixed(2)
@@ -3252,7 +3785,7 @@ function DocPlayerContent({
3252
3785
  }
3253
3786
  )
3254
3787
  ] }),
3255
- !isAvailable && unavailableMessage && /* @__PURE__ */ jsxs5(
3788
+ !isAvailable && unavailableMessage && /* @__PURE__ */ jsxs6(
3256
3789
  "div",
3257
3790
  {
3258
3791
  className: "doc-player__unavailable",
@@ -3273,12 +3806,12 @@ function DocPlayerContent({
3273
3806
  zIndex: 50
3274
3807
  },
3275
3808
  children: [
3276
- /* @__PURE__ */ jsx8("span", { style: { fontSize: "32px" }, children: "\u{1F50A}" }),
3277
- /* @__PURE__ */ jsx8("span", { children: unavailableMessage })
3809
+ /* @__PURE__ */ jsx9("span", { style: { fontSize: "32px" }, children: "\u{1F50A}" }),
3810
+ /* @__PURE__ */ jsx9("span", { children: unavailableMessage })
3278
3811
  ]
3279
3812
  }
3280
3813
  ),
3281
- !renderMode && !isSlideshowMode && showControls && /* @__PURE__ */ jsx8(
3814
+ !renderMode && !isSlideshowMode && showControls && /* @__PURE__ */ jsx9(
3282
3815
  DocControlsOverlay,
3283
3816
  {
3284
3817
  state: playbackState,
@@ -3288,7 +3821,7 @@ function DocPlayerContent({
3288
3821
  getBlockTitle
3289
3822
  }
3290
3823
  ),
3291
- !renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */ jsx8(
3824
+ !renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */ jsx9(
3292
3825
  "div",
3293
3826
  {
3294
3827
  className: "doc-player__scrubber",
@@ -3303,7 +3836,7 @@ function DocPlayerContent({
3303
3836
  alignItems: "center",
3304
3837
  zIndex: 100
3305
3838
  },
3306
- children: /* @__PURE__ */ jsx8(
3839
+ children: /* @__PURE__ */ jsx9(
3307
3840
  DocProgressBar,
3308
3841
  {
3309
3842
  state: playbackState,
@@ -3315,7 +3848,7 @@ function DocPlayerContent({
3315
3848
  )
3316
3849
  }
3317
3850
  ),
3318
- !renderMode && isSlideshowMode && showControls && /* @__PURE__ */ jsx8(
3851
+ !renderMode && isSlideshowMode && showControls && /* @__PURE__ */ jsx9(
3319
3852
  DocControlsSlideshow,
3320
3853
  {
3321
3854
  state: playbackState,
@@ -3325,14 +3858,14 @@ function DocPlayerContent({
3325
3858
  onPickerOpenChange: setIsSlideshowPickerOpen
3326
3859
  }
3327
3860
  ),
3328
- !isSlideshowMode && tapFeedback && /* @__PURE__ */ jsx8("div", { className: "doc-player__tap-feedback", children: /* @__PURE__ */ jsx8("svg", { viewBox: "0 0 24 24", fill: "white", width: "48", height: "48", children: tapFeedback === "pause" ? /* @__PURE__ */ jsx8("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) : /* @__PURE__ */ jsx8("path", { d: "M8 5v14l11-7z" }) }) }, tapFeedback)
3861
+ !isSlideshowMode && tapFeedback && /* @__PURE__ */ jsx9("div", { className: "doc-player__tap-feedback", children: /* @__PURE__ */ jsx9("svg", { viewBox: "0 0 24 24", fill: "white", width: "48", height: "48", children: tapFeedback === "pause" ? /* @__PURE__ */ jsx9("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) : /* @__PURE__ */ jsx9("path", { d: "M8 5v14l11-7z" }) }) }, tapFeedback)
3329
3862
  ]
3330
3863
  }
3331
3864
  );
3332
3865
  }
3333
3866
 
3334
3867
  // src/DocControlsBottom.tsx
3335
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
3868
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
3336
3869
  function DocControlsBottom({
3337
3870
  state,
3338
3871
  actions,
@@ -3340,32 +3873,32 @@ function DocControlsBottom({
3340
3873
  expandedBlocks,
3341
3874
  getBlockTitle
3342
3875
  }) {
3343
- return /* @__PURE__ */ jsxs6("div", { className: "doc-controls-bottom", children: [
3344
- /* @__PURE__ */ jsx9(
3876
+ return /* @__PURE__ */ jsxs7("div", { className: "doc-controls-bottom", children: [
3877
+ /* @__PURE__ */ jsx10(
3345
3878
  "button",
3346
3879
  {
3347
3880
  className: "bottom-ctrl-btn",
3348
3881
  onClick: actions.restart,
3349
3882
  title: "Restart",
3350
3883
  "aria-label": "Restart from beginning",
3351
- children: /* @__PURE__ */ jsx9("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx9("path", { d: "M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z" }) })
3884
+ children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx10("path", { d: "M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z" }) })
3352
3885
  }
3353
3886
  ),
3354
- /* @__PURE__ */ jsx9(
3887
+ /* @__PURE__ */ jsx10(
3355
3888
  "button",
3356
3889
  {
3357
3890
  className: "bottom-ctrl-btn bottom-play-btn",
3358
3891
  onClick: actions.toggle,
3359
3892
  "aria-label": state.isPlaying ? "Pause" : "Play",
3360
- children: state.isPlaying ? /* @__PURE__ */ jsx9("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx9("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx9("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx9("path", { d: "M8 5v14l11-7z" }) })
3893
+ children: state.isPlaying ? /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx10("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx10("path", { d: "M8 5v14l11-7z" }) })
3361
3894
  }
3362
3895
  ),
3363
- /* @__PURE__ */ jsxs6("span", { className: "bottom-time", children: [
3896
+ /* @__PURE__ */ jsxs7("span", { className: "bottom-time", children: [
3364
3897
  formatTime(state.currentTime),
3365
3898
  " / ",
3366
3899
  formatTime(state.totalDuration)
3367
3900
  ] }),
3368
- /* @__PURE__ */ jsx9(
3901
+ /* @__PURE__ */ jsx10(
3369
3902
  DocProgressBar,
3370
3903
  {
3371
3904
  state,
@@ -3375,92 +3908,92 @@ function DocControlsBottom({
3375
3908
  getBlockTitle
3376
3909
  }
3377
3910
  ),
3378
- /* @__PURE__ */ jsxs6("span", { className: "bottom-segment", children: [
3911
+ /* @__PURE__ */ jsxs7("span", { className: "bottom-segment", children: [
3379
3912
  state.currentBlockIndex + 1,
3380
3913
  "/",
3381
3914
  state.totalBlocks
3382
3915
  ] }),
3383
- state.hasCaptions && /* @__PURE__ */ jsx9(
3916
+ state.hasCaptions && /* @__PURE__ */ jsx10(
3384
3917
  "button",
3385
3918
  {
3386
3919
  className: `bottom-ctrl-btn ${state.captionMode !== "off" ? "bottom-ctrl-btn--active" : ""}`,
3387
3920
  onClick: () => actions.cycleCaptionMode(),
3388
3921
  title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
3389
3922
  "aria-label": "Cycle caption style",
3390
- children: /* @__PURE__ */ jsx9("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx9("path", { d: "M19 4H5a2 2 0 00-2 2v12a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1a1 1 0 01-1 1h-3a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1z" }) })
3923
+ children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx10("path", { d: "M19 4H5a2 2 0 00-2 2v12a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1a1 1 0 01-1 1h-3a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1z" }) })
3391
3924
  }
3392
3925
  ),
3393
- actions.toggleFullscreen && /* @__PURE__ */ jsx9(
3926
+ actions.toggleFullscreen && /* @__PURE__ */ jsx10(
3394
3927
  "button",
3395
3928
  {
3396
3929
  className: `bottom-ctrl-btn ${state.isFullscreen ? "bottom-ctrl-btn--active" : ""}`,
3397
3930
  onClick: actions.toggleFullscreen,
3398
3931
  title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
3399
3932
  "aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
3400
- children: state.isFullscreen ? /* @__PURE__ */ jsx9("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx9("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx9("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx9("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
3933
+ children: state.isFullscreen ? /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx10("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx10("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
3401
3934
  }
3402
3935
  )
3403
3936
  ] });
3404
3937
  }
3405
3938
 
3406
3939
  // src/DocControlsSidebar.tsx
3407
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
3940
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
3408
3941
  function DocControlsSidebar({ state, actions }) {
3409
- return /* @__PURE__ */ jsxs7("div", { className: "doc-controls-sidebar", children: [
3410
- /* @__PURE__ */ jsx10(
3942
+ return /* @__PURE__ */ jsxs8("div", { className: "doc-controls-sidebar", children: [
3943
+ /* @__PURE__ */ jsx11(
3411
3944
  "button",
3412
3945
  {
3413
3946
  className: "sidebar-ctrl-btn",
3414
3947
  onClick: actions.restart,
3415
3948
  title: "Restart",
3416
3949
  "aria-label": "Restart from beginning",
3417
- children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx10("path", { d: "M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z" }) })
3950
+ children: /* @__PURE__ */ jsx11("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx11("path", { d: "M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z" }) })
3418
3951
  }
3419
3952
  ),
3420
- /* @__PURE__ */ jsx10(
3953
+ /* @__PURE__ */ jsx11(
3421
3954
  "button",
3422
3955
  {
3423
3956
  className: "sidebar-ctrl-btn sidebar-play-btn",
3424
3957
  onClick: actions.toggle,
3425
3958
  "aria-label": state.isPlaying ? "Pause" : "Play",
3426
- children: state.isPlaying ? /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx10("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx10("path", { d: "M8 5v14l11-7z" }) })
3959
+ children: state.isPlaying ? /* @__PURE__ */ jsx11("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx11("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx11("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx11("path", { d: "M8 5v14l11-7z" }) })
3427
3960
  }
3428
3961
  ),
3429
- /* @__PURE__ */ jsxs7("div", { className: "sidebar-time", children: [
3430
- /* @__PURE__ */ jsx10("div", { children: formatTime(state.currentTime) }),
3431
- /* @__PURE__ */ jsx10("div", { className: "sidebar-time-total", children: formatTime(state.totalDuration) })
3962
+ /* @__PURE__ */ jsxs8("div", { className: "sidebar-time", children: [
3963
+ /* @__PURE__ */ jsx11("div", { children: formatTime(state.currentTime) }),
3964
+ /* @__PURE__ */ jsx11("div", { className: "sidebar-time-total", children: formatTime(state.totalDuration) })
3432
3965
  ] }),
3433
- /* @__PURE__ */ jsxs7("div", { className: "sidebar-segment", children: [
3966
+ /* @__PURE__ */ jsxs8("div", { className: "sidebar-segment", children: [
3434
3967
  state.currentBlockIndex + 1,
3435
3968
  "/",
3436
3969
  state.totalBlocks
3437
3970
  ] }),
3438
- state.hasCaptions && /* @__PURE__ */ jsx10(
3971
+ state.hasCaptions && /* @__PURE__ */ jsx11(
3439
3972
  "button",
3440
3973
  {
3441
3974
  className: `sidebar-ctrl-btn ${state.captionMode !== "off" ? "sidebar-ctrl-btn--active" : ""}`,
3442
3975
  onClick: () => actions.cycleCaptionMode(),
3443
3976
  title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
3444
3977
  "aria-label": "Cycle caption style",
3445
- children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx10("path", { d: "M19 4H5a2 2 0 00-2 2v12a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1a1 1 0 01-1 1h-3a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1z" }) })
3978
+ children: /* @__PURE__ */ jsx11("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx11("path", { d: "M19 4H5a2 2 0 00-2 2v12a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1a1 1 0 01-1 1h-3a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1z" }) })
3446
3979
  }
3447
3980
  ),
3448
- actions.toggleFullscreen && /* @__PURE__ */ jsx10(
3981
+ actions.toggleFullscreen && /* @__PURE__ */ jsx11(
3449
3982
  "button",
3450
3983
  {
3451
3984
  className: `sidebar-ctrl-btn ${state.isFullscreen ? "sidebar-ctrl-btn--active" : ""}`,
3452
3985
  onClick: actions.toggleFullscreen,
3453
3986
  title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
3454
3987
  "aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
3455
- children: state.isFullscreen ? /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx10("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx10("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
3988
+ children: state.isFullscreen ? /* @__PURE__ */ jsx11("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx11("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx11("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx11("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
3456
3989
  }
3457
3990
  )
3458
3991
  ] });
3459
3992
  }
3460
3993
 
3461
3994
  // src/DocPlayerWithSidebar.tsx
3462
- import { useRef as useRef7, useState as useState6, useCallback as useCallback5, useEffect as useEffect7 } from "react";
3463
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
3995
+ import { useRef as useRef8, useState as useState7, useCallback as useCallback6, useEffect as useEffect8 } from "react";
3996
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
3464
3997
  var DEFAULT_STATE = {
3465
3998
  isPlaying: false,
3466
3999
  currentTime: 0,
@@ -3492,11 +4025,11 @@ function DocPlayerWithSidebar({
3492
4025
  onPlayingChange,
3493
4026
  theme
3494
4027
  }) {
3495
- const stateRef = useRef7(DEFAULT_STATE);
3496
- const actionsRef = useRef7(null);
3497
- const wasPlayingRef = useRef7(false);
3498
- const [, setTick] = useState6(0);
3499
- const handleStateChange = useCallback5(
4028
+ const stateRef = useRef8(DEFAULT_STATE);
4029
+ const actionsRef = useRef8(null);
4030
+ const wasPlayingRef = useRef8(false);
4031
+ const [, setTick] = useState7(0);
4032
+ const handleStateChange = useCallback6(
3500
4033
  (state) => {
3501
4034
  stateRef.current = state;
3502
4035
  if (onPlayingChange && state.isPlaying !== wasPlayingRef.current) {
@@ -3507,7 +4040,7 @@ function DocPlayerWithSidebar({
3507
4040
  },
3508
4041
  [onPlayingChange]
3509
4042
  );
3510
- const handleControlsReady = useCallback5(
4043
+ const handleControlsReady = useCallback6(
3511
4044
  (controls) => {
3512
4045
  const isFirst = !actionsRef.current;
3513
4046
  actionsRef.current = controls;
@@ -3515,15 +4048,15 @@ function DocPlayerWithSidebar({
3515
4048
  },
3516
4049
  []
3517
4050
  );
3518
- useEffect7(() => {
4051
+ useEffect8(() => {
3519
4052
  const interval = setInterval(() => {
3520
4053
  if (!stateRef.current.isPlaying) return;
3521
4054
  setTick((t) => t + 1);
3522
4055
  }, 250);
3523
4056
  return () => clearInterval(interval);
3524
4057
  }, []);
3525
- return /* @__PURE__ */ jsxs8("div", { className: "doc-player-sidebar-layout", children: [
3526
- /* @__PURE__ */ jsx11("div", { className: "doc-player-sidebar-layout__video", children: /* @__PURE__ */ jsx11(
4058
+ return /* @__PURE__ */ jsxs9("div", { className: "doc-player-sidebar-layout", children: [
4059
+ /* @__PURE__ */ jsx12("div", { className: "doc-player-sidebar-layout__video", children: /* @__PURE__ */ jsx12(
3527
4060
  DocPlayer,
3528
4061
  {
3529
4062
  doc,
@@ -3546,7 +4079,7 @@ function DocPlayerWithSidebar({
3546
4079
  forceViewport
3547
4080
  }
3548
4081
  ) }),
3549
- actionsRef.current && /* @__PURE__ */ jsx11(DocControlsSidebar, { state: stateRef.current, actions: actionsRef.current })
4082
+ actionsRef.current && /* @__PURE__ */ jsx12(DocControlsSidebar, { state: stateRef.current, actions: actionsRef.current })
3550
4083
  ] });
3551
4084
  }
3552
4085
 
@@ -3560,6 +4093,8 @@ export {
3560
4093
  DocControlsOverlay,
3561
4094
  DocControlsSlideshow,
3562
4095
  DashboardView,
4096
+ FlashcardFaceView,
4097
+ FlashcardView,
3563
4098
  resolveDocPlayerAppearance,
3564
4099
  DocPlayer,
3565
4100
  DocControlsBottom,