@bendyline/squisq-react 1.4.0 → 1.4.1

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/README.md CHANGED
@@ -22,37 +22,54 @@ import { DocPlayer } from '@bendyline/squisq-react';
22
22
  import '@bendyline/squisq-react/styles';
23
23
 
24
24
  function App() {
25
- return <DocPlayer doc={myDoc} />;
25
+ return <DocPlayer markdown={'# Hello\n\nWelcome to Squisq.'} />;
26
26
  }
27
27
  ```
28
28
 
29
+ `DocPlayer` accepts **either** raw `markdown` (parsed and converted internally)
30
+ **or** a parsed `doc`; if you already have a `Doc`, pass `<DocPlayer doc={doc} />`.
31
+ `basePath` is optional (default `'.'`) and is the base URL for resolving relative
32
+ media paths. With neither `markdown` nor `doc`, the player renders a themed empty
33
+ state rather than crashing.
34
+
35
+ > **v1.5 breaking changes:** the old `script` prop is now `doc`, and the
36
+ > `audioProvider` prop is now `audioController` (its type `AudioProvider` was
37
+ > renamed to `AudioController`). `LinearDocView` gained the same `markdown` prop.
38
+
29
39
  ## Components
30
40
 
31
- | Component | Description |
32
- | ---------------------- | ------------------------------------------------------------------ |
33
- | `DocPlayer` | Main document player with timed playback, audio sync, and controls |
34
- | `LinearDocView` | Scroll-based linear rendering of all blocks |
35
- | `BlockRenderer` | SVG-based renderer for a single block |
36
- | `MarkdownRenderer` | Renders Squisq markdown as a visual document |
37
- | `DocPlayerWithSidebar` | DocPlayer with a sidebar navigation panel |
38
- | `CaptionOverlay` | Timed caption/subtitle overlay |
39
- | `DocProgressBar` | Playback progress indicator |
40
- | `DocControlsOverlay` | Floating playback controls |
41
- | `DocControlsBottom` | Bottom-bar playback controls |
42
- | `DocControlsSidebar` | Sidebar navigation controls |
43
- | `DocControlsSlideshow` | Slideshow-style navigation controls |
41
+ | Component | Description |
42
+ | ---------------------- | ------------------------------------------------------------------- |
43
+ | `DocPlayer` | Main document player with timed playback, audio sync, and controls |
44
+ | `LinearDocView` | Scroll-based linear rendering of all blocks |
45
+ | `BlockRenderer` | SVG-based renderer for a single block |
46
+ | `MarkdownRenderer` | Renders Squisq markdown as a visual document |
47
+ | `DocPlayerWithSidebar` | DocPlayer with a sidebar navigation panel |
48
+ | `CaptionOverlay` | Timed caption/subtitle overlay |
49
+ | `DocProgressBar` | Playback progress indicator |
50
+ | `DocControlsOverlay` | Floating playback controls |
51
+ | `DocControlsBottom` | Bottom-bar playback controls |
52
+ | `DocControlsSidebar` | Sidebar navigation controls |
53
+ | `DocControlsSlideshow` | Slideshow-style navigation controls |
54
+ | `SocialCaptionOverlay` | Large centered word-by-word (TikTok/Reels-style) captions |
55
+ | `InlineVideoPlayer` | Native `<video>` wrapper resolving `src`/`poster` via MediaContext |
56
+ | `InlineAudioPlayer` | Native `<audio>` wrapper resolving `src` via MediaContext |
57
+ | `MediaClipLayer` | Hidden `<audio>`/`<video>` elements for timed media clips |
58
+ | `JsonView` | Read-only viewer for JSON values bound to a Squisq-annotated schema |
44
59
 
45
60
  ## Layers
46
61
 
47
62
  Blocks are composed of typed layers rendered as SVG:
48
63
 
49
- | Layer | Description |
50
- | ------------ | ------------------------------------------ |
51
- | `ImageLayer` | Background and foreground images |
52
- | `TextLayer` | Styled text with positioning and animation |
53
- | `ShapeLayer` | SVG shapes (rectangles, circles, lines) |
54
- | `VideoLayer` | Embedded video with playback sync |
55
- | `MapLayer` | Tile-based map rendering |
64
+ | Layer | Description |
65
+ | ------------ | --------------------------------------------- |
66
+ | `ImageLayer` | Background and foreground images |
67
+ | `TextLayer` | Styled text with positioning and animation |
68
+ | `ShapeLayer` | SVG shapes (rectangles, circles, lines) |
69
+ | `PathLayer` | Freeform SVG path drawing |
70
+ | `VideoLayer` | Embedded video with playback sync |
71
+ | `TableLayer` | HTML table embedded via SVG `<foreignObject>` |
72
+ | `MapLayer` | Tile-based map rendering |
56
73
 
57
74
  ## Hooks
58
75
 
@@ -60,21 +77,33 @@ Blocks are composed of typed layers rendered as SVG:
60
77
  | ---------------------------------- | ------------------------------------------------------------------ |
61
78
  | `useDocPlayback` | Core playback state machine — timing, block transitions, scripting |
62
79
  | `useAudioSync` | Synchronizes audio playback with doc timeline |
80
+ | `useMediaSchedule` | Resolves which timed media clips are active at the current time |
63
81
  | `useViewportOrientation` | Tracks viewport orientation for responsive layouts |
82
+ | `useAutoSurface` | Live light/dark surface detection via `prefers-color-scheme` |
64
83
  | `useMediaProvider` / `useMediaUrl` | Media URL resolution via `MediaContext` |
65
84
 
66
85
  ## Standalone Player
67
86
 
68
- A self-contained global build is available for non-React environments:
87
+ A self-contained global build is available for non-React environments. It
88
+ exposes a `SquisqPlayer` global with `mount`, `mountStatic`, `unmount`, and
89
+ `version`:
69
90
 
70
91
  ```html
