@bendyline/squisq-react 1.3.2 → 1.4.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.
- package/dist/index.d.ts +63 -7
- package/dist/index.js +1171 -666
- package/dist/index.js.map +1 -1
- package/dist/squisq-player.css +1 -1
- package/dist/squisq-player.css.map +1 -1
- package/dist/squisq-player.global.js +17 -13
- package/dist/squisq-player.global.js.map +1 -1
- package/dist/standalone-source.js +1 -1
- package/package.json +3 -2
- package/src/BlockRenderer.tsx +15 -7
- package/src/DocPlayer.tsx +65 -12
- package/src/DocProgressBar.tsx +21 -3
- package/src/LinearDocView.tsx +11 -197
- package/src/MarkdownRenderer.tsx +165 -41
- package/src/MediaClipLayer.tsx +135 -0
- package/src/__tests__/DocPlayer.test.tsx +51 -0
- package/src/__tests__/DocProgressBar.test.tsx +76 -0
- package/src/__tests__/MarkdownRenderer.test.tsx +95 -1
- package/src/__tests__/PathLayer.test.tsx +73 -0
- package/src/__tests__/fillStyle.test.tsx +112 -0
- package/src/__tests__/transitionStyles.test.ts +125 -0
- package/src/__tests__/useDocPlayback.transition.test.ts +70 -0
- package/src/hooks/useAudioSync.ts +14 -1
- package/src/hooks/useDocPlayback.ts +81 -100
- package/src/hooks/useMediaSchedule.ts +39 -0
- package/src/index.ts +7 -0
- package/src/layers/ImageLayer.tsx +11 -1
- package/src/layers/PathLayer.tsx +146 -0
- package/src/layers/ShapeLayer.tsx +27 -5
- package/src/layers/TextLayer.tsx +395 -22
- package/src/layers/VideoLayer.tsx +16 -9
- package/src/layers/index.ts +1 -0
- package/src/styles/doc-animations.css +1857 -2
- package/src/utils/fillStyle.tsx +148 -0
package/dist/index.js
CHANGED
|
@@ -1,8 +1,158 @@
|
|
|
1
1
|
// src/DocPlayer.tsx
|
|
2
|
-
import { Fragment as
|
|
3
|
-
import {
|
|
2
|
+
import { Fragment as Fragment3, useRef as useRef6, useState as useState6, useEffect as useEffect7, useCallback as useCallback5, useMemo as useMemo9 } from "react";
|
|
3
|
+
import {
|
|
4
|
+
isTemplateBlock as isTemplateBlock2,
|
|
5
|
+
getCaptionAtTime as getCaptionAtTime2,
|
|
6
|
+
resolveMediaSchedule,
|
|
7
|
+
getDocPlaybackDuration
|
|
8
|
+
} from "@bendyline/squisq/schemas";
|
|
9
|
+
|
|
10
|
+
// src/MediaClipLayer.tsx
|
|
11
|
+
import { useEffect as useEffect2, useRef } from "react";
|
|
12
|
+
|
|
13
|
+
// src/hooks/MediaContext.tsx
|
|
14
|
+
import { createContext, useContext, useState, useEffect, useMemo } from "react";
|
|
15
|
+
var MediaContext = createContext(null);
|
|
16
|
+
function useMediaProvider() {
|
|
17
|
+
return useContext(MediaContext);
|
|
18
|
+
}
|
|
19
|
+
function useMediaUrl(relativePath, basePath) {
|
|
20
|
+
const provider = useMediaProvider();
|
|
21
|
+
const safePath = typeof relativePath === "string" ? relativePath : "";
|
|
22
|
+
const isAbsolute = !safePath || safePath.startsWith("http") || safePath.startsWith("/") || safePath.startsWith("data:") || safePath.startsWith("blob:");
|
|
23
|
+
const fallback = useMemo(
|
|
24
|
+
() => isAbsolute ? safePath : `${basePath}/${safePath}`,
|
|
25
|
+
[isAbsolute, safePath, basePath]
|
|
26
|
+
);
|
|
27
|
+
const needsProvider = !isAbsolute && !!provider;
|
|
28
|
+
const [url, setUrl] = useState(fallback);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!needsProvider) {
|
|
31
|
+
setUrl(fallback);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let cancelled = false;
|
|
35
|
+
provider.resolveUrl(safePath).then((resolved) => {
|
|
36
|
+
if (!cancelled) setUrl(resolved);
|
|
37
|
+
});
|
|
38
|
+
return () => {
|
|
39
|
+
cancelled = true;
|
|
40
|
+
};
|
|
41
|
+
}, [needsProvider, provider, safePath, fallback]);
|
|
42
|
+
return needsProvider ? url : fallback;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/hooks/useMediaSchedule.ts
|
|
46
|
+
import { useMemo as useMemo2 } from "react";
|
|
47
|
+
function useMediaSchedule(schedule, currentTime) {
|
|
48
|
+
const activeIds = useMemo2(() => {
|
|
49
|
+
const ids = /* @__PURE__ */ new Set();
|
|
50
|
+
for (const c of schedule) {
|
|
51
|
+
if (currentTime >= c.absoluteStart && currentTime < c.absoluteEnd) ids.add(c.id);
|
|
52
|
+
}
|
|
53
|
+
return ids;
|
|
54
|
+
}, [schedule, currentTime]);
|
|
55
|
+
return { renderClips: schedule, activeIds };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/MediaClipLayer.tsx
|
|
59
|
+
import { jsx } from "react/jsx-runtime";
|
|
60
|
+
var DRIFT = 0.25;
|
|
61
|
+
function MediaClipLayer({
|
|
62
|
+
schedule,
|
|
63
|
+
currentTime,
|
|
64
|
+
isPlaying,
|
|
65
|
+
basePath,
|
|
66
|
+
renderMode = false
|
|
67
|
+
}) {
|
|
68
|
+
const { renderClips, activeIds } = useMediaSchedule(schedule, currentTime);
|
|
69
|
+
if (renderClips.length === 0) return null;
|
|
70
|
+
return /* @__PURE__ */ jsx("div", { className: "doc-player__media-clips", "aria-hidden": true, children: renderClips.map((clip) => /* @__PURE__ */ jsx(
|
|
71
|
+
MediaClipElement,
|
|
72
|
+
{
|
|
73
|
+
clip,
|
|
74
|
+
active: activeIds.has(clip.id),
|
|
75
|
+
currentTime,
|
|
76
|
+
isPlaying,
|
|
77
|
+
basePath,
|
|
78
|
+
renderMode
|
|
79
|
+
},
|
|
80
|
+
clip.id
|
|
81
|
+
)) });
|
|
82
|
+
}
|
|
83
|
+
function MediaClipElement({
|
|
84
|
+
clip,
|
|
85
|
+
active,
|
|
86
|
+
currentTime,
|
|
87
|
+
isPlaying,
|
|
88
|
+
basePath,
|
|
89
|
+
renderMode
|
|
90
|
+
}) {
|
|
91
|
+
const ref = useRef(null);
|
|
92
|
+
const src = useMediaUrl(clip.src, basePath);
|
|
93
|
+
useEffect2(() => {
|
|
94
|
+
const el = ref.current;
|
|
95
|
+
if (!el) return;
|
|
96
|
+
if (!active) {
|
|
97
|
+
if (!el.paused) el.pause();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const target = Math.max(0, clip.sourceIn + (currentTime - clip.absoluteStart));
|
|
101
|
+
if (renderMode || Math.abs(el.currentTime - target) > DRIFT) {
|
|
102
|
+
try {
|
|
103
|
+
el.currentTime = target;
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (isPlaying && !renderMode) {
|
|
108
|
+
const p = el.play();
|
|
109
|
+
if (p) p.catch(() => {
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
el.pause();
|
|
113
|
+
}
|
|
114
|
+
}, [active, currentTime, isPlaying, renderMode, clip.sourceIn, clip.absoluteStart]);
|
|
115
|
+
const isVideo = clip.kind === "video";
|
|
116
|
+
const common = {
|
|
117
|
+
ref,
|
|
118
|
+
src,
|
|
119
|
+
preload: "auto",
|
|
120
|
+
"data-clip-id": clip.id,
|
|
121
|
+
"data-abs-start": clip.absoluteStart,
|
|
122
|
+
"data-abs-end": clip.absoluteEnd,
|
|
123
|
+
"data-source-in": clip.sourceIn
|
|
124
|
+
};
|
|
125
|
+
if (isVideo) {
|
|
126
|
+
return /* @__PURE__ */ jsx(
|
|
127
|
+
"video",
|
|
128
|
+
{
|
|
129
|
+
...common,
|
|
130
|
+
muted: true,
|
|
131
|
+
playsInline: true,
|
|
132
|
+
style: {
|
|
133
|
+
position: "absolute",
|
|
134
|
+
inset: 0,
|
|
135
|
+
width: "100%",
|
|
136
|
+
height: "100%",
|
|
137
|
+
objectFit: "cover",
|
|
138
|
+
zIndex: 0,
|
|
139
|
+
pointerEvents: "none"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return /* @__PURE__ */ jsx("audio", { ...common, muted: renderMode, style: { position: "absolute", width: 0, height: 0 } });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/DocPlayer.tsx
|
|
4
148
|
import { applySurface as applySurface2 } from "@bendyline/squisq/schemas";
|
|
5
149
|
|
|
150
|
+
// src/BlockRenderer.tsx
|
|
151
|
+
import { resolveTransitionDuration } from "@bendyline/squisq/schemas";
|
|
152
|
+
|
|
153
|
+
// src/layers/ImageLayer.tsx
|
|
154
|
+
import { cssFilterForTreatment } from "@bendyline/squisq/doc";
|
|
155
|
+
|
|
6
156
|
// src/utils/animationUtils.ts
|
|
7
157
|
import {
|
|
8
158
|
getAnimationStyle,
|
|
@@ -38,40 +188,8 @@ function getAnchorOffset(anchor, width, height) {
|
|
|
38
188
|
}
|
|
39
189
|
}
|
|
40
190
|
|
|
41
|
-
// src/hooks/MediaContext.tsx
|
|
42
|
-
import { createContext, useContext, useState, useEffect, useMemo } from "react";
|
|
43
|
-
var MediaContext = createContext(null);
|
|
44
|
-
function useMediaProvider() {
|
|
45
|
-
return useContext(MediaContext);
|
|
46
|
-
}
|
|
47
|
-
function useMediaUrl(relativePath, basePath) {
|
|
48
|
-
const provider = useMediaProvider();
|
|
49
|
-
const safePath = typeof relativePath === "string" ? relativePath : "";
|
|
50
|
-
const isAbsolute = !safePath || safePath.startsWith("http") || safePath.startsWith("/") || safePath.startsWith("data:") || safePath.startsWith("blob:");
|
|
51
|
-
const fallback = useMemo(
|
|
52
|
-
() => isAbsolute ? safePath : `${basePath}/${safePath}`,
|
|
53
|
-
[isAbsolute, safePath, basePath]
|
|
54
|
-
);
|
|
55
|
-
const needsProvider = !isAbsolute && !!provider;
|
|
56
|
-
const [url, setUrl] = useState(fallback);
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
if (!needsProvider) {
|
|
59
|
-
setUrl(fallback);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
let cancelled = false;
|
|
63
|
-
provider.resolveUrl(safePath).then((resolved) => {
|
|
64
|
-
if (!cancelled) setUrl(resolved);
|
|
65
|
-
});
|
|
66
|
-
return () => {
|
|
67
|
-
cancelled = true;
|
|
68
|
-
};
|
|
69
|
-
}, [needsProvider, provider, safePath, fallback]);
|
|
70
|
-
return needsProvider ? url : fallback;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
191
|
// src/layers/ImageLayer.tsx
|
|
74
|
-
import { jsx } from "react/jsx-runtime";
|
|
192
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
75
193
|
function ImageLayer({ layer, basePath, viewport, blockTime }) {
|
|
76
194
|
const { content, position, animation } = layer;
|
|
77
195
|
const x = resolveValue(position.x, viewport.width);
|
|
@@ -83,13 +201,14 @@ function ImageLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
83
201
|
const finalY = y + offset.y;
|
|
84
202
|
const src = useMediaUrl(content.src, basePath);
|
|
85
203
|
const animStyle = getAnimationStyle(animation, blockTime);
|
|
204
|
+
const filter = cssFilterForTreatment(content.treatment, content.blur);
|
|
86
205
|
const preserveAspectRatio = getPreserveAspectRatio(content.fit);
|
|
87
206
|
const isCover = content.fit === "cover";
|
|
88
207
|
const isSpatialAnim = animation && SPATIAL_ANIMATION_TYPES.has(animation.type);
|
|
89
208
|
if (isCover && isSpatialAnim && animation) {
|
|
90
209
|
const kbAnim = remapToKenBurns(animation);
|
|
91
210
|
const kbStyle = getAnimationStyle(kbAnim, blockTime);
|
|
92
|
-
return /* @__PURE__ */
|
|
211
|
+
return /* @__PURE__ */ jsx2("g", { className: "block-layer block-layer--image", "data-layer-id": layer.id, children: /* @__PURE__ */ jsx2("foreignObject", { x: finalX, y: finalY, width, height, children: /* @__PURE__ */ jsx2(
|
|
93
212
|
"div",
|
|
94
213
|
{
|
|
95
214
|
style: {
|
|
@@ -97,7 +216,7 @@ function ImageLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
97
216
|
height: `${height}px`,
|
|
98
217
|
overflow: "hidden"
|
|
99
218
|
},
|
|
100
|
-
children: /* @__PURE__ */
|
|
219
|
+
children: /* @__PURE__ */ jsx2(
|
|
101
220
|
"img",
|
|
102
221
|
{
|
|
103
222
|
src,
|
|
@@ -111,6 +230,7 @@ function ImageLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
111
230
|
display: "block",
|
|
112
231
|
pointerEvents: "none",
|
|
113
232
|
transformOrigin: "center center",
|
|
233
|
+
...filter ? { filter } : {},
|
|
114
234
|
...kbStyle.style
|
|
115
235
|
}
|
|
116
236
|
}
|
|
@@ -119,13 +239,13 @@ function ImageLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
119
239
|
) }) });
|
|
120
240
|
}
|
|
121
241
|
if (isCover) {
|
|
122
|
-
return /* @__PURE__ */
|
|
242
|
+
return /* @__PURE__ */ jsx2(
|
|
123
243
|
"g",
|
|
124
244
|
{
|
|
125
245
|
className: `block-layer block-layer--image ${animStyle.className}`,
|
|
126
246
|
style: animStyle.style,
|
|
127
247
|
"data-layer-id": layer.id,
|
|
128
|
-
children: /* @__PURE__ */
|
|
248
|
+
children: /* @__PURE__ */ jsx2("foreignObject", { x: finalX, y: finalY, width, height, children: /* @__PURE__ */ jsx2(
|
|
129
249
|
"img",
|
|
130
250
|
{
|
|
131
251
|
src,
|
|
@@ -136,20 +256,24 @@ function ImageLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
136
256
|
objectFit: "cover",
|
|
137
257
|
objectPosition: "center",
|
|
138
258
|
display: "block",
|
|
139
|
-
pointerEvents: "none"
|
|
259
|
+
pointerEvents: "none",
|
|
260
|
+
...filter ? { filter } : {},
|
|
261
|
+
// Over-scan blurred imagery so the soft edges never reveal
|
|
262
|
+
// the frame behind the layer.
|
|
263
|
+
...content.blur && content.blur > 0 ? { transform: "scale(1.06)" } : {}
|
|
140
264
|
}
|
|
141
265
|
}
|
|
142
266
|
) })
|
|
143
267
|
}
|
|
144
268
|
);
|
|
145
269
|
}
|
|
146
|
-
return /* @__PURE__ */
|
|
270
|
+
return /* @__PURE__ */ jsx2(
|
|
147
271
|
"g",
|
|
148
272
|
{
|
|
149
273
|
className: `block-layer block-layer--image ${animStyle.className}`,
|
|
150
274
|
style: animStyle.style,
|
|
151
275
|
"data-layer-id": layer.id,
|
|
152
|
-
children: /* @__PURE__ */
|
|
276
|
+
children: /* @__PURE__ */ jsx2(
|
|
153
277
|
"image",
|
|
154
278
|
{
|
|
155
279
|
href: src,
|
|
@@ -158,7 +282,7 @@ function ImageLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
158
282
|
width,
|
|
159
283
|
height,
|
|
160
284
|
preserveAspectRatio,
|
|
161
|
-
style: { pointerEvents: "none" }
|
|
285
|
+
style: { pointerEvents: "none", ...filter ? { filter } : {} }
|
|
162
286
|
}
|
|
163
287
|
)
|
|
164
288
|
}
|
|
@@ -192,16 +316,207 @@ function remapToKenBurns(anim) {
|
|
|
192
316
|
}
|
|
193
317
|
|
|
194
318
|
// src/layers/TextLayer.tsx
|
|
319
|
+
import { useMemo as useMemo3 } from "react";
|
|
195
320
|
import { DEFAULT_DOC_FONT } from "@bendyline/squisq/schemas";
|
|
196
|
-
import {
|
|
197
|
-
|
|
321
|
+
import {
|
|
322
|
+
parseHtmlToNodes,
|
|
323
|
+
sanitizeHtmlNodes,
|
|
324
|
+
stringifyHtmlNodes
|
|
325
|
+
} from "@bendyline/squisq/markdown";
|
|
326
|
+
import {
|
|
327
|
+
hasIconMarker,
|
|
328
|
+
splitIconMarkers,
|
|
329
|
+
stripIconMarkers,
|
|
330
|
+
iconClass
|
|
331
|
+
} from "@bendyline/squisq/icon-marker";
|
|
332
|
+
|
|
333
|
+
// src/utils/fillStyle.tsx
|
|
334
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
335
|
+
function borderDashArray(style, strokeWidth) {
|
|
336
|
+
if (!style || style === "solid") return void 0;
|
|
337
|
+
const w = Math.max(1, strokeWidth ?? 1);
|
|
338
|
+
if (style === "dotted") return `${w} ${w * 2}`;
|
|
339
|
+
return `${w * 3} ${w * 2}`;
|
|
340
|
+
}
|
|
341
|
+
function gradientVector(angle = 0) {
|
|
342
|
+
const a = angle * Math.PI / 180;
|
|
343
|
+
const dx = Math.sin(a);
|
|
344
|
+
const dy = Math.cos(a);
|
|
345
|
+
return { x1: 0.5 - dx / 2, y1: 0.5 - dy / 2, x2: 0.5 + dx / 2, y2: 0.5 + dy / 2 };
|
|
346
|
+
}
|
|
347
|
+
function gradientDefId(layerId) {
|
|
348
|
+
return `squisq-grad-${layerId}`;
|
|
349
|
+
}
|
|
350
|
+
function resolveFill(layerId, color, gradient, pattern) {
|
|
351
|
+
if (pattern) {
|
|
352
|
+
const id = `squisq-pattern-${layerId}`;
|
|
353
|
+
return { fill: `url(#${id})`, def: patternDef(id, pattern) };
|
|
354
|
+
}
|
|
355
|
+
if (gradient) {
|
|
356
|
+
const id = gradientDefId(layerId);
|
|
357
|
+
const v = gradientVector(gradient.angle);
|
|
358
|
+
return {
|
|
359
|
+
fill: `url(#${id})`,
|
|
360
|
+
def: /* @__PURE__ */ jsxs("linearGradient", { id, x1: v.x1, y1: v.y1, x2: v.x2, y2: v.y2, children: [
|
|
361
|
+
/* @__PURE__ */ jsx3("stop", { offset: "0%", stopColor: gradient.from }),
|
|
362
|
+
/* @__PURE__ */ jsx3("stop", { offset: "100%", stopColor: gradient.to })
|
|
363
|
+
] })
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
return { fill: color, def: null };
|
|
367
|
+
}
|
|
368
|
+
function patternDef(id, pattern) {
|
|
369
|
+
const size = pattern.size ?? 24;
|
|
370
|
+
const opacity = pattern.opacity ?? 1;
|
|
371
|
+
const color = pattern.color;
|
|
372
|
+
return /* @__PURE__ */ jsxs(
|
|
373
|
+
"pattern",
|
|
374
|
+
{
|
|
375
|
+
id,
|
|
376
|
+
width: size,
|
|
377
|
+
height: size,
|
|
378
|
+
patternUnits: "userSpaceOnUse",
|
|
379
|
+
patternTransform: pattern.kind === "diagonal" ? "rotate(45)" : void 0,
|
|
380
|
+
children: [
|
|
381
|
+
pattern.kind === "dots" && /* @__PURE__ */ jsx3(
|
|
382
|
+
"circle",
|
|
383
|
+
{
|
|
384
|
+
cx: size / 2,
|
|
385
|
+
cy: size / 2,
|
|
386
|
+
r: Math.max(1, size / 12),
|
|
387
|
+
fill: color,
|
|
388
|
+
opacity
|
|
389
|
+
}
|
|
390
|
+
),
|
|
391
|
+
pattern.kind === "grid" && /* @__PURE__ */ jsx3(
|
|
392
|
+
"path",
|
|
393
|
+
{
|
|
394
|
+
d: `M ${size} 0 L 0 0 0 ${size}`,
|
|
395
|
+
fill: "none",
|
|
396
|
+
stroke: color,
|
|
397
|
+
strokeWidth: 1,
|
|
398
|
+
opacity
|
|
399
|
+
}
|
|
400
|
+
),
|
|
401
|
+
pattern.kind === "diagonal" && /* @__PURE__ */ jsx3("line", { x1: 0, y1: 0, x2: 0, y2: size, stroke: color, strokeWidth: 1, opacity })
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
function resolveShapeFilter(layerId, filter) {
|
|
407
|
+
if (!filter || filter.type !== "noise") return { filterAttr: void 0, def: null };
|
|
408
|
+
const id = `squisq-noise-${layerId}`;
|
|
409
|
+
const opacity = filter.opacity ?? 0.05;
|
|
410
|
+
return {
|
|
411
|
+
filterAttr: `url(#${id})`,
|
|
412
|
+
def: /* @__PURE__ */ jsxs("filter", { id, x: "0%", y: "0%", width: "100%", height: "100%", children: [
|
|
413
|
+
/* @__PURE__ */ jsx3(
|
|
414
|
+
"feTurbulence",
|
|
415
|
+
{
|
|
416
|
+
type: "fractalNoise",
|
|
417
|
+
baseFrequency: filter.baseFrequency ?? 0.8,
|
|
418
|
+
numOctaves: 2,
|
|
419
|
+
stitchTiles: "stitch",
|
|
420
|
+
result: "noise"
|
|
421
|
+
}
|
|
422
|
+
),
|
|
423
|
+
/* @__PURE__ */ jsx3("feColorMatrix", { in: "noise", type: "saturate", values: "0", result: "mono" }),
|
|
424
|
+
/* @__PURE__ */ jsx3("feComponentTransfer", { in: "mono", result: "faded", children: /* @__PURE__ */ jsx3("feFuncA", { type: "linear", slope: opacity, intercept: 0 }) }),
|
|
425
|
+
/* @__PURE__ */ jsx3("feComposite", { in: "faded", in2: "SourceGraphic", operator: "in" })
|
|
426
|
+
] })
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/layers/TextLayer.tsx
|
|
431
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
432
|
+
function TextLayer(props) {
|
|
433
|
+
if (props.layer.content.html?.trim()) return /* @__PURE__ */ jsx4(RichTextLayer, { ...props });
|
|
434
|
+
if (hasIconMarker(props.layer.content.text ?? "")) return /* @__PURE__ */ jsx4(IconTextLayer, { ...props });
|
|
435
|
+
return /* @__PURE__ */ jsx4(PlainTextLayer, { ...props });
|
|
436
|
+
}
|
|
437
|
+
function escapeHtml(value) {
|
|
438
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
439
|
+
}
|
|
440
|
+
function iconRunsToHtml(text) {
|
|
441
|
+
return splitIconMarkers(text).map(
|
|
442
|
+
(run) => run.type === "icon" ? `<i class="${iconClass(run.family, run.name)}" aria-hidden="true"></i>` : escapeHtml(run.text).replace(/\n/g, "<br>")
|
|
443
|
+
).join("");
|
|
444
|
+
}
|
|
445
|
+
function IconTextLayer({ layer, viewport, blockTime }) {
|
|
198
446
|
const { content, position, animation } = layer;
|
|
199
447
|
const { text, style } = content;
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
const
|
|
448
|
+
const rawX = resolveValue(position.x, viewport.width);
|
|
449
|
+
const rawY = resolveValue(position.y, viewport.height);
|
|
450
|
+
const boxWidth = position.width != null ? resolveValue(position.width, viewport.width) : viewport.width;
|
|
451
|
+
const anchor = position.anchor ?? "top-left";
|
|
452
|
+
const lineHeight = style.lineHeight || 1.4;
|
|
453
|
+
const lineHeightPx = style.fontSize * lineHeight;
|
|
454
|
+
const padding = style.padding ?? 0;
|
|
455
|
+
const plain = stripIconMarkers(text ?? "");
|
|
456
|
+
const lines = plain.split("\n").flatMap((line) => wrapText(line, style.fontSize, boxWidth));
|
|
457
|
+
const boxHeight = position.height != null ? resolveValue(position.height, viewport.height) : Math.max(lineHeightPx, lines.length * lineHeightPx) + padding * 2;
|
|
458
|
+
const boxX = rawX - anchorAxis(anchor, boxWidth, "x");
|
|
459
|
+
const boxY = rawY - anchorAxis(anchor, boxHeight, "y");
|
|
460
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
461
|
+
const html = useMemo3(() => iconRunsToHtml(text ?? ""), [text]);
|
|
462
|
+
const verticalJustify = style.verticalAlign === "top" ? "flex-start" : style.verticalAlign === "bottom" ? "flex-end" : "center";
|
|
463
|
+
const boxStyle = {
|
|
464
|
+
boxSizing: "border-box",
|
|
465
|
+
width: "100%",
|
|
466
|
+
height: "100%",
|
|
467
|
+
display: "flex",
|
|
468
|
+
flexDirection: "column",
|
|
469
|
+
justifyContent: verticalJustify,
|
|
470
|
+
padding,
|
|
471
|
+
color: style.color,
|
|
472
|
+
fontSize: `${style.fontSize}px`,
|
|
473
|
+
fontFamily: style.fontFamily || DEFAULT_DOC_FONT,
|
|
474
|
+
fontWeight: style.fontWeight || "normal",
|
|
475
|
+
fontStyle: style.fontStyle || "normal",
|
|
476
|
+
lineHeight,
|
|
477
|
+
textAlign: style.textAlign ?? "left",
|
|
478
|
+
...style.shadow ? { textShadow: "0 2px 3px rgba(0,0,0,0.7)" } : {},
|
|
479
|
+
...animStyle.style
|
|
480
|
+
};
|
|
481
|
+
return /* @__PURE__ */ jsx4("g", { className: `block-layer block-layer--text ${animStyle.className}`, "data-layer-id": layer.id, children: /* @__PURE__ */ jsx4(
|
|
482
|
+
"foreignObject",
|
|
483
|
+
{
|
|
484
|
+
x: boxX,
|
|
485
|
+
y: boxY,
|
|
486
|
+
width: boxWidth,
|
|
487
|
+
height: boxHeight,
|
|
488
|
+
style: { overflow: "visible" },
|
|
489
|
+
children: /* @__PURE__ */ jsx4(
|
|
490
|
+
"div",
|
|
491
|
+
{
|
|
492
|
+
...{ xmlns: "http://www.w3.org/1999/xhtml" },
|
|
493
|
+
style: boxStyle,
|
|
494
|
+
children: /* @__PURE__ */ jsx4(
|
|
495
|
+
"div",
|
|
496
|
+
{
|
|
497
|
+
style: { width: "100%", whiteSpace: "pre-wrap", wordBreak: "break-word" },
|
|
498
|
+
"aria-label": plain,
|
|
499
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
500
|
+
}
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
)
|
|
504
|
+
}
|
|
505
|
+
) });
|
|
506
|
+
}
|
|
507
|
+
function PlainTextLayer({ layer, viewport, blockTime }) {
|
|
508
|
+
const { content, position, animation } = layer;
|
|
509
|
+
const { text, style } = content;
|
|
510
|
+
const rawX = resolveValue(position.x, viewport.width);
|
|
511
|
+
const rawY = resolveValue(position.y, viewport.height);
|
|
512
|
+
const boxWidth = position.width != null ? resolveValue(position.width, viewport.width) : void 0;
|
|
513
|
+
const boxHeight = position.height != null ? resolveValue(position.height, viewport.height) : void 0;
|
|
514
|
+
const maxWidth = boxWidth;
|
|
203
515
|
const textAnchor = getTextAnchor(style.textAlign, position.anchor);
|
|
204
|
-
const dominantBaseline = getDominantBaseline(position.anchor);
|
|
516
|
+
const dominantBaseline = getDominantBaseline(style.verticalAlign, position.anchor);
|
|
517
|
+
const anchor = position.anchor ?? "top-left";
|
|
518
|
+
const x = pivotX(rawX, boxWidth, anchor, textAnchor);
|
|
519
|
+
const y = pivotY(rawY, boxHeight, anchor, dominantBaseline);
|
|
205
520
|
const animStyle = getAnimationStyle(animation, blockTime);
|
|
206
521
|
const rawLines = (text ?? "").split("\n");
|
|
207
522
|
let lines = maxWidth ? rawLines.reduce(
|
|
@@ -219,25 +534,32 @@ function TextLayer({ layer, viewport, blockTime }) {
|
|
|
219
534
|
fontSize: `${style.fontSize}px`,
|
|
220
535
|
fontFamily: style.fontFamily || DEFAULT_DOC_FONT,
|
|
221
536
|
fontWeight: style.fontWeight || "normal",
|
|
537
|
+
fontStyle: style.fontStyle || "normal",
|
|
222
538
|
fill: style.color,
|
|
223
539
|
...animStyle.style
|
|
224
540
|
};
|
|
225
541
|
const filterId = style.shadow ? `shadow-${layer.id}` : void 0;
|
|
226
|
-
return /* @__PURE__ */
|
|
227
|
-
style.shadow && /* @__PURE__ */
|
|
228
|
-
|
|
229
|
-
|
|
542
|
+
return /* @__PURE__ */ jsxs2("g", { className: `block-layer block-layer--text ${animStyle.className}`, "data-layer-id": layer.id, children: [
|
|
543
|
+
style.shadow && /* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsx4("filter", { id: filterId, x: "-20%", y: "-20%", width: "140%", height: "140%", children: /* @__PURE__ */ jsx4("feDropShadow", { dx: "0", dy: "2", stdDeviation: "3", floodColor: "rgba(0,0,0,0.7)" }) }) }),
|
|
544
|
+
/* @__PURE__ */ jsx4(
|
|
545
|
+
TextBox,
|
|
230
546
|
{
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
547
|
+
layerId: layer.id,
|
|
548
|
+
style,
|
|
549
|
+
box: boxWidth != null && boxHeight != null ? {
|
|
550
|
+
x: rawX - anchorAxis(anchor, boxWidth, "x"),
|
|
551
|
+
y: rawY - anchorAxis(anchor, boxHeight, "y"),
|
|
552
|
+
width: boxWidth,
|
|
553
|
+
height: boxHeight
|
|
554
|
+
} : {
|
|
555
|
+
x: x - (style.padding || 16),
|
|
556
|
+
y: y - style.fontSize - (style.padding || 16),
|
|
557
|
+
width: getTextBoxWidth(lines, style) + (style.padding || 16) * 2,
|
|
558
|
+
height: lines.length * lineHeightPx + (style.padding || 16) * 2
|
|
559
|
+
}
|
|
238
560
|
}
|
|
239
561
|
),
|
|
240
|
-
/* @__PURE__ */
|
|
562
|
+
/* @__PURE__ */ jsx4(
|
|
241
563
|
"text",
|
|
242
564
|
{
|
|
243
565
|
x,
|
|
@@ -246,7 +568,7 @@ function TextLayer({ layer, viewport, blockTime }) {
|
|
|
246
568
|
dominantBaseline,
|
|
247
569
|
style: textStyles,
|
|
248
570
|
filter: filterId ? `url(#${filterId})` : void 0,
|
|
249
|
-
children: lines.map((line, i) => /* @__PURE__ */
|
|
571
|
+
children: lines.map((line, i) => /* @__PURE__ */ jsxs2("tspan", { x, dy: i === 0 ? 0 : lineHeightPx, children: [
|
|
250
572
|
line || "\xA0",
|
|
251
573
|
" "
|
|
252
574
|
] }, i))
|
|
@@ -254,6 +576,102 @@ function TextLayer({ layer, viewport, blockTime }) {
|
|
|
254
576
|
)
|
|
255
577
|
] });
|
|
256
578
|
}
|
|
579
|
+
function RichTextLayer({ layer, viewport, blockTime }) {
|
|
580
|
+
const { content, position, animation } = layer;
|
|
581
|
+
const { html, style } = content;
|
|
582
|
+
const rawX = resolveValue(position.x, viewport.width);
|
|
583
|
+
const rawY = resolveValue(position.y, viewport.height);
|
|
584
|
+
const boxWidth = position.width != null ? resolveValue(position.width, viewport.width) : viewport.width;
|
|
585
|
+
const boxHeight = position.height != null ? resolveValue(position.height, viewport.height) : style.fontSize * (style.lineHeight || 1.4) * 2;
|
|
586
|
+
const anchor = position.anchor ?? "top-left";
|
|
587
|
+
const boxX = rawX - anchorAxis(anchor, boxWidth, "x");
|
|
588
|
+
const boxY = rawY - anchorAxis(anchor, boxHeight, "y");
|
|
589
|
+
const safeHtml = useMemo3(
|
|
590
|
+
() => stringifyHtmlNodes(sanitizeHtmlNodes(parseHtmlToNodes(html ?? ""))),
|
|
591
|
+
[html]
|
|
592
|
+
);
|
|
593
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
594
|
+
const verticalJustify = style.verticalAlign === "middle" ? "center" : style.verticalAlign === "bottom" ? "flex-end" : "flex-start";
|
|
595
|
+
const hasBorder = !!(style.borderColor && (style.borderWidth ?? 0) > 0);
|
|
596
|
+
const boxStyle = {
|
|
597
|
+
boxSizing: "border-box",
|
|
598
|
+
width: "100%",
|
|
599
|
+
height: "100%",
|
|
600
|
+
display: "flex",
|
|
601
|
+
flexDirection: "column",
|
|
602
|
+
justifyContent: verticalJustify,
|
|
603
|
+
padding: style.padding ?? 0,
|
|
604
|
+
color: style.color,
|
|
605
|
+
fontSize: `${style.fontSize}px`,
|
|
606
|
+
fontFamily: style.fontFamily || DEFAULT_DOC_FONT,
|
|
607
|
+
fontWeight: style.fontWeight || "normal",
|
|
608
|
+
fontStyle: style.fontStyle || "normal",
|
|
609
|
+
lineHeight: style.lineHeight || 1.4,
|
|
610
|
+
textAlign: style.textAlign ?? "left",
|
|
611
|
+
overflow: "hidden",
|
|
612
|
+
...style.background ? { background: style.background } : {},
|
|
613
|
+
...hasBorder ? {
|
|
614
|
+
border: `${style.borderWidth}px ${style.borderStyle ?? "solid"} ${style.borderColor}`,
|
|
615
|
+
borderRadius: 4
|
|
616
|
+
} : {},
|
|
617
|
+
...style.shadow ? { textShadow: "0 2px 3px rgba(0,0,0,0.7)" } : {},
|
|
618
|
+
...animStyle.style
|
|
619
|
+
};
|
|
620
|
+
const cls = `squisq-rich-text-${cssId(layer.id)}`;
|
|
621
|
+
const scopedCss = `.${cls}{margin:0}.${cls} p{margin:0 0 .4em}.${cls} h1,.${cls} h2,.${cls} h3,.${cls} h4,.${cls} h5,.${cls} h6{margin:0 0 .3em;line-height:1.2}.${cls} ul,.${cls} ol{margin:0 0 .4em;padding-left:1.2em;list-style-position:inside}.${cls} li{margin:0}.${cls} li>p{display:inline;margin:0}.${cls} *:first-child{margin-top:0}.${cls} *:last-child{margin-bottom:0}.${cls} a{color:inherit;text-decoration:underline}`;
|
|
622
|
+
return /* @__PURE__ */ jsx4("g", { className: `block-layer block-layer--text ${animStyle.className}`, "data-layer-id": layer.id, children: /* @__PURE__ */ jsx4("foreignObject", { x: boxX, y: boxY, width: boxWidth, height: boxHeight, children: /* @__PURE__ */ jsxs2(
|
|
623
|
+
"div",
|
|
624
|
+
{
|
|
625
|
+
...{ xmlns: "http://www.w3.org/1999/xhtml" },
|
|
626
|
+
style: boxStyle,
|
|
627
|
+
children: [
|
|
628
|
+
/* @__PURE__ */ jsx4("style", { children: scopedCss }),
|
|
629
|
+
/* @__PURE__ */ jsx4(
|
|
630
|
+
"div",
|
|
631
|
+
{
|
|
632
|
+
className: cls,
|
|
633
|
+
"aria-label": content.text,
|
|
634
|
+
style: { width: "100%", whiteSpace: "pre-wrap", wordBreak: "break-word" },
|
|
635
|
+
dangerouslySetInnerHTML: { __html: safeHtml }
|
|
636
|
+
}
|
|
637
|
+
)
|
|
638
|
+
]
|
|
639
|
+
}
|
|
640
|
+
) }) });
|
|
641
|
+
}
|
|
642
|
+
function cssId(id) {
|
|
643
|
+
return id.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
644
|
+
}
|
|
645
|
+
function TextBox({
|
|
646
|
+
layerId,
|
|
647
|
+
style,
|
|
648
|
+
box
|
|
649
|
+
}) {
|
|
650
|
+
const hasFill = !!(style.background || style.backgroundGradient);
|
|
651
|
+
const hasBorder = !!(style.borderColor && (style.borderWidth ?? 0) > 0);
|
|
652
|
+
if (!hasFill && !hasBorder) return null;
|
|
653
|
+
const { fill, def } = resolveFill(layerId, style.background, style.backgroundGradient);
|
|
654
|
+
const dash = borderDashArray(style.borderStyle, style.borderWidth);
|
|
655
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
656
|
+
def && /* @__PURE__ */ jsx4("defs", { children: def }),
|
|
657
|
+
/* @__PURE__ */ jsx4(
|
|
658
|
+
"rect",
|
|
659
|
+
{
|
|
660
|
+
x: box.x,
|
|
661
|
+
y: box.y,
|
|
662
|
+
width: box.width,
|
|
663
|
+
height: box.height,
|
|
664
|
+
fill: hasFill ? fill : "none",
|
|
665
|
+
fillOpacity: hasFill ? style.backgroundOpacity : void 0,
|
|
666
|
+
stroke: hasBorder ? style.borderColor : void 0,
|
|
667
|
+
strokeWidth: hasBorder ? style.borderWidth : void 0,
|
|
668
|
+
strokeDasharray: hasBorder ? dash : void 0,
|
|
669
|
+
rx: 4,
|
|
670
|
+
ry: 4
|
|
671
|
+
}
|
|
672
|
+
)
|
|
673
|
+
] });
|
|
674
|
+
}
|
|
257
675
|
function getTextAnchor(align, anchor) {
|
|
258
676
|
if (align === "center") return "middle";
|
|
259
677
|
if (align === "right") return "end";
|
|
@@ -262,11 +680,33 @@ function getTextAnchor(align, anchor) {
|
|
|
262
680
|
if (anchor === "center") return "middle";
|
|
263
681
|
return "start";
|
|
264
682
|
}
|
|
265
|
-
function getDominantBaseline(anchor) {
|
|
683
|
+
function getDominantBaseline(verticalAlign, anchor) {
|
|
684
|
+
if (verticalAlign === "top") return "text-before-edge";
|
|
685
|
+
if (verticalAlign === "middle") return "middle";
|
|
686
|
+
if (verticalAlign === "bottom") return "text-after-edge";
|
|
266
687
|
if (anchor?.includes("bottom")) return "text-after-edge";
|
|
267
688
|
if (anchor === "center") return "middle";
|
|
268
689
|
return "text-before-edge";
|
|
269
690
|
}
|
|
691
|
+
function pivotX(rawX, width, anchor, textAnchor) {
|
|
692
|
+
if (width == null) return rawX;
|
|
693
|
+
const boxLeft = rawX - anchorAxis(anchor, width, "x");
|
|
694
|
+
if (textAnchor === "middle") return boxLeft + width / 2;
|
|
695
|
+
if (textAnchor === "end") return boxLeft + width;
|
|
696
|
+
return boxLeft;
|
|
697
|
+
}
|
|
698
|
+
function pivotY(rawY, height, anchor, dominantBaseline) {
|
|
699
|
+
if (height == null) return rawY;
|
|
700
|
+
const boxTop = rawY - anchorAxis(anchor, height, "y");
|
|
701
|
+
if (dominantBaseline === "middle") return boxTop + height / 2;
|
|
702
|
+
if (dominantBaseline === "text-after-edge") return boxTop + height;
|
|
703
|
+
return boxTop;
|
|
704
|
+
}
|
|
705
|
+
function anchorAxis(anchor, size, axis) {
|
|
706
|
+
if (anchor === "center") return size / 2;
|
|
707
|
+
if (axis === "x") return anchor.includes("right") ? size : 0;
|
|
708
|
+
return anchor.includes("bottom") ? size : 0;
|
|
709
|
+
}
|
|
270
710
|
function getTextBoxWidth(lines, style) {
|
|
271
711
|
const maxLineLength = Math.max(...lines.map((l) => l.length));
|
|
272
712
|
return maxLineLength * style.fontSize * 0.55;
|
|
@@ -306,7 +746,7 @@ function wrapText(text, fontSize, maxWidth) {
|
|
|
306
746
|
}
|
|
307
747
|
|
|
308
748
|
// src/layers/ShapeLayer.tsx
|
|
309
|
-
import { jsx as
|
|
749
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
310
750
|
function ShapeLayer({ layer, viewport, blockTime }) {
|
|
311
751
|
const { content, position, animation } = layer;
|
|
312
752
|
const rawX = resolveValue(position.x, viewport.width);
|
|
@@ -319,14 +759,14 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
319
759
|
const animStyle = getAnimationStyle(animation, blockTime);
|
|
320
760
|
const fill = content.fill || "none";
|
|
321
761
|
const isCSSGradient = typeof fill === "string" && fill.includes("gradient(");
|
|
322
|
-
if (content.shape === "rect" && isCSSGradient) {
|
|
323
|
-
return /* @__PURE__ */
|
|
762
|
+
if (content.shape === "rect" && isCSSGradient && !content.gradient) {
|
|
763
|
+
return /* @__PURE__ */ jsx5(
|
|
324
764
|
"g",
|
|
325
765
|
{
|
|
326
766
|
className: `block-layer block-layer--shape ${animStyle.className}`,
|
|
327
767
|
style: animStyle.style,
|
|
328
768
|
"data-layer-id": layer.id,
|
|
329
|
-
children: /* @__PURE__ */
|
|
769
|
+
children: /* @__PURE__ */ jsx5("foreignObject", { x, y, width, height, children: /* @__PURE__ */ jsx5(
|
|
330
770
|
"div",
|
|
331
771
|
{
|
|
332
772
|
style: {
|
|
@@ -341,19 +781,34 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
341
781
|
}
|
|
342
782
|
);
|
|
343
783
|
}
|
|
344
|
-
const
|
|
784
|
+
const { fill: fillValue, def: fillDef } = resolveFill(
|
|
785
|
+
layer.id,
|
|
345
786
|
fill,
|
|
787
|
+
content.gradient,
|
|
788
|
+
content.pattern
|
|
789
|
+
);
|
|
790
|
+
const { filterAttr, def: filterDef } = resolveShapeFilter(layer.id, content.filter);
|
|
791
|
+
const dash = borderDashArray(content.borderStyle, content.strokeWidth);
|
|
792
|
+
const shapeProps = {
|
|
793
|
+
fill: fillValue,
|
|
794
|
+
fillOpacity: content.fillOpacity,
|
|
346
795
|
stroke: content.stroke,
|
|
347
|
-
strokeWidth: content.strokeWidth
|
|
796
|
+
strokeWidth: content.strokeWidth,
|
|
797
|
+
strokeDasharray: dash,
|
|
798
|
+
...filterAttr ? { filter: filterAttr } : {}
|
|
348
799
|
};
|
|
349
|
-
return /* @__PURE__ */
|
|
800
|
+
return /* @__PURE__ */ jsxs3(
|
|
350
801
|
"g",
|
|
351
802
|
{
|
|
352
803
|
className: `block-layer block-layer--shape ${animStyle.className}`,
|
|
353
804
|
style: animStyle.style,
|
|
354
805
|
"data-layer-id": layer.id,
|
|
355
806
|
children: [
|
|
356
|
-
|
|
807
|
+
(fillDef || filterDef) && /* @__PURE__ */ jsxs3("defs", { children: [
|
|
808
|
+
fillDef,
|
|
809
|
+
filterDef
|
|
810
|
+
] }),
|
|
811
|
+
content.shape === "rect" && /* @__PURE__ */ jsx5(
|
|
357
812
|
"rect",
|
|
358
813
|
{
|
|
359
814
|
x,
|
|
@@ -365,7 +820,7 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
365
820
|
...shapeProps
|
|
366
821
|
}
|
|
367
822
|
),
|
|
368
|
-
content.shape === "circle" && /* @__PURE__ */
|
|
823
|
+
content.shape === "circle" && /* @__PURE__ */ jsx5(
|
|
369
824
|
"circle",
|
|
370
825
|
{
|
|
371
826
|
cx: x + width / 2,
|
|
@@ -374,7 +829,7 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
374
829
|
...shapeProps
|
|
375
830
|
}
|
|
376
831
|
),
|
|
377
|
-
content.shape === "line" && /* @__PURE__ */
|
|
832
|
+
content.shape === "line" && /* @__PURE__ */ jsx5(
|
|
378
833
|
"line",
|
|
379
834
|
{
|
|
380
835
|
x1: x,
|
|
@@ -382,16 +837,108 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
382
837
|
x2: x + width,
|
|
383
838
|
y2: y + height,
|
|
384
839
|
stroke: content.stroke || "#ffffff",
|
|
385
|
-
strokeWidth: content.strokeWidth || 2
|
|
840
|
+
strokeWidth: content.strokeWidth || 2,
|
|
841
|
+
strokeDasharray: dash
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
]
|
|
845
|
+
}
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// src/layers/PathLayer.tsx
|
|
850
|
+
import { markerPath, shapePath } from "@bendyline/squisq/doc";
|
|
851
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
852
|
+
function effectivePath(layer, viewport) {
|
|
853
|
+
const { content, position } = layer;
|
|
854
|
+
if (!content.shapeKind) return content.d;
|
|
855
|
+
const w = position.width ? resolveValue(position.width, viewport.width) : 0;
|
|
856
|
+
const h = position.height ? resolveValue(position.height, viewport.height) : 0;
|
|
857
|
+
const rawX = resolveValue(position.x, viewport.width);
|
|
858
|
+
const rawY = resolveValue(position.y, viewport.height);
|
|
859
|
+
const anchor = getAnchorOffset(position.anchor, w, h);
|
|
860
|
+
const derived = shapePath(content.shapeKind, rawX + anchor.x, rawY + anchor.y, w, h);
|
|
861
|
+
return derived ?? content.d;
|
|
862
|
+
}
|
|
863
|
+
function effectiveMarker(explicit, arrow, end) {
|
|
864
|
+
if (explicit) return explicit;
|
|
865
|
+
const wants = arrow === "both" || arrow === end;
|
|
866
|
+
return wants ? "arrow" : "none";
|
|
867
|
+
}
|
|
868
|
+
function PathLayer({ layer, viewport, blockTime }) {
|
|
869
|
+
const { content, animation, id } = layer;
|
|
870
|
+
const d = effectivePath(layer, viewport);
|
|
871
|
+
const stroke = content.stroke ?? "#1e293b";
|
|
872
|
+
const strokeWidth = content.strokeWidth ?? 2;
|
|
873
|
+
const { fill, def: fillDef } = resolveFill(id, content.fill ?? "none", content.gradient);
|
|
874
|
+
const dash = content.borderStyle ? borderDashArray(content.borderStyle, strokeWidth) : content.dasharray;
|
|
875
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
876
|
+
const startId = `marker-start-${id}`;
|
|
877
|
+
const endId = `marker-end-${id}`;
|
|
878
|
+
const start = markerPath(effectiveMarker(content.startMarker, content.arrow, "start"), "start");
|
|
879
|
+
const end = markerPath(effectiveMarker(content.endMarker, content.arrow, "end"), "end");
|
|
880
|
+
return /* @__PURE__ */ jsxs4(
|
|
881
|
+
"g",
|
|
882
|
+
{
|
|
883
|
+
className: `block-layer block-layer--path ${animStyle.className}`,
|
|
884
|
+
style: animStyle.style,
|
|
885
|
+
"data-layer-id": id,
|
|
886
|
+
children: [
|
|
887
|
+
/* @__PURE__ */ jsxs4("defs", { children: [
|
|
888
|
+
fillDef,
|
|
889
|
+
end && /* @__PURE__ */ jsx6(MarkerDef, { id: endId, dir: "end", d: end.d, filled: end.filled, stroke }),
|
|
890
|
+
start && /* @__PURE__ */ jsx6(MarkerDef, { id: startId, dir: "start", d: start.d, filled: start.filled, stroke })
|
|
891
|
+
] }),
|
|
892
|
+
/* @__PURE__ */ jsx6(
|
|
893
|
+
"path",
|
|
894
|
+
{
|
|
895
|
+
d,
|
|
896
|
+
stroke,
|
|
897
|
+
strokeWidth,
|
|
898
|
+
fill,
|
|
899
|
+
fillOpacity: content.fillOpacity,
|
|
900
|
+
strokeDasharray: dash,
|
|
901
|
+
markerStart: start ? `url(#${startId})` : void 0,
|
|
902
|
+
markerEnd: end ? `url(#${endId})` : void 0
|
|
386
903
|
}
|
|
387
904
|
)
|
|
388
905
|
]
|
|
389
906
|
}
|
|
390
907
|
);
|
|
391
908
|
}
|
|
909
|
+
function MarkerDef({
|
|
910
|
+
id,
|
|
911
|
+
dir,
|
|
912
|
+
d,
|
|
913
|
+
filled,
|
|
914
|
+
stroke
|
|
915
|
+
}) {
|
|
916
|
+
return /* @__PURE__ */ jsx6(
|
|
917
|
+
"marker",
|
|
918
|
+
{
|
|
919
|
+
id,
|
|
920
|
+
viewBox: "0 0 10 10",
|
|
921
|
+
refX: dir === "end" ? 9 : 1,
|
|
922
|
+
refY: 5,
|
|
923
|
+
markerWidth: 4,
|
|
924
|
+
markerHeight: 4,
|
|
925
|
+
orient: "auto-start-reverse",
|
|
926
|
+
markerUnits: "strokeWidth",
|
|
927
|
+
children: /* @__PURE__ */ jsx6(
|
|
928
|
+
"path",
|
|
929
|
+
{
|
|
930
|
+
d,
|
|
931
|
+
fill: filled ? stroke : "none",
|
|
932
|
+
stroke: filled ? "none" : stroke,
|
|
933
|
+
strokeWidth: filled ? void 0 : 1.5
|
|
934
|
+
}
|
|
935
|
+
)
|
|
936
|
+
}
|
|
937
|
+
);
|
|
938
|
+
}
|
|
392
939
|
|
|
393
940
|
// src/layers/MapLayer.tsx
|
|
394
|
-
import { useState as useState2, useEffect as
|
|
941
|
+
import { useState as useState2, useEffect as useEffect3 } from "react";
|
|
395
942
|
|
|
396
943
|
// src/utils/mapTileUtils.ts
|
|
397
944
|
var TILE_PROVIDERS = {
|
|
@@ -574,7 +1121,7 @@ function drawAttribution(ctx, text, width, height) {
|
|
|
574
1121
|
}
|
|
575
1122
|
|
|
576
1123
|
// src/layers/MapLayer.tsx
|
|
577
|
-
import { jsx as
|
|
1124
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
578
1125
|
function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
579
1126
|
const { content, position, animation } = layer;
|
|
580
1127
|
const [mapImageUrl, setMapImageUrl] = useState2(null);
|
|
@@ -587,7 +1134,7 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
587
1134
|
const offset = getAnchorOffset(position.anchor, width, height);
|
|
588
1135
|
const finalX = x + offset.x;
|
|
589
1136
|
const finalY = y + offset.y;
|
|
590
|
-
|
|
1137
|
+
useEffect3(() => {
|
|
591
1138
|
let cancelled = false;
|
|
592
1139
|
if (content.staticSrc) {
|
|
593
1140
|
const src = content.staticSrc.startsWith("http") ? content.staticSrc : `${basePath}/${content.staticSrc}`;
|
|
@@ -632,15 +1179,15 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
632
1179
|
]);
|
|
633
1180
|
const animStyle = getAnimationStyle(animation, blockTime);
|
|
634
1181
|
if (isLoading) {
|
|
635
|
-
return /* @__PURE__ */
|
|
1182
|
+
return /* @__PURE__ */ jsxs5(
|
|
636
1183
|
"g",
|
|
637
1184
|
{
|
|
638
1185
|
className: `block-layer block-layer--map ${animStyle.className}`,
|
|
639
1186
|
style: animStyle.style,
|
|
640
1187
|
"data-layer-id": layer.id,
|
|
641
1188
|
children: [
|
|
642
|
-
/* @__PURE__ */
|
|
643
|
-
/* @__PURE__ */
|
|
1189
|
+
/* @__PURE__ */ jsx7("rect", { x: finalX, y: finalY, width, height, fill: "#e5e7eb" }),
|
|
1190
|
+
/* @__PURE__ */ jsx7(
|
|
644
1191
|
"text",
|
|
645
1192
|
{
|
|
646
1193
|
x: finalX + width / 2,
|
|
@@ -658,15 +1205,15 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
658
1205
|
);
|
|
659
1206
|
}
|
|
660
1207
|
if (error || !mapImageUrl) {
|
|
661
|
-
return /* @__PURE__ */
|
|
1208
|
+
return /* @__PURE__ */ jsxs5(
|
|
662
1209
|
"g",
|
|
663
1210
|
{
|
|
664
1211
|
className: `block-layer block-layer--map ${animStyle.className}`,
|
|
665
1212
|
style: animStyle.style,
|
|
666
1213
|
"data-layer-id": layer.id,
|
|
667
1214
|
children: [
|
|
668
|
-
/* @__PURE__ */
|
|
669
|
-
/* @__PURE__ */
|
|
1215
|
+
/* @__PURE__ */ jsx7("rect", { x: finalX, y: finalY, width, height, fill: "#fef2f2" }),
|
|
1216
|
+
/* @__PURE__ */ jsx7(
|
|
670
1217
|
"text",
|
|
671
1218
|
{
|
|
672
1219
|
x: finalX + width / 2,
|
|
@@ -683,15 +1230,15 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
683
1230
|
}
|
|
684
1231
|
);
|
|
685
1232
|
}
|
|
686
|
-
return /* @__PURE__ */
|
|
1233
|
+
return /* @__PURE__ */ jsxs5(
|
|
687
1234
|
"g",
|
|
688
1235
|
{
|
|
689
1236
|
className: `block-layer block-layer--map ${animStyle.className}`,
|
|
690
1237
|
style: animStyle.style,
|
|
691
1238
|
"data-layer-id": layer.id,
|
|
692
1239
|
children: [
|
|
693
|
-
/* @__PURE__ */
|
|
694
|
-
/* @__PURE__ */
|
|
1240
|
+
/* @__PURE__ */ jsx7("defs", { children: /* @__PURE__ */ jsx7("clipPath", { id: `clip-${layer.id}`, children: /* @__PURE__ */ jsx7("rect", { x: finalX, y: finalY, width, height }) }) }),
|
|
1241
|
+
/* @__PURE__ */ jsx7("g", { clipPath: `url(#clip-${layer.id})`, children: /* @__PURE__ */ jsx7(
|
|
695
1242
|
"image",
|
|
696
1243
|
{
|
|
697
1244
|
href: mapImageUrl,
|
|
@@ -709,18 +1256,14 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
709
1256
|
}
|
|
710
1257
|
|
|
711
1258
|
// src/layers/VideoLayer.tsx
|
|
712
|
-
import { useRef, useEffect as
|
|
713
|
-
import { jsx as
|
|
714
|
-
function VideoLayer({
|
|
715
|
-
layer,
|
|
716
|
-
basePath,
|
|
717
|
-
viewport,
|
|
718
|
-
blockTime: _blockTime,
|
|
719
|
-
isPlaying
|
|
720
|
-
}) {
|
|
1259
|
+
import { useRef as useRef2, useEffect as useEffect4 } from "react";
|
|
1260
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1261
|
+
function VideoLayer({ layer, basePath, viewport, blockTime, isPlaying }) {
|
|
721
1262
|
const { content, position } = layer;
|
|
722
|
-
const videoRef =
|
|
723
|
-
const hasStartedRef =
|
|
1263
|
+
const videoRef = useRef2(null);
|
|
1264
|
+
const hasStartedRef = useRef2(false);
|
|
1265
|
+
const startAt = content.startAt ?? 0;
|
|
1266
|
+
const gated = blockTime < startAt;
|
|
724
1267
|
const x = resolveValue(position.x, viewport.width);
|
|
725
1268
|
const y = resolveValue(position.y, viewport.height);
|
|
726
1269
|
const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;
|
|
@@ -731,7 +1274,7 @@ function VideoLayer({
|
|
|
731
1274
|
const src = useMediaUrl(content.src, basePath);
|
|
732
1275
|
const resolvedPoster = useMediaUrl(content.posterSrc || "", basePath);
|
|
733
1276
|
const posterSrc = content.posterSrc ? resolvedPoster : void 0;
|
|
734
|
-
|
|
1277
|
+
useEffect4(() => {
|
|
735
1278
|
const video = videoRef.current;
|
|
736
1279
|
if (!video) return;
|
|
737
1280
|
video.currentTime = content.clipStart;
|
|
@@ -755,9 +1298,14 @@ function VideoLayer({
|
|
|
755
1298
|
video.pause();
|
|
756
1299
|
};
|
|
757
1300
|
}, [content.src, content.clipStart, content.clipEnd]);
|
|
758
|
-
|
|
1301
|
+
useEffect4(() => {
|
|
759
1302
|
const video = videoRef.current;
|
|
760
1303
|
if (!video || !hasStartedRef.current) return;
|
|
1304
|
+
if (gated) {
|
|
1305
|
+
video.pause();
|
|
1306
|
+
video.currentTime = content.clipStart;
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
761
1309
|
if (video.currentTime >= content.clipEnd) return;
|
|
762
1310
|
if (isPlaying) {
|
|
763
1311
|
const playPromise = video.play();
|
|
@@ -768,8 +1316,8 @@ function VideoLayer({
|
|
|
768
1316
|
} else {
|
|
769
1317
|
video.pause();
|
|
770
1318
|
}
|
|
771
|
-
}, [isPlaying, content.clipEnd]);
|
|
772
|
-
return /* @__PURE__ */
|
|
1319
|
+
}, [isPlaying, gated, content.clipStart, content.clipEnd]);
|
|
1320
|
+
return /* @__PURE__ */ jsx8("g", { className: "block-layer block-layer--video", "data-layer-id": layer.id, children: /* @__PURE__ */ jsx8("foreignObject", { x: finalX, y: finalY, width, height, children: /* @__PURE__ */ jsx8(
|
|
773
1321
|
"video",
|
|
774
1322
|
{
|
|
775
1323
|
ref: videoRef,
|
|
@@ -780,6 +1328,7 @@ function VideoLayer({
|
|
|
780
1328
|
preload: "auto",
|
|
781
1329
|
"data-clip-start": content.clipStart,
|
|
782
1330
|
"data-clip-end": content.clipEnd,
|
|
1331
|
+
"data-start-at": startAt,
|
|
783
1332
|
style: {
|
|
784
1333
|
width: `${width}px`,
|
|
785
1334
|
height: `${height}px`,
|
|
@@ -793,7 +1342,7 @@ function VideoLayer({
|
|
|
793
1342
|
}
|
|
794
1343
|
|
|
795
1344
|
// src/layers/TableLayer.tsx
|
|
796
|
-
import { jsx as
|
|
1345
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
797
1346
|
function TableLayer({ layer, viewport, blockTime }) {
|
|
798
1347
|
const { content, position, animation } = layer;
|
|
799
1348
|
const { headers, rows, align, style } = content;
|
|
@@ -810,7 +1359,7 @@ function TableLayer({ layer, viewport, blockTime }) {
|
|
|
810
1359
|
return a ? { textAlign: a } : void 0;
|
|
811
1360
|
};
|
|
812
1361
|
const borderRadius = style.borderRadius ?? 8;
|
|
813
|
-
return /* @__PURE__ */
|
|
1362
|
+
return /* @__PURE__ */ jsx9("foreignObject", { x: finalX, y: finalY, width, height, style: animStyle, children: /* @__PURE__ */ jsx9(
|
|
814
1363
|
"div",
|
|
815
1364
|
{
|
|
816
1365
|
...{ xmlns: "http://www.w3.org/1999/xhtml" },
|
|
@@ -823,7 +1372,7 @@ function TableLayer({ layer, viewport, blockTime }) {
|
|
|
823
1372
|
padding: "16px",
|
|
824
1373
|
boxSizing: "border-box"
|
|
825
1374
|
},
|
|
826
|
-
children: /* @__PURE__ */
|
|
1375
|
+
children: /* @__PURE__ */ jsxs6(
|
|
827
1376
|
"table",
|
|
828
1377
|
{
|
|
829
1378
|
style: {
|
|
@@ -837,7 +1386,7 @@ function TableLayer({ layer, viewport, blockTime }) {
|
|
|
837
1386
|
border: `1px solid ${style.borderColor}`
|
|
838
1387
|
},
|
|
839
1388
|
children: [
|
|
840
|
-
headers.length > 0 && /* @__PURE__ */
|
|
1389
|
+
headers.length > 0 && /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsx9("tr", { children: headers.map((header, ci) => /* @__PURE__ */ jsx9(
|
|
841
1390
|
"th",
|
|
842
1391
|
{
|
|
843
1392
|
style: {
|
|
@@ -854,7 +1403,7 @@ function TableLayer({ layer, viewport, blockTime }) {
|
|
|
854
1403
|
},
|
|
855
1404
|
ci
|
|
856
1405
|
)) }) }),
|
|
857
|
-
rows.length > 0 && /* @__PURE__ */
|
|
1406
|
+
rows.length > 0 && /* @__PURE__ */ jsx9("tbody", { children: rows.map((row, ri) => /* @__PURE__ */ jsx9("tr", { children: row.map((cell, ci) => /* @__PURE__ */ jsx9(
|
|
858
1407
|
"td",
|
|
859
1408
|
{
|
|
860
1409
|
style: {
|
|
@@ -877,7 +1426,7 @@ function TableLayer({ layer, viewport, blockTime }) {
|
|
|
877
1426
|
}
|
|
878
1427
|
|
|
879
1428
|
// src/BlockRenderer.tsx
|
|
880
|
-
import { jsx as
|
|
1429
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
881
1430
|
var VIEWPORT = {
|
|
882
1431
|
width: 1920,
|
|
883
1432
|
height: 1080
|
|
@@ -888,20 +1437,22 @@ function BlockRenderer({
|
|
|
888
1437
|
basePath,
|
|
889
1438
|
isEntering = false,
|
|
890
1439
|
isExiting = false,
|
|
1440
|
+
transition,
|
|
891
1441
|
viewport = VIEWPORT,
|
|
892
1442
|
isPlaying
|
|
893
1443
|
}) {
|
|
894
1444
|
let transitionClass = "";
|
|
895
1445
|
const transitionStyle = {};
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1446
|
+
const activeTransition = transition ?? block.transition;
|
|
1447
|
+
if (activeTransition && isEntering) {
|
|
1448
|
+
transitionClass = getTransitionClass(activeTransition.type, true, activeTransition.direction);
|
|
1449
|
+
transitionStyle["--transition-duration"] = `${resolveTransitionDuration(activeTransition)}s`;
|
|
1450
|
+
} else if (activeTransition && isExiting) {
|
|
1451
|
+
transitionClass = getTransitionClass(activeTransition.type, false, activeTransition.direction);
|
|
1452
|
+
transitionStyle["--transition-duration"] = `${resolveTransitionDuration(activeTransition)}s`;
|
|
902
1453
|
}
|
|
903
1454
|
const clipId = `vb-clip-${block.id}`;
|
|
904
|
-
return /* @__PURE__ */
|
|
1455
|
+
return /* @__PURE__ */ jsxs7(
|
|
905
1456
|
"svg",
|
|
906
1457
|
{
|
|
907
1458
|
className: `block-svg ${transitionClass}`,
|
|
@@ -911,8 +1462,8 @@ function BlockRenderer({
|
|
|
911
1462
|
overflow: "hidden",
|
|
912
1463
|
"data-block-id": block.id,
|
|
913
1464
|
children: [
|
|
914
|
-
/* @__PURE__ */
|
|
915
|
-
/* @__PURE__ */
|
|
1465
|
+
/* @__PURE__ */ jsx10("defs", { children: /* @__PURE__ */ jsx10("clipPath", { id: clipId, children: /* @__PURE__ */ jsx10("rect", { x: "0", y: "0", width: viewport.width, height: viewport.height }) }) }),
|
|
1466
|
+
/* @__PURE__ */ jsx10("g", { clipPath: `url(#${clipId})`, children: (block.layers ?? []).map((layer) => /* @__PURE__ */ jsx10(
|
|
916
1467
|
LayerRenderer,
|
|
917
1468
|
{
|
|
918
1469
|
layer,
|
|
@@ -930,15 +1481,17 @@ function BlockRenderer({
|
|
|
930
1481
|
function LayerRenderer({ layer, basePath, viewport, blockTime, isPlaying }) {
|
|
931
1482
|
switch (layer.type) {
|
|
932
1483
|
case "image":
|
|
933
|
-
return /* @__PURE__ */
|
|
1484
|
+
return /* @__PURE__ */ jsx10(ImageLayer, { layer, basePath, viewport, blockTime });
|
|
934
1485
|
case "text":
|
|
935
|
-
return /* @__PURE__ */
|
|
1486
|
+
return /* @__PURE__ */ jsx10(TextLayer, { layer, viewport, blockTime });
|
|
936
1487
|
case "shape":
|
|
937
|
-
return /* @__PURE__ */
|
|
1488
|
+
return /* @__PURE__ */ jsx10(ShapeLayer, { layer, viewport, blockTime });
|
|
1489
|
+
case "path":
|
|
1490
|
+
return /* @__PURE__ */ jsx10(PathLayer, { layer, viewport, blockTime });
|
|
938
1491
|
case "map":
|
|
939
|
-
return /* @__PURE__ */
|
|
1492
|
+
return /* @__PURE__ */ jsx10(MapLayer, { layer, basePath, viewport, blockTime });
|
|
940
1493
|
case "video":
|
|
941
|
-
return /* @__PURE__ */
|
|
1494
|
+
return /* @__PURE__ */ jsx10(
|
|
942
1495
|
VideoLayer,
|
|
943
1496
|
{
|
|
944
1497
|
layer,
|
|
@@ -949,7 +1502,7 @@ function LayerRenderer({ layer, basePath, viewport, blockTime, isPlaying }) {
|
|
|
949
1502
|
}
|
|
950
1503
|
);
|
|
951
1504
|
case "table":
|
|
952
|
-
return /* @__PURE__ */
|
|
1505
|
+
return /* @__PURE__ */ jsx10(TableLayer, { layer, viewport, blockTime });
|
|
953
1506
|
default:
|
|
954
1507
|
console.warn(`Unknown layer type: ${layer.type}`);
|
|
955
1508
|
return null;
|
|
@@ -960,9 +1513,9 @@ function LayerRenderer({ layer, basePath, viewport, blockTime, isPlaying }) {
|
|
|
960
1513
|
import { getCaptionAtTime } from "@bendyline/squisq/schemas";
|
|
961
1514
|
|
|
962
1515
|
// src/SocialCaptionOverlay.tsx
|
|
963
|
-
import { useMemo as
|
|
1516
|
+
import { useMemo as useMemo4 } from "react";
|
|
964
1517
|
import { resolveFontFamily } from "@bendyline/squisq/schemas";
|
|
965
|
-
import { jsx as
|
|
1518
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
966
1519
|
var TARGET_CHUNK_SIZE = 4;
|
|
967
1520
|
var MIN_CHUNK_SIZE = 2;
|
|
968
1521
|
var MAX_CHUNK_SIZE = 6;
|
|
@@ -1016,12 +1569,12 @@ function SocialCaptionOverlay({
|
|
|
1016
1569
|
theme,
|
|
1017
1570
|
viewport
|
|
1018
1571
|
}) {
|
|
1019
|
-
const { chunks } =
|
|
1572
|
+
const { chunks } = useMemo4(
|
|
1020
1573
|
() => captions ? buildWordStream(captions) : { words: [], chunks: [] },
|
|
1021
1574
|
[captions]
|
|
1022
1575
|
);
|
|
1023
1576
|
if (!enabled || chunks.length === 0) {
|
|
1024
|
-
return /* @__PURE__ */
|
|
1577
|
+
return /* @__PURE__ */ jsx11(
|
|
1025
1578
|
"div",
|
|
1026
1579
|
{
|
|
1027
1580
|
className: "social-caption-overlay",
|
|
@@ -1083,7 +1636,7 @@ function SocialCaptionOverlay({
|
|
|
1083
1636
|
const viewportHeight = viewport?.height ?? 720;
|
|
1084
1637
|
const baseFontSize = Math.round(viewportHeight * 0.055);
|
|
1085
1638
|
const fontSize = Math.max(24, Math.min(72, baseFontSize));
|
|
1086
|
-
return /* @__PURE__ */
|
|
1639
|
+
return /* @__PURE__ */ jsx11(
|
|
1087
1640
|
"div",
|
|
1088
1641
|
{
|
|
1089
1642
|
className: "social-caption-overlay",
|
|
@@ -1100,7 +1653,7 @@ function SocialCaptionOverlay({
|
|
|
1100
1653
|
opacity: 1,
|
|
1101
1654
|
transition: "opacity 0.15s ease-in-out"
|
|
1102
1655
|
},
|
|
1103
|
-
children: /* @__PURE__ */
|
|
1656
|
+
children: /* @__PURE__ */ jsx11(
|
|
1104
1657
|
"div",
|
|
1105
1658
|
{
|
|
1106
1659
|
style: {
|
|
@@ -1109,7 +1662,7 @@ function SocialCaptionOverlay({
|
|
|
1109
1662
|
},
|
|
1110
1663
|
children: activeChunk.words.map((word, i) => {
|
|
1111
1664
|
const isActive = i === activeWordIndex;
|
|
1112
|
-
return /* @__PURE__ */
|
|
1665
|
+
return /* @__PURE__ */ jsx11(
|
|
1113
1666
|
"span",
|
|
1114
1667
|
{
|
|
1115
1668
|
style: {
|
|
@@ -1134,7 +1687,7 @@ function SocialCaptionOverlay({
|
|
|
1134
1687
|
}
|
|
1135
1688
|
|
|
1136
1689
|
// src/CaptionOverlay.tsx
|
|
1137
|
-
import { jsx as
|
|
1690
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1138
1691
|
function CaptionOverlay({
|
|
1139
1692
|
captions,
|
|
1140
1693
|
currentTime,
|
|
@@ -1145,7 +1698,7 @@ function CaptionOverlay({
|
|
|
1145
1698
|
viewport
|
|
1146
1699
|
}) {
|
|
1147
1700
|
if (captionStyle === "social") {
|
|
1148
|
-
return /* @__PURE__ */
|
|
1701
|
+
return /* @__PURE__ */ jsx12(
|
|
1149
1702
|
SocialCaptionOverlay,
|
|
1150
1703
|
{
|
|
1151
1704
|
captions,
|
|
@@ -1158,7 +1711,7 @@ function CaptionOverlay({
|
|
|
1158
1711
|
}
|
|
1159
1712
|
const phrase = enabled && captions ? getCaptionAtTime(captions, currentTime) : null;
|
|
1160
1713
|
const captionText = phrase?.text ?? null;
|
|
1161
|
-
return /* @__PURE__ */
|
|
1714
|
+
return /* @__PURE__ */ jsx12(
|
|
1162
1715
|
"div",
|
|
1163
1716
|
{
|
|
1164
1717
|
className: "caption-overlay",
|
|
@@ -1177,7 +1730,7 @@ function CaptionOverlay({
|
|
|
1177
1730
|
padding: "0 4px",
|
|
1178
1731
|
boxSizing: "border-box"
|
|
1179
1732
|
},
|
|
1180
|
-
children: captionText && /* @__PURE__ */
|
|
1733
|
+
children: captionText && /* @__PURE__ */ jsx12(
|
|
1181
1734
|
"div",
|
|
1182
1735
|
{
|
|
1183
1736
|
style: {
|
|
@@ -1187,7 +1740,7 @@ function CaptionOverlay({
|
|
|
1187
1740
|
borderRadius: "4px",
|
|
1188
1741
|
backdropFilter: "blur(4px)"
|
|
1189
1742
|
},
|
|
1190
|
-
children: /* @__PURE__ */
|
|
1743
|
+
children: /* @__PURE__ */ jsx12(
|
|
1191
1744
|
"span",
|
|
1192
1745
|
{
|
|
1193
1746
|
style: {
|
|
@@ -1210,12 +1763,12 @@ function CaptionOverlay({
|
|
|
1210
1763
|
}
|
|
1211
1764
|
|
|
1212
1765
|
// src/hooks/useAutoSurface.ts
|
|
1213
|
-
import { useCallback, useMemo as
|
|
1766
|
+
import { useCallback, useMemo as useMemo5, useSyncExternalStore } from "react";
|
|
1214
1767
|
import { DARK_SURFACE, LIGHT_SURFACE } from "@bendyline/squisq/schemas";
|
|
1215
1768
|
var DARK_QUERY = "(prefers-color-scheme: dark)";
|
|
1216
1769
|
var getServerSnapshot = () => LIGHT_SURFACE;
|
|
1217
1770
|
function useAutoSurface(enabled) {
|
|
1218
|
-
const mql =
|
|
1771
|
+
const mql = useMemo5(
|
|
1219
1772
|
() => enabled && typeof window !== "undefined" ? window.matchMedia(DARK_QUERY) : null,
|
|
1220
1773
|
[enabled]
|
|
1221
1774
|
);
|
|
@@ -1233,7 +1786,7 @@ function useAutoSurface(enabled) {
|
|
|
1233
1786
|
}
|
|
1234
1787
|
|
|
1235
1788
|
// src/hooks/useAudioSync.ts
|
|
1236
|
-
import { useState as useState3, useEffect as
|
|
1789
|
+
import { useState as useState3, useEffect as useEffect5, useRef as useRef3, useCallback as useCallback2 } from "react";
|
|
1237
1790
|
function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
1238
1791
|
const [currentTime, setCurrentTime] = useState3(0);
|
|
1239
1792
|
const [isPlaying, setIsPlaying] = useState3(false);
|
|
@@ -1241,13 +1794,13 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1241
1794
|
const [isEnded, setIsEnded] = useState3(false);
|
|
1242
1795
|
const [isAudioReady, setIsAudioReady] = useState3(false);
|
|
1243
1796
|
const [totalDuration, setTotalDuration] = useState3(0);
|
|
1244
|
-
const segmentStarts =
|
|
1245
|
-
const pendingSeekTime =
|
|
1246
|
-
const shouldPlayAfterLoad =
|
|
1247
|
-
const blobUrls =
|
|
1248
|
-
const loadingPromises =
|
|
1249
|
-
const fallbackMode =
|
|
1250
|
-
|
|
1797
|
+
const segmentStarts = useRef3([]);
|
|
1798
|
+
const pendingSeekTime = useRef3(null);
|
|
1799
|
+
const shouldPlayAfterLoad = useRef3(false);
|
|
1800
|
+
const blobUrls = useRef3(/* @__PURE__ */ new Map());
|
|
1801
|
+
const loadingPromises = useRef3(/* @__PURE__ */ new Map());
|
|
1802
|
+
const fallbackMode = useRef3(false);
|
|
1803
|
+
useEffect5(() => {
|
|
1251
1804
|
if (!audioTrack?.segments) {
|
|
1252
1805
|
return;
|
|
1253
1806
|
}
|
|
@@ -1287,7 +1840,7 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1287
1840
|
},
|
|
1288
1841
|
[basePath]
|
|
1289
1842
|
);
|
|
1290
|
-
|
|
1843
|
+
useEffect5(() => {
|
|
1291
1844
|
if (!audioTrack?.segments) return;
|
|
1292
1845
|
audioTrack.segments.forEach((segment) => {
|
|
1293
1846
|
preloadAudio(segment.src);
|
|
@@ -1300,16 +1853,16 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1300
1853
|
currentBlobUrls.clear();
|
|
1301
1854
|
};
|
|
1302
1855
|
}, [audioTrack, preloadAudio]);
|
|
1303
|
-
|
|
1856
|
+
useEffect5(() => {
|
|
1304
1857
|
const audio = audioRef.current;
|
|
1305
1858
|
if (!audio) return;
|
|
1306
1859
|
const handleTimeUpdate = () => {
|
|
1860
|
+
if (fallbackMode.current) return;
|
|
1307
1861
|
const segmentStart = segmentStarts.current[currentSegment] || 0;
|
|
1308
1862
|
const overallTime = segmentStart + audio.currentTime;
|
|
1309
1863
|
setCurrentTime(overallTime);
|
|
1310
1864
|
};
|
|
1311
1865
|
const handlePlay = () => {
|
|
1312
|
-
fallbackMode.current = false;
|
|
1313
1866
|
setIsPlaying(true);
|
|
1314
1867
|
};
|
|
1315
1868
|
const handlePause = () => setIsPlaying(false);
|
|
@@ -1338,7 +1891,7 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1338
1891
|
audio.removeEventListener("error", handleError);
|
|
1339
1892
|
};
|
|
1340
1893
|
}, [audioRef, currentSegment, audioTrack]);
|
|
1341
|
-
|
|
1894
|
+
useEffect5(() => {
|
|
1342
1895
|
const audio = audioRef.current;
|
|
1343
1896
|
if (!audio || !audioTrack?.segments) return;
|
|
1344
1897
|
const segment = audioTrack.segments[currentSegment];
|
|
@@ -1461,7 +2014,7 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1461
2014
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1462
2015
|
play();
|
|
1463
2016
|
}, [seekTo, play]);
|
|
1464
|
-
|
|
2017
|
+
useEffect5(() => {
|
|
1465
2018
|
if (!isPlaying || !fallbackMode.current || !totalDuration) return;
|
|
1466
2019
|
let lastTime = performance.now();
|
|
1467
2020
|
let raf;
|
|
@@ -1505,27 +2058,34 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1505
2058
|
}
|
|
1506
2059
|
|
|
1507
2060
|
// src/hooks/useDocPlayback.ts
|
|
1508
|
-
import {
|
|
1509
|
-
import {
|
|
2061
|
+
import { useMemo as useMemo6, useCallback as useCallback3, useRef as useRef4 } from "react";
|
|
2062
|
+
import {
|
|
2063
|
+
DEFAULT_THEME,
|
|
2064
|
+
getBlockAtTime,
|
|
2065
|
+
resolveBlockTransition,
|
|
2066
|
+
resolveTransitionDuration as resolveTransitionDuration2
|
|
2067
|
+
} from "@bendyline/squisq/schemas";
|
|
1510
2068
|
import {
|
|
1511
2069
|
expandDocBlocks,
|
|
1512
|
-
|
|
2070
|
+
flattenRenderableBlocks,
|
|
1513
2071
|
isTemplateBlock,
|
|
2072
|
+
resolvePersistentLayers,
|
|
1514
2073
|
VIEWPORT_PRESETS
|
|
1515
2074
|
} from "@bendyline/squisq/doc";
|
|
1516
2075
|
function useDocPlayback(script, currentTime, viewport = VIEWPORT_PRESETS.landscape, renderMode = false, theme) {
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
exiting: false,
|
|
1520
|
-
previousBlock: null
|
|
1521
|
-
});
|
|
1522
|
-
const blocks = useMemo4(() => {
|
|
2076
|
+
void renderMode;
|
|
2077
|
+
const blocks = useMemo6(() => {
|
|
1523
2078
|
if (!script?.blocks) {
|
|
1524
2079
|
return [];
|
|
1525
2080
|
}
|
|
1526
2081
|
const hasChildren = script.blocks.some((b) => b.children && b.children.length > 0);
|
|
1527
|
-
const flatBlocks = hasChildren ?
|
|
2082
|
+
const flatBlocks = hasChildren ? flattenRenderableBlocks(script.blocks) : script.blocks;
|
|
1528
2083
|
const hasTemplates = flatBlocks.some(isTemplateBlock);
|
|
2084
|
+
const resolvedTheme = theme ?? DEFAULT_THEME;
|
|
2085
|
+
const persistentLayers = resolvePersistentLayers(
|
|
2086
|
+
{ persistentLayers: script.persistentLayers },
|
|
2087
|
+
resolvedTheme
|
|
2088
|
+
);
|
|
1529
2089
|
if (hasTemplates) {
|
|
1530
2090
|
const audioSegments = script.audio?.segments?.map((seg) => ({
|
|
1531
2091
|
startTime: seg.startTime,
|
|
@@ -1534,78 +2094,58 @@ function useDocPlayback(script, currentTime, viewport = VIEWPORT_PRESETS.landsca
|
|
|
1534
2094
|
const expanded = expandDocBlocks(flatBlocks, {
|
|
1535
2095
|
audioSegments,
|
|
1536
2096
|
viewport,
|
|
1537
|
-
persistentLayers
|
|
1538
|
-
theme
|
|
2097
|
+
persistentLayers,
|
|
2098
|
+
theme,
|
|
2099
|
+
// Custom (user-defined) templates inlined into the doc's
|
|
2100
|
+
// frontmatter — see CustomTemplates.ts. Merged onto the
|
|
2101
|
+
// built-in registry so blocks annotated with `{[myhero]}`
|
|
2102
|
+
// resolve through the user's design.
|
|
2103
|
+
customTemplates: script.customTemplates
|
|
1539
2104
|
});
|
|
1540
2105
|
return expanded;
|
|
1541
2106
|
}
|
|
1542
|
-
return flatBlocks
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
2107
|
+
return flatBlocks.map((block, index) => {
|
|
2108
|
+
const transition = resolveBlockTransition(block, resolvedTheme, index);
|
|
2109
|
+
return transition !== block.transition ? { ...block, transition } : block;
|
|
2110
|
+
});
|
|
2111
|
+
}, [
|
|
2112
|
+
script?.blocks,
|
|
2113
|
+
script?.audio?.segments,
|
|
2114
|
+
script?.persistentLayers,
|
|
2115
|
+
script?.customTemplates,
|
|
2116
|
+
viewport,
|
|
2117
|
+
theme
|
|
2118
|
+
]);
|
|
2119
|
+
const currentBlock = useMemo6(() => getBlockAtTime(blocks, currentTime), [blocks, currentTime]);
|
|
2120
|
+
const currentBlockIndex = useMemo6(
|
|
1546
2121
|
() => currentBlock ? blocks.indexOf(currentBlock) : -1,
|
|
1547
2122
|
[blocks, currentBlock]
|
|
1548
2123
|
);
|
|
1549
|
-
const blockTime =
|
|
2124
|
+
const blockTime = useMemo6(() => {
|
|
1550
2125
|
if (!currentBlock) return 0;
|
|
1551
2126
|
return Math.max(0, currentTime - currentBlock.startTime);
|
|
1552
2127
|
}, [currentBlock, currentTime]);
|
|
1553
|
-
const blockProgress =
|
|
2128
|
+
const blockProgress = useMemo6(() => {
|
|
1554
2129
|
if (!currentBlock || currentBlock.duration === 0) return 0;
|
|
1555
2130
|
return Math.min(1, blockTime / currentBlock.duration);
|
|
1556
2131
|
}, [currentBlock, blockTime]);
|
|
1557
|
-
const docProgress =
|
|
2132
|
+
const docProgress = useMemo6(() => {
|
|
1558
2133
|
if (!script || script.duration === 0) return 0;
|
|
1559
2134
|
return Math.min(1, currentTime / script.duration);
|
|
1560
2135
|
}, [script, currentTime]);
|
|
1561
|
-
const
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
exiting: true,
|
|
1575
|
-
previousBlock: transitionState.previousBlock
|
|
1576
|
-
});
|
|
1577
|
-
const timer = setTimeout(() => {
|
|
1578
|
-
setTransitionState({
|
|
1579
|
-
entering: false,
|
|
1580
|
-
exiting: false,
|
|
1581
|
-
previousBlock: currentBlock
|
|
1582
|
-
});
|
|
1583
|
-
}, transitionDuration * 1e3);
|
|
1584
|
-
return () => clearTimeout(timer);
|
|
1585
|
-
} else {
|
|
1586
|
-
setTransitionState({
|
|
1587
|
-
entering: false,
|
|
1588
|
-
exiting: false,
|
|
1589
|
-
previousBlock: currentBlock
|
|
1590
|
-
});
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
|
-
}, [currentBlock?.id, renderMode]);
|
|
1594
|
-
const renderPrevBlockRef = useRef3(null);
|
|
1595
|
-
useEffect5(() => {
|
|
1596
|
-
if (!renderMode || !currentBlock) return;
|
|
1597
|
-
if (transitionState.previousBlock?.id !== currentBlock.id) {
|
|
1598
|
-
const oldPrev = transitionState.previousBlock;
|
|
1599
|
-
renderPrevBlockRef.current = oldPrev;
|
|
1600
|
-
setTransitionState((prev) => ({
|
|
1601
|
-
...prev,
|
|
1602
|
-
previousBlock: currentBlock
|
|
1603
|
-
}));
|
|
1604
|
-
}
|
|
1605
|
-
}, [currentBlock?.id, renderMode]);
|
|
1606
|
-
const renderTransitionDuration = currentBlock?.transition?.duration || 0;
|
|
1607
|
-
const renderIsEntering = renderMode && renderTransitionDuration > 0 && blockTime < renderTransitionDuration;
|
|
1608
|
-
const renderIsExiting = renderIsEntering && renderPrevBlockRef.current !== null;
|
|
2136
|
+
const outgoingBlockRef = useRef4(null);
|
|
2137
|
+
const activeBlockIdRef = useRef4(null);
|
|
2138
|
+
const lastRenderedBlockRef = useRef4(null);
|
|
2139
|
+
if (currentBlock && currentBlock.id !== activeBlockIdRef.current) {
|
|
2140
|
+
outgoingBlockRef.current = lastRenderedBlockRef.current;
|
|
2141
|
+
activeBlockIdRef.current = currentBlock.id;
|
|
2142
|
+
}
|
|
2143
|
+
lastRenderedBlockRef.current = currentBlock;
|
|
2144
|
+
const transitionDuration = currentBlock?.transition ? resolveTransitionDuration2(currentBlock.transition) : 0;
|
|
2145
|
+
const isEntering = !!currentBlock && transitionDuration > 0 && blockTime < transitionDuration;
|
|
2146
|
+
const outgoingBlock = outgoingBlockRef.current;
|
|
2147
|
+
const isExiting = isEntering && outgoingBlock != null && outgoingBlock.id !== currentBlock?.id;
|
|
2148
|
+
const previousBlock = isExiting ? outgoingBlock : null;
|
|
1609
2149
|
const goToBlock = useCallback3(
|
|
1610
2150
|
(index) => {
|
|
1611
2151
|
if (!script || index < 0 || index >= blocks.length) return;
|
|
@@ -1629,9 +2169,9 @@ function useDocPlayback(script, currentTime, viewport = VIEWPORT_PRESETS.landsca
|
|
|
1629
2169
|
return {
|
|
1630
2170
|
currentBlock,
|
|
1631
2171
|
currentBlockIndex,
|
|
1632
|
-
previousBlock
|
|
1633
|
-
isEntering
|
|
1634
|
-
isExiting
|
|
2172
|
+
previousBlock,
|
|
2173
|
+
isEntering,
|
|
2174
|
+
isExiting,
|
|
1635
2175
|
blockTime,
|
|
1636
2176
|
blockProgress,
|
|
1637
2177
|
docProgress,
|
|
@@ -1644,7 +2184,7 @@ function useDocPlayback(script, currentTime, viewport = VIEWPORT_PRESETS.landsca
|
|
|
1644
2184
|
}
|
|
1645
2185
|
|
|
1646
2186
|
// src/hooks/useViewportOrientation.ts
|
|
1647
|
-
import { useState as
|
|
2187
|
+
import { useState as useState4, useEffect as useEffect6, useMemo as useMemo7 } from "react";
|
|
1648
2188
|
import {
|
|
1649
2189
|
VIEWPORT_PRESETS as VIEWPORT_PRESETS2
|
|
1650
2190
|
} from "@bendyline/squisq/doc";
|
|
@@ -1670,7 +2210,7 @@ function getViewportForOrientation(orientation) {
|
|
|
1670
2210
|
}
|
|
1671
2211
|
}
|
|
1672
2212
|
function useViewportOrientation() {
|
|
1673
|
-
const [windowSize, setWindowSize] =
|
|
2213
|
+
const [windowSize, setWindowSize] = useState4(() => ({
|
|
1674
2214
|
width: typeof window !== "undefined" ? window.innerWidth : 1920,
|
|
1675
2215
|
height: typeof window !== "undefined" ? window.innerHeight : 1080
|
|
1676
2216
|
}));
|
|
@@ -1693,11 +2233,11 @@ function useViewportOrientation() {
|
|
|
1693
2233
|
clearTimeout(timeoutId);
|
|
1694
2234
|
};
|
|
1695
2235
|
}, []);
|
|
1696
|
-
const orientation =
|
|
2236
|
+
const orientation = useMemo7(
|
|
1697
2237
|
() => getOrientationFromWindow(windowSize.width, windowSize.height),
|
|
1698
2238
|
[windowSize.width, windowSize.height]
|
|
1699
2239
|
);
|
|
1700
|
-
const viewport =
|
|
2240
|
+
const viewport = useMemo7(() => getViewportForOrientation(orientation), [orientation]);
|
|
1701
2241
|
return {
|
|
1702
2242
|
viewport,
|
|
1703
2243
|
orientation,
|
|
@@ -1709,12 +2249,12 @@ function useViewportOrientation() {
|
|
|
1709
2249
|
import {
|
|
1710
2250
|
expandCoverBlock,
|
|
1711
2251
|
createTemplateContext,
|
|
1712
|
-
DEFAULT_THEME as
|
|
2252
|
+
DEFAULT_THEME as DEFAULT_THEME3,
|
|
1713
2253
|
VIEWPORT_PRESETS as VIEWPORT_PRESETS4
|
|
1714
2254
|
} from "@bendyline/squisq/doc";
|
|
1715
2255
|
|
|
1716
2256
|
// src/DocProgressBar.tsx
|
|
1717
|
-
import { useRef as
|
|
2257
|
+
import { useRef as useRef5, useState as useState5, useCallback as useCallback4 } from "react";
|
|
1718
2258
|
|
|
1719
2259
|
// src/types.ts
|
|
1720
2260
|
function formatTime(seconds) {
|
|
@@ -1724,7 +2264,7 @@ function formatTime(seconds) {
|
|
|
1724
2264
|
}
|
|
1725
2265
|
|
|
1726
2266
|
// src/DocProgressBar.tsx
|
|
1727
|
-
import { jsx as
|
|
2267
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1728
2268
|
function DocProgressBar({
|
|
1729
2269
|
state,
|
|
1730
2270
|
actions,
|
|
@@ -1732,8 +2272,9 @@ function DocProgressBar({
|
|
|
1732
2272
|
expandedBlocks,
|
|
1733
2273
|
getBlockTitle
|
|
1734
2274
|
}) {
|
|
1735
|
-
const progressBarRef =
|
|
1736
|
-
const [hoverPosition, setHoverPosition] =
|
|
2275
|
+
const progressBarRef = useRef5(null);
|
|
2276
|
+
const [hoverPosition, setHoverPosition] = useState5(null);
|
|
2277
|
+
const playProgress = state.totalDuration > 0 ? Math.max(0, Math.min(1, state.currentTime / state.totalDuration)) : 0;
|
|
1737
2278
|
const handleProgressHover = useCallback4((e) => {
|
|
1738
2279
|
const bar = progressBarRef.current;
|
|
1739
2280
|
if (!bar) return;
|
|
@@ -1757,7 +2298,7 @@ function DocProgressBar({
|
|
|
1757
2298
|
},
|
|
1758
2299
|
[expandedBlocks]
|
|
1759
2300
|
);
|
|
1760
|
-
return /* @__PURE__ */
|
|
2301
|
+
return /* @__PURE__ */ jsxs8(
|
|
1761
2302
|
"div",
|
|
1762
2303
|
{
|
|
1763
2304
|
ref: progressBarRef,
|
|
@@ -1778,7 +2319,7 @@ function DocProgressBar({
|
|
|
1778
2319
|
onMouseMove: handleProgressHover,
|
|
1779
2320
|
onMouseLeave: handleProgressLeave,
|
|
1780
2321
|
children: [
|
|
1781
|
-
/* @__PURE__ */
|
|
2322
|
+
/* @__PURE__ */ jsx13(
|
|
1782
2323
|
"div",
|
|
1783
2324
|
{
|
|
1784
2325
|
style: {
|
|
@@ -1791,21 +2332,21 @@ function DocProgressBar({
|
|
|
1791
2332
|
}
|
|
1792
2333
|
}
|
|
1793
2334
|
),
|
|
1794
|
-
/* @__PURE__ */
|
|
2335
|
+
/* @__PURE__ */ jsx13(
|
|
1795
2336
|
"div",
|
|
1796
2337
|
{
|
|
2338
|
+
"data-testid": "doc-progress-fill",
|
|
1797
2339
|
style: {
|
|
1798
2340
|
position: "absolute",
|
|
1799
2341
|
left: 0,
|
|
1800
|
-
width: `${
|
|
2342
|
+
width: `${playProgress * 100}%`,
|
|
1801
2343
|
height: "6px",
|
|
1802
2344
|
background: "#5b9bd5",
|
|
1803
|
-
borderRadius: "3px"
|
|
1804
|
-
transition: "width 0.1s"
|
|
2345
|
+
borderRadius: "3px"
|
|
1805
2346
|
}
|
|
1806
2347
|
}
|
|
1807
2348
|
),
|
|
1808
|
-
blockMarkers.map((marker, i) => /* @__PURE__ */
|
|
2349
|
+
blockMarkers.map((marker, i) => /* @__PURE__ */ jsx13(
|
|
1809
2350
|
"div",
|
|
1810
2351
|
{
|
|
1811
2352
|
style: {
|
|
@@ -1835,7 +2376,7 @@ function DocProgressBar({
|
|
|
1835
2376
|
},
|
|
1836
2377
|
`${marker.block.id}-${i}`
|
|
1837
2378
|
)),
|
|
1838
|
-
hoverPosition !== null && /* @__PURE__ */
|
|
2379
|
+
hoverPosition !== null && /* @__PURE__ */ jsxs8(
|
|
1839
2380
|
"div",
|
|
1840
2381
|
{
|
|
1841
2382
|
style: {
|
|
@@ -1852,19 +2393,19 @@ function DocProgressBar({
|
|
|
1852
2393
|
zIndex: 10
|
|
1853
2394
|
},
|
|
1854
2395
|
children: [
|
|
1855
|
-
/* @__PURE__ */
|
|
2396
|
+
/* @__PURE__ */ jsx13("div", { style: { color: "white", fontSize: "12px", fontFamily: "monospace" }, children: formatTime(hoverPosition * state.totalDuration) }),
|
|
1856
2397
|
(() => {
|
|
1857
2398
|
const hoverTime = hoverPosition * state.totalDuration;
|
|
1858
2399
|
const slideInfo = getBlockAtTimeLocal(hoverTime);
|
|
1859
2400
|
if (slideInfo && getBlockTitle) {
|
|
1860
|
-
return /* @__PURE__ */
|
|
2401
|
+
return /* @__PURE__ */ jsx13("div", { style: { color: "rgba(255,255,255,0.7)", fontSize: "11px", marginTop: "2px" }, children: getBlockTitle(slideInfo.block) });
|
|
1861
2402
|
}
|
|
1862
2403
|
return null;
|
|
1863
2404
|
})()
|
|
1864
2405
|
]
|
|
1865
2406
|
}
|
|
1866
2407
|
),
|
|
1867
|
-
hoverPosition !== null && /* @__PURE__ */
|
|
2408
|
+
hoverPosition !== null && /* @__PURE__ */ jsx13(
|
|
1868
2409
|
"div",
|
|
1869
2410
|
{
|
|
1870
2411
|
style: {
|
|
@@ -1886,7 +2427,7 @@ function DocProgressBar({
|
|
|
1886
2427
|
}
|
|
1887
2428
|
|
|
1888
2429
|
// src/DocControlsOverlay.tsx
|
|
1889
|
-
import { jsx as
|
|
2430
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1890
2431
|
function DocControlsOverlay({
|
|
1891
2432
|
state,
|
|
1892
2433
|
actions,
|
|
@@ -1894,7 +2435,7 @@ function DocControlsOverlay({
|
|
|
1894
2435
|
expandedBlocks,
|
|
1895
2436
|
getBlockTitle
|
|
1896
2437
|
}) {
|
|
1897
|
-
return /* @__PURE__ */
|
|
2438
|
+
return /* @__PURE__ */ jsxs9(
|
|
1898
2439
|
"div",
|
|
1899
2440
|
{
|
|
1900
2441
|
className: "doc-player__controls",
|
|
@@ -1911,7 +2452,7 @@ function DocControlsOverlay({
|
|
|
1911
2452
|
zIndex: 100
|
|
1912
2453
|
},
|
|
1913
2454
|
children: [
|
|
1914
|
-
/* @__PURE__ */
|
|
2455
|
+
/* @__PURE__ */ jsx14(
|
|
1915
2456
|
"button",
|
|
1916
2457
|
{
|
|
1917
2458
|
onClick: actions.restart,
|
|
@@ -1927,10 +2468,10 @@ function DocControlsOverlay({
|
|
|
1927
2468
|
},
|
|
1928
2469
|
title: "Restart",
|
|
1929
2470
|
"aria-label": "Restart from beginning",
|
|
1930
|
-
children: /* @__PURE__ */
|
|
2471
|
+
children: /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx14("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" }) })
|
|
1931
2472
|
}
|
|
1932
2473
|
),
|
|
1933
|
-
/* @__PURE__ */
|
|
2474
|
+
/* @__PURE__ */ jsx14(
|
|
1934
2475
|
"button",
|
|
1935
2476
|
{
|
|
1936
2477
|
onClick: actions.toggle,
|
|
@@ -1949,15 +2490,15 @@ function DocControlsOverlay({
|
|
|
1949
2490
|
height: "40px"
|
|
1950
2491
|
},
|
|
1951
2492
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
1952
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
2493
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx14("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx14("path", { d: "M8 5v14l11-7z" }) })
|
|
1953
2494
|
}
|
|
1954
2495
|
),
|
|
1955
|
-
/* @__PURE__ */
|
|
2496
|
+
/* @__PURE__ */ jsxs9("span", { style: { color: "white", fontSize: "12px", minWidth: "80px", fontFamily: "monospace" }, children: [
|
|
1956
2497
|
formatTime(state.currentTime),
|
|
1957
2498
|
" / ",
|
|
1958
2499
|
formatTime(state.totalDuration)
|
|
1959
2500
|
] }),
|
|
1960
|
-
/* @__PURE__ */
|
|
2501
|
+
/* @__PURE__ */ jsx14(
|
|
1961
2502
|
DocProgressBar,
|
|
1962
2503
|
{
|
|
1963
2504
|
state,
|
|
@@ -1967,12 +2508,12 @@ function DocControlsOverlay({
|
|
|
1967
2508
|
getBlockTitle
|
|
1968
2509
|
}
|
|
1969
2510
|
),
|
|
1970
|
-
/* @__PURE__ */
|
|
2511
|
+
/* @__PURE__ */ jsxs9("span", { style: { color: "rgba(255,255,255,0.5)", fontSize: "11px", whiteSpace: "nowrap" }, children: [
|
|
1971
2512
|
state.currentBlockIndex + 1,
|
|
1972
2513
|
"/",
|
|
1973
2514
|
state.totalBlocks
|
|
1974
2515
|
] }),
|
|
1975
|
-
state.hasCaptions && /* @__PURE__ */
|
|
2516
|
+
state.hasCaptions && /* @__PURE__ */ jsxs9(
|
|
1976
2517
|
"button",
|
|
1977
2518
|
{
|
|
1978
2519
|
onClick: () => actions.cycleCaptionMode(),
|
|
@@ -1991,12 +2532,12 @@ function DocControlsOverlay({
|
|
|
1991
2532
|
title: state.captionMode === "off" ? "Captions: Off (click for Standard)" : state.captionMode === "standard" ? "Captions: Standard (click for Social)" : "Captions: Social (click to turn off)",
|
|
1992
2533
|
"aria-label": "Cycle caption style",
|
|
1993
2534
|
children: [
|
|
1994
|
-
/* @__PURE__ */
|
|
1995
|
-
state.captionMode !== "off" && /* @__PURE__ */
|
|
2535
|
+
/* @__PURE__ */ jsx14("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx14("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" }) }),
|
|
2536
|
+
state.captionMode !== "off" && /* @__PURE__ */ jsx14("span", { style: { fontSize: "10px", textTransform: "uppercase", letterSpacing: "0.5px" }, children: state.captionMode === "standard" ? "CC" : "SM" })
|
|
1996
2537
|
]
|
|
1997
2538
|
}
|
|
1998
2539
|
),
|
|
1999
|
-
actions.toggleFullscreen && /* @__PURE__ */
|
|
2540
|
+
actions.toggleFullscreen && /* @__PURE__ */ jsx14(
|
|
2000
2541
|
"button",
|
|
2001
2542
|
{
|
|
2002
2543
|
onClick: actions.toggleFullscreen,
|
|
@@ -2012,7 +2553,7 @@ function DocControlsOverlay({
|
|
|
2012
2553
|
},
|
|
2013
2554
|
title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
|
|
2014
2555
|
"aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
2015
|
-
children: state.isFullscreen ? /* @__PURE__ */
|
|
2556
|
+
children: state.isFullscreen ? /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx14("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx14("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
|
|
2016
2557
|
}
|
|
2017
2558
|
)
|
|
2018
2559
|
]
|
|
@@ -2021,12 +2562,12 @@ function DocControlsOverlay({
|
|
|
2021
2562
|
}
|
|
2022
2563
|
|
|
2023
2564
|
// src/DocControlsSlideshow.tsx
|
|
2024
|
-
import { jsx as
|
|
2565
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2025
2566
|
function DocControlsSlideshow({ state, slideNav }) {
|
|
2026
2567
|
const { currentBlockIndex, totalBlocks } = state;
|
|
2027
2568
|
const isFirst = currentBlockIndex <= 0;
|
|
2028
2569
|
const isLast = currentBlockIndex >= totalBlocks - 1;
|
|
2029
|
-
return /* @__PURE__ */
|
|
2570
|
+
return /* @__PURE__ */ jsxs10(
|
|
2030
2571
|
"div",
|
|
2031
2572
|
{
|
|
2032
2573
|
className: "doc-controls-slideshow",
|
|
@@ -2047,7 +2588,7 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2047
2588
|
WebkitBackdropFilter: "blur(8px)"
|
|
2048
2589
|
},
|
|
2049
2590
|
children: [
|
|
2050
|
-
/* @__PURE__ */
|
|
2591
|
+
/* @__PURE__ */ jsx15(
|
|
2051
2592
|
"button",
|
|
2052
2593
|
{
|
|
2053
2594
|
onClick: (e) => {
|
|
@@ -2076,10 +2617,10 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2076
2617
|
onMouseLeave: (e) => {
|
|
2077
2618
|
e.currentTarget.style.background = "none";
|
|
2078
2619
|
},
|
|
2079
|
-
children: /* @__PURE__ */
|
|
2620
|
+
children: /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx15("path", { d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" }) })
|
|
2080
2621
|
}
|
|
2081
2622
|
),
|
|
2082
|
-
/* @__PURE__ */
|
|
2623
|
+
/* @__PURE__ */ jsx15(
|
|
2083
2624
|
"span",
|
|
2084
2625
|
{
|
|
2085
2626
|
"data-testid": "slide-counter",
|
|
@@ -2096,7 +2637,7 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2096
2637
|
children: totalBlocks > 0 ? `${currentBlockIndex + 1} / ${totalBlocks}` : "\u2014"
|
|
2097
2638
|
}
|
|
2098
2639
|
),
|
|
2099
|
-
/* @__PURE__ */
|
|
2640
|
+
/* @__PURE__ */ jsx15(
|
|
2100
2641
|
"button",
|
|
2101
2642
|
{
|
|
2102
2643
|
onClick: (e) => {
|
|
@@ -2125,7 +2666,7 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2125
2666
|
onMouseLeave: (e) => {
|
|
2126
2667
|
e.currentTarget.style.background = "none";
|
|
2127
2668
|
},
|
|
2128
|
-
children: /* @__PURE__ */
|
|
2669
|
+
children: /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx15("path", { d: "M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z" }) })
|
|
2129
2670
|
}
|
|
2130
2671
|
)
|
|
2131
2672
|
]
|
|
@@ -2134,20 +2675,24 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2134
2675
|
}
|
|
2135
2676
|
|
|
2136
2677
|
// src/LinearDocView.tsx
|
|
2137
|
-
import { useMemo as
|
|
2678
|
+
import { useMemo as useMemo8 } from "react";
|
|
2138
2679
|
import {
|
|
2139
2680
|
applySurface,
|
|
2140
2681
|
resolveFontFamily as resolveFontFamily2
|
|
2141
2682
|
} from "@bendyline/squisq/schemas";
|
|
2142
2683
|
import { VIEWPORT_PRESETS as VIEWPORT_PRESETS3 } from "@bendyline/squisq/schemas";
|
|
2143
|
-
import { getLayers, hasTemplate, DEFAULT_THEME } from "@bendyline/squisq/doc";
|
|
2684
|
+
import { getLayers, hasTemplate, DEFAULT_THEME as DEFAULT_THEME2, deriveTemplateInputs } from "@bendyline/squisq/doc";
|
|
2144
2685
|
import { extractPlainText } from "@bendyline/squisq/markdown";
|
|
2145
2686
|
|
|
2146
2687
|
// src/MarkdownRenderer.tsx
|
|
2147
|
-
import { Fragment } from "react";
|
|
2688
|
+
import { Fragment as Fragment2 } from "react";
|
|
2689
|
+
import {
|
|
2690
|
+
sanitizeHtmlNodes as sanitizeHtmlNodes2,
|
|
2691
|
+
sanitizeUrl
|
|
2692
|
+
} from "@bendyline/squisq/markdown";
|
|
2148
2693
|
|
|
2149
2694
|
// src/InlineVideoPlayer.tsx
|
|
2150
|
-
import { jsx as
|
|
2695
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2151
2696
|
function InlineVideoPlayer({
|
|
2152
2697
|
src,
|
|
2153
2698
|
basePath = "",
|
|
@@ -2162,7 +2707,7 @@ function InlineVideoPlayer({
|
|
|
2162
2707
|
const resolvedPoster = useMediaUrl(poster ?? "", basePath);
|
|
2163
2708
|
const posterUrl = poster ? resolvedPoster : void 0;
|
|
2164
2709
|
if (!resolvedSrc) return null;
|
|
2165
|
-
return /* @__PURE__ */
|
|
2710
|
+
return /* @__PURE__ */ jsx16("span", { className: `squisq-inline-video-player ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx16(
|
|
2166
2711
|
"video",
|
|
2167
2712
|
{
|
|
2168
2713
|
src: resolvedSrc,
|
|
@@ -2177,7 +2722,7 @@ function InlineVideoPlayer({
|
|
|
2177
2722
|
}
|
|
2178
2723
|
|
|
2179
2724
|
// src/InlineAudioPlayer.tsx
|
|
2180
|
-
import { jsx as
|
|
2725
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2181
2726
|
function InlineAudioPlayer({
|
|
2182
2727
|
src,
|
|
2183
2728
|
basePath = "",
|
|
@@ -2187,55 +2732,61 @@ function InlineAudioPlayer({
|
|
|
2187
2732
|
}) {
|
|
2188
2733
|
const resolvedSrc = useMediaUrl(src, basePath);
|
|
2189
2734
|
if (!resolvedSrc) return null;
|
|
2190
|
-
return /* @__PURE__ */
|
|
2735
|
+
return /* @__PURE__ */ jsx17("span", { className: `squisq-inline-audio-player ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx17("audio", { src: resolvedSrc, controls, preload }) });
|
|
2191
2736
|
}
|
|
2192
2737
|
|
|
2193
2738
|
// src/MarkdownRenderer.tsx
|
|
2194
|
-
import { jsx as
|
|
2195
|
-
function renderInline(nodes, keyPrefix = "") {
|
|
2739
|
+
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2740
|
+
function renderInline(nodes, keyPrefix = "", htmlPolicy = "sanitize") {
|
|
2196
2741
|
return nodes.map((node, i) => {
|
|
2197
2742
|
const key = `${keyPrefix}i${i}`;
|
|
2198
2743
|
switch (node.type) {
|
|
2199
2744
|
case "text": {
|
|
2200
2745
|
if (!node.value.includes("\n")) {
|
|
2201
|
-
return /* @__PURE__ */
|
|
2746
|
+
return /* @__PURE__ */ jsx18(Fragment2, { children: node.value }, key);
|
|
2202
2747
|
}
|
|
2203
2748
|
const parts = node.value.split("\n");
|
|
2204
|
-
return /* @__PURE__ */
|
|
2205
|
-
j > 0 && /* @__PURE__ */
|
|
2749
|
+
return /* @__PURE__ */ jsx18(Fragment2, { children: parts.map((part, j) => /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
2750
|
+
j > 0 && /* @__PURE__ */ jsx18("br", {}),
|
|
2206
2751
|
part
|
|
2207
2752
|
] }, j)) }, key);
|
|
2208
2753
|
}
|
|
2209
2754
|
case "emphasis":
|
|
2210
|
-
return /* @__PURE__ */
|
|
2755
|
+
return /* @__PURE__ */ jsx18("em", { className: "squisq-md-em", children: renderInline(node.children, key, htmlPolicy) }, key);
|
|
2211
2756
|
case "strong":
|
|
2212
|
-
return /* @__PURE__ */
|
|
2757
|
+
return /* @__PURE__ */ jsx18("strong", { className: "squisq-md-strong", children: renderInline(node.children, key, htmlPolicy) }, key);
|
|
2213
2758
|
case "delete":
|
|
2214
|
-
return /* @__PURE__ */
|
|
2759
|
+
return /* @__PURE__ */ jsx18("del", { className: "squisq-md-del", children: renderInline(node.children, key, htmlPolicy) }, key);
|
|
2215
2760
|
case "inlineCode":
|
|
2216
|
-
return /* @__PURE__ */
|
|
2217
|
-
case "link":
|
|
2218
|
-
|
|
2761
|
+
return /* @__PURE__ */ jsx18("code", { className: "squisq-md-inline-code", children: node.value }, key);
|
|
2762
|
+
case "link": {
|
|
2763
|
+
const href = sanitizeUrl(node.url, "link");
|
|
2764
|
+
if (!href) {
|
|
2765
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-link squisq-md-link--blocked", children: renderInline(node.children, key, htmlPolicy) }, key);
|
|
2766
|
+
}
|
|
2767
|
+
return /* @__PURE__ */ jsx18(
|
|
2219
2768
|
"a",
|
|
2220
2769
|
{
|
|
2221
2770
|
className: "squisq-md-link",
|
|
2222
|
-
href
|
|
2771
|
+
href,
|
|
2223
2772
|
title: node.title ?? void 0,
|
|
2224
2773
|
target: "_blank",
|
|
2225
2774
|
rel: "noopener noreferrer",
|
|
2226
|
-
children: renderInline(node.children, key)
|
|
2775
|
+
children: renderInline(node.children, key, htmlPolicy)
|
|
2227
2776
|
},
|
|
2228
2777
|
key
|
|
2229
2778
|
);
|
|
2779
|
+
}
|
|
2230
2780
|
case "image":
|
|
2231
|
-
return /* @__PURE__ */
|
|
2781
|
+
return /* @__PURE__ */ jsx18(MdImage, { src: node.url, alt: node.alt ?? "", title: node.title ?? void 0 }, key);
|
|
2232
2782
|
case "break":
|
|
2233
|
-
return /* @__PURE__ */
|
|
2783
|
+
return /* @__PURE__ */ jsx18("br", {}, key);
|
|
2234
2784
|
case "inlineMath":
|
|
2235
|
-
return /* @__PURE__ */
|
|
2785
|
+
return /* @__PURE__ */ jsx18("code", { className: "squisq-md-inline-math", children: node.value }, key);
|
|
2236
2786
|
case "htmlInline":
|
|
2237
|
-
if (
|
|
2238
|
-
|
|
2787
|
+
if (htmlPolicy === "strip") return null;
|
|
2788
|
+
if (htmlPolicy === "trusted" && !containsMediaTag(node.htmlChildren) && !containsDangerousTag(node.htmlChildren) && !hasDangerousRawHtml(node.rawHtml)) {
|
|
2789
|
+
return /* @__PURE__ */ jsx18(
|
|
2239
2790
|
"span",
|
|
2240
2791
|
{
|
|
2241
2792
|
className: "squisq-md-html-inline",
|
|
@@ -2244,25 +2795,25 @@ function renderInline(nodes, keyPrefix = "") {
|
|
|
2244
2795
|
key
|
|
2245
2796
|
);
|
|
2246
2797
|
}
|
|
2247
|
-
return /* @__PURE__ */
|
|
2798
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-html-inline", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, htmlPolicy), `${key}h`) }, key);
|
|
2248
2799
|
case "footnoteReference":
|
|
2249
|
-
return /* @__PURE__ */
|
|
2800
|
+
return /* @__PURE__ */ jsx18("sup", { className: "squisq-md-footnote-ref", children: /* @__PURE__ */ jsxs11("a", { href: `#fn-${node.identifier}`, children: [
|
|
2250
2801
|
"[",
|
|
2251
2802
|
node.label ?? node.identifier,
|
|
2252
2803
|
"]"
|
|
2253
2804
|
] }) }, key);
|
|
2254
2805
|
case "linkReference":
|
|
2255
|
-
return /* @__PURE__ */
|
|
2806
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-link-ref", children: renderInline(node.children, key, htmlPolicy) }, key);
|
|
2256
2807
|
case "imageReference":
|
|
2257
|
-
return /* @__PURE__ */
|
|
2808
|
+
return /* @__PURE__ */ jsxs11("span", { className: "squisq-md-image-ref", children: [
|
|
2258
2809
|
"[",
|
|
2259
2810
|
node.alt ?? node.identifier,
|
|
2260
2811
|
"]"
|
|
2261
2812
|
] }, key);
|
|
2262
2813
|
case "textDirective":
|
|
2263
|
-
return /* @__PURE__ */
|
|
2814
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-text-directive", "data-directive": node.name, children: renderInline(node.children, key, htmlPolicy) }, key);
|
|
2264
2815
|
case "mention":
|
|
2265
|
-
return /* @__PURE__ */
|
|
2816
|
+
return /* @__PURE__ */ jsxs11(
|
|
2266
2817
|
"span",
|
|
2267
2818
|
{
|
|
2268
2819
|
className: "squisq-md-mention mention",
|
|
@@ -2282,30 +2833,31 @@ function renderInline(nodes, keyPrefix = "") {
|
|
|
2282
2833
|
}
|
|
2283
2834
|
});
|
|
2284
2835
|
}
|
|
2285
|
-
function renderBlock(node, key) {
|
|
2836
|
+
function renderBlock(node, key, htmlPolicy = "sanitize") {
|
|
2286
2837
|
switch (node.type) {
|
|
2287
2838
|
case "paragraph":
|
|
2288
|
-
return /* @__PURE__ */
|
|
2839
|
+
return /* @__PURE__ */ jsx18("p", { className: "squisq-md-p", children: renderInline(node.children, key, htmlPolicy) }, key);
|
|
2289
2840
|
case "heading": {
|
|
2290
2841
|
const Tag = `h${node.depth}`;
|
|
2291
|
-
return /* @__PURE__ */
|
|
2842
|
+
return /* @__PURE__ */ jsx18(Tag, { className: `squisq-md-heading squisq-md-h${node.depth}`, children: renderInline(node.children, key, htmlPolicy) }, key);
|
|
2292
2843
|
}
|
|
2293
2844
|
case "blockquote":
|
|
2294
|
-
return /* @__PURE__ */
|
|
2845
|
+
return /* @__PURE__ */ jsx18("blockquote", { className: "squisq-md-blockquote", children: renderBlocks(node.children, key, htmlPolicy) }, key);
|
|
2295
2846
|
case "list":
|
|
2296
2847
|
if (node.ordered) {
|
|
2297
|
-
return /* @__PURE__ */
|
|
2848
|
+
return /* @__PURE__ */ jsx18("ol", { className: "squisq-md-list squisq-md-ol", start: node.start ?? void 0, children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, htmlPolicy)) }, key);
|
|
2298
2849
|
}
|
|
2299
|
-
return /* @__PURE__ */
|
|
2850
|
+
return /* @__PURE__ */ jsx18("ul", { className: "squisq-md-list squisq-md-ul", children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, htmlPolicy)) }, key);
|
|
2300
2851
|
case "code":
|
|
2301
|
-
return /* @__PURE__ */
|
|
2852
|
+
return /* @__PURE__ */ jsx18("pre", { className: "squisq-md-code-block", children: /* @__PURE__ */ jsx18("code", { className: node.lang ? `language-${node.lang}` : void 0, children: node.value }) }, key);
|
|
2302
2853
|
case "thematicBreak":
|
|
2303
|
-
return /* @__PURE__ */
|
|
2854
|
+
return /* @__PURE__ */ jsx18("hr", { className: "squisq-md-hr" }, key);
|
|
2304
2855
|
case "table":
|
|
2305
|
-
return renderTable(node.children, node.align, key);
|
|
2856
|
+
return renderTable(node.children, node.align, key, htmlPolicy);
|
|
2306
2857
|
case "htmlBlock":
|
|
2307
|
-
if (
|
|
2308
|
-
|
|
2858
|
+
if (htmlPolicy === "strip") return null;
|
|
2859
|
+
if (htmlPolicy === "trusted" && !containsMediaTag(node.htmlChildren) && !containsDangerousTag(node.htmlChildren) && !hasDangerousRawHtml(node.rawHtml)) {
|
|
2860
|
+
return /* @__PURE__ */ jsx18(
|
|
2309
2861
|
"div",
|
|
2310
2862
|
{
|
|
2311
2863
|
className: "squisq-md-html-block",
|
|
@@ -2314,95 +2866,126 @@ function renderBlock(node, key) {
|
|
|
2314
2866
|
key
|
|
2315
2867
|
);
|
|
2316
2868
|
}
|
|
2317
|
-
return /* @__PURE__ */
|
|
2869
|
+
return /* @__PURE__ */ jsx18("div", { className: "squisq-md-html-block", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, htmlPolicy), `${key}h`) }, key);
|
|
2318
2870
|
case "math":
|
|
2319
|
-
return /* @__PURE__ */
|
|
2871
|
+
return /* @__PURE__ */ jsx18("pre", { className: "squisq-md-math-block", children: /* @__PURE__ */ jsx18("code", { children: node.value }) }, key);
|
|
2320
2872
|
case "definition":
|
|
2321
2873
|
return null;
|
|
2322
2874
|
case "footnoteDefinition":
|
|
2323
|
-
return /* @__PURE__ */
|
|
2324
|
-
/* @__PURE__ */
|
|
2325
|
-
renderBlocks(node.children, key)
|
|
2875
|
+
return /* @__PURE__ */ jsxs11("div", { className: "squisq-md-footnote-def", id: `fn-${node.identifier}`, children: [
|
|
2876
|
+
/* @__PURE__ */ jsx18("sup", { children: node.label ?? node.identifier }),
|
|
2877
|
+
renderBlocks(node.children, key, htmlPolicy)
|
|
2326
2878
|
] }, key);
|
|
2327
2879
|
case "containerDirective":
|
|
2328
|
-
return /* @__PURE__ */
|
|
2880
|
+
return /* @__PURE__ */ jsxs11(
|
|
2329
2881
|
"div",
|
|
2330
2882
|
{
|
|
2331
2883
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
2332
2884
|
"data-directive": node.name,
|
|
2333
2885
|
children: [
|
|
2334
|
-
node.label && /* @__PURE__ */
|
|
2335
|
-
renderBlocks(node.children, key)
|
|
2886
|
+
node.label && /* @__PURE__ */ jsx18("div", { className: "squisq-md-directive-label", children: node.label }),
|
|
2887
|
+
renderBlocks(node.children, key, htmlPolicy)
|
|
2336
2888
|
]
|
|
2337
2889
|
},
|
|
2338
2890
|
key
|
|
2339
2891
|
);
|
|
2340
2892
|
case "leafDirective":
|
|
2341
|
-
return /* @__PURE__ */
|
|
2893
|
+
return /* @__PURE__ */ jsx18(
|
|
2342
2894
|
"div",
|
|
2343
2895
|
{
|
|
2344
2896
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
2345
2897
|
"data-directive": node.name,
|
|
2346
|
-
children: renderInline(node.children, key)
|
|
2898
|
+
children: renderInline(node.children, key, htmlPolicy)
|
|
2347
2899
|
},
|
|
2348
2900
|
key
|
|
2349
2901
|
);
|
|
2350
2902
|
case "definitionList":
|
|
2351
|
-
return /* @__PURE__ */
|
|
2903
|
+
return /* @__PURE__ */ jsx18("dl", { className: "squisq-md-dl", children: node.children.map((child, i) => {
|
|
2352
2904
|
if (child.type === "definitionTerm") {
|
|
2353
|
-
return /* @__PURE__ */
|
|
2905
|
+
return /* @__PURE__ */ jsx18("dt", { className: "squisq-md-dt", children: renderInline(child.children, `${key}dt${i}`, htmlPolicy) }, `${key}dt${i}`);
|
|
2354
2906
|
}
|
|
2355
|
-
return /* @__PURE__ */
|
|
2907
|
+
return /* @__PURE__ */ jsx18("dd", { className: "squisq-md-dd", children: renderBlocks(child.children, `${key}dd${i}`, htmlPolicy) }, `${key}dd${i}`);
|
|
2356
2908
|
}) }, key);
|
|
2357
2909
|
default:
|
|
2358
2910
|
return null;
|
|
2359
2911
|
}
|
|
2360
2912
|
}
|
|
2361
|
-
function renderListItem(item, key) {
|
|
2913
|
+
function renderListItem(item, key, htmlPolicy = "sanitize") {
|
|
2362
2914
|
const isTask = item.checked !== null && item.checked !== void 0;
|
|
2363
|
-
return /* @__PURE__ */
|
|
2364
|
-
isTask && /* @__PURE__ */
|
|
2365
|
-
renderBlocks(item.children, key)
|
|
2915
|
+
return /* @__PURE__ */ jsxs11("li", { className: `squisq-md-li${isTask ? " squisq-md-task" : ""}`, children: [
|
|
2916
|
+
isTask && /* @__PURE__ */ jsx18("input", { type: "checkbox", checked: !!item.checked, readOnly: true, className: "squisq-md-checkbox" }),
|
|
2917
|
+
renderBlocks(item.children, key, htmlPolicy)
|
|
2366
2918
|
] }, key);
|
|
2367
2919
|
}
|
|
2368
|
-
function renderTable(rows, align, key) {
|
|
2920
|
+
function renderTable(rows, align, key, htmlPolicy = "sanitize") {
|
|
2369
2921
|
const [headerRow, ...bodyRows] = rows;
|
|
2370
|
-
return /* @__PURE__ */
|
|
2371
|
-
headerRow && /* @__PURE__ */
|
|
2922
|
+
return /* @__PURE__ */ jsxs11("table", { className: "squisq-md-table", children: [
|
|
2923
|
+
headerRow && /* @__PURE__ */ jsx18("thead", { children: /* @__PURE__ */ jsx18("tr", { children: headerRow.children.map((cell, ci) => /* @__PURE__ */ jsx18(
|
|
2372
2924
|
"th",
|
|
2373
2925
|
{
|
|
2374
2926
|
className: "squisq-md-th",
|
|
2375
2927
|
style: align?.[ci] ? { textAlign: align[ci] } : void 0,
|
|
2376
|
-
children: renderInline(cell.children, `${key}th${ci}
|
|
2928
|
+
children: renderInline(cell.children, `${key}th${ci}`, htmlPolicy)
|
|
2377
2929
|
},
|
|
2378
2930
|
`${key}th${ci}`
|
|
2379
2931
|
)) }) }),
|
|
2380
|
-
bodyRows.length > 0 && /* @__PURE__ */
|
|
2932
|
+
bodyRows.length > 0 && /* @__PURE__ */ jsx18("tbody", { children: bodyRows.map((row, ri) => /* @__PURE__ */ jsx18("tr", { children: row.children.map((cell, ci) => /* @__PURE__ */ jsx18(
|
|
2381
2933
|
"td",
|
|
2382
2934
|
{
|
|
2383
2935
|
className: "squisq-md-td",
|
|
2384
2936
|
style: align?.[ci] ? { textAlign: align[ci] } : void 0,
|
|
2385
|
-
children: renderInline(cell.children, `${key}td${ri}-${ci}
|
|
2937
|
+
children: renderInline(cell.children, `${key}td${ri}-${ci}`, htmlPolicy)
|
|
2386
2938
|
},
|
|
2387
2939
|
`${key}td${ri}-${ci}`
|
|
2388
2940
|
)) }, `${key}tr${ri}`)) })
|
|
2389
2941
|
] }, key);
|
|
2390
2942
|
}
|
|
2391
|
-
function renderBlocks(nodes, keyPrefix = "") {
|
|
2392
|
-
return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}
|
|
2943
|
+
function renderBlocks(nodes, keyPrefix = "", htmlPolicy = "sanitize") {
|
|
2944
|
+
return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}`, htmlPolicy));
|
|
2393
2945
|
}
|
|
2394
2946
|
function MdImage({ src, alt, title }) {
|
|
2395
|
-
const
|
|
2396
|
-
|
|
2947
|
+
const safeSrc = sanitizeUrl(src, "media");
|
|
2948
|
+
const resolved = useMediaUrl(safeSrc ?? "", ".");
|
|
2949
|
+
if (!safeSrc) return null;
|
|
2950
|
+
return /* @__PURE__ */ jsx18("img", { className: "squisq-md-image", src: resolved, alt, title });
|
|
2951
|
+
}
|
|
2952
|
+
function resolveHtmlNodes(nodes, htmlPolicy) {
|
|
2953
|
+
if (htmlPolicy === "strip") return [];
|
|
2954
|
+
if (htmlPolicy === "trusted") return nodes;
|
|
2955
|
+
return sanitizeHtmlNodes2(nodes);
|
|
2397
2956
|
}
|
|
2398
2957
|
function containsMediaTag(nodes) {
|
|
2399
2958
|
for (const node of nodes) {
|
|
2400
2959
|
if (node.type !== "htmlElement") continue;
|
|
2401
|
-
|
|
2960
|
+
const tagName = node.tagName.toLowerCase();
|
|
2961
|
+
if (tagName === "video" || tagName === "audio") return true;
|
|
2402
2962
|
if (containsMediaTag(node.children)) return true;
|
|
2403
2963
|
}
|
|
2404
2964
|
return false;
|
|
2405
2965
|
}
|
|
2966
|
+
var DANGEROUS_HTML_TAGS = /* @__PURE__ */ new Set([
|
|
2967
|
+
"base",
|
|
2968
|
+
"embed",
|
|
2969
|
+
"iframe",
|
|
2970
|
+
"link",
|
|
2971
|
+
"meta",
|
|
2972
|
+
"object",
|
|
2973
|
+
"script",
|
|
2974
|
+
"style",
|
|
2975
|
+
"title"
|
|
2976
|
+
]);
|
|
2977
|
+
function containsDangerousTag(nodes) {
|
|
2978
|
+
for (const node of nodes) {
|
|
2979
|
+
if (node.type !== "htmlElement") continue;
|
|
2980
|
+
if (DANGEROUS_HTML_TAGS.has(node.tagName.toLowerCase())) return true;
|
|
2981
|
+
if (containsDangerousTag(node.children)) return true;
|
|
2982
|
+
}
|
|
2983
|
+
return false;
|
|
2984
|
+
}
|
|
2985
|
+
var DANGEROUS_RAW_HTML_RE = /<\s*\/?\s*(?:base|embed|iframe|link|meta|object|script|style|title)\b/i;
|
|
2986
|
+
function hasDangerousRawHtml(rawHtml) {
|
|
2987
|
+
return DANGEROUS_RAW_HTML_RE.test(rawHtml);
|
|
2988
|
+
}
|
|
2406
2989
|
var PASSTHROUGH_ATTRS = {
|
|
2407
2990
|
// common
|
|
2408
2991
|
class: "className",
|
|
@@ -2412,6 +2995,10 @@ var PASSTHROUGH_ATTRS = {
|
|
|
2412
2995
|
// media-adjacent (used when video/audio appear inside other wrappers)
|
|
2413
2996
|
width: "width",
|
|
2414
2997
|
height: "height",
|
|
2998
|
+
src: "src",
|
|
2999
|
+
alt: "alt",
|
|
3000
|
+
loading: "loading",
|
|
3001
|
+
decoding: "decoding",
|
|
2415
3002
|
// anchor
|
|
2416
3003
|
href: "href",
|
|
2417
3004
|
target: "target",
|
|
@@ -2431,8 +3018,10 @@ function reactPropsFromAttrs(attrs) {
|
|
|
2431
3018
|
return out;
|
|
2432
3019
|
}
|
|
2433
3020
|
function renderHtmlElement(el, key) {
|
|
2434
|
-
|
|
2435
|
-
|
|
3021
|
+
const tagName = el.tagName.toLowerCase();
|
|
3022
|
+
if (DANGEROUS_HTML_TAGS.has(tagName)) return null;
|
|
3023
|
+
if (tagName === "video") {
|
|
3024
|
+
return /* @__PURE__ */ jsx18(
|
|
2436
3025
|
InlineVideoPlayer,
|
|
2437
3026
|
{
|
|
2438
3027
|
src: el.attributes.src ?? "",
|
|
@@ -2445,8 +3034,8 @@ function renderHtmlElement(el, key) {
|
|
|
2445
3034
|
key
|
|
2446
3035
|
);
|
|
2447
3036
|
}
|
|
2448
|
-
if (
|
|
2449
|
-
return /* @__PURE__ */
|
|
3037
|
+
if (tagName === "audio") {
|
|
3038
|
+
return /* @__PURE__ */ jsx18(
|
|
2450
3039
|
InlineAudioPlayer,
|
|
2451
3040
|
{
|
|
2452
3041
|
src: el.attributes.src ?? "",
|
|
@@ -2456,12 +3045,12 @@ function renderHtmlElement(el, key) {
|
|
|
2456
3045
|
key
|
|
2457
3046
|
);
|
|
2458
3047
|
}
|
|
2459
|
-
const Tag =
|
|
3048
|
+
const Tag = tagName;
|
|
2460
3049
|
const props = reactPropsFromAttrs(el.attributes);
|
|
2461
3050
|
if (el.selfClosing) {
|
|
2462
|
-
return /* @__PURE__ */
|
|
3051
|
+
return /* @__PURE__ */ jsx18(Tag, { ...props }, key);
|
|
2463
3052
|
}
|
|
2464
|
-
return /* @__PURE__ */
|
|
3053
|
+
return /* @__PURE__ */ jsx18(Tag, { ...props, children: renderHtmlNodes(el.children, `${key}c`) }, key);
|
|
2465
3054
|
}
|
|
2466
3055
|
function renderHtmlNodes(nodes, keyPrefix) {
|
|
2467
3056
|
return nodes.map((node, i) => {
|
|
@@ -2470,7 +3059,7 @@ function renderHtmlNodes(nodes, keyPrefix) {
|
|
|
2470
3059
|
case "htmlElement":
|
|
2471
3060
|
return renderHtmlElement(node, key);
|
|
2472
3061
|
case "htmlText":
|
|
2473
|
-
return /* @__PURE__ */
|
|
3062
|
+
return /* @__PURE__ */ jsx18(Fragment2, { children: node.value }, key);
|
|
2474
3063
|
case "htmlComment":
|
|
2475
3064
|
return null;
|
|
2476
3065
|
default:
|
|
@@ -2478,13 +3067,17 @@ function renderHtmlNodes(nodes, keyPrefix) {
|
|
|
2478
3067
|
}
|
|
2479
3068
|
});
|
|
2480
3069
|
}
|
|
2481
|
-
function MarkdownRenderer({
|
|
3070
|
+
function MarkdownRenderer({
|
|
3071
|
+
nodes,
|
|
3072
|
+
className,
|
|
3073
|
+
htmlPolicy = "sanitize"
|
|
3074
|
+
}) {
|
|
2482
3075
|
if (!nodes || nodes.length === 0) return null;
|
|
2483
|
-
return /* @__PURE__ */
|
|
3076
|
+
return /* @__PURE__ */ jsx18("div", { className: `squisq-md ${className || ""}`, children: renderBlocks(nodes, "", htmlPolicy) });
|
|
2484
3077
|
}
|
|
2485
3078
|
|
|
2486
3079
|
// src/LinearDocView.tsx
|
|
2487
|
-
import { jsx as
|
|
3080
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2488
3081
|
function isAnnotatedBlock(block) {
|
|
2489
3082
|
const annotation = block.sourceHeading?.templateAnnotation;
|
|
2490
3083
|
if (!annotation) return false;
|
|
@@ -2500,11 +3093,10 @@ function countAll(blocks) {
|
|
|
2500
3093
|
}
|
|
2501
3094
|
function BlockSection({ block, basePath, viewport, renderContext, blockIndex }) {
|
|
2502
3095
|
const isAnnotated = isAnnotatedBlock(block);
|
|
2503
|
-
const visualBlock =
|
|
3096
|
+
const visualBlock = useMemo8(() => {
|
|
2504
3097
|
if (!isAnnotated) return null;
|
|
2505
3098
|
const annotation = block.sourceHeading.templateAnnotation;
|
|
2506
3099
|
const headingText = extractPlainText(block.sourceHeading);
|
|
2507
|
-
const bodyText = extractBodyPlainText(block.contents);
|
|
2508
3100
|
const templateBlock = {
|
|
2509
3101
|
id: block.id,
|
|
2510
3102
|
template: annotation.template,
|
|
@@ -2512,12 +3104,14 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2512
3104
|
duration: 1,
|
|
2513
3105
|
audioSegment: 0,
|
|
2514
3106
|
title: headingText,
|
|
2515
|
-
...
|
|
3107
|
+
...deriveTemplateInputs(
|
|
2516
3108
|
annotation.template ?? "sectionHeader",
|
|
2517
3109
|
headingText,
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
3110
|
+
block.contents,
|
|
3111
|
+
{
|
|
3112
|
+
placeholders: true
|
|
3113
|
+
}
|
|
3114
|
+
) ?? {},
|
|
2521
3115
|
...annotation.params,
|
|
2522
3116
|
...block.templateOverrides
|
|
2523
3117
|
};
|
|
@@ -2532,15 +3126,15 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2532
3126
|
template: annotation.template
|
|
2533
3127
|
};
|
|
2534
3128
|
}, [block, isAnnotated, renderContext, blockIndex]);
|
|
2535
|
-
return /* @__PURE__ */
|
|
3129
|
+
return /* @__PURE__ */ jsxs12(
|
|
2536
3130
|
"div",
|
|
2537
3131
|
{
|
|
2538
3132
|
className: "squisq-linear-section",
|
|
2539
3133
|
"data-block-id": block.id,
|
|
2540
3134
|
"data-template": isAnnotated ? block.sourceHeading?.templateAnnotation?.template : void 0,
|
|
2541
3135
|
children: [
|
|
2542
|
-
block.sourceHeading && !isAnnotated && /* @__PURE__ */
|
|
2543
|
-
isAnnotated && visualBlock && /* @__PURE__ */
|
|
3136
|
+
block.sourceHeading && !isAnnotated && /* @__PURE__ */ jsx19(MarkdownRenderer, { nodes: [block.sourceHeading] }),
|
|
3137
|
+
isAnnotated && visualBlock && /* @__PURE__ */ jsx19("div", { className: "squisq-linear-card", children: /* @__PURE__ */ jsx19(
|
|
2544
3138
|
"div",
|
|
2545
3139
|
{
|
|
2546
3140
|
className: "squisq-linear-card-svg",
|
|
@@ -2550,7 +3144,7 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2550
3144
|
overflow: "hidden",
|
|
2551
3145
|
marginBottom: "1em"
|
|
2552
3146
|
},
|
|
2553
|
-
children: /* @__PURE__ */
|
|
3147
|
+
children: /* @__PURE__ */ jsx19(
|
|
2554
3148
|
BlockRenderer,
|
|
2555
3149
|
{
|
|
2556
3150
|
block: visualBlock,
|
|
@@ -2561,8 +3155,8 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2561
3155
|
)
|
|
2562
3156
|
}
|
|
2563
3157
|
) }),
|
|
2564
|
-
!isAnnotated && block.contents && block.contents.length > 0 && /* @__PURE__ */
|
|
2565
|
-
block.children && block.children.length > 0 && /* @__PURE__ */
|
|
3158
|
+
!isAnnotated && block.contents && block.contents.length > 0 && /* @__PURE__ */ jsx19(MarkdownRenderer, { nodes: block.contents }),
|
|
3159
|
+
block.children && block.children.length > 0 && /* @__PURE__ */ jsx19("div", { className: "squisq-linear-children", children: block.children.map((child, i) => /* @__PURE__ */ jsx19(
|
|
2566
3160
|
BlockSection,
|
|
2567
3161
|
{
|
|
2568
3162
|
block: child,
|
|
@@ -2577,139 +3171,6 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2577
3171
|
}
|
|
2578
3172
|
);
|
|
2579
3173
|
}
|
|
2580
|
-
function extractBodyPlainText(contents) {
|
|
2581
|
-
if (!contents || contents.length === 0) return "";
|
|
2582
|
-
return contents.map((n) => extractPlainText(n)).join("\n").trim();
|
|
2583
|
-
}
|
|
2584
|
-
function extractListItems(contents) {
|
|
2585
|
-
if (!contents) return [];
|
|
2586
|
-
const items = [];
|
|
2587
|
-
for (const node of contents) {
|
|
2588
|
-
if (node.type === "list") {
|
|
2589
|
-
for (const item of node.children) {
|
|
2590
|
-
const text = extractPlainText(item).trim();
|
|
2591
|
-
if (text) items.push(text);
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
return items;
|
|
2596
|
-
}
|
|
2597
|
-
function extractFirstImage(contents) {
|
|
2598
|
-
if (!contents || contents.length === 0) return null;
|
|
2599
|
-
function fromHtml(nodes) {
|
|
2600
|
-
for (const node of nodes) {
|
|
2601
|
-
if (!node || typeof node !== "object") continue;
|
|
2602
|
-
const n = node;
|
|
2603
|
-
if (n.type === "htmlElement" && n.tagName === "img") {
|
|
2604
|
-
const attrs = n.attributes;
|
|
2605
|
-
if (attrs && typeof attrs.src === "string" && attrs.src) {
|
|
2606
|
-
return {
|
|
2607
|
-
src: attrs.src,
|
|
2608
|
-
alt: typeof attrs.alt === "string" ? attrs.alt : "",
|
|
2609
|
-
width: parseDim(attrs.width),
|
|
2610
|
-
height: parseDim(attrs.height)
|
|
2611
|
-
};
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
2614
|
-
if (Array.isArray(n.children)) {
|
|
2615
|
-
const found = fromHtml(n.children);
|
|
2616
|
-
if (found) return found;
|
|
2617
|
-
}
|
|
2618
|
-
}
|
|
2619
|
-
return null;
|
|
2620
|
-
}
|
|
2621
|
-
function walk(node) {
|
|
2622
|
-
if (!node || typeof node !== "object") return null;
|
|
2623
|
-
const n = node;
|
|
2624
|
-
if (n.type === "image" && typeof n.url === "string" && n.url) {
|
|
2625
|
-
return { src: n.url, alt: typeof n.alt === "string" ? n.alt : "" };
|
|
2626
|
-
}
|
|
2627
|
-
if ((n.type === "htmlBlock" || n.type === "htmlInline") && Array.isArray(n.htmlChildren)) {
|
|
2628
|
-
const found = fromHtml(n.htmlChildren);
|
|
2629
|
-
if (found) return found;
|
|
2630
|
-
}
|
|
2631
|
-
if (Array.isArray(n.children)) {
|
|
2632
|
-
for (const child of n.children) {
|
|
2633
|
-
const found = walk(child);
|
|
2634
|
-
if (found) return found;
|
|
2635
|
-
}
|
|
2636
|
-
}
|
|
2637
|
-
return null;
|
|
2638
|
-
}
|
|
2639
|
-
for (const node of contents) {
|
|
2640
|
-
const found = walk(node);
|
|
2641
|
-
if (found) return found;
|
|
2642
|
-
}
|
|
2643
|
-
return null;
|
|
2644
|
-
}
|
|
2645
|
-
function parseDim(raw) {
|
|
2646
|
-
if (raw === void 0) return void 0;
|
|
2647
|
-
const n = parseFloat(raw);
|
|
2648
|
-
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
2649
|
-
}
|
|
2650
|
-
function extractTableData(contents) {
|
|
2651
|
-
if (!contents) return null;
|
|
2652
|
-
for (const node of contents) {
|
|
2653
|
-
if (node.type === "table") {
|
|
2654
|
-
const table = node;
|
|
2655
|
-
const [headerRow, ...bodyRows] = table.children;
|
|
2656
|
-
if (!headerRow) return null;
|
|
2657
|
-
const headers = headerRow.children.map((cell) => extractPlainText(cell).trim());
|
|
2658
|
-
const rows = bodyRows.map((row) => row.children.map((cell) => extractPlainText(cell).trim()));
|
|
2659
|
-
return { headers, rows, align: table.align };
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
return null;
|
|
2663
|
-
}
|
|
2664
|
-
function getTemplateDefaults(templateName, headingText, bodyText, contents) {
|
|
2665
|
-
switch (templateName) {
|
|
2666
|
-
case "statHighlight":
|
|
2667
|
-
return { stat: headingText, description: bodyText || headingText };
|
|
2668
|
-
case "quote":
|
|
2669
|
-
case "fullBleedQuote":
|
|
2670
|
-
case "pullQuote":
|
|
2671
|
-
return { quote: bodyText || headingText };
|
|
2672
|
-
case "factCard":
|
|
2673
|
-
return { fact: headingText, explanation: bodyText || headingText };
|
|
2674
|
-
case "comparisonBar":
|
|
2675
|
-
return { leftLabel: "A", leftValue: 60, rightLabel: "B", rightValue: 40 };
|
|
2676
|
-
case "list": {
|
|
2677
|
-
const items = extractListItems(contents);
|
|
2678
|
-
return { items: items.length > 0 ? items : ["Item 1", "Item 2", "Item 3"] };
|
|
2679
|
-
}
|
|
2680
|
-
case "definitionCard":
|
|
2681
|
-
return { term: headingText, definition: bodyText || headingText };
|
|
2682
|
-
case "dateEvent":
|
|
2683
|
-
return { date: headingText, description: bodyText || headingText };
|
|
2684
|
-
case "dataTable": {
|
|
2685
|
-
const tableData = extractTableData(contents);
|
|
2686
|
-
return tableData ?? { headers: ["Column"], rows: [["Data"]] };
|
|
2687
|
-
}
|
|
2688
|
-
case "imageWithCaption": {
|
|
2689
|
-
const img = extractFirstImage(contents);
|
|
2690
|
-
if (!img) return { caption: headingText };
|
|
2691
|
-
return {
|
|
2692
|
-
imageSrc: img.src,
|
|
2693
|
-
imageAlt: img.alt || headingText,
|
|
2694
|
-
caption: headingText
|
|
2695
|
-
};
|
|
2696
|
-
}
|
|
2697
|
-
case "leftFeature":
|
|
2698
|
-
case "rightFeature": {
|
|
2699
|
-
const img = extractFirstImage(contents);
|
|
2700
|
-
return {
|
|
2701
|
-
imageSrc: img?.src ?? "",
|
|
2702
|
-
imageAlt: img?.alt || headingText,
|
|
2703
|
-
imageWidth: img?.width,
|
|
2704
|
-
imageHeight: img?.height,
|
|
2705
|
-
title: headingText,
|
|
2706
|
-
body: bodyText
|
|
2707
|
-
};
|
|
2708
|
-
}
|
|
2709
|
-
default:
|
|
2710
|
-
return {};
|
|
2711
|
-
}
|
|
2712
|
-
}
|
|
2713
3174
|
function LinearDocView({
|
|
2714
3175
|
doc,
|
|
2715
3176
|
basePath = "/",
|
|
@@ -2721,15 +3182,19 @@ function LinearDocView({
|
|
|
2721
3182
|
imageDisplayMode = "inline"
|
|
2722
3183
|
}) {
|
|
2723
3184
|
const activeViewport = viewport ?? VIEWPORT_PRESETS3.landscape;
|
|
2724
|
-
const totalBlocks =
|
|
3185
|
+
const totalBlocks = useMemo8(() => countAll(doc.blocks), [doc.blocks]);
|
|
2725
3186
|
const autoSurface = useAutoSurface(surface === "auto");
|
|
2726
3187
|
const resolvedSurface = surface === "auto" ? autoSurface : surface;
|
|
2727
|
-
const renderContext =
|
|
2728
|
-
const baseTheme = theme ??
|
|
3188
|
+
const renderContext = useMemo8(() => {
|
|
3189
|
+
const baseTheme = theme ?? DEFAULT_THEME2;
|
|
3190
|
+
const effectiveTheme = resolvedSurface ? applySurface(baseTheme, resolvedSurface) : baseTheme;
|
|
2729
3191
|
return {
|
|
2730
|
-
theme:
|
|
3192
|
+
theme: effectiveTheme,
|
|
2731
3193
|
viewport: activeViewport,
|
|
2732
|
-
totalBlocks
|
|
3194
|
+
totalBlocks,
|
|
3195
|
+
// Theme atmosphere (vignette/grain/gradient persistent layers) shows
|
|
3196
|
+
// on the inline template cards so they match the player's look.
|
|
3197
|
+
persistentLayers: effectiveTheme.persistentLayers
|
|
2733
3198
|
};
|
|
2734
3199
|
}, [activeViewport, totalBlocks, theme, resolvedSurface]);
|
|
2735
3200
|
const activeTheme = renderContext.theme;
|
|
@@ -2740,7 +3205,7 @@ function LinearDocView({
|
|
|
2740
3205
|
const bodyFont = resolveFontFamily2(activeTheme.typography.bodyFont, "system-ui, sans-serif");
|
|
2741
3206
|
const titleFont = resolveFontFamily2(activeTheme.typography.titleFont, "Georgia, serif");
|
|
2742
3207
|
const lineHt = activeTheme.typography.lineHeight ?? 1.7;
|
|
2743
|
-
return /* @__PURE__ */
|
|
3208
|
+
return /* @__PURE__ */ jsx19(
|
|
2744
3209
|
"div",
|
|
2745
3210
|
{
|
|
2746
3211
|
className: `squisq-linear ${className || ""}`,
|
|
@@ -2756,7 +3221,7 @@ function LinearDocView({
|
|
|
2756
3221
|
overflowX: "hidden",
|
|
2757
3222
|
background: bgColor
|
|
2758
3223
|
},
|
|
2759
|
-
children: /* @__PURE__ */
|
|
3224
|
+
children: /* @__PURE__ */ jsxs12(
|
|
2760
3225
|
"div",
|
|
2761
3226
|
{
|
|
2762
3227
|
className: `squisq-linear-content squisq-md${thinMargins ? " squisq-linear-content--thin" : ""}${imageDisplayMode === "thumbnail" ? " squisq-linear-content--thumbnail-images" : ""}`,
|
|
@@ -2781,7 +3246,7 @@ function LinearDocView({
|
|
|
2781
3246
|
"--squisq-linear-bg": bgColor
|
|
2782
3247
|
},
|
|
2783
3248
|
children: [
|
|
2784
|
-
/* @__PURE__ */
|
|
3249
|
+
/* @__PURE__ */ jsx19("style", { children: `
|
|
2785
3250
|
.squisq-linear-content h1,
|
|
2786
3251
|
.squisq-linear-content h2,
|
|
2787
3252
|
.squisq-linear-content h3,
|
|
@@ -2893,7 +3358,7 @@ function LinearDocView({
|
|
|
2893
3358
|
background: color-mix(in srgb, var(--squisq-linear-primary) 8%, transparent);
|
|
2894
3359
|
}
|
|
2895
3360
|
` }),
|
|
2896
|
-
doc.blocks.map((block, i) => /* @__PURE__ */
|
|
3361
|
+
doc.blocks.map((block, i) => /* @__PURE__ */ jsx19(
|
|
2897
3362
|
BlockSection,
|
|
2898
3363
|
{
|
|
2899
3364
|
block,
|
|
@@ -2912,7 +3377,7 @@ function LinearDocView({
|
|
|
2912
3377
|
}
|
|
2913
3378
|
|
|
2914
3379
|
// src/DocPlayer.tsx
|
|
2915
|
-
import { jsx as
|
|
3380
|
+
import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2916
3381
|
var SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
2917
3382
|
"a",
|
|
2918
3383
|
"an",
|
|
@@ -2984,13 +3449,13 @@ function DocPlayer({
|
|
|
2984
3449
|
}) {
|
|
2985
3450
|
const isSlideshowMode = displayMode === "slideshow";
|
|
2986
3451
|
const isLinearMode = displayMode === "linear";
|
|
2987
|
-
const audioRef =
|
|
2988
|
-
const containerRef =
|
|
2989
|
-
const [tapFeedback, setTapFeedback] =
|
|
2990
|
-
const tapFeedbackTimer =
|
|
3452
|
+
const audioRef = useRef6(null);
|
|
3453
|
+
const containerRef = useRef6(null);
|
|
3454
|
+
const [tapFeedback, setTapFeedback] = useState6(null);
|
|
3455
|
+
const tapFeedbackTimer = useRef6();
|
|
2991
3456
|
const { viewport, orientation } = useViewportOrientation();
|
|
2992
3457
|
const activeViewport = forceViewport || (renderMode ? VIEWPORT_PRESETS4.landscape : viewport);
|
|
2993
|
-
const isDebugMode =
|
|
3458
|
+
const isDebugMode = useMemo9(() => {
|
|
2994
3459
|
if (typeof window === "undefined") return false;
|
|
2995
3460
|
const params = new URLSearchParams(window.location.search);
|
|
2996
3461
|
return params.get("debug") === "true";
|
|
@@ -3013,11 +3478,12 @@ function DocPlayer({
|
|
|
3013
3478
|
skipToSegment: _skipToSegment,
|
|
3014
3479
|
restart
|
|
3015
3480
|
} = audio;
|
|
3016
|
-
const
|
|
3481
|
+
const mediaSchedule = useMemo9(() => resolveMediaSchedule(script), [script]);
|
|
3482
|
+
const currentTimeRef = useRef6(currentTime);
|
|
3017
3483
|
currentTimeRef.current = currentTime;
|
|
3018
|
-
const totalDurationRef =
|
|
3484
|
+
const totalDurationRef = useRef6(totalDuration);
|
|
3019
3485
|
totalDurationRef.current = totalDuration;
|
|
3020
|
-
const expandedBlocksLenRef =
|
|
3486
|
+
const expandedBlocksLenRef = useRef6(0);
|
|
3021
3487
|
const handleContainerClick = useCallback5(
|
|
3022
3488
|
(e) => {
|
|
3023
3489
|
if (renderMode || isSlideshowMode || isLinearMode) return;
|
|
@@ -3036,8 +3502,8 @@ function DocPlayer({
|
|
|
3036
3502
|
);
|
|
3037
3503
|
const autoSurface = useAutoSurface(surface === "auto");
|
|
3038
3504
|
const resolvedSurface = surface === "auto" ? autoSurface : surface;
|
|
3039
|
-
const effectiveTheme =
|
|
3040
|
-
const base = theme ??
|
|
3505
|
+
const effectiveTheme = useMemo9(() => {
|
|
3506
|
+
const base = theme ?? DEFAULT_THEME3;
|
|
3041
3507
|
return resolvedSurface ? applySurface2(base, resolvedSurface) : base;
|
|
3042
3508
|
}, [theme, resolvedSurface]);
|
|
3043
3509
|
const {
|
|
@@ -3053,7 +3519,7 @@ function DocPlayer({
|
|
|
3053
3519
|
prevBlock: _prevBlock,
|
|
3054
3520
|
blocks: expandedBlocks
|
|
3055
3521
|
} = useDocPlayback(script, currentTime, activeViewport, renderMode, effectiveTheme);
|
|
3056
|
-
const coverBlock =
|
|
3522
|
+
const coverBlock = useMemo9(() => {
|
|
3057
3523
|
const startBlockConfig = script.startBlock;
|
|
3058
3524
|
if (!startBlockConfig) return null;
|
|
3059
3525
|
const context = createTemplateContext(effectiveTheme, 0, 1, activeViewport);
|
|
@@ -3068,11 +3534,11 @@ function DocPlayer({
|
|
|
3068
3534
|
layers
|
|
3069
3535
|
};
|
|
3070
3536
|
}, [script.startBlock, activeViewport, effectiveTheme]);
|
|
3071
|
-
const [coverForced, setCoverForced] =
|
|
3072
|
-
const [coverGraceActive, setCoverGraceActive] =
|
|
3073
|
-
const coverGraceTimer =
|
|
3074
|
-
const coverWasShowing =
|
|
3075
|
-
const hasPlayedOnce =
|
|
3537
|
+
const [coverForced, setCoverForced] = useState6(false);
|
|
3538
|
+
const [coverGraceActive, setCoverGraceActive] = useState6(false);
|
|
3539
|
+
const coverGraceTimer = useRef6();
|
|
3540
|
+
const coverWasShowing = useRef6(false);
|
|
3541
|
+
const hasPlayedOnce = useRef6(false);
|
|
3076
3542
|
const atRest = !!(coverBlock && !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
3077
3543
|
if (atRest) coverWasShowing.current = true;
|
|
3078
3544
|
useEffect7(() => {
|
|
@@ -3085,7 +3551,7 @@ function DocPlayer({
|
|
|
3085
3551
|
}, [isPlaying, coverBlock, renderMode]);
|
|
3086
3552
|
useEffect7(() => () => clearTimeout(coverGraceTimer.current), []);
|
|
3087
3553
|
const showCoverBlock = !isSlideshowMode && !isLinearMode && coverBlock && (coverForced || coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
3088
|
-
const hasAutoPlayed =
|
|
3554
|
+
const hasAutoPlayed = useRef6(false);
|
|
3089
3555
|
useEffect7(() => {
|
|
3090
3556
|
if (isAudioReady && autoPlay && !hasAutoPlayed.current) {
|
|
3091
3557
|
hasAutoPlayed.current = true;
|
|
@@ -3133,7 +3599,11 @@ function DocPlayer({
|
|
|
3133
3599
|
const video = el;
|
|
3134
3600
|
const clipStart = parseFloat(video.dataset.clipStart || "0");
|
|
3135
3601
|
const clipEnd = parseFloat(video.dataset.clipEnd || "0");
|
|
3136
|
-
const
|
|
3602
|
+
const startAt = parseFloat(video.dataset.startAt || "0");
|
|
3603
|
+
const targetTime = Math.min(
|
|
3604
|
+
clipStart + Math.max(0, blockElapsed - startAt),
|
|
3605
|
+
clipEnd
|
|
3606
|
+
);
|
|
3137
3607
|
video.pause();
|
|
3138
3608
|
video.currentTime = targetTime;
|
|
3139
3609
|
videoSeekPromises.push(
|
|
@@ -3148,6 +3618,26 @@ function DocPlayer({
|
|
|
3148
3618
|
);
|
|
3149
3619
|
});
|
|
3150
3620
|
}
|
|
3621
|
+
document.querySelectorAll("video[data-clip-id]").forEach((el) => {
|
|
3622
|
+
const video = el;
|
|
3623
|
+
const absStart = parseFloat(video.dataset.absStart || "0");
|
|
3624
|
+
const absEnd = parseFloat(video.dataset.absEnd || "0");
|
|
3625
|
+
const sourceIn = parseFloat(video.dataset.sourceIn || "0");
|
|
3626
|
+
video.pause();
|
|
3627
|
+
if (time < absStart || time >= absEnd) return;
|
|
3628
|
+
const targetTime = sourceIn + (time - absStart);
|
|
3629
|
+
video.currentTime = targetTime;
|
|
3630
|
+
videoSeekPromises.push(
|
|
3631
|
+
new Promise((r) => {
|
|
3632
|
+
if (Math.abs(video.currentTime - targetTime) < 0.1) {
|
|
3633
|
+
r();
|
|
3634
|
+
} else {
|
|
3635
|
+
video.addEventListener("seeked", () => r(), { once: true });
|
|
3636
|
+
setTimeout(r, 200);
|
|
3637
|
+
}
|
|
3638
|
+
})
|
|
3639
|
+
);
|
|
3640
|
+
});
|
|
3151
3641
|
Promise.all(videoSeekPromises).then(() => {
|
|
3152
3642
|
requestAnimationFrame(() => resolve());
|
|
3153
3643
|
});
|
|
@@ -3155,12 +3645,9 @@ function DocPlayer({
|
|
|
3155
3645
|
});
|
|
3156
3646
|
};
|
|
3157
3647
|
w.getDuration = () => {
|
|
3158
|
-
|
|
3159
|
-
if (
|
|
3160
|
-
|
|
3161
|
-
return last.startTime + last.duration;
|
|
3162
|
-
}
|
|
3163
|
-
return 0;
|
|
3648
|
+
const mediaDuration = getDocPlaybackDuration(script);
|
|
3649
|
+
if (totalDuration > 0) return Math.max(totalDuration, mediaDuration);
|
|
3650
|
+
return mediaDuration;
|
|
3164
3651
|
};
|
|
3165
3652
|
w.getBlocks = () => expandedBlocks.map((s) => ({
|
|
3166
3653
|
id: s.id,
|
|
@@ -3213,7 +3700,7 @@ function DocPlayer({
|
|
|
3213
3700
|
};
|
|
3214
3701
|
}, [renderMode, isDebugMode, seekTo, totalDuration, expandedBlocks, coverBlock]);
|
|
3215
3702
|
const defaultMode = captionsEnabledProp === false ? "off" : captionStyle || "standard";
|
|
3216
|
-
const [captionMode, setCaptionMode] =
|
|
3703
|
+
const [captionMode, setCaptionMode] = useState6(defaultMode);
|
|
3217
3704
|
const captionsEnabled = captionMode !== "off";
|
|
3218
3705
|
const activeCaptionStyle = captionMode === "social" ? "social" : "standard";
|
|
3219
3706
|
const setCaptionsEnabled = useCallback5(
|
|
@@ -3231,8 +3718,8 @@ function DocPlayer({
|
|
|
3231
3718
|
});
|
|
3232
3719
|
}, [onCaptionsToggle]);
|
|
3233
3720
|
const hasCaptions = script.captions && script.captions.phrases.length > 0;
|
|
3234
|
-
const segmentTitleMap =
|
|
3235
|
-
const playbackState =
|
|
3721
|
+
const segmentTitleMap = useMemo9(() => buildSegmentTitleMap(script), [script]);
|
|
3722
|
+
const playbackState = useMemo9(
|
|
3236
3723
|
() => ({
|
|
3237
3724
|
isPlaying,
|
|
3238
3725
|
currentTime,
|
|
@@ -3265,7 +3752,7 @@ function DocPlayer({
|
|
|
3265
3752
|
currentBlock
|
|
3266
3753
|
]
|
|
3267
3754
|
);
|
|
3268
|
-
const playbackActions =
|
|
3755
|
+
const playbackActions = useMemo9(
|
|
3269
3756
|
() => ({
|
|
3270
3757
|
toggle,
|
|
3271
3758
|
restart,
|
|
@@ -3276,7 +3763,7 @@ function DocPlayer({
|
|
|
3276
3763
|
}),
|
|
3277
3764
|
[toggle, restart, seekTo, setCaptionsEnabled, cycleCaptionMode, onFullscreenToggle]
|
|
3278
3765
|
);
|
|
3279
|
-
const slideNavActions =
|
|
3766
|
+
const slideNavActions = useMemo9(
|
|
3280
3767
|
() => ({
|
|
3281
3768
|
nextSlide: () => {
|
|
3282
3769
|
if (currentBlockIndex < expandedBlocks.length - 1) {
|
|
@@ -3338,7 +3825,7 @@ function DocPlayer({
|
|
|
3338
3825
|
}
|
|
3339
3826
|
return block.id.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
3340
3827
|
}, []);
|
|
3341
|
-
const blockMarkers =
|
|
3828
|
+
const blockMarkers = useMemo9(() => {
|
|
3342
3829
|
if (!totalDuration || !expandedBlocks.length) return [];
|
|
3343
3830
|
let prevSegment = -1;
|
|
3344
3831
|
return expandedBlocks.map((block, index) => {
|
|
@@ -3411,7 +3898,7 @@ function DocPlayer({
|
|
|
3411
3898
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
3412
3899
|
}, [handleKeyDown, renderMode]);
|
|
3413
3900
|
if (isLinearMode) {
|
|
3414
|
-
return /* @__PURE__ */
|
|
3901
|
+
return /* @__PURE__ */ jsx20(
|
|
3415
3902
|
"div",
|
|
3416
3903
|
{
|
|
3417
3904
|
ref: containerRef,
|
|
@@ -3422,7 +3909,7 @@ function DocPlayer({
|
|
|
3422
3909
|
height: "100%",
|
|
3423
3910
|
overflow: "hidden"
|
|
3424
3911
|
},
|
|
3425
|
-
children: /* @__PURE__ */
|
|
3912
|
+
children: /* @__PURE__ */ jsx20(
|
|
3426
3913
|
LinearDocView,
|
|
3427
3914
|
{
|
|
3428
3915
|
doc: script,
|
|
@@ -3435,7 +3922,7 @@ function DocPlayer({
|
|
|
3435
3922
|
}
|
|
3436
3923
|
);
|
|
3437
3924
|
}
|
|
3438
|
-
return /* @__PURE__ */
|
|
3925
|
+
return /* @__PURE__ */ jsxs13(
|
|
3439
3926
|
"div",
|
|
3440
3927
|
{
|
|
3441
3928
|
ref: containerRef,
|
|
@@ -3450,9 +3937,19 @@ function DocPlayer({
|
|
|
3450
3937
|
cursor: renderMode ? void 0 : "pointer"
|
|
3451
3938
|
},
|
|
3452
3939
|
children: [
|
|
3453
|
-
/* @__PURE__ */
|
|
3454
|
-
/* @__PURE__ */
|
|
3455
|
-
|
|
3940
|
+
/* @__PURE__ */ jsx20("audio", { ref: audioRef, preload: "auto", muted }),
|
|
3941
|
+
/* @__PURE__ */ jsx20(
|
|
3942
|
+
MediaClipLayer,
|
|
3943
|
+
{
|
|
3944
|
+
schedule: mediaSchedule,
|
|
3945
|
+
currentTime,
|
|
3946
|
+
isPlaying,
|
|
3947
|
+
basePath,
|
|
3948
|
+
renderMode
|
|
3949
|
+
}
|
|
3950
|
+
),
|
|
3951
|
+
/* @__PURE__ */ jsxs13("div", { className: "doc-player__viewport", children: [
|
|
3952
|
+
showCoverBlock && coverBlock && /* @__PURE__ */ jsx20("div", { className: "doc-player__block doc-player__block--cover", children: /* @__PURE__ */ jsx20(
|
|
3456
3953
|
BlockRenderer,
|
|
3457
3954
|
{
|
|
3458
3955
|
block: coverBlock,
|
|
@@ -3462,17 +3959,22 @@ function DocPlayer({
|
|
|
3462
3959
|
viewport: activeViewport
|
|
3463
3960
|
}
|
|
3464
3961
|
) }),
|
|
3465
|
-
!showCoverBlock && previousBlock && isExiting &&
|
|
3962
|
+
!showCoverBlock && previousBlock && isExiting && // Keyed by block id so each block is its own DOM subtree: React never
|
|
3963
|
+
// reconciles one block's layers onto another's (templates reuse layer
|
|
3964
|
+
// ids like `title`/`background`), which would otherwise reuse stale
|
|
3965
|
+
// DOM / skip entrance animations mid-transition.
|
|
3966
|
+
/* @__PURE__ */ jsx20("div", { className: "doc-player__block doc-player__block--previous", children: /* @__PURE__ */ jsx20(
|
|
3466
3967
|
BlockRenderer,
|
|
3467
3968
|
{
|
|
3468
3969
|
block: previousBlock,
|
|
3469
3970
|
blockTime,
|
|
3470
3971
|
basePath,
|
|
3471
3972
|
isExiting: true,
|
|
3973
|
+
transition: currentBlock?.transition,
|
|
3472
3974
|
viewport: activeViewport
|
|
3473
3975
|
}
|
|
3474
|
-
) }),
|
|
3475
|
-
!showCoverBlock && currentBlock && /* @__PURE__ */
|
|
3976
|
+
) }, previousBlock.id),
|
|
3977
|
+
!showCoverBlock && currentBlock && /* @__PURE__ */ jsx20("div", { className: "doc-player__block doc-player__block--active", children: /* @__PURE__ */ jsx20(
|
|
3476
3978
|
BlockRenderer,
|
|
3477
3979
|
{
|
|
3478
3980
|
block: currentBlock,
|
|
@@ -3482,8 +3984,8 @@ function DocPlayer({
|
|
|
3482
3984
|
viewport: activeViewport,
|
|
3483
3985
|
isPlaying
|
|
3484
3986
|
}
|
|
3485
|
-
) }),
|
|
3486
|
-
hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */
|
|
3987
|
+
) }, currentBlock.id),
|
|
3988
|
+
hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */ jsx20(
|
|
3487
3989
|
CaptionOverlay,
|
|
3488
3990
|
{
|
|
3489
3991
|
captions: script.captions,
|
|
@@ -3495,7 +3997,7 @@ function DocPlayer({
|
|
|
3495
3997
|
viewport: activeViewport
|
|
3496
3998
|
}
|
|
3497
3999
|
),
|
|
3498
|
-
isDebugMode && /* @__PURE__ */
|
|
4000
|
+
isDebugMode && /* @__PURE__ */ jsxs13(
|
|
3499
4001
|
"div",
|
|
3500
4002
|
{
|
|
3501
4003
|
className: "doc-player__debug",
|
|
@@ -3516,27 +4018,27 @@ function DocPlayer({
|
|
|
3516
4018
|
textAlign: "left"
|
|
3517
4019
|
},
|
|
3518
4020
|
children: [
|
|
3519
|
-
/* @__PURE__ */
|
|
3520
|
-
/* @__PURE__ */
|
|
3521
|
-
/* @__PURE__ */
|
|
4021
|
+
/* @__PURE__ */ jsx20("div", { style: { color: "#ffcc00", fontWeight: "bold", marginBottom: "4px" }, children: "DEBUG MODE" }),
|
|
4022
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4023
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "template:" }),
|
|
3522
4024
|
" ",
|
|
3523
|
-
/* @__PURE__ */
|
|
4025
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#ff6b6b" }, children: currentBlock?.template ?? "raw" })
|
|
3524
4026
|
] }),
|
|
3525
|
-
/* @__PURE__ */
|
|
3526
|
-
/* @__PURE__ */
|
|
4027
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4028
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "block:" }),
|
|
3527
4029
|
" ",
|
|
3528
4030
|
currentBlockIndex + 1,
|
|
3529
4031
|
"/",
|
|
3530
4032
|
expandedBlocks.length,
|
|
3531
4033
|
" ",
|
|
3532
|
-
/* @__PURE__ */
|
|
4034
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3533
4035
|
"(",
|
|
3534
4036
|
currentBlock?.id || "none",
|
|
3535
4037
|
")"
|
|
3536
4038
|
] })
|
|
3537
4039
|
] }),
|
|
3538
|
-
/* @__PURE__ */
|
|
3539
|
-
/* @__PURE__ */
|
|
4040
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4041
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "time:" }),
|
|
3540
4042
|
" ",
|
|
3541
4043
|
currentTime.toFixed(2),
|
|
3542
4044
|
"s /",
|
|
@@ -3544,7 +4046,7 @@ function DocPlayer({
|
|
|
3544
4046
|
totalDuration.toFixed(1),
|
|
3545
4047
|
"s",
|
|
3546
4048
|
" ",
|
|
3547
|
-
/* @__PURE__ */
|
|
4049
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3548
4050
|
"(progress: ",
|
|
3549
4051
|
(docProgress * 100).toFixed(1),
|
|
3550
4052
|
"%, scriptDur:",
|
|
@@ -3553,8 +4055,8 @@ function DocPlayer({
|
|
|
3553
4055
|
")"
|
|
3554
4056
|
] })
|
|
3555
4057
|
] }),
|
|
3556
|
-
/* @__PURE__ */
|
|
3557
|
-
/* @__PURE__ */
|
|
4058
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4059
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "blockTime:" }),
|
|
3558
4060
|
" ",
|
|
3559
4061
|
blockTime.toFixed(2),
|
|
3560
4062
|
"s /",
|
|
@@ -3562,58 +4064,58 @@ function DocPlayer({
|
|
|
3562
4064
|
(currentBlock?.duration || 0).toFixed(1),
|
|
3563
4065
|
"s"
|
|
3564
4066
|
] }),
|
|
3565
|
-
/* @__PURE__ */
|
|
3566
|
-
/* @__PURE__ */
|
|
4067
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4068
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "segment:" }),
|
|
3567
4069
|
" ",
|
|
3568
4070
|
currentSegment,
|
|
3569
4071
|
"/",
|
|
3570
4072
|
script.audio.segments.length - 1,
|
|
3571
4073
|
" ",
|
|
3572
|
-
/* @__PURE__ */
|
|
4074
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3573
4075
|
"(",
|
|
3574
4076
|
script.audio.segments[currentSegment]?.name || "none",
|
|
3575
4077
|
")"
|
|
3576
4078
|
] })
|
|
3577
4079
|
] }),
|
|
3578
|
-
/* @__PURE__ */
|
|
3579
|
-
/* @__PURE__ */
|
|
4080
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4081
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "viewport:" }),
|
|
3580
4082
|
" ",
|
|
3581
4083
|
activeViewport.name || `${activeViewport.width}x${activeViewport.height}`,
|
|
3582
4084
|
" ",
|
|
3583
|
-
/* @__PURE__ */
|
|
4085
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3584
4086
|
"(",
|
|
3585
4087
|
orientation,
|
|
3586
4088
|
")"
|
|
3587
4089
|
] })
|
|
3588
4090
|
] }),
|
|
3589
|
-
/* @__PURE__ */
|
|
3590
|
-
/* @__PURE__ */
|
|
4091
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4092
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "playing:" }),
|
|
3591
4093
|
" ",
|
|
3592
|
-
/* @__PURE__ */
|
|
3593
|
-
showCoverBlock && /* @__PURE__ */
|
|
4094
|
+
/* @__PURE__ */ jsx20("span", { style: { color: isPlaying ? "#4ade80" : "#f87171" }, children: isPlaying ? "yes" : "no" }),
|
|
4095
|
+
showCoverBlock && /* @__PURE__ */ jsx20("span", { style: { color: "#60a5fa" }, children: " (cover)" })
|
|
3594
4096
|
] }),
|
|
3595
4097
|
hasCaptions && (() => {
|
|
3596
4098
|
const debugPhrase = getCaptionAtTime2(script.captions, currentTime);
|
|
3597
4099
|
const debugEnabled = captionsEnabled && (isPlaying || currentTime > 0);
|
|
3598
|
-
return /* @__PURE__ */
|
|
3599
|
-
/* @__PURE__ */
|
|
3600
|
-
/* @__PURE__ */
|
|
4100
|
+
return /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
4101
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4102
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "captions:" }),
|
|
3601
4103
|
" ",
|
|
3602
4104
|
script.captions?.phrases.length || 0,
|
|
3603
4105
|
" phrases",
|
|
3604
4106
|
" ",
|
|
3605
|
-
/* @__PURE__ */
|
|
4107
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: captionsEnabled ? "#4ade80" : "#666" }, children: [
|
|
3606
4108
|
"(",
|
|
3607
4109
|
captionsEnabled ? "on" : "off",
|
|
3608
4110
|
")"
|
|
3609
4111
|
] })
|
|
3610
4112
|
] }),
|
|
3611
|
-
/* @__PURE__ */
|
|
3612
|
-
/* @__PURE__ */
|
|
4113
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4114
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "cc.enabled:" }),
|
|
3613
4115
|
" ",
|
|
3614
|
-
/* @__PURE__ */
|
|
4116
|
+
/* @__PURE__ */ jsx20("span", { style: { color: debugEnabled ? "#4ade80" : "#f87171" }, children: String(debugEnabled) }),
|
|
3615
4117
|
" ",
|
|
3616
|
-
/* @__PURE__ */
|
|
4118
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3617
4119
|
"(playing=",
|
|
3618
4120
|
String(isPlaying),
|
|
3619
4121
|
" t>0=",
|
|
@@ -3621,15 +4123,15 @@ function DocPlayer({
|
|
|
3621
4123
|
")"
|
|
3622
4124
|
] })
|
|
3623
4125
|
] }),
|
|
3624
|
-
/* @__PURE__ */
|
|
3625
|
-
/* @__PURE__ */
|
|
4126
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4127
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "cc.phrase:" }),
|
|
3626
4128
|
" ",
|
|
3627
|
-
/* @__PURE__ */
|
|
4129
|
+
/* @__PURE__ */ jsx20("span", { style: { color: debugPhrase ? "#4ade80" : "#f87171" }, children: debugPhrase ? `"${debugPhrase.text.slice(0, 30)}..."` : "null" })
|
|
3628
4130
|
] }),
|
|
3629
|
-
debugPhrase && /* @__PURE__ */
|
|
3630
|
-
/* @__PURE__ */
|
|
4131
|
+
debugPhrase && /* @__PURE__ */ jsxs13("div", { children: [
|
|
4132
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "cc.range:" }),
|
|
3631
4133
|
" ",
|
|
3632
|
-
/* @__PURE__ */
|
|
4134
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#60a5fa" }, children: [
|
|
3633
4135
|
debugPhrase.startTime.toFixed(2),
|
|
3634
4136
|
"-",
|
|
3635
4137
|
debugPhrase.endTime.toFixed(2)
|
|
@@ -3641,7 +4143,7 @@ function DocPlayer({
|
|
|
3641
4143
|
}
|
|
3642
4144
|
)
|
|
3643
4145
|
] }),
|
|
3644
|
-
!isAvailable && unavailableMessage && /* @__PURE__ */
|
|
4146
|
+
!isAvailable && unavailableMessage && /* @__PURE__ */ jsxs13(
|
|
3645
4147
|
"div",
|
|
3646
4148
|
{
|
|
3647
4149
|
className: "doc-player__unavailable",
|
|
@@ -3662,12 +4164,12 @@ function DocPlayer({
|
|
|
3662
4164
|
zIndex: 50
|
|
3663
4165
|
},
|
|
3664
4166
|
children: [
|
|
3665
|
-
/* @__PURE__ */
|
|
3666
|
-
/* @__PURE__ */
|
|
4167
|
+
/* @__PURE__ */ jsx20("span", { style: { fontSize: "32px" }, children: "\u{1F50A}" }),
|
|
4168
|
+
/* @__PURE__ */ jsx20("span", { children: unavailableMessage })
|
|
3667
4169
|
]
|
|
3668
4170
|
}
|
|
3669
4171
|
),
|
|
3670
|
-
!renderMode && !isSlideshowMode && showControls && /* @__PURE__ */
|
|
4172
|
+
!renderMode && !isSlideshowMode && showControls && /* @__PURE__ */ jsx20(
|
|
3671
4173
|
DocControlsOverlay,
|
|
3672
4174
|
{
|
|
3673
4175
|
state: playbackState,
|
|
@@ -3677,7 +4179,7 @@ function DocPlayer({
|
|
|
3677
4179
|
getBlockTitle
|
|
3678
4180
|
}
|
|
3679
4181
|
),
|
|
3680
|
-
!renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */
|
|
4182
|
+
!renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */ jsx20(
|
|
3681
4183
|
"div",
|
|
3682
4184
|
{
|
|
3683
4185
|
className: "doc-player__scrubber",
|
|
@@ -3692,7 +4194,7 @@ function DocPlayer({
|
|
|
3692
4194
|
alignItems: "center",
|
|
3693
4195
|
zIndex: 100
|
|
3694
4196
|
},
|
|
3695
|
-
children: /* @__PURE__ */
|
|
4197
|
+
children: /* @__PURE__ */ jsx20(
|
|
3696
4198
|
DocProgressBar,
|
|
3697
4199
|
{
|
|
3698
4200
|
state: playbackState,
|
|
@@ -3704,15 +4206,15 @@ function DocPlayer({
|
|
|
3704
4206
|
)
|
|
3705
4207
|
}
|
|
3706
4208
|
),
|
|
3707
|
-
!renderMode && isSlideshowMode && /* @__PURE__ */
|
|
3708
|
-
!isSlideshowMode && tapFeedback && /* @__PURE__ */
|
|
4209
|
+
!renderMode && isSlideshowMode && /* @__PURE__ */ jsx20(DocControlsSlideshow, { state: playbackState, slideNav: slideNavActions }),
|
|
4210
|
+
!isSlideshowMode && tapFeedback && /* @__PURE__ */ jsx20("div", { className: "doc-player__tap-feedback", children: /* @__PURE__ */ jsx20("svg", { viewBox: "0 0 24 24", fill: "white", width: "48", height: "48", children: tapFeedback === "pause" ? /* @__PURE__ */ jsx20("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) : /* @__PURE__ */ jsx20("path", { d: "M8 5v14l11-7z" }) }) }, Date.now())
|
|
3709
4211
|
]
|
|
3710
4212
|
}
|
|
3711
4213
|
);
|
|
3712
4214
|
}
|
|
3713
4215
|
|
|
3714
4216
|
// src/DocControlsBottom.tsx
|
|
3715
|
-
import { jsx as
|
|
4217
|
+
import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3716
4218
|
function DocControlsBottom({
|
|
3717
4219
|
state,
|
|
3718
4220
|
actions,
|
|
@@ -3720,32 +4222,32 @@ function DocControlsBottom({
|
|
|
3720
4222
|
expandedBlocks,
|
|
3721
4223
|
getBlockTitle
|
|
3722
4224
|
}) {
|
|
3723
|
-
return /* @__PURE__ */
|
|
3724
|
-
/* @__PURE__ */
|
|
4225
|
+
return /* @__PURE__ */ jsxs14("div", { className: "doc-controls-bottom", children: [
|
|
4226
|
+
/* @__PURE__ */ jsx21(
|
|
3725
4227
|
"button",
|
|
3726
4228
|
{
|
|
3727
4229
|
className: "bottom-ctrl-btn",
|
|
3728
4230
|
onClick: actions.restart,
|
|
3729
4231
|
title: "Restart",
|
|
3730
4232
|
"aria-label": "Restart from beginning",
|
|
3731
|
-
children: /* @__PURE__ */
|
|
4233
|
+
children: /* @__PURE__ */ jsx21("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx21("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" }) })
|
|
3732
4234
|
}
|
|
3733
4235
|
),
|
|
3734
|
-
/* @__PURE__ */
|
|
4236
|
+
/* @__PURE__ */ jsx21(
|
|
3735
4237
|
"button",
|
|
3736
4238
|
{
|
|
3737
4239
|
className: "bottom-ctrl-btn bottom-play-btn",
|
|
3738
4240
|
onClick: actions.toggle,
|
|
3739
4241
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
3740
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
4242
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx21("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx21("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx21("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx21("path", { d: "M8 5v14l11-7z" }) })
|
|
3741
4243
|
}
|
|
3742
4244
|
),
|
|
3743
|
-
/* @__PURE__ */
|
|
4245
|
+
/* @__PURE__ */ jsxs14("span", { className: "bottom-time", children: [
|
|
3744
4246
|
formatTime(state.currentTime),
|
|
3745
4247
|
" / ",
|
|
3746
4248
|
formatTime(state.totalDuration)
|
|
3747
4249
|
] }),
|
|
3748
|
-
/* @__PURE__ */
|
|
4250
|
+
/* @__PURE__ */ jsx21(
|
|
3749
4251
|
DocProgressBar,
|
|
3750
4252
|
{
|
|
3751
4253
|
state,
|
|
@@ -3755,82 +4257,82 @@ function DocControlsBottom({
|
|
|
3755
4257
|
getBlockTitle
|
|
3756
4258
|
}
|
|
3757
4259
|
),
|
|
3758
|
-
/* @__PURE__ */
|
|
4260
|
+
/* @__PURE__ */ jsxs14("span", { className: "bottom-segment", children: [
|
|
3759
4261
|
state.currentBlockIndex + 1,
|
|
3760
4262
|
"/",
|
|
3761
4263
|
state.totalBlocks
|
|
3762
4264
|
] }),
|
|
3763
|
-
state.hasCaptions && /* @__PURE__ */
|
|
4265
|
+
state.hasCaptions && /* @__PURE__ */ jsx21(
|
|
3764
4266
|
"button",
|
|
3765
4267
|
{
|
|
3766
4268
|
className: `bottom-ctrl-btn ${state.captionMode !== "off" ? "bottom-ctrl-btn--active" : ""}`,
|
|
3767
4269
|
onClick: () => actions.cycleCaptionMode(),
|
|
3768
4270
|
title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
|
|
3769
4271
|
"aria-label": "Cycle caption style",
|
|
3770
|
-
children: /* @__PURE__ */
|
|
4272
|
+
children: /* @__PURE__ */ jsx21("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx21("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" }) })
|
|
3771
4273
|
}
|
|
3772
4274
|
)
|
|
3773
4275
|
] });
|
|
3774
4276
|
}
|
|
3775
4277
|
|
|
3776
4278
|
// src/DocControlsSidebar.tsx
|
|
3777
|
-
import { jsx as
|
|
4279
|
+
import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3778
4280
|
function DocControlsSidebar({ state, actions }) {
|
|
3779
|
-
return /* @__PURE__ */
|
|
3780
|
-
/* @__PURE__ */
|
|
4281
|
+
return /* @__PURE__ */ jsxs15("div", { className: "doc-controls-sidebar", children: [
|
|
4282
|
+
/* @__PURE__ */ jsx22(
|
|
3781
4283
|
"button",
|
|
3782
4284
|
{
|
|
3783
4285
|
className: "sidebar-ctrl-btn",
|
|
3784
4286
|
onClick: actions.restart,
|
|
3785
4287
|
title: "Restart",
|
|
3786
4288
|
"aria-label": "Restart from beginning",
|
|
3787
|
-
children: /* @__PURE__ */
|
|
4289
|
+
children: /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx22("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" }) })
|
|
3788
4290
|
}
|
|
3789
4291
|
),
|
|
3790
|
-
/* @__PURE__ */
|
|
4292
|
+
/* @__PURE__ */ jsx22(
|
|
3791
4293
|
"button",
|
|
3792
4294
|
{
|
|
3793
4295
|
className: "sidebar-ctrl-btn sidebar-play-btn",
|
|
3794
4296
|
onClick: actions.toggle,
|
|
3795
4297
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
3796
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
4298
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx22("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx22("path", { d: "M8 5v14l11-7z" }) })
|
|
3797
4299
|
}
|
|
3798
4300
|
),
|
|
3799
|
-
/* @__PURE__ */
|
|
3800
|
-
/* @__PURE__ */
|
|
3801
|
-
/* @__PURE__ */
|
|
4301
|
+
/* @__PURE__ */ jsxs15("div", { className: "sidebar-time", children: [
|
|
4302
|
+
/* @__PURE__ */ jsx22("div", { children: formatTime(state.currentTime) }),
|
|
4303
|
+
/* @__PURE__ */ jsx22("div", { className: "sidebar-time-total", children: formatTime(state.totalDuration) })
|
|
3802
4304
|
] }),
|
|
3803
|
-
/* @__PURE__ */
|
|
4305
|
+
/* @__PURE__ */ jsxs15("div", { className: "sidebar-segment", children: [
|
|
3804
4306
|
state.currentBlockIndex + 1,
|
|
3805
4307
|
"/",
|
|
3806
4308
|
state.totalBlocks
|
|
3807
4309
|
] }),
|
|
3808
|
-
state.hasCaptions && /* @__PURE__ */
|
|
4310
|
+
state.hasCaptions && /* @__PURE__ */ jsx22(
|
|
3809
4311
|
"button",
|
|
3810
4312
|
{
|
|
3811
4313
|
className: `sidebar-ctrl-btn ${state.captionMode !== "off" ? "sidebar-ctrl-btn--active" : ""}`,
|
|
3812
4314
|
onClick: () => actions.cycleCaptionMode(),
|
|
3813
4315
|
title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
|
|
3814
4316
|
"aria-label": "Cycle caption style",
|
|
3815
|
-
children: /* @__PURE__ */
|
|
4317
|
+
children: /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx22("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" }) })
|
|
3816
4318
|
}
|
|
3817
4319
|
),
|
|
3818
|
-
actions.toggleFullscreen && /* @__PURE__ */
|
|
4320
|
+
actions.toggleFullscreen && /* @__PURE__ */ jsx22(
|
|
3819
4321
|
"button",
|
|
3820
4322
|
{
|
|
3821
4323
|
className: `sidebar-ctrl-btn ${state.isFullscreen ? "sidebar-ctrl-btn--active" : ""}`,
|
|
3822
4324
|
onClick: actions.toggleFullscreen,
|
|
3823
4325
|
title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
|
|
3824
4326
|
"aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
3825
|
-
children: state.isFullscreen ? /* @__PURE__ */
|
|
4327
|
+
children: state.isFullscreen ? /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx22("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx22("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
|
|
3826
4328
|
}
|
|
3827
4329
|
)
|
|
3828
4330
|
] });
|
|
3829
4331
|
}
|
|
3830
4332
|
|
|
3831
4333
|
// src/DocPlayerWithSidebar.tsx
|
|
3832
|
-
import { useRef as
|
|
3833
|
-
import { jsx as
|
|
4334
|
+
import { useRef as useRef7, useState as useState7, useCallback as useCallback6, useEffect as useEffect8 } from "react";
|
|
4335
|
+
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3834
4336
|
var DEFAULT_STATE = {
|
|
3835
4337
|
isPlaying: false,
|
|
3836
4338
|
currentTime: 0,
|
|
@@ -3859,10 +4361,10 @@ function DocPlayerWithSidebar({
|
|
|
3859
4361
|
forceViewport,
|
|
3860
4362
|
onPlayingChange
|
|
3861
4363
|
}) {
|
|
3862
|
-
const stateRef =
|
|
3863
|
-
const actionsRef =
|
|
3864
|
-
const wasPlayingRef =
|
|
3865
|
-
const [, setTick] =
|
|
4364
|
+
const stateRef = useRef7(DEFAULT_STATE);
|
|
4365
|
+
const actionsRef = useRef7(null);
|
|
4366
|
+
const wasPlayingRef = useRef7(false);
|
|
4367
|
+
const [, setTick] = useState7(0);
|
|
3866
4368
|
const handleStateChange = useCallback6(
|
|
3867
4369
|
(state) => {
|
|
3868
4370
|
stateRef.current = state;
|
|
@@ -3887,8 +4389,8 @@ function DocPlayerWithSidebar({
|
|
|
3887
4389
|
}, 250);
|
|
3888
4390
|
return () => clearInterval(interval);
|
|
3889
4391
|
}, []);
|
|
3890
|
-
return /* @__PURE__ */
|
|
3891
|
-
/* @__PURE__ */
|
|
4392
|
+
return /* @__PURE__ */ jsxs16("div", { className: "doc-player-sidebar-layout", children: [
|
|
4393
|
+
/* @__PURE__ */ jsx23("div", { className: "doc-player-sidebar-layout__video", children: /* @__PURE__ */ jsx23(
|
|
3892
4394
|
DocPlayer,
|
|
3893
4395
|
{
|
|
3894
4396
|
script,
|
|
@@ -3908,22 +4410,22 @@ function DocPlayerWithSidebar({
|
|
|
3908
4410
|
forceViewport
|
|
3909
4411
|
}
|
|
3910
4412
|
) }),
|
|
3911
|
-
actionsRef.current && /* @__PURE__ */
|
|
4413
|
+
actionsRef.current && /* @__PURE__ */ jsx23(DocControlsSidebar, { state: stateRef.current, actions: actionsRef.current })
|
|
3912
4414
|
] });
|
|
3913
4415
|
}
|
|
3914
4416
|
|
|
3915
4417
|
// src/jsonView/useJsonViewTokens.ts
|
|
3916
|
-
import { useMemo as
|
|
4418
|
+
import { useMemo as useMemo10 } from "react";
|
|
3917
4419
|
import {
|
|
3918
4420
|
applySurface as applySurface3,
|
|
3919
4421
|
resolveFontFamily as resolveFontFamily3
|
|
3920
4422
|
} from "@bendyline/squisq/schemas";
|
|
3921
|
-
import { DEFAULT_THEME as
|
|
4423
|
+
import { DEFAULT_THEME as DEFAULT_THEME4 } from "@bendyline/squisq/doc";
|
|
3922
4424
|
function useJsonViewTokens(theme, surface) {
|
|
3923
4425
|
const auto = useAutoSurface(surface === "auto");
|
|
3924
4426
|
const effectiveSurface = surface === "auto" ? auto : surface ?? void 0;
|
|
3925
|
-
return
|
|
3926
|
-
const baseTheme = theme ??
|
|
4427
|
+
return useMemo10(() => {
|
|
4428
|
+
const baseTheme = theme ?? DEFAULT_THEME4;
|
|
3927
4429
|
const finalTheme = effectiveSurface ? applySurface3(baseTheme, effectiveSurface) : baseTheme;
|
|
3928
4430
|
const titleFont = resolveFontFamily3(finalTheme.typography.titleFont, "system-ui, sans-serif");
|
|
3929
4431
|
const bodyFont = resolveFontFamily3(finalTheme.typography.bodyFont, "system-ui, sans-serif");
|
|
@@ -3955,26 +4457,26 @@ import {
|
|
|
3955
4457
|
} from "@bendyline/squisq/jsonForm";
|
|
3956
4458
|
|
|
3957
4459
|
// src/jsonView/viewers.tsx
|
|
3958
|
-
import { Fragment as
|
|
4460
|
+
import { Fragment as Fragment4, useMemo as useMemo11 } from "react";
|
|
3959
4461
|
import {
|
|
3960
4462
|
arrayItemKind
|
|
3961
4463
|
} from "@bendyline/squisq/jsonForm";
|
|
3962
4464
|
import { parseMarkdown } from "@bendyline/squisq/markdown";
|
|
3963
|
-
import { Fragment as
|
|
4465
|
+
import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3964
4466
|
function TextViewer({ value }) {
|
|
3965
4467
|
if (value === void 0 || value === null || value === "") {
|
|
3966
|
-
return /* @__PURE__ */
|
|
4468
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
3967
4469
|
}
|
|
3968
|
-
return /* @__PURE__ */
|
|
4470
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value", children: String(value) });
|
|
3969
4471
|
}
|
|
3970
4472
|
function MultilineViewer({ value }) {
|
|
3971
4473
|
if (value === void 0 || value === null || value === "") {
|
|
3972
|
-
return /* @__PURE__ */
|
|
4474
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
3973
4475
|
}
|
|
3974
|
-
return /* @__PURE__ */
|
|
4476
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value squisq-jv-value--multiline", children: String(value) });
|
|
3975
4477
|
}
|
|
3976
4478
|
function RichTextViewer({ value }) {
|
|
3977
|
-
const nodes =
|
|
4479
|
+
const nodes = useMemo11(() => {
|
|
3978
4480
|
if (typeof value !== "string" || value === "") return null;
|
|
3979
4481
|
try {
|
|
3980
4482
|
const doc = parseMarkdown(value);
|
|
@@ -3983,39 +4485,39 @@ function RichTextViewer({ value }) {
|
|
|
3983
4485
|
return null;
|
|
3984
4486
|
}
|
|
3985
4487
|
}, [value]);
|
|
3986
|
-
if (!nodes) return /* @__PURE__ */
|
|
3987
|
-
return /* @__PURE__ */
|
|
4488
|
+
if (!nodes) return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4489
|
+
return /* @__PURE__ */ jsx24("div", { className: "squisq-jv-richtext", children: /* @__PURE__ */ jsx24(MarkdownRenderer, { nodes }) });
|
|
3988
4490
|
}
|
|
3989
4491
|
function NumberViewer({ value }) {
|
|
3990
4492
|
if (value === void 0 || value === null) {
|
|
3991
|
-
return /* @__PURE__ */
|
|
4493
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
3992
4494
|
}
|
|
3993
|
-
return /* @__PURE__ */
|
|
4495
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value", children: String(value) });
|
|
3994
4496
|
}
|
|
3995
4497
|
function BooleanViewer({ value }) {
|
|
3996
4498
|
const on = Boolean(value);
|
|
3997
|
-
return /* @__PURE__ */
|
|
4499
|
+
return /* @__PURE__ */ jsx24("span", { className: `squisq-jv-toggle squisq-jv-toggle--${on ? "on" : "off"}`, children: on ? "On" : "Off" });
|
|
3998
4500
|
}
|
|
3999
4501
|
function EnumViewer({ value, schema }) {
|
|
4000
4502
|
if (value === void 0 || value === null || value === "") {
|
|
4001
|
-
return /* @__PURE__ */
|
|
4503
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4002
4504
|
}
|
|
4003
4505
|
const labels = schema.squisq?.enumLabels;
|
|
4004
4506
|
const display = labels && typeof value === "string" ? labels[value] ?? value : String(value);
|
|
4005
|
-
return /* @__PURE__ */
|
|
4507
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value", children: display });
|
|
4006
4508
|
}
|
|
4007
4509
|
function ColorViewer({ value }) {
|
|
4008
4510
|
if (typeof value !== "string" || value === "") {
|
|
4009
|
-
return /* @__PURE__ */
|
|
4511
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4010
4512
|
}
|
|
4011
|
-
return /* @__PURE__ */
|
|
4012
|
-
/* @__PURE__ */
|
|
4013
|
-
/* @__PURE__ */
|
|
4513
|
+
return /* @__PURE__ */ jsxs17("span", { className: "squisq-jv-color", children: [
|
|
4514
|
+
/* @__PURE__ */ jsx24("span", { className: "squisq-jv-color__swatch", style: { background: value }, "aria-hidden": true }),
|
|
4515
|
+
/* @__PURE__ */ jsx24("span", { className: "squisq-jv-color__hex", children: value })
|
|
4014
4516
|
] });
|
|
4015
4517
|
}
|
|
4016
4518
|
function DateViewer({ value, schema }) {
|
|
4017
4519
|
if (typeof value !== "string" || value === "") {
|
|
4018
|
-
return /* @__PURE__ */
|
|
4520
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4019
4521
|
}
|
|
4020
4522
|
const fmt = schema.format;
|
|
4021
4523
|
let display = value;
|
|
@@ -4032,31 +4534,31 @@ function DateViewer({ value, schema }) {
|
|
|
4032
4534
|
}
|
|
4033
4535
|
} catch {
|
|
4034
4536
|
}
|
|
4035
|
-
return /* @__PURE__ */
|
|
4537
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value", children: display });
|
|
4036
4538
|
}
|
|
4037
4539
|
function ChipBinViewer({ value, schema }) {
|
|
4038
4540
|
if (!Array.isArray(value) || value.length === 0) {
|
|
4039
|
-
return /* @__PURE__ */
|
|
4541
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4040
4542
|
}
|
|
4041
4543
|
const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
|
|
4042
4544
|
const labels = itemSchema?.squisq?.enumLabels;
|
|
4043
|
-
return /* @__PURE__ */
|
|
4545
|
+
return /* @__PURE__ */ jsx24("div", { className: "squisq-jv-chip-bin", children: value.map((item, i) => {
|
|
4044
4546
|
const label = labels && typeof item === "string" ? labels[item] ?? String(item) : String(item);
|
|
4045
|
-
return /* @__PURE__ */
|
|
4547
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-chip", children: label }, i);
|
|
4046
4548
|
}) });
|
|
4047
4549
|
}
|
|
4048
4550
|
function CardStackViewer(props) {
|
|
4049
4551
|
const { value, schema, rootSchema, rootData, pointer, density } = props;
|
|
4050
4552
|
if (!Array.isArray(value) || value.length === 0) {
|
|
4051
|
-
return /* @__PURE__ */
|
|
4553
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "No items" });
|
|
4052
4554
|
}
|
|
4053
4555
|
const itemSchema = (Array.isArray(schema.items) ? schema.items[0] : schema.items) ?? {};
|
|
4054
4556
|
const itemLabel = itemSchema.squisq?.itemLabel;
|
|
4055
|
-
return /* @__PURE__ */
|
|
4557
|
+
return /* @__PURE__ */ jsx24("div", { className: "squisq-jv-card-stack", children: value.map((item, i) => {
|
|
4056
4558
|
const title = resolveItemTitle(itemLabel, item, i);
|
|
4057
|
-
return /* @__PURE__ */
|
|
4058
|
-
title ? /* @__PURE__ */
|
|
4059
|
-
/* @__PURE__ */
|
|
4559
|
+
return /* @__PURE__ */ jsxs17("div", { className: "squisq-jv-card", children: [
|
|
4560
|
+
title ? /* @__PURE__ */ jsx24("h4", { className: "squisq-jv-card__title", children: title }) : null,
|
|
4561
|
+
/* @__PURE__ */ jsx24(
|
|
4060
4562
|
RenderNode,
|
|
4061
4563
|
{
|
|
4062
4564
|
value: item,
|
|
@@ -4087,16 +4589,16 @@ function GroupViewer(props) {
|
|
|
4087
4589
|
const help = schema.squisq?.help ?? schema.description;
|
|
4088
4590
|
const obj = (value && typeof value === "object" ? value : {}) ?? {};
|
|
4089
4591
|
const propEntries = Object.entries(schema.properties ?? {});
|
|
4090
|
-
return /* @__PURE__ */
|
|
4091
|
-
title ? /* @__PURE__ */
|
|
4092
|
-
help ? /* @__PURE__ */
|
|
4093
|
-
propEntries.map(([key, propSchema]) => /* @__PURE__ */
|
|
4592
|
+
return /* @__PURE__ */ jsxs17("section", { className: "squisq-jv-group", children: [
|
|
4593
|
+
title ? /* @__PURE__ */ jsx24("h3", { className: "squisq-jv-group__title", children: title }) : null,
|
|
4594
|
+
help ? /* @__PURE__ */ jsx24("p", { className: "squisq-jv-group__help", children: help }) : null,
|
|
4595
|
+
propEntries.map(([key, propSchema]) => /* @__PURE__ */ jsx24(Fragment4, { children: /* @__PURE__ */ jsx24(
|
|
4094
4596
|
RowOrSection,
|
|
4095
4597
|
{
|
|
4096
4598
|
label: propSchema.squisq?.label ?? propSchema.title ?? key,
|
|
4097
4599
|
help: propSchema.squisq?.help ?? propSchema.description,
|
|
4098
4600
|
kindHint: propSchema,
|
|
4099
|
-
children: /* @__PURE__ */
|
|
4601
|
+
children: /* @__PURE__ */ jsx24(
|
|
4100
4602
|
RenderNode,
|
|
4101
4603
|
{
|
|
4102
4604
|
value: obj[key],
|
|
@@ -4120,11 +4622,11 @@ function RowOrSection({
|
|
|
4120
4622
|
}) {
|
|
4121
4623
|
const composite = isCompositeKind(kindHint);
|
|
4122
4624
|
if (composite) {
|
|
4123
|
-
return /* @__PURE__ */
|
|
4625
|
+
return /* @__PURE__ */ jsx24(Fragment5, { children });
|
|
4124
4626
|
}
|
|
4125
|
-
return /* @__PURE__ */
|
|
4126
|
-
/* @__PURE__ */
|
|
4127
|
-
/* @__PURE__ */
|
|
4627
|
+
return /* @__PURE__ */ jsxs17("div", { className: "squisq-jv-row", children: [
|
|
4628
|
+
/* @__PURE__ */ jsx24("div", { className: "squisq-jv-label", title: help, children: label }),
|
|
4629
|
+
/* @__PURE__ */ jsx24("div", { children })
|
|
4128
4630
|
] });
|
|
4129
4631
|
}
|
|
4130
4632
|
function isCompositeKind(schema) {
|
|
@@ -4145,11 +4647,11 @@ function TabsViewer(props) {
|
|
|
4145
4647
|
const matchedIndex = pickMatchingBranch(branches, value);
|
|
4146
4648
|
const branch = branches[matchedIndex];
|
|
4147
4649
|
if (!branch) {
|
|
4148
|
-
return /* @__PURE__ */
|
|
4650
|
+
return /* @__PURE__ */ jsx24(TextViewer, { ...props });
|
|
4149
4651
|
}
|
|
4150
|
-
return /* @__PURE__ */
|
|
4151
|
-
/* @__PURE__ */
|
|
4152
|
-
/* @__PURE__ */
|
|
4652
|
+
return /* @__PURE__ */ jsxs17("div", { className: "squisq-jv-tabs", children: [
|
|
4653
|
+
/* @__PURE__ */ jsx24("span", { className: "squisq-jv-tabs__discriminator", children: branch.squisq?.label ?? branch.title ?? `Option ${matchedIndex + 1}` }),
|
|
4654
|
+
/* @__PURE__ */ jsx24(
|
|
4153
4655
|
RenderNode,
|
|
4154
4656
|
{
|
|
4155
4657
|
value,
|
|
@@ -4213,7 +4715,7 @@ var VIEWERS = {
|
|
|
4213
4715
|
};
|
|
4214
4716
|
|
|
4215
4717
|
// src/jsonView/RenderNode.tsx
|
|
4216
|
-
import { jsx as
|
|
4718
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
4217
4719
|
function RenderNode(props) {
|
|
4218
4720
|
const resolved = resolveRef(props.schema, props.rootSchema) ?? props.schema;
|
|
4219
4721
|
if (resolveFlag(resolved.squisq?.hidden, props.rootData)) return null;
|
|
@@ -4229,18 +4731,18 @@ function RenderNode(props) {
|
|
|
4229
4731
|
};
|
|
4230
4732
|
if (kind === "group" || kind === "card") {
|
|
4231
4733
|
const Group = Viewer;
|
|
4232
|
-
return /* @__PURE__ */
|
|
4734
|
+
return /* @__PURE__ */ jsx25(Group, { ...viewerProps, suppressTitle: props.suppressTopGroupTitle });
|
|
4233
4735
|
}
|
|
4234
|
-
return /* @__PURE__ */
|
|
4736
|
+
return /* @__PURE__ */ jsx25(Viewer, { ...viewerProps });
|
|
4235
4737
|
}
|
|
4236
4738
|
|
|
4237
4739
|
// src/jsonView/JsonView.tsx
|
|
4238
|
-
import { jsx as
|
|
4740
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
4239
4741
|
function JsonView(props) {
|
|
4240
4742
|
const { schema, value, theme, surface, density = "comfortable", className } = props;
|
|
4241
4743
|
const { style } = useJsonViewTokens(theme, surface);
|
|
4242
4744
|
const cls = "squisq-json-view" + (density === "compact" ? " squisq-json-view--compact" : "") + (className ? ` ${className}` : "");
|
|
4243
|
-
return /* @__PURE__ */
|
|
4745
|
+
return /* @__PURE__ */ jsx26("div", { className: cls, style, children: /* @__PURE__ */ jsx26(
|
|
4244
4746
|
RenderNode,
|
|
4245
4747
|
{
|
|
4246
4748
|
value,
|
|
@@ -4269,7 +4771,9 @@ export {
|
|
|
4269
4771
|
LinearDocView,
|
|
4270
4772
|
MapLayer,
|
|
4271
4773
|
MarkdownRenderer,
|
|
4774
|
+
MediaClipLayer,
|
|
4272
4775
|
MediaContext,
|
|
4776
|
+
PathLayer,
|
|
4273
4777
|
ShapeLayer,
|
|
4274
4778
|
SocialCaptionOverlay,
|
|
4275
4779
|
TableLayer,
|
|
@@ -4283,6 +4787,7 @@ export {
|
|
|
4283
4787
|
useAutoSurface,
|
|
4284
4788
|
useDocPlayback,
|
|
4285
4789
|
useMediaProvider,
|
|
4790
|
+
useMediaSchedule,
|
|
4286
4791
|
useMediaUrl,
|
|
4287
4792
|
useViewportOrientation
|
|
4288
4793
|
};
|