pdfjs-reader-core 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1782,11 +1782,45 @@ interface TutorModeContainerProps {
1782
1782
  * Default: 3500ms.
1783
1783
  */
1784
1784
  minOverlayDurationMs?: number;
1785
+ /**
1786
+ * Background colour of the container surround (visible around the PDF when
1787
+ * the viewport is larger than the page fit). Default: `#ffffff`. Pass a
1788
+ * dark value for dark-themed hosts.
1789
+ */
1790
+ backgroundColor?: string;
1791
+ /**
1792
+ * Optional content to render while the PDF document/page is still loading.
1793
+ * Receives the loading stage so the host can show a spinner, a skeleton,
1794
+ * or a custom brand. If omitted, a minimal default spinner is rendered on
1795
+ * the `backgroundColor` surround.
1796
+ */
1797
+ loadingComponent?: react__default.ReactNode;
1798
+ /**
1799
+ * Fired when the underlying viewer's page changes from any source — the
1800
+ * agent API (`agentTools.goToPage / nextPage / previousPage`), the sidebar
1801
+ * (bookmarks, thumbnails, search), or a programmatic
1802
+ * `useViewerStore().goToPage` call. Use this to keep your own
1803
+ * `pageNumber` state (the controlled prop you pass in) in lockstep with
1804
+ * the viewer, so agent-driven navigation actually moves the rendered
1805
+ * page and downstream concerns (progress save, recap, etc.) see the
1806
+ * right value.
1807
+ *
1808
+ * ```tsx
1809
+ * const [currentPage, setCurrentPage] = useState(1);
1810
+ * <TutorModeContainer
1811
+ * pageNumber={currentPage}
1812
+ * onPageChange={setCurrentPage} // ← bidirectional sync
1813
+ * />
1814
+ * ```
1815
+ *
1816
+ * Added in v0.4.2.
1817
+ */
1818
+ onPageChange?: (page: number) => void;
1785
1819
  className?: string;
1786
1820
  }
1787
1821
  /** Build a cross-page/block index from the raw bbox list. */
1788
1822
  declare function buildBBoxIndex(bboxData: PageBBoxData[]): BBoxIndex;
1789
- declare function TutorModeContainer({ pageNumber, bboxData, narrationStore, scale, rotation, currentChunk, llm, idleTimeoutMs, llmTimeoutMs, embeddingProvider, showSubtitles, showExitButton, onExitTutorMode, minOverlayDurationMs, className, }: TutorModeContainerProps): react_jsx_runtime.JSX.Element;
1823
+ declare function TutorModeContainer({ pageNumber, bboxData, narrationStore, scale, rotation, currentChunk, llm, idleTimeoutMs, llmTimeoutMs, embeddingProvider, showSubtitles, showExitButton, onExitTutorMode, minOverlayDurationMs, backgroundColor, loadingComponent, onPageChange, className, }: TutorModeContainerProps): react_jsx_runtime.JSX.Element;
1790
1824
 