71
92
  <script src="https://unpkg.com/@bendyline/squisq-react/dist/squisq-player.global.js"></script>
72
93
  <div id="player"></div>
73
94
  <script>
74
- SquisqPlayer.render(document.getElementById('player'), { markdown: '# Hello' });
95
+ // docJson is a Doc (e.g. produced by markdownToDoc and serialized)
96
+ SquisqPlayer.mount(document.getElementById('player'), docJson, {
97
+ mode: 'slideshow', // or 'static' for a scrollable document view
98
+ basePath: '/',
99
+ });
75
100
  </script>
76
101
  ```
77
102
 
103
+ For build-time embedding, `@bendyline/squisq-react/standalone-source` exports
104
+ the same bundle as a string constant (`PLAYER_BUNDLE`) — used by
105
+ `@bendyline/squisq-formats` and the CLI to produce single-file HTML exports.
106
+
78
107
  ## Styles
79
108
 
80
109
  Import the animation CSS for block transitions:
@@ -83,6 +112,11 @@ Import the animation CSS for block transitions:
83
112
  import '@bendyline/squisq-react/styles';
84
113
  ```
85
114
 
115
+ ## Full API Reference
116
+
117
+ See [docs/API.md](https://github.com/bendyline/squisq/blob/main/docs/API.md)
118
+ for the complete prop tables, hook signatures, and types.
119
+
86
120
  ## Related Packages
87
121
 
88
122
  | Package | Description |
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ import { RefObject } from 'react';
8
8
  import { SquisqAnnotatedSchema } from '@bendyline/squisq/jsonForm';
9
9
 
10
10
  /**
11
- * AudioProvider - Abstraction for audio playback in DocPlayer
11
+ * AudioController - Abstraction for audio playback in DocPlayer
12
12
  *
13
13
  * This module defines an interface for audio playback operations that can have
14
14
  * different implementations depending on the runtime environment:
@@ -52,7 +52,7 @@ interface AudioActions {
52
52
  /** Restart from beginning */
53
53
  restart: () => Promise<void>;
54
54
  }
55
- type AudioProvider = AudioState & AudioActions;
55
+ type AudioController = AudioState & AudioActions;
56
56
 
