@bendyline/squisq-react 2.0.1 → 2.2.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +20 -0
  3. package/README.md +6 -0
  4. package/dist/index.d.ts +101 -96
  5. package/dist/index.js +1483 -782
  6. package/dist/squisq-player.full.global.js +4165 -0
  7. package/dist/squisq-player.global.js +561 -168
  8. package/dist/standalone-source.d.ts +1 -0
  9. package/dist/standalone-source.js +1 -1
  10. package/dist/styles/index.css +64 -0
  11. package/package.json +8 -4
  12. package/dist/index.js.map +0 -1
  13. package/dist/squisq-player.css +0 -2
  14. package/dist/squisq-player.css.map +0 -1
  15. package/dist/squisq-player.global.js.map +0 -1
  16. package/src/BlockRenderer.tsx +0 -194
  17. package/src/CaptionOverlay.tsx +0 -111
  18. package/src/DocControlsBottom.tsx +0 -109
  19. package/src/DocControlsOverlay.tsx +0 -190
  20. package/src/DocControlsSidebar.tsx +0 -113
  21. package/src/DocControlsSlideshow.tsx +0 -360
  22. package/src/DocPlayer.tsx +0 -1528
  23. package/src/DocPlayerWithSidebar.tsx +0 -155
  24. package/src/DocProgressBar.tsx +0 -257
  25. package/src/InlineAudioPlayer.tsx +0 -46
  26. package/src/InlineVideoPlayer.tsx +0 -70
  27. package/src/LinearDocView.tsx +0 -569
  28. package/src/MarkdownRenderer.tsx +0 -624
  29. package/src/MediaClipLayer.tsx +0 -145
  30. package/src/SocialCaptionOverlay.tsx +0 -255
  31. package/src/__tests__/BlockRenderer.test.tsx +0 -235
  32. package/src/__tests__/DocControlsSlideshow.test.tsx +0 -221
  33. package/src/__tests__/DocPlayer.test.tsx +0 -635
  34. package/src/__tests__/DocPlayerStylesSentinel.test.tsx +0 -41
  35. package/src/__tests__/DocProgressBar.test.tsx +0 -102
  36. package/src/__tests__/JsonView.test.tsx +0 -111
  37. package/src/__tests__/LinearDocView.test.tsx +0 -426
  38. package/src/__tests__/MapLayer.test.tsx +0 -63
  39. package/src/__tests__/MarkdownRenderer.test.tsx +0 -357
  40. package/src/__tests__/MediaClipLayer.test.tsx +0 -70
  41. package/src/__tests__/MediaContext.test.tsx +0 -51
  42. package/src/__tests__/PathLayer.test.tsx +0 -84
  43. package/src/__tests__/TableLayer.test.tsx +0 -142
  44. package/src/__tests__/VideoLayer.test.tsx +0 -94
  45. package/src/__tests__/exports.test.ts +0 -55
  46. package/src/__tests__/fillStyle.test.tsx +0 -160
  47. package/src/__tests__/standaloneEntry.test.tsx +0 -103
  48. package/src/__tests__/transitionStyles.test.ts +0 -125
  49. package/src/__tests__/useAudioSync.test.ts +0 -49
  50. package/src/__tests__/useDocPlayback.transition.test.ts +0 -113
  51. package/src/__tests__/useJsonViewTokens.test.ts +0 -41
  52. package/src/__tests__/useSlideSwipe.test.ts +0 -81
  53. package/src/__tests__/useViewportOrientation.test.ts +0 -22
  54. package/src/hooks/AudioController.ts +0 -114
  55. package/src/hooks/MediaContext.tsx +0 -97
  56. package/src/hooks/index.ts +0 -11
  57. package/src/hooks/useAudioSync.ts +0 -456
  58. package/src/hooks/useAutoSurface.ts +0 -33
  59. package/src/hooks/useDocPlayback.ts +0 -265
  60. package/src/hooks/useMediaSchedule.ts +0 -39
  61. package/src/hooks/useSlideSwipe.ts +0 -265
  62. package/src/hooks/useViewportOrientation.ts +0 -115
  63. package/src/index.ts +0 -70
  64. package/src/jsonView/JsonView.tsx +0 -51
  65. package/src/jsonView/RenderNode.tsx +0 -51
  66. package/src/jsonView/index.ts +0 -2
  67. package/src/jsonView/json-view.css +0 -206
  68. package/src/jsonView/useJsonViewTokens.ts +0 -32
  69. package/src/jsonView/viewers.tsx +0 -343
  70. package/src/layers/ImageLayer.tsx +0 -297
  71. package/src/layers/MapLayer.tsx +0 -185
  72. package/src/layers/PathLayer.tsx +0 -155
  73. package/src/layers/ShapeLayer.tsx +0 -158
  74. package/src/layers/TableLayer.tsx +0 -129
  75. package/src/layers/TextLayer.tsx +0 -571
  76. package/src/layers/TreeLayer.tsx +0 -167
  77. package/src/layers/VideoLayer.tsx +0 -171
  78. package/src/layers/index.ts +0 -6
  79. package/src/standalone-entry.tsx +0 -337
  80. package/src/standalone-source.d.ts +0 -10
  81. package/src/styles/doc-animations.css +0 -2431
  82. package/src/styles/index.css +0 -7
  83. package/src/types.ts +0 -183
  84. package/src/utils/animationUtils.ts +0 -13
  85. package/src/utils/fillStyle.tsx +0 -148
  86. package/src/utils/layerUtils.ts +0 -42
  87. package/src/utils/mapTileUtils.ts +0 -375