1791
1825
  interface CinemaLayerProps {
1792
1826
  page: PageBBoxData;
@@ -1816,13 +1850,19 @@ interface SpotlightMaskProps {
1816
1850
  durationMs?: number;
1817
1851
  }
1818
1852
  /**
1819
- * Full-page SVG overlay: dims the entire page with action.dim_opacity,
1820
- * then "cuts out" a rounded/rect/ellipse hole over the target bbox so
1821
- * the underlying page shows through. Uses an SVG mask with a blur filter
1822
- * to feather the edge.
1853
+ * Design: **Theatrical spotlight.** A warm-ink dim (not pure black)
1854
+ * closes over the whole page, then a feathered cutout reveals the
1855
+ * target block. A thin terracotta accent ring follows the cutout shape
1856
+ * the same colour used by every other overlay so the spotlit region
1857
+ * is clearly part of the same annotation system.
1858
+ *
1859
+ * The dim colour is warm INK (`#2a2420`) rather than `#000` so the
1860
+ * dimmed area looks like the PDF pushed into shadow rather than
1861
+ * blacked out. On light textbook pages this reads as "atmosphere"
1862
+ * instead of "censorship".
1823
1863
  *
1824
- * Rendered in PAGE coords (viewBox spans the full page). The parent
1825
- * CinemaLayer applies the overall scale transform.
1864
+ * The accent ring animates with a slight stagger after the dim, so the
1865
+ * reveal has a subtle two-beat cadence: darken → point.
1826
1866
  */
1827
1867
  declare function SpotlightMask({ page, bbox, action, durationMs, }: SpotlightMaskProps): react_jsx_runtime.JSX.Element;
1828
1868
 
@@ -1836,12 +1876,40 @@ interface AnimatedHighlightProps {
1836
1876
  bbox: BBoxCoords;
1837
1877
  action: ActionHighlight;
1838
1878
  }
1879
+ /**
1880
+ * Design: **Real highlighter stroke.** A flat rectangle feels digital;
1881
+ * a real highlighter has fibrous texture, a soft inner + harder outer
1882
+ * edge, a slight over-bleed past the baseline, and visible uneven
1883
+ * starts/ends where the pen hit and lifted the paper.
1884
+ *
1885
+ * Implementation:
1886
+ * - Double-stroke — a broad soft wash behind a tighter primary stroke
1887
+ * so the mark has depth where they overlap.
1888
+ * - Slight vertical over-bleed (2–3 px above/below the bbox) so the
1889
+ * highlight doesn't look clipped to the text baseline.
1890
+ * - Rounded caps + uneven left/right ends via tapered start/finish
1891
+ * for the "pen hit the page" look.
1892
+ * - Feathered edge mask via SVG filter for the fibrous marker feel.
1893
+ */
1839
1894
  declare function AnimatedHighlight({ bbox, action }: AnimatedHighlightProps): react_jsx_runtime.JSX.Element;
1840
1895
 
1841
1896
  interface PulseOverlayProps {
1842
1897
  bbox: BBoxCoords;
1843
1898
  action: ActionPulse;
1844
1899
  }
1900
+ /**
1901
+ * Design: **Camera viewfinder brackets.** Four L-shapes slide in from
1902
+ * each corner of the target block, like framing crosshairs on a camera.
1903
+ * Behind them, a soft radial glow pulses in sync with the bracket
1904
+ * emphasis — a "look here" cue that points without blocking content.
1905
+ *
1906
+ * Intensity controls bracket length + stroke weight + glow strength.
1907
+ * `count` controls how many emphasis pulses happen before the overlay
1908
+ * settles.
1909
+ *
1910
+ * Brackets slide in from OUTSIDE the block then snap to the corners,
1911
+ * which reads as "framing" rather than the usual bordered box.
1912
+ */
1845
1913
  declare function PulseOverlay({ bbox, action }: PulseOverlayProps): react_jsx_runtime.JSX.Element;
1846
1914
 
1847
1915
  interface CalloutArrowProps {
@@ -1849,6 +1917,17 @@ interface CalloutArrowProps {
1849
1917
  toBbox: BBoxCoords;
1850
1918
  action: ActionCallout;
1851
1919
  }
1920
+ /**
1921
+ * Design: **Editorial connector.** Hand-drawn feel via a refined stroke
1922
+ * + a small origin dot at the "from" end (like a teacher's pen planted
1923
+ * on the page before they draw the arrow). The arrowhead is a slender
1924
+ * open caret rather than a heavy filled triangle — reads as annotation,
1925
+ * not direction-signage.
1926
+ *
1927
+ * An optional label pill rides the arrow's trailing end using the same
1928
+ * editorial pin language as StickyLabel (cream paper, terracotta rule,
1929
+ * serif small-caps). Everything uses ACCENT for colour consistency.
1930
+ */
1852
1931
  declare function CalloutArrow({ fromBbox, toBbox, action }: CalloutArrowProps): react_jsx_runtime.JSX.Element;
1853
1932
 
1854
1933
  interface GhostReferenceProps {
@@ -1858,24 +1937,50 @@ interface GhostReferenceProps {
1858
1937
  sourcePageNumber: number;
1859
1938
  action: ActionGhostReference;
1860
1939
  }
1861
- /**
1862
- * Renders a floating "ghost" card referencing a block from another page.
1863
- * Shows a minimap of the source page with the target bbox highlighted,
1864
- * plus the block's text description as a caption.
1865
- */
1866
- declare function GhostReference({ page, sourceBbox, sourceBlockText, sourcePageNumber, action, }: GhostReferenceProps): react_jsx_runtime.JSX.Element;
1940
+ declare function GhostReference({ page, sourceBbox, sourceBlockText, action, }: GhostReferenceProps): react_jsx_runtime.JSX.Element;
1867
1941
 
1868
1942
  interface BoxOverlayProps {
1869
1943
  bbox: BBoxCoords;
1870
1944
  action: ActionBox;
1871
1945
  }
1946
+ /**
1947
+ * Design: **Framed region.** A structural region marker — thinner and
1948
+ * more refined than the previous generic 3px coloured border. A subtle
1949
+ * accent-tinted wash fills the interior so the framed region reads as
1950
+ * "selected" even without a thick border. Dashed style uses a custom
1951
+ * dash pattern that matches the editorial vocabulary.
1952
+ *
1953
+ * Honours `action.color` when the LLM specifies something non-default,
1954
+ * otherwise falls through to the terracotta accent so boxes fit the
1955
+ * system.
1956
+ */
1872
1957
  declare function BoxOverlay({ bbox, action }: BoxOverlayProps): react_jsx_runtime.JSX.Element;
1873
1958
 
1874
1959
  interface StickyLabelProps {
1875
- bbox: BBoxCoords;
1960
+ /**
1961
+ * Pre-computed anchor point in viewport pixels — the label will be
1962
+ * placed at this point and positioned relative to it via
1963
+ * `action.position`. See `LabelOverlay` for the coordinate math that
1964
+ * maps a block's bbox + camera state to this value.
1965
+ */
1966
+ screenAnchor: {
1967
+ x: number;
1968
+ y: number;
1969
+ };
1876
1970
  action: ActionLabel;
1877
1971
  }
1878
- declare function StickyLabel({ bbox, action }: StickyLabelProps): react_jsx_runtime.JSX.Element;
1972
+ /**
1973
+ * Editorial annotation pin. The label body sits a short distance from
1974
+ * the target block, connected by a hairline stem and a small marker
1975
+ * disc at the anchor point. Body uses a classical serif at small-caps
1976
+ * sizing — feels like a scholar's tag, not a browser toast.
1977
+ *
1978
+ * Positioning math: caller pre-computes the `screenAnchor` (usually the
1979
+ * midpoint of the block edge the label is attached to), and this
1980
+ * component lays out the pin on the appropriate side via CSS translate
1981
+ * offsets.
1982
+ */
1983
+ declare function StickyLabel({ screenAnchor, action }: StickyLabelProps): react_jsx_runtime.JSX.Element;
1879
1984
 
1880
1985
  interface SubtitleBarProps {
1881
1986
  text: string | null;
@@ -1902,7 +2007,20 @@ interface EngineDeps {
1902
2007
  }
1903
2008
  declare class StoryboardEngine {
1904
2009
  private deps;
1905
- private pendingTimers;
2010
+ /**
2011
+ * Timers that schedule the START of a step (via `setTimeout(runStep, at_ms)`).
2012
+ * These are storyboard-scoped: when a new storyboard arrives, anything still
2013
+ * pending should be abandoned.
2014
+ */
2015
+ private pendingStepTimers;
2016
+ /**
2017
+ * Timers that auto-REMOVE an already-placed overlay after its visible
2018
+ * duration. Keyed by overlay id so we can cancel one specifically. These are
2019
+ * NOT cancelled when a new storyboard starts — otherwise the still-visible
2020
+ * overlay from the previous beat would get stranded in the store forever
2021
+ * (the "stuck spotlight" bug).
2022
+ */
2023
+ private overlayRemovalTimers;
1906
2024
  private currentStoryboardId;
1907
2025
  constructor(deps: EngineDeps);
1908
2026
  /**
@@ -1910,9 +2028,16 @@ declare class StoryboardEngine {
1910
2028
  * and smoothly transitions the camera/overlays from the current state.
1911
2029
  */
1912
2030
  execute(storyboard: Storyboard): void;
1913
- /** Abort all pending steps and set engine status to idle. */
2031
+ /**
2032
+ * Abort pending STEP dispatches from the current storyboard. Overlay
2033
+ * removal timers are left alone so already-visible overlays still auto-
2034
+ * expire on their own schedule. To force-clear every overlay, call
2035
+ * `resetVisuals()` instead.
2036
+ */
1914
2037
  cancelPending(): void;
1915
- /** Reset visuals: clear overlays, fit camera back to page. */
2038
+ /** Cancel every removal timer (used by resetVisuals only). */
2039
+ private cancelAllRemovalTimers;
2040
+ /** Reset visuals: clear overlays, cancel every removal timer, fit camera. */
1916
2041
  resetVisuals(): void;
1917
2042
  /** Execute one step — dispatch to narrationStore. Returns true if applied. */
1918
2043
  private runStep;
@@ -2012,14 +2137,14 @@ declare const StoryboardActionSchema: z.ZodUnion<[z.ZodEffects<z.ZodObject<{
2012
2137
  intensity: z.ZodDefault<z.ZodEnum<["subtle", "normal", "strong"]>>;
2013
2138
  }, "strip", z.ZodTypeAny, {
2014
2139
  type: "pulse";
2140
+ intensity: "subtle" | "normal" | "strong";
2015
2141
  target_block: string;
2016
2142
  count: number;
2017
- intensity: "subtle" | "normal" | "strong";
2018
2143
  }, {
2019
2144
  type: "pulse";
2020
2145
  target_block: string;
2021
- count?: number | undefined;
2022
2146
  intensity?: "subtle" | "normal" | "strong" | undefined;
2147
+ count?: number | undefined;
2023
2148
  }>, z.ZodObject<{
2024
2149
  type: z.ZodLiteral<"callout">;
2025
2150
  from_block: z.ZodString;
@@ -2192,14 +2317,14 @@ declare const StoryboardSchema: z.ZodObject<{
2192
2317
  intensity: z.ZodDefault<z.ZodEnum<["subtle", "normal", "strong"]>>;
2193
2318
  }, "strip", z.ZodTypeAny, {
2194
2319
  type: "pulse";
2320
+ intensity: "subtle" | "normal" | "strong";
2195
2321
  target_block: string;
2196
2322
  count: number;
2197
- intensity: "subtle" | "normal" | "strong";
2198
2323
  }, {
2199
2324
  type: "pulse";
2200
2325
  target_block: string;
2201
- count?: number | undefined;
2202
2326
  intensity?: "subtle" | "normal" | "strong" | undefined;
2327
+ count?: number | undefined;
2203
2328
  }>, z.ZodObject<{
2204
2329
  type: z.ZodLiteral<"callout">;
2205
2330
  from_block: z.ZodString;
@@ -2300,9 +2425,9 @@ declare const StoryboardSchema: z.ZodObject<{
2300
2425
  draw_duration_ms: number;
2301
2426
  } | {
2302
2427
  type: "pulse";
2428
+ intensity: "subtle" | "normal" | "strong";
2303
2429
  target_block: string;
2304
2430
  count: number;
2305
- intensity: "subtle" | "normal" | "strong";
2306
2431
  } | {
2307
2432
  type: "callout";
2308
2433
  curve: "straight" | "curved" | "zigzag";
@@ -2358,8 +2483,8 @@ declare const StoryboardSchema: z.ZodObject<{
2358
2483
  } | {
2359
2484
  type: "pulse";
2360
2485
  target_block: string;
2361
- count?: number | undefined;
2362
2486
  intensity?: "subtle" | "normal" | "strong" | undefined;
2487
+ count?: number | undefined;
2363
2488
  } | {
2364
2489
  type: "callout";
2365
2490
  from_block: string;
@@ -2418,9 +2543,9 @@ declare const StoryboardSchema: z.ZodObject<{
2418
2543
  draw_duration_ms: number;
2419
2544
  } | {
2420
2545
  type: "pulse";
2546
+ intensity: "subtle" | "normal" | "strong";
2421
2547
  target_block: string;
2422
2548
  count: number;
2423
- intensity: "subtle" | "normal" | "strong";
2424
2549
  } | {
2425
2550
  type: "callout";
2426
2551
  curve: "straight" | "curved" | "zigzag";
@@ -2479,8 +2604,8 @@ declare const StoryboardSchema: z.ZodObject<{
2479
2604
  } | {
2480
2605
  type: "pulse";
2481
2606
  target_block: string;
2482
- count?: number | undefined;
2483
2607
  intensity?: "subtle" | "normal" | "strong" | undefined;
2608
+ count?: number | undefined;
2484
2609
  } | {
2485
2610
  type: "callout";
2486
2611
  from_block: string;
package/dist/index.d.ts CHANGED
@@ -1782,11 +1782,45 @@ interface TutorModeContainerProps {
1782
1782
  * Default: 3500ms.
1783
1783
  */
1784
1784
  minOverlayDurationMs?: number;
1785
+ /**
1786
+ * Background colour of the container surround (visible around the PDF when
1787
+ * the viewport is larger than the page fit). Default: `#ffffff`. Pass a
1788
+ * dark value for dark-themed hosts.
1789
+ */
1790
+ backgroundColor?: string;
1791
+ /**
1792
+ * Optional content to render while the PDF document/page is still loading.
1793
+ * Receives the loading stage so the host can show a spinner, a skeleton,
1794
+ * or a custom brand. If omitted, a minimal default spinner is rendered on
1795
+ * the `backgroundColor` surround.
1796
+ */
1797
+ loadingComponent?: react__default.ReactNode;
1798
+ /**
1799
+ * Fired when the underlying viewer's page changes from any source — the
1800
+ * agent API (`agentTools.goToPage / nextPage / previousPage`), the sidebar
1801
+ * (bookmarks, thumbnails, search), or a programmatic
1802
+ * `useViewerStore().goToPage` call. Use this to keep your own
1803
+ * `pageNumber` state (the controlled prop you pass in) in lockstep with
1804
+ * the viewer, so agent-driven navigation actually moves the rendered
1805
+ * page and downstream concerns (progress save, recap, etc.) see the
1806
+ * right value.
1807
+ *
1808
+ * ```tsx
1809
+ * const [currentPage, setCurrentPage] = useState(1);
1810
+ * <TutorModeContainer
1811
+ * pageNumber={currentPage}
1812
+ * onPageChange={setCurrentPage} // ← bidirectional sync
1813
+ * />
1814
+ * ```
1815
+ *
1816
+ * Added in v0.4.2.
1817
+ */
1818
+ onPageChange?: (page: number) => void;
1785
1819
  className?: string;
1786
1820
  }
1787
1821
  /** Build a cross-page/block index from the raw bbox list. */
1788
1822
  declare function buildBBoxIndex(bboxData: PageBBoxData[]): BBoxIndex;
1789
- declare function TutorModeContainer({ pageNumber, bboxData, narrationStore, scale, rotation, currentChunk, llm, idleTimeoutMs, llmTimeoutMs, embeddingProvider, showSubtitles, showExitButton, onExitTutorMode, minOverlayDurationMs, className, }: TutorModeContainerProps): react_jsx_runtime.JSX.Element;
1823
+ declare function TutorModeContainer({ pageNumber, bboxData, narrationStore, scale, rotation, currentChunk, llm, idleTimeoutMs, llmTimeoutMs, embeddingProvider, showSubtitles, showExitButton, onExitTutorMode, minOverlayDurationMs, backgroundColor, loadingComponent, onPageChange, className, }: TutorModeContainerProps): react_jsx_runtime.JSX.Element;
1790
1824
 
1791
1825
  interface CinemaLayerProps {
1792
1826
  page: PageBBoxData;
@@ -1816,13 +1850,19 @@ interface SpotlightMaskProps {
1816
1850
  durationMs?: number;
1817
1851
  }
1818
1852
  /**
1819
- * Full-page SVG overlay: dims the entire page with action.dim_opacity,
1820
- * then "cuts out" a rounded/rect/ellipse hole over the target bbox so
1821
- * the underlying page shows through. Uses an SVG mask with a blur filter
1822
- * to feather the edge.
1853
+ * Design: **Theatrical spotlight.** A warm-ink dim (not pure black)
1854
+ * closes over the whole page, then a feathered cutout reveals the
1855
+ * target block. A thin terracotta accent ring follows the cutout shape
1856
+ * the same colour used by every other overlay so the spotlit region
1857
+ * is clearly part of the same annotation system.
1858
+ *
1859
+ * The dim colour is warm INK (`#2a2420`) rather than `#000` so the
1860
+ * dimmed area looks like the PDF pushed into shadow rather than
1861
+ * blacked out. On light textbook pages this reads as "atmosphere"
1862
+ * instead of "censorship".
1823
1863
  *
1824
- * Rendered in PAGE coords (viewBox spans the full page). The parent
1825
- * CinemaLayer applies the overall scale transform.
1864
+ * The accent ring animates with a slight stagger after the dim, so the
1865
+ * reveal has a subtle two-beat cadence: darken → point.
1826
1866
  */
1827
1867
  declare function SpotlightMask({ page, bbox, action, durationMs, }: SpotlightMaskProps): react_jsx_runtime.JSX.Element;
1828
1868
 
@@ -1836,12 +1876,40 @@ interface AnimatedHighlightProps {
1836
1876
  bbox: BBoxCoords;
1837
1877
  action: ActionHighlight;
1838
1878
  }
1879
+ /**
1880
+ * Design: **Real highlighter stroke.** A flat rectangle feels digital;
1881
+ * a real highlighter has fibrous texture, a soft inner + harder outer
1882
+ * edge, a slight over-bleed past the baseline, and visible uneven
1883
+ * starts/ends where the pen hit and lifted the paper.
1884
+ *
1885
+ * Implementation:
1886
+ * - Double-stroke — a broad soft wash behind a tighter primary stroke
1887
+ * so the mark has depth where they overlap.
1888
+ * - Slight vertical over-bleed (2–3 px above/below the bbox) so the
1889
+ * highlight doesn't look clipped to the text baseline.
1890
+ * - Rounded caps + uneven left/right ends via tapered start/finish
1891
+ * for the "pen hit the page" look.
1892
+ * - Feathered edge mask via SVG filter for the fibrous marker feel.
1893
+ */
1839
1894
  declare function AnimatedHighlight({ bbox, action }: AnimatedHighlightProps): react_jsx_runtime.JSX.Element;
1840
1895
 
1841
1896
  interface PulseOverlayProps {
1842
1897
  bbox: BBoxCoords;
1843
1898
  action: ActionPulse;
1844
1899
  }
1900
+ /**
1901
+ * Design: **Camera viewfinder brackets.** Four L-shapes slide in from
1902
+ * each corner of the target block, like framing crosshairs on a camera.
1903
+ * Behind them, a soft radial glow pulses in sync with the bracket
1904
+ * emphasis — a "look here" cue that points without blocking content.
1905
+ *
1906
+ * Intensity controls bracket length + stroke weight + glow strength.
1907
+ * `count` controls how many emphasis pulses happen before the overlay
1908
+ * settles.
1909
+ *
1910
+ * Brackets slide in from OUTSIDE the block then snap to the corners,
1911
+ * which reads as "framing" rather than the usual bordered box.
1912
+ */
1845
1913
  declare function PulseOverlay({ bbox, action }: PulseOverlayProps): react_jsx_runtime.JSX.Element;
1846
1914
 
1847
1915
  interface CalloutArrowProps {
@@ -1849,6 +1917,17 @@ interface CalloutArrowProps {
1849
1917
  toBbox: BBoxCoords;
1850
1918
  action: ActionCallout;
1851
1919
  }
1920
+ /**
1921
+ * Design: **Editorial connector.** Hand-drawn feel via a refined stroke
1922
+ * + a small origin dot at the "from" end (like a teacher's pen planted
1923
+ * on the page before they draw the arrow). The arrowhead is a slender
1924
+ * open caret rather than a heavy filled triangle — reads as annotation,
1925
+ * not direction-signage.
1926
+ *
1927
+ * An optional label pill rides the arrow's trailing end using the same
1928
+ * editorial pin language as StickyLabel (cream paper, terracotta rule,
1929
+ * serif small-caps). Everything uses ACCENT for colour consistency.
1930
+ */
1852
1931
  declare function CalloutArrow({ fromBbox, toBbox, action }: CalloutArrowProps): react_jsx_runtime.JSX.Element;
1853
1932
 
1854
1933
  interface GhostReferenceProps {
@@ -1858,24 +1937,50 @@ interface GhostReferenceProps {
1858
1937
  sourcePageNumber: number;
1859
1938
  action: ActionGhostReference;
1860
1939
  }
1861
- /**
1862
- * Renders a floating "ghost" card referencing a block from another page.
1863
- * Shows a minimap of the source page with the target bbox highlighted,
1864
- * plus the block's text description as a caption.
1865
- */
1866
- declare function GhostReference({ page, sourceBbox, sourceBlockText, sourcePageNumber, action, }: GhostReferenceProps): react_jsx_runtime.JSX.Element;
1940
+ declare function GhostReference({ page, sourceBbox, sourceBlockText, action, }: GhostReferenceProps): react_jsx_runtime.JSX.Element;
1867
1941
 
1868
1942
  interface BoxOverlayProps {
1869
1943
  bbox: BBoxCoords;
1870
1944
  action: ActionBox;
1871
1945
  }
1946
+ /**
1947
+ * Design: **Framed region.** A structural region marker — thinner and
1948
+ * more refined than the previous generic 3px coloured border. A subtle
1949
+ * accent-tinted wash fills the interior so the framed region reads as
1950
+ * "selected" even without a thick border. Dashed style uses a custom
1951
+ * dash pattern that matches the editorial vocabulary.
1952
+ *
1953
+ * Honours `action.color` when the LLM specifies something non-default,
1954
+ * otherwise falls through to the terracotta accent so boxes fit the
1955
+ * system.
1956
+ */
1872
1957
  declare function BoxOverlay({ bbox, action }: BoxOverlayProps): react_jsx_runtime.JSX.Element;
1873
1958
 
1874
1959
  interface StickyLabelProps {
1875
- bbox: BBoxCoords;
1960
+ /**
1961
+ * Pre-computed anchor point in viewport pixels — the label will be
1962
+ * placed at this point and positioned relative to it via
1963
+ * `action.position`. See `LabelOverlay` for the coordinate math that
1964
+ * maps a block's bbox + camera state to this value.
1965
+ */
1966
+ screenAnchor: {
1967
+ x: number;
1968
+ y: number;
1969
+ };
1876
1970
  action: ActionLabel;
1877
1971
  }
1878
- declare function StickyLabel({ bbox, action }: StickyLabelProps): react_jsx_runtime.JSX.Element;
1972
+ /**
1973
+ * Editorial annotation pin. The label body sits a short distance from
1974
+ * the target block, connected by a hairline stem and a small marker
1975
+ * disc at the anchor point. Body uses a classical serif at small-caps
1976
+ * sizing — feels like a scholar's tag, not a browser toast.
1977
+ *
1978
+ * Positioning math: caller pre-computes the `screenAnchor` (usually the
1979
+ * midpoint of the block edge the label is attached to), and this
1980
+ * component lays out the pin on the appropriate side via CSS translate
1981
+ * offsets.
1982
+ */
1983
+ declare function StickyLabel({ screenAnchor, action }: StickyLabelProps): react_jsx_runtime.JSX.Element;
1879
1984
 
1880
1985
  interface SubtitleBarProps {
1881
1986
  text: string | null;
@@ -1902,7 +2007,20 @@ interface EngineDeps {
1902
2007
  }
1903
2008
  declare class StoryboardEngine {
1904
2009
  private deps;
1905
- private pendingTimers;
2010
+ /**
2011
+ * Timers that schedule the START of a step (via `setTimeout(runStep, at_ms)`).
2012
+ * These are storyboard-scoped: when a new storyboard arrives, anything still
2013
+ * pending should be abandoned.
2014
+ */
2015
+ private pendingStepTimers;
2016
+ /**
2017
+ * Timers that auto-REMOVE an already-placed overlay after its visible
2018
+ * duration. Keyed by overlay id so we can cancel one specifically. These are
2019
+ * NOT cancelled when a new storyboard starts — otherwise the still-visible
2020
+ * overlay from the previous beat would get stranded in the store forever
2021
+ * (the "stuck spotlight" bug).
2022
+ */
2023
+ private overlayRemovalTimers;
1906
2024
  private currentStoryboardId;
1907
2025
  constructor(deps: EngineDeps);
1908
2026
  /**
@@ -1910,9 +2028,16 @@ declare class StoryboardEngine {
1910
2028
  * and smoothly transitions the camera/overlays from the current state.
1911
2029
  */
1912
2030
  execute(storyboard: Storyboard): void;
1913
- /** Abort all pending steps and set engine status to idle. */
2031
+ /**
2032
+ * Abort pending STEP dispatches from the current storyboard. Overlay
2033
+ * removal timers are left alone so already-visible overlays still auto-
2034
+ * expire on their own schedule. To force-clear every overlay, call
2035
+ * `resetVisuals()` instead.
2036
+ */
1914
2037
  cancelPending(): void;
1915
- /** Reset visuals: clear overlays, fit camera back to page. */
2038
+ /** Cancel every removal timer (used by resetVisuals only). */
2039
+ private cancelAllRemovalTimers;
2040
+ /** Reset visuals: clear overlays, cancel every removal timer, fit camera. */
1916
2041
  resetVisuals(): void;
1917
2042
  /** Execute one step — dispatch to narrationStore. Returns true if applied. */
1918
2043
  private runStep;
@@ -2012,14 +2137,14 @@ declare const StoryboardActionSchema: z.ZodUnion<[z.ZodEffects<z.ZodObject<{
2012
2137
  intensity: z.ZodDefault<z.ZodEnum<["subtle", "normal", "strong"]>>;
2013
2138
  }, "strip", z.ZodTypeAny, {
2014
2139
  type: "pulse";
2140
+ intensity: "subtle" | "normal" | "strong";
2015
2141
  target_block: string;
2016
2142
  count: number;
2017
- intensity: "subtle" | "normal" | "strong";
2018
2143
  }, {
2019
2144
  type: "pulse";
2020
2145
  target_block: string;
2021
- count?: number | undefined;
2022
2146
  intensity?: "subtle" | "normal" | "strong" | undefined;
2147
+ count?: number | undefined;
2023
2148
  }>, z.ZodObject<{
2024
2149
  type: z.ZodLiteral<"callout">;
2025
2150
  from_block: z.ZodString;
@@ -2192,14 +2317,14 @@ declare const StoryboardSchema: z.ZodObject<{
2192
2317
  intensity: z.ZodDefault<z.ZodEnum<["subtle", "normal", "strong"]>>;
2193
2318
  }, "strip", z.ZodTypeAny, {
2194
2319
  type: "pulse";
2320
+ intensity: "subtle" | "normal" | "strong";
2195
2321
  target_block: string;
2196
2322
  count: number;
2197
- intensity: "subtle" | "normal" | "strong";
2198
2323
  }, {
2199
2324
  type: "pulse";
2200
2325
  target_block: string;
2201
- count?: number | undefined;
2202
2326
  intensity?: "subtle" | "normal" | "strong" | undefined;
2327
+ count?: number | undefined;
2203
2328
  }>, z.ZodObject<{
2204
2329
  type: z.ZodLiteral<"callout">;
2205
2330
  from_block: z.ZodString;
@@ -2300,9 +2425,9 @@ declare const StoryboardSchema: z.ZodObject<{
2300
2425
  draw_duration_ms: number;
2301
2426
  } | {
2302
2427
  type: "pulse";
2428
+ intensity: "subtle" | "normal" | "strong";
2303
2429
  target_block: string;
2304
2430
  count: number;
2305
- intensity: "subtle" | "normal" | "strong";
2306
2431
  } | {
2307
2432
  type: "callout";
2308
2433
  curve: "straight" | "curved" | "zigzag";
@@ -2358,8 +2483,8 @@ declare const StoryboardSchema: z.ZodObject<{
2358
2483
  } | {
2359
2484
  type: "pulse";
2360
2485
  target_block: string;
2361
- count?: number | undefined;
2362
2486
  intensity?: "subtle" | "normal" | "strong" | undefined;
2487
+ count?: number | undefined;
2363
2488
  } | {
2364
2489
  type: "callout";
2365
2490
  from_block: string;
@@ -2418,9 +2543,9 @@ declare const StoryboardSchema: z.ZodObject<{
2418
2543
  draw_duration_ms: number;
2419
2544
  } | {
2420
2545
  type: "pulse";
2546
+ intensity: "subtle" | "normal" | "strong";
2421
2547
  target_block: string;
2422
2548
  count: number;
2423
- intensity: "subtle" | "normal" | "strong";
2424
2549
  } | {
2425
2550
  type: "callout";
2426
2551
  curve: "straight" | "curved" | "zigzag";
@@ -2479,8 +2604,8 @@ declare const StoryboardSchema: z.ZodObject<{
2479
2604
  } | {
2480
2605
  type: "pulse";
2481
2606
  target_block: string;
2482
- count?: number | undefined;
2483
2607
  intensity?: "subtle" | "normal" | "strong" | undefined;
2608
+ count?: number | undefined;
2484
2609
  } | {
2485
2610
  type: "callout";
2486
2611
  from_block: string;