57
57
  /**
58
58
  * Doc Player Control Types
@@ -209,10 +209,20 @@ type SquisqWindow = Window & typeof globalThis & Partial<SquisqRenderAPI>;
209
209
  declare function formatTime(seconds: number): string;
210
210
 
211
211
  interface DocPlayerProps {
212
- /** Doc script to play */
213
- script: Doc;
214
- /** Base path for resolving media URLs */
215
- basePath: string;
212
+ /**
213
+ * The Doc to play. Wins over `markdown` when both are provided.
214
+ * When neither `doc` nor `markdown` is given, the player renders a
215
+ * minimal themed empty state instead of crashing.
216
+ */
217
+ doc?: Doc;
218
+ /**
219
+ * Markdown source to play. When `doc` is absent, the markdown is parsed
220
+ * and converted to a Doc via `markdownToDoc(parseMarkdown(markdown))`.
221
+ * Ignored when `doc` is provided.
222
+ */
223
+ markdown?: string;
224
+ /** Base path for resolving media URLs (default: `'.'`) */
225
+ basePath?: string;
216
226
  /** Render mode for video capture (hides controls, exposes seekTo) */
217
227
  renderMode?: boolean;
218
228
  /** Auto-play when loaded */
@@ -221,8 +231,8 @@ interface DocPlayerProps {
221
231
  onEnded?: () => void;
222
232
  /** Callback for time updates */
223
233
  onTimeUpdate?: (time: number) => void;
224
- /** Optional audio provider (if not provided, uses default HTML5 audio) */
225
- audioProvider?: AudioProvider;
234
+ /** Optional audio controller (if not provided, uses default HTML5 audio) */
235
+ audioController?: AudioController;
226
236
  /** Show built-in controls (default: true). Set to false for custom controls. */
227
237
  showControls?: boolean;
228
238
  /** Show only the progress bar/scrubber at bottom (no other controls).
@@ -273,8 +283,21 @@ interface DocPlayerProps {
273
283
  /** Caption display style (default: 'standard').
274
284
  * 'social' shows large centered words with the active word highlighted. */
275
285
  captionStyle?: CaptionStyle;
286
+ /**
287
+ * Enable drag-to-swipe slide navigation in slideshow mode (default: true).
288
+ * When enabled, press-and-drag on a slide advances/rewinds on release past a
289
+ * threshold (or a quick flick), and snaps back otherwise. Only applies when
290
+ * `displayMode === 'slideshow'` and not in render/headless mode.
291
+ */
292
+ enableSwipe?: boolean;
276
293
  }
277
- declare function DocPlayer({ script, basePath, renderMode, autoPlay, onEnded, onTimeUpdate, audioProvider: externalAudioProvider, showControls, showScrubber, muted, captionsEnabled: captionsEnabledProp, onCaptionsToggle, onPlaybackStateChange, onControlsReady, isFullscreen, onFullscreenToggle, onBlockMarkers, forceViewport, displayMode, theme, surface, captionStyle, }: DocPlayerProps): react_jsx_runtime.JSX.Element;
294
+ /**
295
+ * Front-door component: resolves the `doc` / `markdown` props into a Doc
296
+ * and renders a themed empty state when neither is provided. The playback
297
+ * machinery lives in `DocPlayerContent` so its hook order never changes
298
+ * when a doc appears or disappears.
299
+ */
300
+ declare function DocPlayer(props: DocPlayerProps): react_jsx_runtime.JSX.Element;
278
301
 
279
302
  /** Default viewport dimensions (1080p landscape) - for backwards compatibility */