@@ -1,7 +0,0 @@
1
- /**
2
- * @bendyline/squisq-react — bundled stylesheet entry.
3
- * Aggregates all CSS shipped with this package.
4
- */
5
-
6
- @import './doc-animations.css';
7
- @import '../jsonView/json-view.css';
package/src/types.ts DELETED
@@ -1,183 +0,0 @@
1
- /**
2
- * Doc Player Control Types
3
- *
4
- * Shared type definitions for doc player controls across different
5
- * layout modes (overlay, sidebar, bottom). These interfaces allow
6
- * control components to be decoupled from the core DocPlayer.
7
- *
8
- * Related Files:
9
- * - DocPlayer.tsx — Core player component
10
- * - DocProgressBar.tsx — Extracted progress bar
11
- * - DocControlsOverlay.tsx — Overlay controls (default)
12
- * - DocControlsSidebar.tsx — Vertical sidebar controls
13
- * - DocControlsBottom.tsx — Horizontal bottom strip controls
14
- */
15
-
16
- import type { Block } from '@bendyline/squisq/schemas';
17
-
18
- /** Layout mode for doc player controls */
19
- export type ControlsLayout = 'overlay' | 'sidebar' | 'bottom';
20
-
21
- /**
22
- * Display mode for DocPlayer.
23
- *
24
- * - `'video'` — Traditional video-style playback with play/pause, scrub bar,
25
- * and time-based auto-advance. Default mode.
26
- * - `'slideshow'` — PowerPoint-style presentation with prev/next navigation.
27
- * Blocks are treated as static slides that only change on user click.
28
- * No auto-advance, no scrub bar.
29
- * - `'linear'` — Long-scrolling document view. Renders the full markdown as
30
- * readable HTML with template-annotated sections shown as inline SVG cards.
31
- * No audio, no timeline, no controls — just a scrollable page.
32
- * - `'page'` — Plain semantic HTML preview matching what the
33
- * `markdownDocToPlainHtml` export produces. No SquisqPlayer, no SVG
34
- * cards — just `<h1>`/`<p>`/`<ul>` etc. inside a sandboxed iframe.
35
- * Use when you want a WYSIWYG view of the simple HTML export.
36
- * - `'narrate'` — Teleprompter/performance surface. DocPlayer does not
37
- * implement this mode; it is owned by the editor package
38
- * (`@bendyline/squisq-editor-react`), which renders its own
39
- * voice-paced teleprompter view for it.
40
- */
41
- export type DisplayMode = 'video' | 'slideshow' | 'linear' | 'page' | 'narrate';
42
-
43
- /**
44
- * Caption display style.
45
- *
46
- * - `'standard'` — Traditional broadcast-style captions: small white text
47
- * on a semi-transparent black badge at the top of the player.
48
- * - `'social'` — Social media-style (Instagram/TikTok): large centered words
49
- * showing 3-5 words at a time with the active word highlighted in the
50
- * theme's primary color. Font and colors pulled from the active theme.
51
- */
52
- export type CaptionStyle = 'standard' | 'social';
53
-
54
- /**
55
- * Caption display mode — combines enable/disable with style selection.
56
- * The CC button cycles through: off → standard → social → off.
57
- */
58
- export type CaptionMode = 'off' | 'standard' | 'social';
59
-
60
- /** Slide navigation actions for slideshow display mode */
61
- export interface SlideNavActions {
62
- /** Navigate to the next slide */
63
- nextSlide: () => void;
64
- /** Navigate to the previous slide */
65
- prevSlide: () => void;
66
- /** Navigate to a specific slide by index (0-based) */
67
- goToSlide: (index: number) => void;
68
- }
69
-
70
- /** Playback state exposed to external control components */
71
- export interface PlaybackState {
72
- isPlaying: boolean;
73
- currentTime: number;
74
- totalDuration: number;
75
- /** Whether the managed cover is the visual currently shown. */
76
- isCoverVisible?: boolean;
77
- currentBlockIndex: number;
78
- totalBlocks: number;
79
- docProgress: number;
80
- hasCaptions: boolean;
81
- captionsEnabled: boolean;
82
- /** Current caption display mode (off, standard, social). */
83
- captionMode: CaptionMode;
84
- isFullscreen?: boolean;
85
- /** Current audio segment index (0-based) */
86
- currentSegmentIndex: number;
87
- /** Current audio segment name (e.g., 'intro', 'history') */
88
- currentSegmentName: string | null;
89
- /** Current block data (for extracting image info, etc.) */
90
- currentBlock: Block | null;
91
- /** Optional display label for non-block slides, e.g. the managed cover. */
92
- currentSlideLabel?: string;
93
- /** Optional human-facing slide number, separate from internal nav index. */
94
- currentSlideNumber?: number;
95
- /** Optional human-facing slide total, separate from internal nav total. */
96
- totalSlideNumber?: number;
97
- }
98
-
99
- /** Playback actions exposed to external control components */
100
- export interface PlaybackActions {
101
- toggle: () => void;
102
- restart: () => void;
103
- seekTo: (time: number) => void;
104
- setCaptionsEnabled: (enabled: boolean) => void;
105
- /** Cycle caption mode: off → standard → social → off */
106
- cycleCaptionMode: () => void;
107
- toggleFullscreen?: () => void;
108
- }
109
-
110
- /** Block marker data for the progress bar */
111
- export interface BlockMarker {
112
- block: Block;
113
- index: number;
114
- position: number;
115
- title: string;
116
- /** True if this block is the first in a new audio segment (section boundary) */
117
- isSectionStart: boolean;
118
- }
119
-
120
- // ============================================
121
- // Render-mode API (exposed on window for Playwright / debug)
122
- // ============================================
123
-
124
- /** Block metadata returned by SquisqRenderAPI.getBlocks() */
125
- export interface RenderBlockInfo {
126
- id: string;
127
- template: string;
128
- startTime: number;
129
- duration: number;
130
- }
131
-
132
- /** Audio segment info returned by SquisqRenderAPI.getAudioSegments() */
133
- export interface RenderAudioSegmentInfo {
134
- src: string;
135
- name: string;
136
- duration: number;
137
- startTime: number;
138
- }
139
-
140
- /** Caption phrase info returned by SquisqRenderAPI.getCaptions() */
141
- export interface RenderCaptionInfo {
142
- text: string;
143
- startTime: number;
144
- endTime: number;
145
- }
146
-
147
- /** Chapter marker returned by SquisqRenderAPI.getChapters() */
148
- export interface RenderChapterInfo {
149
- title: string;
150
- startTime: number;
151
- duration: number;
152
- }
153
-
154
- /**
155
- * Instance-scoped API created in render mode and debug mode.
156
- * React hosts receive it via `DocPlayer.onRenderAPIReady`; standalone hosts
157
- * receive it from their mount handle.
158
- *
159
- * @example
160
- * ```ts
161
- * const handle = SquisqPlayer.getHandle(rootElement);
162
- * const api = await handle?.renderAPI;
163
- * await api?.seekTo(5.0);
164
- * ```
165
- */
166
- export interface SquisqRenderAPI {
167
- seekTo: (time: number) => Promise<void>;
168
- getDuration: () => number;
169
- getBlocks: () => RenderBlockInfo[];
170
- getAudioSegments: () => RenderAudioSegmentInfo[];
171
- getCaptions: () => RenderCaptionInfo[];
172
- getChapters: () => RenderChapterInfo[];
173
- showCover: () => Promise<void>;
174
- hideCover: () => Promise<void>;
175
- hasCoverBlock: () => boolean;
176
- }
177
-
178
- /** Format time in seconds to MM:SS string */
179
- export function formatTime(seconds: number): string {
180
- const mins = Math.floor(seconds / 60);
181
- const secs = Math.floor(seconds % 60);
182
- return `${mins}:${secs.toString().padStart(2, '0')}`;
183
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Animation Utilities (React)
3
- *
4
- * Re-exports the shared animation utilities for use in the React package.
5
- * All animation logic is in shared/doc/utils.
6
- */
7
-
8
- export {
9
- getAnimationStyle,
10
- getDefaultAnimationDuration,
11
- getTransitionClass,
12
- getAnimationProgress,
13
- } from '@bendyline/squisq/doc';
@@ -1,148 +0,0 @@
1
- /**
2
- * Shared fill / border helpers for the SVG layer renderers (shape, path,
3
- * and the text-box background). Keeping the gradient `<defs>` markup and
4
- * the dash-pattern math in one place means rect, circle, path, and text
5
- * all paint fills and borders identically.
6
- */
7
-
8
- import type {
9
- LinearGradient,
10
- BorderStyle,
11
- ShapePattern,
12
- ShapeFilter,
13
- } from '@bendyline/squisq/schemas';
14
-
15
- /**
16
- * SVG `stroke-dasharray` for a border style, scaled by stroke width so the
17
- * pattern stays proportional. `solid`/undefined → no dashes.
18
- */
19
- export function borderDashArray(
20
- style: BorderStyle | undefined,
21
- strokeWidth: number | undefined,
22
- ): string | undefined {
23
- if (!style || style === 'solid') return undefined;
24
- const w = Math.max(1, strokeWidth ?? 1);
25
- if (style === 'dotted') return `${w} ${w * 2}`;
26
- return `${w * 3} ${w * 2}`; // dashed
27
- }
28
-
29
- /**
30
- * Gradient line endpoints in objectBoundingBox units (0–1) for an angle in
31
- * degrees: 0 = top→bottom, 90 = left→right, increasing clockwise.
32
- */
33
- function gradientVector(angle = 0): { x1: number; y1: number; x2: number; y2: number } {
34
- const a = (angle * Math.PI) / 180;
35
- const dx = Math.sin(a);
36
- const dy = Math.cos(a);
37
- return { x1: 0.5 - dx / 2, y1: 0.5 - dy / 2, x2: 0.5 + dx / 2, y2: 0.5 + dy / 2 };
38
- }
39
-
40
- /** Stable id for a layer's gradient def. */
41
- export function gradientDefId(layerId: string): string {
42
- return `squisq-grad-${layerId}`;
43
- }
44
-
45
- /**
46
- * Resolve a fill to an SVG `fill` value plus the gradient `<defs>` to
47
- * render (or null). When a gradient is present it wins over the solid
48
- * color; otherwise the color (or undefined) is returned verbatim.
49
- */
50
- export function resolveFill(
51
- layerId: string,
52
- color: string | undefined,
53
- gradient: LinearGradient | undefined,
54
- pattern?: ShapePattern,
55
- ): { fill: string | undefined; def: JSX.Element | null } {
56
- if (pattern) {
57
- const id = `squisq-pattern-${layerId}`;
58
- return { fill: `url(#${id})`, def: patternDef(id, pattern) };
59
- }
60
- if (gradient) {
61
- const id = gradientDefId(layerId);
62
- const v = gradientVector(gradient.angle);
63
- return {
64
- fill: `url(#${id})`,
65
- def: (
66
- <linearGradient id={id} x1={v.x1} y1={v.y1} x2={v.x2} y2={v.y2}>
67
- <stop offset="0%" stopColor={gradient.from} />
68
- <stop offset="100%" stopColor={gradient.to} />
69
- </linearGradient>
70
- ),
71
- };
72
- }
73
- return { fill: color, def: null };
74
- }
75
-
76
- /**
77
- * Native SVG `<pattern>` def for a repeating shape fill — fully vector,
78
- * identical in the player and headless frame capture.
79
- */
80
- function patternDef(id: string, pattern: ShapePattern): JSX.Element {
81
- const size = pattern.size ?? 24;
82
- const opacity = pattern.opacity ?? 1;
83
- const color = pattern.color;
84
- return (
85
- <pattern
86
- id={id}
87
- width={size}
88
- height={size}
89
- patternUnits="userSpaceOnUse"
90
- // Diagonal tiles rotate the whole pattern; dots/grid stay axis-aligned.
91
- patternTransform={pattern.kind === 'diagonal' ? 'rotate(45)' : undefined}
92
- >
93
- {pattern.kind === 'dots' && (
94
- <circle
95
- cx={size / 2}
96
- cy={size / 2}
97
- r={Math.max(1, size / 12)}
98
- fill={color}
99
- opacity={opacity}
100
- />
101
- )}
102
- {pattern.kind === 'grid' && (
103
- <path
104
- d={`M ${size} 0 L 0 0 0 ${size}`}
105
- fill="none"
106
- stroke={color}
107
- strokeWidth={1}
108
- opacity={opacity}
109
- />
110
- )}
111
- {pattern.kind === 'diagonal' && (
112
- <line x1={0} y1={0} x2={0} y2={size} stroke={color} strokeWidth={1} opacity={opacity} />
113
- )}
114
- </pattern>
115
- );
116
- }
117
-
118
- /**
119
- * SVG filter def for a shape's procedural filter (static film grain via
120
- * feTurbulence). Returns the def plus the `filter` attribute value.
121
- */
122
- export function resolveShapeFilter(
123
- layerId: string,
124
- filter: ShapeFilter | undefined,
125
- ): { filterAttr: string | undefined; def: JSX.Element | null } {
126
- if (!filter || filter.type !== 'noise') return { filterAttr: undefined, def: null };
127
- const id = `squisq-noise-${layerId}`;
128
- const opacity = filter.opacity ?? 0.05;
129
- return {
130
- filterAttr: `url(#${id})`,
131
- def: (
132
- <filter id={id} x="0%" y="0%" width="100%" height="100%">
133
- <feTurbulence
134
- type="fractalNoise"
135
- baseFrequency={filter.baseFrequency ?? 0.8}
136
- numOctaves={2}
137
- stitchTiles="stitch"
138
- result="noise"
139
- />
140
- <feColorMatrix in="noise" type="saturate" values="0" result="mono" />
141
- <feComponentTransfer in="mono" result="faded">
142
- <feFuncA type="linear" slope={opacity} intercept={0} />
143
- </feComponentTransfer>
144
- <feComposite in="faded" in2="SourceGraphic" operator="in" />
145
- </filter>
146
- ),
147
- };
148
- }
@@ -1,42 +0,0 @@
1
- /**
2
- * Shared utilities for layer components.
3
- *
4
- * Resolves position values and anchor offsets used by all layer types.
5
- */
6
-
7
- /**
8
- * Resolve a position value (number or percentage string) to pixels.
9
- */
10
- export function resolveValue(value: number | string, dimension: number): number {
11
- if (typeof value === 'number') {
12
- return value;
13
- }
14
- if (value.endsWith('%')) {
15
- const percent = parseFloat(value);
16
- return (percent / 100) * dimension;
17
- }
18
- return parseFloat(value);
19
- }
20
-
21
- /**
22
- * Get offset based on anchor point.
23
- */
24
- export function getAnchorOffset(
25
- anchor: string | undefined,
26
- width: number,
27
- height: number,
28
- ): { x: number; y: number } {
29
- switch (anchor) {
30
- case 'center':
31
- return { x: -width / 2, y: -height / 2 };
32
- case 'top-right':
33
- return { x: -width, y: 0 };
34
- case 'bottom-left':
35
- return { x: 0, y: -height };
36
- case 'bottom-right':
37
- return { x: -width, y: -height };
38
- case 'top-left':
39
- default:
40
- return { x: 0, y: 0 };
41
- }
42
- }