280
303
  declare const VIEWPORT: {
@@ -364,12 +387,15 @@ interface DocControlsSlideshowProps {
364
387
  declare function DocControlsSlideshow({ state, slideNav }: DocControlsSlideshowProps): react_jsx_runtime.JSX.Element;
365
388
 
366
389
  interface DocPlayerWithSidebarProps {
367
- script: Doc;
368
- basePath: string;
390
+ /** The Doc to play */
391
+ doc: Doc;
392
+ /** Base path for resolving media URLs (default: `'.'`) */
393
+ basePath?: string;
369
394
  autoPlay?: boolean;
370
395
  onEnded?: () => void;
371
396
  onTimeUpdate?: (time: number) => void;
372
- audioProvider?: AudioProvider;
397
+ /** Optional audio controller (if not provided, uses default HTML5 audio) */
398
+ audioController?: AudioController;
373
399
  muted?: boolean;
374
400
  captionsEnabled?: boolean;
375
401
  isFullscreen?: boolean;
@@ -378,8 +404,15 @@ interface DocPlayerWithSidebarProps {
378
404
  forceViewport?: ViewportConfig$1;
379
405
  /** Called when playing state changes */
380
406
  onPlayingChange?: (isPlaying: boolean) => void;
407
+ /**
408
+ * Theme for rendering. Forwarded to the inner DocPlayer so the sidebar
409
+ * (portrait) layout matches the default (landscape) layout — without it the
410
+ * inner player falls back to DEFAULT_THEME, whose dark text is unreadable
411
+ * over a hero cover image.
412
+ */
413
+ theme?: Theme;
381
414
  }
382
- declare function DocPlayerWithSidebar({ script, basePath, autoPlay, onEnded, onTimeUpdate, audioProvider, muted, captionsEnabled, isFullscreen, onFullscreenToggle, forceViewport, onPlayingChange, }: DocPlayerWithSidebarProps): react_jsx_runtime.JSX.Element;
415
+ declare function DocPlayerWithSidebar({ doc, basePath, autoPlay, onEnded, onTimeUpdate, audioController, muted, captionsEnabled, isFullscreen, onFullscreenToggle, forceViewport, onPlayingChange, theme, }: DocPlayerWithSidebarProps): react_jsx_runtime.JSX.Element;
383
416
 
384
417
  interface DocProgressBarProps {
385
418
  state: PlaybackState$1;
@@ -402,6 +435,12 @@ interface MarkdownRendererProps {
402
435
  * event handlers, and executable URL schemes before rendering.
403
436
  */
404
437
  htmlPolicy?: HtmlPolicy;
438
+ /**
439
+ * Extra URL schemes to allow on links (e.g. a host app's internal
440
+ * navigation scheme it intercepts on click). Executable schemes are
441
+ * never allowed regardless. See {@link SanitizeUrlOptions}.
442
+ */
443
+ linkSchemes?: readonly string[];
405
444
  }
406
445
  /**
407
446
  * Renders MarkdownBlockNode[] AST as React HTML elements.
@@ -411,11 +450,20 @@ interface MarkdownRendererProps {
411
450
  * <MarkdownRenderer nodes={block.contents} />
412
451
  * ```
413
452
  */
414
- declare function MarkdownRenderer({ nodes, className, htmlPolicy, }: MarkdownRendererProps): react_jsx_runtime.JSX.Element | null;
453
+ declare function MarkdownRenderer({ nodes, className, htmlPolicy, linkSchemes, }: MarkdownRendererProps): react_jsx_runtime.JSX.Element | null;
415
454
 
416
455
  interface LinearDocViewProps {
417
- /** The Doc to render */
418
- doc: Doc;
456
+ /**
457
+ * The Doc to render. Wins over `markdown` when both are provided.
458
+ * When neither `doc` nor `markdown` is given, an empty container renders.
459
+ */
460
+ doc?: Doc;
461
+ /**
462
+ * Markdown source to render. When `doc` is absent, the markdown is parsed
463
+ * and converted to a Doc via `markdownToDoc(parseMarkdown(markdown))`.
464
+ * Ignored when `doc` is provided.
465
+ */
466
+ markdown?: string;
419
467
  /** Base path for resolving media URLs (images, etc.) */
420
468
  basePath?: string;
421
469
  /** Viewport config for SVG card rendering (default: landscape) */
@@ -463,7 +511,7 @@ type ImageDisplayMode = 'inline' | 'thumbnail';
463
511
  * <LinearDocView doc={doc} basePath="/media/" />
464
512
  * ```
465
513
  */
466
- declare function LinearDocView({ doc, basePath, viewport, className, theme, surface, thinMargins, imageDisplayMode, }: LinearDocViewProps): react_jsx_runtime.JSX.Element;
514
+ declare function LinearDocView({ doc, markdown, basePath, viewport, className, theme, surface, thinMargins, imageDisplayMode, }: LinearDocViewProps): react_jsx_runtime.JSX.Element;
467
515
 
468
516
  interface InlineVideoPlayerProps {
469
517
  /** Source path — resolved through MediaContext when relative. */
@@ -613,11 +661,12 @@ declare function MediaClipLayer({ schedule, currentTime, isPlaying, basePath, re
613
661
  * Handles multiple audio segments (MP3 files) by tracking which segment
614
662
  * is currently playing and calculating the overall timeline position.
615
663
  *
616
- * This is the HTML5 Audio implementation of the AudioProvider interface.
617
- * For EFB/MSFS environments, use useCompanionAudioSync instead.
664
+ * This is the HTML5 Audio implementation of the AudioController interface.
665
+ * Hosts that drive audio through an external player (e.g. a native shell)
666
+ * can supply their own AudioController to DocPlayer instead of this hook.
618
667
  */
619
668
 
620
- declare function useAudioSync(audioRef: RefObject<HTMLAudioElement>, audioTrack: AudioTrack | undefined, basePath?: string): AudioProvider;
669
+ declare function useAudioSync(audioRef: RefObject<HTMLAudioElement>, audioTrack: AudioTrack | undefined, basePath?: string): AudioController;
621
670
 
622
671
  /**
623
672
  * useMediaSchedule
@@ -763,4 +812,4 @@ interface JsonViewProps {
763
812
  }
764
813
  declare function JsonView(props: JsonViewProps): react_jsx_runtime.JSX.Element;
765
814
 
766
- export { type AudioActions, type AudioProvider, type AudioState, type BlockMarker, BlockRenderer, type CaptionMode, CaptionOverlay, type CaptionStyle, type ControlsLayout, type DisplayMode, DocControlsBottom, DocControlsOverlay, DocControlsSidebar, DocControlsSlideshow, DocPlayer, DocPlayerWithSidebar, DocProgressBar, type ImageDisplayMode, ImageLayer, InlineAudioPlayer, type InlineAudioPlayerProps, InlineVideoPlayer, type InlineVideoPlayerProps, JsonView, type JsonViewProps, LinearDocView, type LinearDocViewProps, MapLayer, MarkdownRenderer, MediaClipLayer, type MediaClipLayerProps, MediaContext, type MediaScheduleController, PathLayer, type PlaybackActions$1 as PlaybackActions, type PlaybackState$1 as PlaybackState, type RenderAudioSegmentInfo, type RenderBlockInfo, type RenderCaptionInfo, type RenderChapterInfo, ShapeLayer, type SlideNavActions, SocialCaptionOverlay, type SquisqRenderAPI, type SquisqWindow, TableLayer, TextLayer, VIEWPORT, VideoLayer, formatTime, useAudioSync, useAutoSurface, useDocPlayback, useMediaProvider, useMediaSchedule, useMediaUrl, useViewportOrientation };
815
+ export { type AudioActions, type AudioController, type AudioState, type BlockMarker, BlockRenderer, type CaptionMode, CaptionOverlay, type CaptionStyle, type ControlsLayout, type DisplayMode, DocControlsBottom, DocControlsOverlay, DocControlsSidebar, DocControlsSlideshow, DocPlayer, DocPlayerWithSidebar, DocProgressBar, type ImageDisplayMode, ImageLayer, InlineAudioPlayer, type InlineAudioPlayerProps, InlineVideoPlayer, type InlineVideoPlayerProps, JsonView, type JsonViewProps, LinearDocView, type LinearDocViewProps, MapLayer, MarkdownRenderer, MediaClipLayer, type MediaClipLayerProps, MediaContext, type MediaScheduleController, PathLayer, type PlaybackActions$1 as PlaybackActions, type PlaybackState$1 as PlaybackState, type RenderAudioSegmentInfo, type RenderBlockInfo, type RenderCaptionInfo, type RenderChapterInfo, ShapeLayer, type SlideNavActions, SocialCaptionOverlay, type SquisqRenderAPI, type SquisqWindow, TableLayer, TextLayer, VIEWPORT, VideoLayer, formatTime, useAudioSync, useAutoSurface, useDocPlayback, useMediaProvider, useMediaSchedule, useMediaUrl, useViewportOrientation };