@bendyline/squisq-react 1.3.2 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -23
- package/dist/index.d.ts +131 -26
- package/dist/index.js +1475 -755
- 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 +49 -13
- package/dist/squisq-player.global.js.map +1 -1
- package/dist/standalone-source.js +1 -1
- package/dist/styles/index.css +2263 -0
- package/package.json +9 -5
- package/src/BlockRenderer.tsx +15 -7
- package/src/DocPlayer.tsx +222 -55
- package/src/DocPlayerWithSidebar.tsx +21 -9
- package/src/DocProgressBar.tsx +21 -3
- package/src/LinearDocView.tsx +69 -206
- package/src/MarkdownRenderer.tsx +182 -41
- package/src/MediaClipLayer.tsx +135 -0
- package/src/__tests__/DocPlayer.test.tsx +81 -0
- package/src/__tests__/DocPlayerStylesSentinel.test.tsx +41 -0
- package/src/__tests__/DocProgressBar.test.tsx +76 -0
- package/src/__tests__/LinearDocView.test.tsx +53 -1
- package/src/__tests__/MarkdownRenderer.test.tsx +113 -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/__tests__/useJsonViewTokens.test.ts +41 -0
- package/src/__tests__/useSlideSwipe.test.ts +81 -0
- package/src/hooks/{AudioProvider.ts → AudioController.ts} +3 -3
- package/src/hooks/index.ts +7 -2
- package/src/hooks/useAudioSync.ts +19 -5
- package/src/hooks/useDocPlayback.ts +81 -100
- package/src/hooks/useMediaSchedule.ts +39 -0
- package/src/hooks/useSlideSwipe.ts +265 -0
- package/src/index.ts +8 -1
- package/src/jsonView/useJsonViewTokens.ts +6 -31
- 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/standalone-entry.tsx +1 -1
- package/src/styles/doc-animations.css +1936 -35
- 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 useRef7, useState as useState7, useEffect as useEffect8, useCallback as useCallback6, 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,
|
|
@@ -1705,16 +2245,164 @@ function useViewportOrientation() {
|
|
|
1705
2245
|
};
|
|
1706
2246
|
}
|
|
1707
2247
|
|
|
2248
|
+
// src/hooks/useSlideSwipe.ts
|
|
2249
|
+
import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5, useState as useState5 } from "react";
|
|
2250
|
+
var DISTANCE_RATIO = 0.3;
|
|
2251
|
+
var FLICK_VELOCITY = 0.5;
|
|
2252
|
+
var MIN_FLICK_DISTANCE = 12;
|
|
2253
|
+
var RUBBER_BAND = 0.35;
|
|
2254
|
+
var DEFAULT_SETTLE_MS = 260;
|
|
2255
|
+
function decideSwipe({
|
|
2256
|
+
dx,
|
|
2257
|
+
width,
|
|
2258
|
+
elapsedMs,
|
|
2259
|
+
canNext,
|
|
2260
|
+
canPrev
|
|
2261
|
+
}) {
|
|
2262
|
+
const distanceThreshold = width > 0 ? width * DISTANCE_RATIO : Infinity;
|
|
2263
|
+
const velocity = elapsedMs > 0 ? Math.abs(dx) / elapsedMs : 0;
|
|
2264
|
+
const passesDistance = Math.abs(dx) >= distanceThreshold;
|
|
2265
|
+
const passesFlick = velocity >= FLICK_VELOCITY && Math.abs(dx) >= MIN_FLICK_DISTANCE;
|
|
2266
|
+
if (!passesDistance && !passesFlick) return "snap";
|
|
2267
|
+
if (dx < 0) return canNext ? "next" : "snap";
|
|
2268
|
+
if (dx > 0) return canPrev ? "prev" : "snap";
|
|
2269
|
+
return "snap";
|
|
2270
|
+
}
|
|
2271
|
+
function useSlideSwipe(opts) {
|
|
2272
|
+
const settleMs = opts.settleMs ?? DEFAULT_SETTLE_MS;
|
|
2273
|
+
const [offsetPx, setOffsetPx] = useState5(0);
|
|
2274
|
+
const [phase, setPhase] = useState5("idle");
|
|
2275
|
+
const optsRef = useRef5(opts);
|
|
2276
|
+
optsRef.current = opts;
|
|
2277
|
+
const dragRef = useRef5(null);
|
|
2278
|
+
const phaseRef = useRef5("idle");
|
|
2279
|
+
phaseRef.current = phase;
|
|
2280
|
+
const settleTimer = useRef5(null);
|
|
2281
|
+
const settleRaf = useRef5(null);
|
|
2282
|
+
const clearPending = useCallback4(() => {
|
|
2283
|
+
if (settleTimer.current != null) {
|
|
2284
|
+
clearTimeout(settleTimer.current);
|
|
2285
|
+
settleTimer.current = null;
|
|
2286
|
+
}
|
|
2287
|
+
if (settleRaf.current != null) {
|
|
2288
|
+
cancelAnimationFrame(settleRaf.current);
|
|
2289
|
+
settleRaf.current = null;
|
|
2290
|
+
}
|
|
2291
|
+
}, []);
|
|
2292
|
+
const onPointerDown = useCallback4((e) => {
|
|
2293
|
+
const o = optsRef.current;
|
|
2294
|
+
if (!o.enabled) return;
|
|
2295
|
+
if (phaseRef.current === "settling") return;
|
|
2296
|
+
if (e.pointerType === "mouse" && e.button !== 0) return;
|
|
2297
|
+
const target = e.target;
|
|
2298
|
+
if (target.closest?.("button, a, input, textarea, select, [data-no-swipe]")) return;
|
|
2299
|
+
dragRef.current = {
|
|
2300
|
+
pointerId: e.pointerId,
|
|
2301
|
+
startX: e.clientX,
|
|
2302
|
+
startTime: performance.now(),
|
|
2303
|
+
target
|
|
2304
|
+
};
|
|
2305
|
+
try {
|
|
2306
|
+
target.setPointerCapture?.(e.pointerId);
|
|
2307
|
+
} catch {
|
|
2308
|
+
}
|
|
2309
|
+
setPhase("dragging");
|
|
2310
|
+
setOffsetPx(0);
|
|
2311
|
+
}, []);
|
|
2312
|
+
useEffect7(() => {
|
|
2313
|
+
function currentWidth() {
|
|
2314
|
+
return optsRef.current.containerRef.current?.getBoundingClientRect().width ?? 0;
|
|
2315
|
+
}
|
|
2316
|
+
function endDrag(drag) {
|
|
2317
|
+
dragRef.current = null;
|
|
2318
|
+
try {
|
|
2319
|
+
drag.target.releasePointerCapture?.(drag.pointerId);
|
|
2320
|
+
} catch {
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
function settleTo(target, onArrive) {
|
|
2324
|
+
setPhase("settling");
|
|
2325
|
+
settleRaf.current = requestAnimationFrame(() => {
|
|
2326
|
+
settleRaf.current = null;
|
|
2327
|
+
setOffsetPx(target);
|
|
2328
|
+
settleTimer.current = setTimeout(() => {
|
|
2329
|
+
settleTimer.current = null;
|
|
2330
|
+
onArrive?.();
|
|
2331
|
+
setOffsetPx(0);
|
|
2332
|
+
setPhase("idle");
|
|
2333
|
+
}, settleMs);
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
function onMove(e) {
|
|
2337
|
+
const drag = dragRef.current;
|
|
2338
|
+
if (!drag || e.pointerId !== drag.pointerId) return;
|
|
2339
|
+
const o = optsRef.current;
|
|
2340
|
+
const raw = e.clientX - drag.startX;
|
|
2341
|
+
const blocked = raw > 0 && !o.canGoPrev || raw < 0 && !o.canGoNext;
|
|
2342
|
+
setOffsetPx(blocked ? raw * RUBBER_BAND : raw);
|
|
2343
|
+
}
|
|
2344
|
+
function onUp(e) {
|
|
2345
|
+
const drag = dragRef.current;
|
|
2346
|
+
if (!drag || e.pointerId !== drag.pointerId) return;
|
|
2347
|
+
endDrag(drag);
|
|
2348
|
+
const o = optsRef.current;
|
|
2349
|
+
const rawDx = e.clientX - drag.startX;
|
|
2350
|
+
const width = currentWidth();
|
|
2351
|
+
const elapsedMs = performance.now() - drag.startTime;
|
|
2352
|
+
const decision = decideSwipe({
|
|
2353
|
+
dx: rawDx,
|
|
2354
|
+
width,
|
|
2355
|
+
elapsedMs,
|
|
2356
|
+
canNext: o.canGoNext,
|
|
2357
|
+
canPrev: o.canGoPrev
|
|
2358
|
+
});
|
|
2359
|
+
if (decision === "snap") {
|
|
2360
|
+
settleTo(0);
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
const distance = Math.max(width, Math.abs(rawDx));
|
|
2364
|
+
const target = decision === "next" ? -distance : distance;
|
|
2365
|
+
settleTo(target, decision === "next" ? o.onNext : o.onPrev);
|
|
2366
|
+
}
|
|
2367
|
+
function onCancel(e) {
|
|
2368
|
+
const drag = dragRef.current;
|
|
2369
|
+
if (!drag || e.pointerId !== drag.pointerId) return;
|
|
2370
|
+
endDrag(drag);
|
|
2371
|
+
settleTo(0);
|
|
2372
|
+
}
|
|
2373
|
+
window.addEventListener("pointermove", onMove);
|
|
2374
|
+
window.addEventListener("pointerup", onUp);
|
|
2375
|
+
window.addEventListener("pointercancel", onCancel);
|
|
2376
|
+
return () => {
|
|
2377
|
+
window.removeEventListener("pointermove", onMove);
|
|
2378
|
+
window.removeEventListener("pointerup", onUp);
|
|
2379
|
+
window.removeEventListener("pointercancel", onCancel);
|
|
2380
|
+
};
|
|
2381
|
+
}, [settleMs]);
|
|
2382
|
+
useEffect7(() => {
|
|
2383
|
+
if (!opts.enabled) {
|
|
2384
|
+
dragRef.current = null;
|
|
2385
|
+
clearPending();
|
|
2386
|
+
setPhase("idle");
|
|
2387
|
+
setOffsetPx(0);
|
|
2388
|
+
}
|
|
2389
|
+
}, [opts.enabled, clearPending]);
|
|
2390
|
+
useEffect7(() => clearPending, [clearPending]);
|
|
2391
|
+
return { offsetPx, phase, onPointerDown };
|
|
2392
|
+
}
|
|
2393
|
+
|
|
1708
2394
|
// src/DocPlayer.tsx
|
|
1709
2395
|
import {
|
|
1710
2396
|
expandCoverBlock,
|
|
1711
2397
|
createTemplateContext,
|
|
1712
|
-
|
|
2398
|
+
markdownToDoc as markdownToDoc2,
|
|
2399
|
+
DEFAULT_THEME as DEFAULT_THEME3,
|
|
1713
2400
|
VIEWPORT_PRESETS as VIEWPORT_PRESETS4
|
|
1714
2401
|
} from "@bendyline/squisq/doc";
|
|
2402
|
+
import { parseMarkdown as parseMarkdown2 } from "@bendyline/squisq/markdown";
|
|
1715
2403
|
|
|
1716
2404
|
// src/DocProgressBar.tsx
|
|
1717
|
-
import { useRef as
|
|
2405
|
+
import { useRef as useRef6, useState as useState6, useCallback as useCallback5 } from "react";
|
|
1718
2406
|
|
|
1719
2407
|
// src/types.ts
|
|
1720
2408
|
function formatTime(seconds) {
|
|
@@ -1724,7 +2412,7 @@ function formatTime(seconds) {
|
|
|
1724
2412
|
}
|
|
1725
2413
|
|
|
1726
2414
|
// src/DocProgressBar.tsx
|
|
1727
|
-
import { jsx as
|
|
2415
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1728
2416
|
function DocProgressBar({
|
|
1729
2417
|
state,
|
|
1730
2418
|
actions,
|
|
@@ -1732,9 +2420,10 @@ function DocProgressBar({
|
|
|
1732
2420
|
expandedBlocks,
|
|
1733
2421
|
getBlockTitle
|
|
1734
2422
|
}) {
|
|
1735
|
-
const progressBarRef =
|
|
2423
|
+
const progressBarRef = useRef6(null);
|
|
1736
2424
|
const [hoverPosition, setHoverPosition] = useState6(null);
|
|
1737
|
-
const
|
|
2425
|
+
const playProgress = state.totalDuration > 0 ? Math.max(0, Math.min(1, state.currentTime / state.totalDuration)) : 0;
|
|
2426
|
+
const handleProgressHover = useCallback5((e) => {
|
|
1738
2427
|
const bar = progressBarRef.current;
|
|
1739
2428
|
if (!bar) return;
|
|
1740
2429
|
const rect = bar.getBoundingClientRect();
|
|
@@ -1742,10 +2431,10 @@ function DocProgressBar({
|
|
|
1742
2431
|
const progress = Math.max(0, Math.min(1, x / rect.width));
|
|
1743
2432
|
setHoverPosition(progress);
|
|
1744
2433
|
}, []);
|
|
1745
|
-
const handleProgressLeave =
|
|
2434
|
+
const handleProgressLeave = useCallback5(() => {
|
|
1746
2435
|
setHoverPosition(null);
|
|
1747
2436
|
}, []);
|
|
1748
|
-
const getBlockAtTimeLocal =
|
|
2437
|
+
const getBlockAtTimeLocal = useCallback5(
|
|
1749
2438
|
(time) => {
|
|
1750
2439
|
for (let i = expandedBlocks.length - 1; i >= 0; i--) {
|
|
1751
2440
|
const blk = expandedBlocks[i];
|
|
@@ -1757,7 +2446,7 @@ function DocProgressBar({
|
|
|
1757
2446
|
},
|
|
1758
2447
|
[expandedBlocks]
|
|
1759
2448
|
);
|
|
1760
|
-
return /* @__PURE__ */
|
|
2449
|
+
return /* @__PURE__ */ jsxs8(
|
|
1761
2450
|
"div",
|
|
1762
2451
|
{
|
|
1763
2452
|
ref: progressBarRef,
|
|
@@ -1778,7 +2467,7 @@ function DocProgressBar({
|
|
|
1778
2467
|
onMouseMove: handleProgressHover,
|
|
1779
2468
|
onMouseLeave: handleProgressLeave,
|
|
1780
2469
|
children: [
|
|
1781
|
-
/* @__PURE__ */
|
|
2470
|
+
/* @__PURE__ */ jsx13(
|
|
1782
2471
|
"div",
|
|
1783
2472
|
{
|
|
1784
2473
|
style: {
|
|
@@ -1791,21 +2480,21 @@ function DocProgressBar({
|
|
|
1791
2480
|
}
|
|
1792
2481
|
}
|
|
1793
2482
|
),
|
|
1794
|
-
/* @__PURE__ */
|
|
2483
|
+
/* @__PURE__ */ jsx13(
|
|
1795
2484
|
"div",
|
|
1796
2485
|
{
|
|
2486
|
+
"data-testid": "doc-progress-fill",
|
|
1797
2487
|
style: {
|
|
1798
2488
|
position: "absolute",
|
|
1799
2489
|
left: 0,
|
|
1800
|
-
width: `${
|
|
2490
|
+
width: `${playProgress * 100}%`,
|
|
1801
2491
|
height: "6px",
|
|
1802
2492
|
background: "#5b9bd5",
|
|
1803
|
-
borderRadius: "3px"
|
|
1804
|
-
transition: "width 0.1s"
|
|
2493
|
+
borderRadius: "3px"
|
|
1805
2494
|
}
|
|
1806
2495
|
}
|
|
1807
2496
|
),
|
|
1808
|
-
blockMarkers.map((marker, i) => /* @__PURE__ */
|
|
2497
|
+
blockMarkers.map((marker, i) => /* @__PURE__ */ jsx13(
|
|
1809
2498
|
"div",
|
|
1810
2499
|
{
|
|
1811
2500
|
style: {
|
|
@@ -1835,7 +2524,7 @@ function DocProgressBar({
|
|
|
1835
2524
|
},
|
|
1836
2525
|
`${marker.block.id}-${i}`
|
|
1837
2526
|
)),
|
|
1838
|
-
hoverPosition !== null && /* @__PURE__ */
|
|
2527
|
+
hoverPosition !== null && /* @__PURE__ */ jsxs8(
|
|
1839
2528
|
"div",
|
|
1840
2529
|
{
|
|
1841
2530
|
style: {
|
|
@@ -1852,19 +2541,19 @@ function DocProgressBar({
|
|
|
1852
2541
|
zIndex: 10
|
|
1853
2542
|
},
|
|
1854
2543
|
children: [
|
|
1855
|
-
/* @__PURE__ */
|
|
2544
|
+
/* @__PURE__ */ jsx13("div", { style: { color: "white", fontSize: "12px", fontFamily: "monospace" }, children: formatTime(hoverPosition * state.totalDuration) }),
|
|
1856
2545
|
(() => {
|
|
1857
2546
|
const hoverTime = hoverPosition * state.totalDuration;
|
|
1858
2547
|
const slideInfo = getBlockAtTimeLocal(hoverTime);
|
|
1859
2548
|
if (slideInfo && getBlockTitle) {
|
|
1860
|
-
return /* @__PURE__ */
|
|
2549
|
+
return /* @__PURE__ */ jsx13("div", { style: { color: "rgba(255,255,255,0.7)", fontSize: "11px", marginTop: "2px" }, children: getBlockTitle(slideInfo.block) });
|
|
1861
2550
|
}
|
|
1862
2551
|
return null;
|
|
1863
2552
|
})()
|
|
1864
2553
|
]
|
|
1865
2554
|
}
|
|
1866
2555
|
),
|
|
1867
|
-
hoverPosition !== null && /* @__PURE__ */
|
|
2556
|
+
hoverPosition !== null && /* @__PURE__ */ jsx13(
|
|
1868
2557
|
"div",
|
|
1869
2558
|
{
|
|
1870
2559
|
style: {
|
|
@@ -1886,7 +2575,7 @@ function DocProgressBar({
|
|
|
1886
2575
|
}
|
|
1887
2576
|
|
|
1888
2577
|
// src/DocControlsOverlay.tsx
|
|
1889
|
-
import { jsx as
|
|
2578
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1890
2579
|
function DocControlsOverlay({
|
|
1891
2580
|
state,
|
|
1892
2581
|
actions,
|
|
@@ -1894,7 +2583,7 @@ function DocControlsOverlay({
|
|
|
1894
2583
|
expandedBlocks,
|
|
1895
2584
|
getBlockTitle
|
|
1896
2585
|
}) {
|
|
1897
|
-
return /* @__PURE__ */
|
|
2586
|
+
return /* @__PURE__ */ jsxs9(
|
|
1898
2587
|
"div",
|
|
1899
2588
|
{
|
|
1900
2589
|
className: "doc-player__controls",
|
|
@@ -1911,7 +2600,7 @@ function DocControlsOverlay({
|
|
|
1911
2600
|
zIndex: 100
|
|
1912
2601
|
},
|
|
1913
2602
|
children: [
|
|
1914
|
-
/* @__PURE__ */
|
|
2603
|
+
/* @__PURE__ */ jsx14(
|
|
1915
2604
|
"button",
|
|
1916
2605
|
{
|
|
1917
2606
|
onClick: actions.restart,
|
|
@@ -1927,10 +2616,10 @@ function DocControlsOverlay({
|
|
|
1927
2616
|
},
|
|
1928
2617
|
title: "Restart",
|
|
1929
2618
|
"aria-label": "Restart from beginning",
|
|
1930
|
-
children: /* @__PURE__ */
|
|
2619
|
+
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
2620
|
}
|
|
1932
2621
|
),
|
|
1933
|
-
/* @__PURE__ */
|
|
2622
|
+
/* @__PURE__ */ jsx14(
|
|
1934
2623
|
"button",
|
|
1935
2624
|
{
|
|
1936
2625
|
onClick: actions.toggle,
|
|
@@ -1949,15 +2638,15 @@ function DocControlsOverlay({
|
|
|
1949
2638
|
height: "40px"
|
|
1950
2639
|
},
|
|
1951
2640
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
1952
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
2641
|
+
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
2642
|
}
|
|
1954
2643
|
),
|
|
1955
|
-
/* @__PURE__ */
|
|
2644
|
+
/* @__PURE__ */ jsxs9("span", { style: { color: "white", fontSize: "12px", minWidth: "80px", fontFamily: "monospace" }, children: [
|
|
1956
2645
|
formatTime(state.currentTime),
|
|
1957
2646
|
" / ",
|
|
1958
2647
|
formatTime(state.totalDuration)
|
|
1959
2648
|
] }),
|
|
1960
|
-
/* @__PURE__ */
|
|
2649
|
+
/* @__PURE__ */ jsx14(
|
|
1961
2650
|
DocProgressBar,
|
|
1962
2651
|
{
|
|
1963
2652
|
state,
|
|
@@ -1967,12 +2656,12 @@ function DocControlsOverlay({
|
|
|
1967
2656
|
getBlockTitle
|
|
1968
2657
|
}
|
|
1969
2658
|
),
|
|
1970
|
-
/* @__PURE__ */
|
|
2659
|
+
/* @__PURE__ */ jsxs9("span", { style: { color: "rgba(255,255,255,0.5)", fontSize: "11px", whiteSpace: "nowrap" }, children: [
|
|
1971
2660
|
state.currentBlockIndex + 1,
|
|
1972
2661
|
"/",
|
|
1973
2662
|
state.totalBlocks
|
|
1974
2663
|
] }),
|
|
1975
|
-
state.hasCaptions && /* @__PURE__ */
|
|
2664
|
+
state.hasCaptions && /* @__PURE__ */ jsxs9(
|
|
1976
2665
|
"button",
|
|
1977
2666
|
{
|
|
1978
2667
|
onClick: () => actions.cycleCaptionMode(),
|
|
@@ -1991,12 +2680,12 @@ function DocControlsOverlay({
|
|
|
1991
2680
|
title: state.captionMode === "off" ? "Captions: Off (click for Standard)" : state.captionMode === "standard" ? "Captions: Standard (click for Social)" : "Captions: Social (click to turn off)",
|
|
1992
2681
|
"aria-label": "Cycle caption style",
|
|
1993
2682
|
children: [
|
|
1994
|
-
/* @__PURE__ */
|
|
1995
|
-
state.captionMode !== "off" && /* @__PURE__ */
|
|
2683
|
+
/* @__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" }) }),
|
|
2684
|
+
state.captionMode !== "off" && /* @__PURE__ */ jsx14("span", { style: { fontSize: "10px", textTransform: "uppercase", letterSpacing: "0.5px" }, children: state.captionMode === "standard" ? "CC" : "SM" })
|
|
1996
2685
|
]
|
|
1997
2686
|
}
|
|
1998
2687
|
),
|
|
1999
|
-
actions.toggleFullscreen && /* @__PURE__ */
|
|
2688
|
+
actions.toggleFullscreen && /* @__PURE__ */ jsx14(
|
|
2000
2689
|
"button",
|
|
2001
2690
|
{
|
|
2002
2691
|
onClick: actions.toggleFullscreen,
|
|
@@ -2012,7 +2701,7 @@ function DocControlsOverlay({
|
|
|
2012
2701
|
},
|
|
2013
2702
|
title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
|
|
2014
2703
|
"aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
2015
|
-
children: state.isFullscreen ? /* @__PURE__ */
|
|
2704
|
+
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
2705
|
}
|
|
2017
2706
|
)
|
|
2018
2707
|
]
|
|
@@ -2021,12 +2710,12 @@ function DocControlsOverlay({
|
|
|
2021
2710
|
}
|
|
2022
2711
|
|
|
2023
2712
|
// src/DocControlsSlideshow.tsx
|
|
2024
|
-
import { jsx as
|
|
2713
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2025
2714
|
function DocControlsSlideshow({ state, slideNav }) {
|
|
2026
2715
|
const { currentBlockIndex, totalBlocks } = state;
|
|
2027
2716
|
const isFirst = currentBlockIndex <= 0;
|
|
2028
2717
|
const isLast = currentBlockIndex >= totalBlocks - 1;
|
|
2029
|
-
return /* @__PURE__ */
|
|
2718
|
+
return /* @__PURE__ */ jsxs10(
|
|
2030
2719
|
"div",
|
|
2031
2720
|
{
|
|
2032
2721
|
className: "doc-controls-slideshow",
|
|
@@ -2047,7 +2736,7 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2047
2736
|
WebkitBackdropFilter: "blur(8px)"
|
|
2048
2737
|
},
|
|
2049
2738
|
children: [
|
|
2050
|
-
/* @__PURE__ */
|
|
2739
|
+
/* @__PURE__ */ jsx15(
|
|
2051
2740
|
"button",
|
|
2052
2741
|
{
|
|
2053
2742
|
onClick: (e) => {
|
|
@@ -2076,10 +2765,10 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2076
2765
|
onMouseLeave: (e) => {
|
|
2077
2766
|
e.currentTarget.style.background = "none";
|
|
2078
2767
|
},
|
|
2079
|
-
children: /* @__PURE__ */
|
|
2768
|
+
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
2769
|
}
|
|
2081
2770
|
),
|
|
2082
|
-
/* @__PURE__ */
|
|
2771
|
+
/* @__PURE__ */ jsx15(
|
|
2083
2772
|
"span",
|
|
2084
2773
|
{
|
|
2085
2774
|
"data-testid": "slide-counter",
|
|
@@ -2096,7 +2785,7 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2096
2785
|
children: totalBlocks > 0 ? `${currentBlockIndex + 1} / ${totalBlocks}` : "\u2014"
|
|
2097
2786
|
}
|
|
2098
2787
|
),
|
|
2099
|
-
/* @__PURE__ */
|
|
2788
|
+
/* @__PURE__ */ jsx15(
|
|
2100
2789
|
"button",
|
|
2101
2790
|
{
|
|
2102
2791
|
onClick: (e) => {
|
|
@@ -2125,7 +2814,7 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2125
2814
|
onMouseLeave: (e) => {
|
|
2126
2815
|
e.currentTarget.style.background = "none";
|
|
2127
2816
|
},
|
|
2128
|
-
children: /* @__PURE__ */
|
|
2817
|
+
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
2818
|
}
|
|
2130
2819
|
)
|
|
2131
2820
|
]
|
|
@@ -2134,20 +2823,30 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2134
2823
|
}
|
|
2135
2824
|
|
|
2136
2825
|
// src/LinearDocView.tsx
|
|
2137
|
-
import { useMemo as
|
|
2826
|
+
import { useMemo as useMemo8 } from "react";
|
|
2138
2827
|
import {
|
|
2139
2828
|
applySurface,
|
|
2140
2829
|
resolveFontFamily as resolveFontFamily2
|
|
2141
2830
|
} from "@bendyline/squisq/schemas";
|
|
2142
2831
|
import { VIEWPORT_PRESETS as VIEWPORT_PRESETS3 } from "@bendyline/squisq/schemas";
|
|
2143
|
-
import {
|
|
2144
|
-
|
|
2832
|
+
import {
|
|
2833
|
+
getLayers,
|
|
2834
|
+
hasTemplate,
|
|
2835
|
+
markdownToDoc,
|
|
2836
|
+
DEFAULT_THEME as DEFAULT_THEME2,
|
|
2837
|
+
deriveTemplateInputs
|
|
2838
|
+
} from "@bendyline/squisq/doc";
|
|
2839
|
+
import { extractPlainText, parseMarkdown } from "@bendyline/squisq/markdown";
|
|
2145
2840
|
|
|
2146
2841
|
// src/MarkdownRenderer.tsx
|
|
2147
|
-
import { Fragment } from "react";
|
|
2842
|
+
import { Fragment as Fragment2 } from "react";
|
|
2843
|
+
import {
|
|
2844
|
+
sanitizeHtmlNodes as sanitizeHtmlNodes2,
|
|
2845
|
+
sanitizeUrl
|
|
2846
|
+
} from "@bendyline/squisq/markdown";
|
|
2148
2847
|
|
|
2149
2848
|
// src/InlineVideoPlayer.tsx
|
|
2150
|
-
import { jsx as
|
|
2849
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2151
2850
|
function InlineVideoPlayer({
|
|
2152
2851
|
src,
|
|
2153
2852
|
basePath = "",
|
|
@@ -2162,7 +2861,7 @@ function InlineVideoPlayer({
|
|
|
2162
2861
|
const resolvedPoster = useMediaUrl(poster ?? "", basePath);
|
|
2163
2862
|
const posterUrl = poster ? resolvedPoster : void 0;
|
|
2164
2863
|
if (!resolvedSrc) return null;
|
|
2165
|
-
return /* @__PURE__ */
|
|
2864
|
+
return /* @__PURE__ */ jsx16("span", { className: `squisq-inline-video-player ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx16(
|
|
2166
2865
|
"video",
|
|
2167
2866
|
{
|
|
2168
2867
|
src: resolvedSrc,
|
|
@@ -2177,7 +2876,7 @@ function InlineVideoPlayer({
|
|
|
2177
2876
|
}
|
|
2178
2877
|
|
|
2179
2878
|
// src/InlineAudioPlayer.tsx
|
|
2180
|
-
import { jsx as
|
|
2879
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2181
2880
|
function InlineAudioPlayer({
|
|
2182
2881
|
src,
|
|
2183
2882
|
basePath = "",
|
|
@@ -2187,55 +2886,62 @@ function InlineAudioPlayer({
|
|
|
2187
2886
|
}) {
|
|
2188
2887
|
const resolvedSrc = useMediaUrl(src, basePath);
|
|
2189
2888
|
if (!resolvedSrc) return null;
|
|
2190
|
-
return /* @__PURE__ */
|
|
2889
|
+
return /* @__PURE__ */ jsx17("span", { className: `squisq-inline-audio-player ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx17("audio", { src: resolvedSrc, controls, preload }) });
|
|
2191
2890
|
}
|
|
2192
2891
|
|
|
2193
2892
|
// src/MarkdownRenderer.tsx
|
|
2194
|
-
import { jsx as
|
|
2195
|
-
|
|
2893
|
+
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2894
|
+
var DEFAULT_CTX = { htmlPolicy: "sanitize" };
|
|
2895
|
+
function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
2196
2896
|
return nodes.map((node, i) => {
|
|
2197
2897
|
const key = `${keyPrefix}i${i}`;
|
|
2198
2898
|
switch (node.type) {
|
|
2199
2899
|
case "text": {
|
|
2200
2900
|
if (!node.value.includes("\n")) {
|
|
2201
|
-
return /* @__PURE__ */
|
|
2901
|
+
return /* @__PURE__ */ jsx18(Fragment2, { children: node.value }, key);
|
|
2202
2902
|
}
|
|
2203
2903
|
const parts = node.value.split("\n");
|
|
2204
|
-
return /* @__PURE__ */
|
|
2205
|
-
j > 0 && /* @__PURE__ */
|
|
2904
|
+
return /* @__PURE__ */ jsx18(Fragment2, { children: parts.map((part, j) => /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
2905
|
+
j > 0 && /* @__PURE__ */ jsx18("br", {}),
|
|
2206
2906
|
part
|
|
2207
2907
|
] }, j)) }, key);
|
|
2208
2908
|
}
|
|
2209
2909
|
case "emphasis":
|
|
2210
|
-
return /* @__PURE__ */
|
|
2910
|
+
return /* @__PURE__ */ jsx18("em", { className: "squisq-md-em", children: renderInline(node.children, key, ctx) }, key);
|
|
2211
2911
|
case "strong":
|
|
2212
|
-
return /* @__PURE__ */
|
|
2912
|
+
return /* @__PURE__ */ jsx18("strong", { className: "squisq-md-strong", children: renderInline(node.children, key, ctx) }, key);
|
|
2213
2913
|
case "delete":
|
|
2214
|
-
return /* @__PURE__ */
|
|
2914
|
+
return /* @__PURE__ */ jsx18("del", { className: "squisq-md-del", children: renderInline(node.children, key, ctx) }, key);
|
|
2215
2915
|
case "inlineCode":
|
|
2216
|
-
return /* @__PURE__ */
|
|
2217
|
-
case "link":
|
|
2218
|
-
|
|
2916
|
+
return /* @__PURE__ */ jsx18("code", { className: "squisq-md-inline-code", children: node.value }, key);
|
|
2917
|
+
case "link": {
|
|
2918
|
+
const href = sanitizeUrl(node.url, "link", { extraLinkSchemes: ctx.linkSchemes });
|
|
2919
|
+
if (!href) {
|
|
2920
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-link squisq-md-link--blocked", children: renderInline(node.children, key, ctx) }, key);
|
|
2921
|
+
}
|
|
2922
|
+
return /* @__PURE__ */ jsx18(
|
|
2219
2923
|
"a",
|
|
2220
2924
|
{
|
|
2221
2925
|
className: "squisq-md-link",
|
|
2222
|
-
href
|
|
2926
|
+
href,
|
|
2223
2927
|
title: node.title ?? void 0,
|
|
2224
2928
|
target: "_blank",
|
|
2225
2929
|
rel: "noopener noreferrer",
|
|
2226
|
-
children: renderInline(node.children, key)
|
|
2930
|
+
children: renderInline(node.children, key, ctx)
|
|
2227
2931
|
},
|
|
2228
2932
|
key
|
|
2229
2933
|
);
|
|
2934
|
+
}
|
|
2230
2935
|
case "image":
|
|
2231
|
-
return /* @__PURE__ */
|
|
2936
|
+
return /* @__PURE__ */ jsx18(MdImage, { src: node.url, alt: node.alt ?? "", title: node.title ?? void 0 }, key);
|
|
2232
2937
|
case "break":
|
|
2233
|
-
return /* @__PURE__ */
|
|
2938
|
+
return /* @__PURE__ */ jsx18("br", {}, key);
|
|
2234
2939
|
case "inlineMath":
|
|
2235
|
-
return /* @__PURE__ */
|
|
2940
|
+
return /* @__PURE__ */ jsx18("code", { className: "squisq-md-inline-math", children: node.value }, key);
|
|
2236
2941
|
case "htmlInline":
|
|
2237
|
-
if (
|
|
2238
|
-
|
|
2942
|
+
if (ctx.htmlPolicy === "strip") return null;
|
|
2943
|
+
if (ctx.htmlPolicy === "trusted" && !containsMediaTag(node.htmlChildren) && !containsDangerousTag(node.htmlChildren) && !hasDangerousRawHtml(node.rawHtml)) {
|
|
2944
|
+
return /* @__PURE__ */ jsx18(
|
|
2239
2945
|
"span",
|
|
2240
2946
|
{
|
|
2241
2947
|
className: "squisq-md-html-inline",
|
|
@@ -2244,25 +2950,25 @@ function renderInline(nodes, keyPrefix = "") {
|
|
|
2244
2950
|
key
|
|
2245
2951
|
);
|
|
2246
2952
|
}
|
|
2247
|
-
return /* @__PURE__ */
|
|
2953
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-html-inline", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`) }, key);
|
|
2248
2954
|
case "footnoteReference":
|
|
2249
|
-
return /* @__PURE__ */
|
|
2955
|
+
return /* @__PURE__ */ jsx18("sup", { className: "squisq-md-footnote-ref", children: /* @__PURE__ */ jsxs11("a", { href: `#fn-${node.identifier}`, children: [
|
|
2250
2956
|
"[",
|
|
2251
2957
|
node.label ?? node.identifier,
|
|
2252
2958
|
"]"
|
|
2253
2959
|
] }) }, key);
|
|
2254
2960
|
case "linkReference":
|
|
2255
|
-
return /* @__PURE__ */
|
|
2961
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-link-ref", children: renderInline(node.children, key, ctx) }, key);
|
|
2256
2962
|
case "imageReference":
|
|
2257
|
-
return /* @__PURE__ */
|
|
2963
|
+
return /* @__PURE__ */ jsxs11("span", { className: "squisq-md-image-ref", children: [
|
|
2258
2964
|
"[",
|
|
2259
2965
|
node.alt ?? node.identifier,
|
|
2260
2966
|
"]"
|
|
2261
2967
|
] }, key);
|
|
2262
2968
|
case "textDirective":
|
|
2263
|
-
return /* @__PURE__ */
|
|
2969
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-text-directive", "data-directive": node.name, children: renderInline(node.children, key, ctx) }, key);
|
|
2264
2970
|
case "mention":
|
|
2265
|
-
return /* @__PURE__ */
|
|
2971
|
+
return /* @__PURE__ */ jsxs11(
|
|
2266
2972
|
"span",
|
|
2267
2973
|
{
|
|
2268
2974
|
className: "squisq-md-mention mention",
|
|
@@ -2282,30 +2988,31 @@ function renderInline(nodes, keyPrefix = "") {
|
|
|
2282
2988
|
}
|
|
2283
2989
|
});
|
|
2284
2990
|
}
|
|
2285
|
-
function renderBlock(node, key) {
|
|
2991
|
+
function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
2286
2992
|
switch (node.type) {
|
|
2287
2993
|
case "paragraph":
|
|
2288
|
-
return /* @__PURE__ */
|
|
2994
|
+
return /* @__PURE__ */ jsx18("p", { className: "squisq-md-p", children: renderInline(node.children, key, ctx) }, key);
|
|
2289
2995
|
case "heading": {
|
|
2290
2996
|
const Tag = `h${node.depth}`;
|
|
2291
|
-
return /* @__PURE__ */
|
|
2997
|
+
return /* @__PURE__ */ jsx18(Tag, { className: `squisq-md-heading squisq-md-h${node.depth}`, children: renderInline(node.children, key, ctx) }, key);
|
|
2292
2998
|
}
|
|
2293
2999
|
case "blockquote":
|
|
2294
|
-
return /* @__PURE__ */
|
|
3000
|
+
return /* @__PURE__ */ jsx18("blockquote", { className: "squisq-md-blockquote", children: renderBlocks(node.children, key, ctx) }, key);
|
|
2295
3001
|
case "list":
|
|
2296
3002
|
if (node.ordered) {
|
|
2297
|
-
return /* @__PURE__ */
|
|
3003
|
+
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}`, ctx)) }, key);
|
|
2298
3004
|
}
|
|
2299
|
-
return /* @__PURE__ */
|
|
3005
|
+
return /* @__PURE__ */ jsx18("ul", { className: "squisq-md-list squisq-md-ul", children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, ctx)) }, key);
|
|
2300
3006
|
case "code":
|
|
2301
|
-
return /* @__PURE__ */
|
|
3007
|
+
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
3008
|
case "thematicBreak":
|
|
2303
|
-
return /* @__PURE__ */
|
|
3009
|
+
return /* @__PURE__ */ jsx18("hr", { className: "squisq-md-hr" }, key);
|
|
2304
3010
|
case "table":
|
|
2305
|
-
return renderTable(node.children, node.align, key);
|
|
3011
|
+
return renderTable(node.children, node.align, key, ctx);
|
|
2306
3012
|
case "htmlBlock":
|
|
2307
|
-
if (
|
|
2308
|
-
|
|
3013
|
+
if (ctx.htmlPolicy === "strip") return null;
|
|
3014
|
+
if (ctx.htmlPolicy === "trusted" && !containsMediaTag(node.htmlChildren) && !containsDangerousTag(node.htmlChildren) && !hasDangerousRawHtml(node.rawHtml)) {
|
|
3015
|
+
return /* @__PURE__ */ jsx18(
|
|
2309
3016
|
"div",
|
|
2310
3017
|
{
|
|
2311
3018
|
className: "squisq-md-html-block",
|
|
@@ -2314,95 +3021,126 @@ function renderBlock(node, key) {
|
|
|
2314
3021
|
key
|
|
2315
3022
|
);
|
|
2316
3023
|
}
|
|
2317
|
-
return /* @__PURE__ */
|
|
3024
|
+
return /* @__PURE__ */ jsx18("div", { className: "squisq-md-html-block", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`) }, key);
|
|
2318
3025
|
case "math":
|
|
2319
|
-
return /* @__PURE__ */
|
|
3026
|
+
return /* @__PURE__ */ jsx18("pre", { className: "squisq-md-math-block", children: /* @__PURE__ */ jsx18("code", { children: node.value }) }, key);
|
|
2320
3027
|
case "definition":
|
|
2321
3028
|
return null;
|
|
2322
3029
|
case "footnoteDefinition":
|
|
2323
|
-
return /* @__PURE__ */
|
|
2324
|
-
/* @__PURE__ */
|
|
2325
|
-
renderBlocks(node.children, key)
|
|
3030
|
+
return /* @__PURE__ */ jsxs11("div", { className: "squisq-md-footnote-def", id: `fn-${node.identifier}`, children: [
|
|
3031
|
+
/* @__PURE__ */ jsx18("sup", { children: node.label ?? node.identifier }),
|
|
3032
|
+
renderBlocks(node.children, key, ctx)
|
|
2326
3033
|
] }, key);
|
|
2327
3034
|
case "containerDirective":
|
|
2328
|
-
return /* @__PURE__ */
|
|
3035
|
+
return /* @__PURE__ */ jsxs11(
|
|
2329
3036
|
"div",
|
|
2330
3037
|
{
|
|
2331
3038
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
2332
3039
|
"data-directive": node.name,
|
|
2333
3040
|
children: [
|
|
2334
|
-
node.label && /* @__PURE__ */
|
|
2335
|
-
renderBlocks(node.children, key)
|
|
3041
|
+
node.label && /* @__PURE__ */ jsx18("div", { className: "squisq-md-directive-label", children: node.label }),
|
|
3042
|
+
renderBlocks(node.children, key, ctx)
|
|
2336
3043
|
]
|
|
2337
3044
|
},
|
|
2338
3045
|
key
|
|
2339
3046
|
);
|
|
2340
3047
|
case "leafDirective":
|
|
2341
|
-
return /* @__PURE__ */
|
|
3048
|
+
return /* @__PURE__ */ jsx18(
|
|
2342
3049
|
"div",
|
|
2343
3050
|
{
|
|
2344
3051
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
2345
3052
|
"data-directive": node.name,
|
|
2346
|
-
children: renderInline(node.children, key)
|
|
3053
|
+
children: renderInline(node.children, key, ctx)
|
|
2347
3054
|
},
|
|
2348
3055
|
key
|
|
2349
3056
|
);
|
|
2350
3057
|
case "definitionList":
|
|
2351
|
-
return /* @__PURE__ */
|
|
3058
|
+
return /* @__PURE__ */ jsx18("dl", { className: "squisq-md-dl", children: node.children.map((child, i) => {
|
|
2352
3059
|
if (child.type === "definitionTerm") {
|
|
2353
|
-
return /* @__PURE__ */
|
|
3060
|
+
return /* @__PURE__ */ jsx18("dt", { className: "squisq-md-dt", children: renderInline(child.children, `${key}dt${i}`, ctx) }, `${key}dt${i}`);
|
|
2354
3061
|
}
|
|
2355
|
-
return /* @__PURE__ */
|
|
3062
|
+
return /* @__PURE__ */ jsx18("dd", { className: "squisq-md-dd", children: renderBlocks(child.children, `${key}dd${i}`, ctx) }, `${key}dd${i}`);
|
|
2356
3063
|
}) }, key);
|
|
2357
3064
|
default:
|
|
2358
3065
|
return null;
|
|
2359
3066
|
}
|
|
2360
3067
|
}
|
|
2361
|
-
function renderListItem(item, key) {
|
|
3068
|
+
function renderListItem(item, key, ctx = DEFAULT_CTX) {
|
|
2362
3069
|
const isTask = item.checked !== null && item.checked !== void 0;
|
|
2363
|
-
return /* @__PURE__ */
|
|
2364
|
-
isTask && /* @__PURE__ */
|
|
2365
|
-
renderBlocks(item.children, key)
|
|
3070
|
+
return /* @__PURE__ */ jsxs11("li", { className: `squisq-md-li${isTask ? " squisq-md-task" : ""}`, children: [
|
|
3071
|
+
isTask && /* @__PURE__ */ jsx18("input", { type: "checkbox", checked: !!item.checked, readOnly: true, className: "squisq-md-checkbox" }),
|
|
3072
|
+
renderBlocks(item.children, key, ctx)
|
|
2366
3073
|
] }, key);
|
|
2367
3074
|
}
|
|
2368
|
-
function renderTable(rows, align, key) {
|
|
3075
|
+
function renderTable(rows, align, key, ctx = DEFAULT_CTX) {
|
|
2369
3076
|
const [headerRow, ...bodyRows] = rows;
|
|
2370
|
-
return /* @__PURE__ */
|
|
2371
|
-
headerRow && /* @__PURE__ */
|
|
3077
|
+
return /* @__PURE__ */ jsxs11("table", { className: "squisq-md-table", children: [
|
|
3078
|
+
headerRow && /* @__PURE__ */ jsx18("thead", { children: /* @__PURE__ */ jsx18("tr", { children: headerRow.children.map((cell, ci) => /* @__PURE__ */ jsx18(
|
|
2372
3079
|
"th",
|
|
2373
3080
|
{
|
|
2374
3081
|
className: "squisq-md-th",
|
|
2375
3082
|
style: align?.[ci] ? { textAlign: align[ci] } : void 0,
|
|
2376
|
-
children: renderInline(cell.children, `${key}th${ci}
|
|
3083
|
+
children: renderInline(cell.children, `${key}th${ci}`, ctx)
|
|
2377
3084
|
},
|
|
2378
3085
|
`${key}th${ci}`
|
|
2379
3086
|
)) }) }),
|
|
2380
|
-
bodyRows.length > 0 && /* @__PURE__ */
|
|
3087
|
+
bodyRows.length > 0 && /* @__PURE__ */ jsx18("tbody", { children: bodyRows.map((row, ri) => /* @__PURE__ */ jsx18("tr", { children: row.children.map((cell, ci) => /* @__PURE__ */ jsx18(
|
|
2381
3088
|
"td",
|
|
2382
3089
|
{
|
|
2383
3090
|
className: "squisq-md-td",
|
|
2384
3091
|
style: align?.[ci] ? { textAlign: align[ci] } : void 0,
|
|
2385
|
-
children: renderInline(cell.children, `${key}td${ri}-${ci}
|
|
3092
|
+
children: renderInline(cell.children, `${key}td${ri}-${ci}`, ctx)
|
|
2386
3093
|
},
|
|
2387
3094
|
`${key}td${ri}-${ci}`
|
|
2388
3095
|
)) }, `${key}tr${ri}`)) })
|
|
2389
3096
|
] }, key);
|
|
2390
3097
|
}
|
|
2391
|
-
function renderBlocks(nodes, keyPrefix = "") {
|
|
2392
|
-
return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}
|
|
3098
|
+
function renderBlocks(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
3099
|
+
return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}`, ctx));
|
|
2393
3100
|
}
|
|
2394
3101
|
function MdImage({ src, alt, title }) {
|
|
2395
|
-
const
|
|
2396
|
-
|
|
3102
|
+
const safeSrc = sanitizeUrl(src, "media");
|
|
3103
|
+
const resolved = useMediaUrl(safeSrc ?? "", ".");
|
|
3104
|
+
if (!safeSrc) return null;
|
|
3105
|
+
return /* @__PURE__ */ jsx18("img", { className: "squisq-md-image", src: resolved, alt, title });
|
|
3106
|
+
}
|
|
3107
|
+
function resolveHtmlNodes(nodes, htmlPolicy) {
|
|
3108
|
+
if (htmlPolicy === "strip") return [];
|
|
3109
|
+
if (htmlPolicy === "trusted") return nodes;
|
|
3110
|
+
return sanitizeHtmlNodes2(nodes);
|
|
2397
3111
|
}
|
|
2398
3112
|
function containsMediaTag(nodes) {
|
|
2399
3113
|
for (const node of nodes) {
|
|
2400
3114
|
if (node.type !== "htmlElement") continue;
|
|
2401
|
-
|
|
3115
|
+
const tagName = node.tagName.toLowerCase();
|
|
3116
|
+
if (tagName === "video" || tagName === "audio") return true;
|
|
2402
3117
|
if (containsMediaTag(node.children)) return true;
|
|
2403
3118
|
}
|
|
2404
3119
|
return false;
|
|
2405
3120
|
}
|
|
3121
|
+
var DANGEROUS_HTML_TAGS = /* @__PURE__ */ new Set([
|
|
3122
|
+
"base",
|
|
3123
|
+
"embed",
|
|
3124
|
+
"iframe",
|
|
3125
|
+
"link",
|
|
3126
|
+
"meta",
|
|
3127
|
+
"object",
|
|
3128
|
+
"script",
|
|
3129
|
+
"style",
|
|
3130
|
+
"title"
|
|
3131
|
+
]);
|
|
3132
|
+
function containsDangerousTag(nodes) {
|
|
3133
|
+
for (const node of nodes) {
|
|
3134
|
+
if (node.type !== "htmlElement") continue;
|
|
3135
|
+
if (DANGEROUS_HTML_TAGS.has(node.tagName.toLowerCase())) return true;
|
|
3136
|
+
if (containsDangerousTag(node.children)) return true;
|
|
3137
|
+
}
|
|
3138
|
+
return false;
|
|
3139
|
+
}
|
|
3140
|
+
var DANGEROUS_RAW_HTML_RE = /<\s*\/?\s*(?:base|embed|iframe|link|meta|object|script|style|title)\b/i;
|
|
3141
|
+
function hasDangerousRawHtml(rawHtml) {
|
|
3142
|
+
return DANGEROUS_RAW_HTML_RE.test(rawHtml);
|
|
3143
|
+
}
|
|
2406
3144
|
var PASSTHROUGH_ATTRS = {
|
|
2407
3145
|
// common
|
|
2408
3146
|
class: "className",
|
|
@@ -2412,6 +3150,10 @@ var PASSTHROUGH_ATTRS = {
|
|
|
2412
3150
|
// media-adjacent (used when video/audio appear inside other wrappers)
|
|
2413
3151
|
width: "width",
|
|
2414
3152
|
height: "height",
|
|
3153
|
+
src: "src",
|
|
3154
|
+
alt: "alt",
|
|
3155
|
+
loading: "loading",
|
|
3156
|
+
decoding: "decoding",
|
|
2415
3157
|
// anchor
|
|
2416
3158
|
href: "href",
|
|
2417
3159
|
target: "target",
|
|
@@ -2431,8 +3173,10 @@ function reactPropsFromAttrs(attrs) {
|
|
|
2431
3173
|
return out;
|
|
2432
3174
|
}
|
|
2433
3175
|
function renderHtmlElement(el, key) {
|
|
2434
|
-
|
|
2435
|
-
|
|
3176
|
+
const tagName = el.tagName.toLowerCase();
|
|
3177
|
+
if (DANGEROUS_HTML_TAGS.has(tagName)) return null;
|
|
3178
|
+
if (tagName === "video") {
|
|
3179
|
+
return /* @__PURE__ */ jsx18(
|
|
2436
3180
|
InlineVideoPlayer,
|
|
2437
3181
|
{
|
|
2438
3182
|
src: el.attributes.src ?? "",
|
|
@@ -2445,8 +3189,8 @@ function renderHtmlElement(el, key) {
|
|
|
2445
3189
|
key
|
|
2446
3190
|
);
|
|
2447
3191
|
}
|
|
2448
|
-
if (
|
|
2449
|
-
return /* @__PURE__ */
|
|
3192
|
+
if (tagName === "audio") {
|
|
3193
|
+
return /* @__PURE__ */ jsx18(
|
|
2450
3194
|
InlineAudioPlayer,
|
|
2451
3195
|
{
|
|
2452
3196
|
src: el.attributes.src ?? "",
|
|
@@ -2456,12 +3200,12 @@ function renderHtmlElement(el, key) {
|
|
|
2456
3200
|
key
|
|
2457
3201
|
);
|
|
2458
3202
|
}
|
|
2459
|
-
const Tag =
|
|
3203
|
+
const Tag = tagName;
|
|
2460
3204
|
const props = reactPropsFromAttrs(el.attributes);
|
|
2461
3205
|
if (el.selfClosing) {
|
|
2462
|
-
return /* @__PURE__ */
|
|
3206
|
+
return /* @__PURE__ */ jsx18(Tag, { ...props }, key);
|
|
2463
3207
|
}
|
|
2464
|
-
return /* @__PURE__ */
|
|
3208
|
+
return /* @__PURE__ */ jsx18(Tag, { ...props, children: renderHtmlNodes(el.children, `${key}c`) }, key);
|
|
2465
3209
|
}
|
|
2466
3210
|
function renderHtmlNodes(nodes, keyPrefix) {
|
|
2467
3211
|
return nodes.map((node, i) => {
|
|
@@ -2470,7 +3214,7 @@ function renderHtmlNodes(nodes, keyPrefix) {
|
|
|
2470
3214
|
case "htmlElement":
|
|
2471
3215
|
return renderHtmlElement(node, key);
|
|
2472
3216
|
case "htmlText":
|
|
2473
|
-
return /* @__PURE__ */
|
|
3217
|
+
return /* @__PURE__ */ jsx18(Fragment2, { children: node.value }, key);
|
|
2474
3218
|
case "htmlComment":
|
|
2475
3219
|
return null;
|
|
2476
3220
|
default:
|
|
@@ -2478,17 +3222,32 @@ function renderHtmlNodes(nodes, keyPrefix) {
|
|
|
2478
3222
|
}
|
|
2479
3223
|
});
|
|
2480
3224
|
}
|
|
2481
|
-
function MarkdownRenderer({
|
|
3225
|
+
function MarkdownRenderer({
|
|
3226
|
+
nodes,
|
|
3227
|
+
className,
|
|
3228
|
+
htmlPolicy = "sanitize",
|
|
3229
|
+
linkSchemes
|
|
3230
|
+
}) {
|
|
2482
3231
|
if (!nodes || nodes.length === 0) return null;
|
|
2483
|
-
return /* @__PURE__ */
|
|
3232
|
+
return /* @__PURE__ */ jsx18("div", { className: `squisq-md ${className || ""}`, children: renderBlocks(nodes, "", { htmlPolicy, linkSchemes }) });
|
|
2484
3233
|
}
|
|
2485
3234
|
|
|
2486
3235
|
// src/LinearDocView.tsx
|
|
2487
|
-
import { jsx as
|
|
3236
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3237
|
+
var warnedUnknownTemplates = /* @__PURE__ */ new Set();
|
|
2488
3238
|
function isAnnotatedBlock(block) {
|
|
2489
3239
|
const annotation = block.sourceHeading?.templateAnnotation;
|
|
2490
|
-
if (!annotation) return false;
|
|
2491
|
-
|
|
3240
|
+
if (!annotation?.template) return false;
|
|
3241
|
+
if (!hasTemplate(annotation.template)) {
|
|
3242
|
+
if (!warnedUnknownTemplates.has(annotation.template)) {
|
|
3243
|
+
warnedUnknownTemplates.add(annotation.template);
|
|
3244
|
+
console.warn(
|
|
3245
|
+
`[squisq] Unknown template "${annotation.template}" \u2014 rendering the block as plain markdown.`
|
|
3246
|
+
);
|
|
3247
|
+
}
|
|
3248
|
+
return false;
|
|
3249
|
+
}
|
|
3250
|
+
return true;
|
|
2492
3251
|
}
|
|
2493
3252
|
function countAll(blocks) {
|
|
2494
3253
|
let count = 0;
|
|
@@ -2500,11 +3259,10 @@ function countAll(blocks) {
|
|
|
2500
3259
|
}
|
|
2501
3260
|
function BlockSection({ block, basePath, viewport, renderContext, blockIndex }) {
|
|
2502
3261
|
const isAnnotated = isAnnotatedBlock(block);
|
|
2503
|
-
const visualBlock =
|
|
3262
|
+
const visualBlock = useMemo8(() => {
|
|
2504
3263
|
if (!isAnnotated) return null;
|
|
2505
3264
|
const annotation = block.sourceHeading.templateAnnotation;
|
|
2506
3265
|
const headingText = extractPlainText(block.sourceHeading);
|
|
2507
|
-
const bodyText = extractBodyPlainText(block.contents);
|
|
2508
3266
|
const templateBlock = {
|
|
2509
3267
|
id: block.id,
|
|
2510
3268
|
template: annotation.template,
|
|
@@ -2512,12 +3270,14 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2512
3270
|
duration: 1,
|
|
2513
3271
|
audioSegment: 0,
|
|
2514
3272
|
title: headingText,
|
|
2515
|
-
...
|
|
3273
|
+
...deriveTemplateInputs(
|
|
2516
3274
|
annotation.template ?? "sectionHeader",
|
|
2517
3275
|
headingText,
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
3276
|
+
block.contents,
|
|
3277
|
+
{
|
|
3278
|
+
placeholders: true
|
|
3279
|
+
}
|
|
3280
|
+
) ?? {},
|
|
2521
3281
|
...annotation.params,
|
|
2522
3282
|
...block.templateOverrides
|
|
2523
3283
|
};
|
|
@@ -2532,15 +3292,15 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2532
3292
|
template: annotation.template
|
|
2533
3293
|
};
|
|
2534
3294
|
}, [block, isAnnotated, renderContext, blockIndex]);
|
|
2535
|
-
return /* @__PURE__ */
|
|
3295
|
+
return /* @__PURE__ */ jsxs12(
|
|
2536
3296
|
"div",
|
|
2537
3297
|
{
|
|
2538
3298
|
className: "squisq-linear-section",
|
|
2539
3299
|
"data-block-id": block.id,
|
|
2540
3300
|
"data-template": isAnnotated ? block.sourceHeading?.templateAnnotation?.template : void 0,
|
|
2541
3301
|
children: [
|
|
2542
|
-
block.sourceHeading && !isAnnotated && /* @__PURE__ */
|
|
2543
|
-
isAnnotated && visualBlock && /* @__PURE__ */
|
|
3302
|
+
block.sourceHeading && !isAnnotated && /* @__PURE__ */ jsx19(MarkdownRenderer, { nodes: [block.sourceHeading] }),
|
|
3303
|
+
isAnnotated && visualBlock && /* @__PURE__ */ jsx19("div", { className: "squisq-linear-card", children: /* @__PURE__ */ jsx19(
|
|
2544
3304
|
"div",
|
|
2545
3305
|
{
|
|
2546
3306
|
className: "squisq-linear-card-svg",
|
|
@@ -2550,7 +3310,7 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2550
3310
|
overflow: "hidden",
|
|
2551
3311
|
marginBottom: "1em"
|
|
2552
3312
|
},
|
|
2553
|
-
children: /* @__PURE__ */
|
|
3313
|
+
children: /* @__PURE__ */ jsx19(
|
|
2554
3314
|
BlockRenderer,
|
|
2555
3315
|
{
|
|
2556
3316
|
block: visualBlock,
|
|
@@ -2561,8 +3321,8 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2561
3321
|
)
|
|
2562
3322
|
}
|
|
2563
3323
|
) }),
|
|
2564
|
-
!isAnnotated && block.contents && block.contents.length > 0 && /* @__PURE__ */
|
|
2565
|
-
block.children && block.children.length > 0 && /* @__PURE__ */
|
|
3324
|
+
!isAnnotated && block.contents && block.contents.length > 0 && /* @__PURE__ */ jsx19(MarkdownRenderer, { nodes: block.contents }),
|
|
3325
|
+
block.children && block.children.length > 0 && /* @__PURE__ */ jsx19("div", { className: "squisq-linear-children", children: block.children.map((child, i) => /* @__PURE__ */ jsx19(
|
|
2566
3326
|
BlockSection,
|
|
2567
3327
|
{
|
|
2568
3328
|
block: child,
|
|
@@ -2577,141 +3337,9 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
2577
3337
|
}
|
|
2578
3338
|
);
|
|
2579
3339
|
}
|
|
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
3340
|
function LinearDocView({
|
|
2714
3341
|
doc,
|
|
3342
|
+
markdown,
|
|
2715
3343
|
basePath = "/",
|
|
2716
3344
|
viewport,
|
|
2717
3345
|
className,
|
|
@@ -2721,18 +3349,33 @@ function LinearDocView({
|
|
|
2721
3349
|
imageDisplayMode = "inline"
|
|
2722
3350
|
}) {
|
|
2723
3351
|
const activeViewport = viewport ?? VIEWPORT_PRESETS3.landscape;
|
|
2724
|
-
const
|
|
3352
|
+
const markdownDoc = useMemo8(
|
|
3353
|
+
() => !doc && markdown !== void 0 ? markdownToDoc(parseMarkdown(markdown)) : void 0,
|
|
3354
|
+
[doc, markdown]
|
|
3355
|
+
);
|
|
3356
|
+
const resolvedDoc = doc ?? markdownDoc;
|
|
3357
|
+
const totalBlocks = useMemo8(
|
|
3358
|
+
() => resolvedDoc ? countAll(resolvedDoc.blocks) : 0,
|
|
3359
|
+
[resolvedDoc]
|
|
3360
|
+
);
|
|
2725
3361
|
const autoSurface = useAutoSurface(surface === "auto");
|
|
2726
3362
|
const resolvedSurface = surface === "auto" ? autoSurface : surface;
|
|
2727
|
-
const renderContext =
|
|
2728
|
-
const baseTheme = theme ??
|
|
3363
|
+
const renderContext = useMemo8(() => {
|
|
3364
|
+
const baseTheme = theme ?? DEFAULT_THEME2;
|
|
3365
|
+
const effectiveTheme = resolvedSurface ? applySurface(baseTheme, resolvedSurface) : baseTheme;
|
|
2729
3366
|
return {
|
|
2730
|
-
theme:
|
|
3367
|
+
theme: effectiveTheme,
|
|
2731
3368
|
viewport: activeViewport,
|
|
2732
|
-
totalBlocks
|
|
3369
|
+
totalBlocks,
|
|
3370
|
+
// Theme atmosphere (vignette/grain/gradient persistent layers) shows
|
|
3371
|
+
// on the inline template cards so they match the player's look.
|
|
3372
|
+
persistentLayers: effectiveTheme.persistentLayers
|
|
2733
3373
|
};
|
|
2734
3374
|
}, [activeViewport, totalBlocks, theme, resolvedSurface]);
|
|
2735
3375
|
const activeTheme = renderContext.theme;
|
|
3376
|
+
if (!resolvedDoc) {
|
|
3377
|
+
return /* @__PURE__ */ jsx19("div", { className: `squisq-linear squisq-linear--empty ${className || ""}` });
|
|
3378
|
+
}
|
|
2736
3379
|
const bgColor = activeTheme.colors.background;
|
|
2737
3380
|
const textColor = activeTheme.colors.text;
|
|
2738
3381
|
const mutedColor = activeTheme.colors.textMuted;
|
|
@@ -2740,7 +3383,7 @@ function LinearDocView({
|
|
|
2740
3383
|
const bodyFont = resolveFontFamily2(activeTheme.typography.bodyFont, "system-ui, sans-serif");
|
|
2741
3384
|
const titleFont = resolveFontFamily2(activeTheme.typography.titleFont, "Georgia, serif");
|
|
2742
3385
|
const lineHt = activeTheme.typography.lineHeight ?? 1.7;
|
|
2743
|
-
return /* @__PURE__ */
|
|
3386
|
+
return /* @__PURE__ */ jsx19(
|
|
2744
3387
|
"div",
|
|
2745
3388
|
{
|
|
2746
3389
|
className: `squisq-linear ${className || ""}`,
|
|
@@ -2756,7 +3399,7 @@ function LinearDocView({
|
|
|
2756
3399
|
overflowX: "hidden",
|
|
2757
3400
|
background: bgColor
|
|
2758
3401
|
},
|
|
2759
|
-
children: /* @__PURE__ */
|
|
3402
|
+
children: /* @__PURE__ */ jsxs12(
|
|
2760
3403
|
"div",
|
|
2761
3404
|
{
|
|
2762
3405
|
className: `squisq-linear-content squisq-md${thinMargins ? " squisq-linear-content--thin" : ""}${imageDisplayMode === "thumbnail" ? " squisq-linear-content--thumbnail-images" : ""}`,
|
|
@@ -2781,7 +3424,7 @@ function LinearDocView({
|
|
|
2781
3424
|
"--squisq-linear-bg": bgColor
|
|
2782
3425
|
},
|
|
2783
3426
|
children: [
|
|
2784
|
-
/* @__PURE__ */
|
|
3427
|
+
/* @__PURE__ */ jsx19("style", { children: `
|
|
2785
3428
|
.squisq-linear-content h1,
|
|
2786
3429
|
.squisq-linear-content h2,
|
|
2787
3430
|
.squisq-linear-content h3,
|
|
@@ -2893,7 +3536,7 @@ function LinearDocView({
|
|
|
2893
3536
|
background: color-mix(in srgb, var(--squisq-linear-primary) 8%, transparent);
|
|
2894
3537
|
}
|
|
2895
3538
|
` }),
|
|
2896
|
-
|
|
3539
|
+
resolvedDoc.blocks.map((block, i) => /* @__PURE__ */ jsx19(
|
|
2897
3540
|
BlockSection,
|
|
2898
3541
|
{
|
|
2899
3542
|
block,
|
|
@@ -2912,7 +3555,7 @@ function LinearDocView({
|
|
|
2912
3555
|
}
|
|
2913
3556
|
|
|
2914
3557
|
// src/DocPlayer.tsx
|
|
2915
|
-
import { jsx as
|
|
3558
|
+
import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2916
3559
|
var SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
2917
3560
|
"a",
|
|
2918
3561
|
"an",
|
|
@@ -2930,9 +3573,9 @@ var SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
|
2930
3573
|
"by",
|
|
2931
3574
|
"is"
|
|
2932
3575
|
]);
|
|
2933
|
-
function buildSegmentTitleMap(
|
|
3576
|
+
function buildSegmentTitleMap(doc) {
|
|
2934
3577
|
const map = /* @__PURE__ */ new Map();
|
|
2935
|
-
for (const block of
|
|
3578
|
+
for (const block of doc.blocks) {
|
|
2936
3579
|
if (isTemplateBlock2(block) && block.template === "sectionHeader" && "title" in block) {
|
|
2937
3580
|
const segIdx = block.audioSegment;
|
|
2938
3581
|
if (!map.has(segIdx)) {
|
|
@@ -2940,9 +3583,9 @@ function buildSegmentTitleMap(script) {
|
|
|
2940
3583
|
}
|
|
2941
3584
|
}
|
|
2942
3585
|
}
|
|
2943
|
-
for (let i = 0; i <
|
|
3586
|
+
for (let i = 0; i < doc.audio.segments.length; i++) {
|
|
2944
3587
|
if (!map.has(i)) {
|
|
2945
|
-
const name =
|
|
3588
|
+
const name = doc.audio.segments[i].name;
|
|
2946
3589
|
if (name === "intro" || name.includes("intro")) {
|
|
2947
3590
|
map.set(i, "Introduction");
|
|
2948
3591
|
} else if (name === "flight-context" || name.includes("flight-context")) {
|
|
@@ -2958,14 +3601,34 @@ function buildSegmentTitleMap(script) {
|
|
|
2958
3601
|
}
|
|
2959
3602
|
return map;
|
|
2960
3603
|
}
|
|
2961
|
-
function
|
|
2962
|
-
|
|
2963
|
-
|
|
3604
|
+
function isDevEnvironment() {
|
|
3605
|
+
try {
|
|
3606
|
+
return typeof process !== "undefined" && process.env.NODE_ENV !== "production";
|
|
3607
|
+
} catch {
|
|
3608
|
+
return false;
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
var warnedMissingStyles = false;
|
|
3612
|
+
function DocPlayer(props) {
|
|
3613
|
+
const { doc, markdown } = props;
|
|
3614
|
+
const markdownDoc = useMemo9(
|
|
3615
|
+
() => !doc && markdown !== void 0 ? markdownToDoc2(parseMarkdown2(markdown)) : void 0,
|
|
3616
|
+
[doc, markdown]
|
|
3617
|
+
);
|
|
3618
|
+
const resolvedDoc = doc ?? markdownDoc;
|
|
3619
|
+
if (!resolvedDoc) {
|
|
3620
|
+
return /* @__PURE__ */ jsx20("div", { className: "doc-player doc-player--empty" });
|
|
3621
|
+
}
|
|
3622
|
+
return /* @__PURE__ */ jsx20(DocPlayerContent, { ...props, doc: resolvedDoc });
|
|
3623
|
+
}
|
|
3624
|
+
function DocPlayerContent({
|
|
3625
|
+
doc,
|
|
3626
|
+
basePath = ".",
|
|
2964
3627
|
renderMode = false,
|
|
2965
3628
|
autoPlay = false,
|
|
2966
3629
|
onEnded,
|
|
2967
3630
|
onTimeUpdate,
|
|
2968
|
-
|
|
3631
|
+
audioController: externalAudioController,
|
|
2969
3632
|
showControls = true,
|
|
2970
3633
|
showScrubber = false,
|
|
2971
3634
|
muted = false,
|
|
@@ -2980,23 +3643,36 @@ function DocPlayer({
|
|
|
2980
3643
|
displayMode = "video",
|
|
2981
3644
|
theme,
|
|
2982
3645
|
surface,
|
|
2983
|
-
captionStyle = "standard"
|
|
3646
|
+
captionStyle = "standard",
|
|
3647
|
+
enableSwipe = true
|
|
2984
3648
|
}) {
|
|
2985
3649
|
const isSlideshowMode = displayMode === "slideshow";
|
|
2986
3650
|
const isLinearMode = displayMode === "linear";
|
|
2987
|
-
const audioRef =
|
|
2988
|
-
const containerRef =
|
|
3651
|
+
const audioRef = useRef7(null);
|
|
3652
|
+
const containerRef = useRef7(null);
|
|
2989
3653
|
const [tapFeedback, setTapFeedback] = useState7(null);
|
|
2990
|
-
const tapFeedbackTimer =
|
|
3654
|
+
const tapFeedbackTimer = useRef7();
|
|
2991
3655
|
const { viewport, orientation } = useViewportOrientation();
|
|
2992
3656
|
const activeViewport = forceViewport || (renderMode ? VIEWPORT_PRESETS4.landscape : viewport);
|
|
2993
|
-
const isDebugMode =
|
|
3657
|
+
const isDebugMode = useMemo9(() => {
|
|
2994
3658
|
if (typeof window === "undefined") return false;
|
|
2995
3659
|
const params = new URLSearchParams(window.location.search);
|
|
2996
3660
|
return params.get("debug") === "true";
|
|
2997
3661
|
}, []);
|
|
2998
|
-
const internalAudio = useAudioSync(audioRef,
|
|
2999
|
-
const audio =
|
|
3662
|
+
const internalAudio = useAudioSync(audioRef, doc.audio, basePath);
|
|
3663
|
+
const audio = externalAudioController || internalAudio;
|
|
3664
|
+
useEffect8(() => {
|
|
3665
|
+
if (warnedMissingStyles || !isDevEnvironment()) return;
|
|
3666
|
+
const el = containerRef.current;
|
|
3667
|
+
if (!el || typeof getComputedStyle !== "function") return;
|
|
3668
|
+
const value = getComputedStyle(el).getPropertyValue("--squisq-styles-loaded");
|
|
3669
|
+
if (!value.trim()) {
|
|
3670
|
+
warnedMissingStyles = true;
|
|
3671
|
+
console.warn(
|
|
3672
|
+
'[squisq] @bendyline/squisq-react/styles is not loaded \u2014 import "@bendyline/squisq-react/styles"'
|
|
3673
|
+
);
|
|
3674
|
+
}
|
|
3675
|
+
}, []);
|
|
3000
3676
|
const {
|
|
3001
3677
|
currentTime,
|
|
3002
3678
|
isPlaying,
|
|
@@ -3013,12 +3689,13 @@ function DocPlayer({
|
|
|
3013
3689
|
skipToSegment: _skipToSegment,
|
|
3014
3690
|
restart
|
|
3015
3691
|
} = audio;
|
|
3016
|
-
const
|
|
3692
|
+
const mediaSchedule = useMemo9(() => resolveMediaSchedule(doc), [doc]);
|
|
3693
|
+
const currentTimeRef = useRef7(currentTime);
|
|
3017
3694
|
currentTimeRef.current = currentTime;
|
|
3018
|
-
const totalDurationRef =
|
|
3695
|
+
const totalDurationRef = useRef7(totalDuration);
|
|
3019
3696
|
totalDurationRef.current = totalDuration;
|
|
3020
|
-
const expandedBlocksLenRef =
|
|
3021
|
-
const handleContainerClick =
|
|
3697
|
+
const expandedBlocksLenRef = useRef7(0);
|
|
3698
|
+
const handleContainerClick = useCallback6(
|
|
3022
3699
|
(e) => {
|
|
3023
3700
|
if (renderMode || isSlideshowMode || isLinearMode) return;
|
|
3024
3701
|
const target = e.target;
|
|
@@ -3036,8 +3713,8 @@ function DocPlayer({
|
|
|
3036
3713
|
);
|
|
3037
3714
|
const autoSurface = useAutoSurface(surface === "auto");
|
|
3038
3715
|
const resolvedSurface = surface === "auto" ? autoSurface : surface;
|
|
3039
|
-
const effectiveTheme =
|
|
3040
|
-
const base = theme ??
|
|
3716
|
+
const effectiveTheme = useMemo9(() => {
|
|
3717
|
+
const base = theme ?? DEFAULT_THEME3;
|
|
3041
3718
|
return resolvedSurface ? applySurface2(base, resolvedSurface) : base;
|
|
3042
3719
|
}, [theme, resolvedSurface]);
|
|
3043
3720
|
const {
|
|
@@ -3052,9 +3729,9 @@ function DocPlayer({
|
|
|
3052
3729
|
nextBlock: _nextBlock,
|
|
3053
3730
|
prevBlock: _prevBlock,
|
|
3054
3731
|
blocks: expandedBlocks
|
|
3055
|
-
} = useDocPlayback(
|
|
3056
|
-
const coverBlock =
|
|
3057
|
-
const startBlockConfig =
|
|
3732
|
+
} = useDocPlayback(doc, currentTime, activeViewport, renderMode, effectiveTheme);
|
|
3733
|
+
const coverBlock = useMemo9(() => {
|
|
3734
|
+
const startBlockConfig = doc.startBlock;
|
|
3058
3735
|
if (!startBlockConfig) return null;
|
|
3059
3736
|
const context = createTemplateContext(effectiveTheme, 0, 1, activeViewport);
|
|
3060
3737
|
const layers = expandCoverBlock(startBlockConfig, context);
|
|
@@ -3067,15 +3744,15 @@ function DocPlayer({
|
|
|
3067
3744
|
audioSegment: -1,
|
|
3068
3745
|
layers
|
|
3069
3746
|
};
|
|
3070
|
-
}, [
|
|
3747
|
+
}, [doc.startBlock, activeViewport, effectiveTheme]);
|
|
3071
3748
|
const [coverForced, setCoverForced] = useState7(false);
|
|
3072
3749
|
const [coverGraceActive, setCoverGraceActive] = useState7(false);
|
|
3073
|
-
const coverGraceTimer =
|
|
3074
|
-
const coverWasShowing =
|
|
3075
|
-
const hasPlayedOnce =
|
|
3750
|
+
const coverGraceTimer = useRef7();
|
|
3751
|
+
const coverWasShowing = useRef7(false);
|
|
3752
|
+
const hasPlayedOnce = useRef7(false);
|
|
3076
3753
|
const atRest = !!(coverBlock && !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
3077
3754
|
if (atRest) coverWasShowing.current = true;
|
|
3078
|
-
|
|
3755
|
+
useEffect8(() => {
|
|
3079
3756
|
if (isPlaying && coverWasShowing.current && coverBlock && !renderMode) {
|
|
3080
3757
|
coverWasShowing.current = false;
|
|
3081
3758
|
hasPlayedOnce.current = true;
|
|
@@ -3083,24 +3760,24 @@ function DocPlayer({
|
|
|
3083
3760
|
coverGraceTimer.current = setTimeout(() => setCoverGraceActive(false), 3e3);
|
|
3084
3761
|
}
|
|
3085
3762
|
}, [isPlaying, coverBlock, renderMode]);
|
|
3086
|
-
|
|
3763
|
+
useEffect8(() => () => clearTimeout(coverGraceTimer.current), []);
|
|
3087
3764
|
const showCoverBlock = !isSlideshowMode && !isLinearMode && coverBlock && (coverForced || coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
3088
|
-
const hasAutoPlayed =
|
|
3089
|
-
|
|
3765
|
+
const hasAutoPlayed = useRef7(false);
|
|
3766
|
+
useEffect8(() => {
|
|
3090
3767
|
if (isAudioReady && autoPlay && !hasAutoPlayed.current) {
|
|
3091
3768
|
hasAutoPlayed.current = true;
|
|
3092
3769
|
play();
|
|
3093
3770
|
}
|
|
3094
3771
|
}, [isAudioReady, autoPlay, play]);
|
|
3095
|
-
|
|
3772
|
+
useEffect8(() => {
|
|
3096
3773
|
onTimeUpdate?.(currentTime);
|
|
3097
3774
|
}, [currentTime, onTimeUpdate]);
|
|
3098
|
-
|
|
3775
|
+
useEffect8(() => {
|
|
3099
3776
|
if (isEnded) {
|
|
3100
3777
|
onEnded?.();
|
|
3101
3778
|
}
|
|
3102
3779
|
}, [isEnded, onEnded]);
|
|
3103
|
-
|
|
3780
|
+
useEffect8(() => {
|
|
3104
3781
|
if ((renderMode || isDebugMode) && typeof window !== "undefined") {
|
|
3105
3782
|
const w = window;
|
|
3106
3783
|
w.seekTo = (time) => {
|
|
@@ -3133,7 +3810,11 @@ function DocPlayer({
|
|
|
3133
3810
|
const video = el;
|
|
3134
3811
|
const clipStart = parseFloat(video.dataset.clipStart || "0");
|
|
3135
3812
|
const clipEnd = parseFloat(video.dataset.clipEnd || "0");
|
|
3136
|
-
const
|
|
3813
|
+
const startAt = parseFloat(video.dataset.startAt || "0");
|
|
3814
|
+
const targetTime = Math.min(
|
|
3815
|
+
clipStart + Math.max(0, blockElapsed - startAt),
|
|
3816
|
+
clipEnd
|
|
3817
|
+
);
|
|
3137
3818
|
video.pause();
|
|
3138
3819
|
video.currentTime = targetTime;
|
|
3139
3820
|
videoSeekPromises.push(
|
|
@@ -3148,6 +3829,26 @@ function DocPlayer({
|
|
|
3148
3829
|
);
|
|
3149
3830
|
});
|
|
3150
3831
|
}
|
|
3832
|
+
document.querySelectorAll("video[data-clip-id]").forEach((el) => {
|
|
3833
|
+
const video = el;
|
|
3834
|
+
const absStart = parseFloat(video.dataset.absStart || "0");
|
|
3835
|
+
const absEnd = parseFloat(video.dataset.absEnd || "0");
|
|
3836
|
+
const sourceIn = parseFloat(video.dataset.sourceIn || "0");
|
|
3837
|
+
video.pause();
|
|
3838
|
+
if (time < absStart || time >= absEnd) return;
|
|
3839
|
+
const targetTime = sourceIn + (time - absStart);
|
|
3840
|
+
video.currentTime = targetTime;
|
|
3841
|
+
videoSeekPromises.push(
|
|
3842
|
+
new Promise((r) => {
|
|
3843
|
+
if (Math.abs(video.currentTime - targetTime) < 0.1) {
|
|
3844
|
+
r();
|
|
3845
|
+
} else {
|
|
3846
|
+
video.addEventListener("seeked", () => r(), { once: true });
|
|
3847
|
+
setTimeout(r, 200);
|
|
3848
|
+
}
|
|
3849
|
+
})
|
|
3850
|
+
);
|
|
3851
|
+
});
|
|
3151
3852
|
Promise.all(videoSeekPromises).then(() => {
|
|
3152
3853
|
requestAnimationFrame(() => resolve());
|
|
3153
3854
|
});
|
|
@@ -3155,12 +3856,9 @@ function DocPlayer({
|
|
|
3155
3856
|
});
|
|
3156
3857
|
};
|
|
3157
3858
|
w.getDuration = () => {
|
|
3158
|
-
|
|
3159
|
-
if (
|
|
3160
|
-
|
|
3161
|
-
return last.startTime + last.duration;
|
|
3162
|
-
}
|
|
3163
|
-
return 0;
|
|
3859
|
+
const mediaDuration = getDocPlaybackDuration(doc);
|
|
3860
|
+
if (totalDuration > 0) return Math.max(totalDuration, mediaDuration);
|
|
3861
|
+
return mediaDuration;
|
|
3164
3862
|
};
|
|
3165
3863
|
w.getBlocks = () => expandedBlocks.map((s) => ({
|
|
3166
3864
|
id: s.id,
|
|
@@ -3168,20 +3866,20 @@ function DocPlayer({
|
|
|
3168
3866
|
startTime: s.startTime,
|
|
3169
3867
|
duration: s.duration
|
|
3170
3868
|
}));
|
|
3171
|
-
w.getAudioSegments = () =>
|
|
3869
|
+
w.getAudioSegments = () => doc.audio.segments.map((seg) => ({
|
|
3172
3870
|
src: seg.src,
|
|
3173
3871
|
name: seg.name,
|
|
3174
3872
|
duration: seg.duration,
|
|
3175
3873
|
startTime: seg.startTime
|
|
3176
3874
|
}));
|
|
3177
|
-
w.getCaptions = () =>
|
|
3875
|
+
w.getCaptions = () => doc.captions?.phrases?.map((p) => ({
|
|
3178
3876
|
text: p.text,
|
|
3179
3877
|
startTime: p.startTime,
|
|
3180
3878
|
endTime: p.endTime
|
|
3181
3879
|
})) || [];
|
|
3182
3880
|
w.getChapters = () => {
|
|
3183
|
-
const titleMap = buildSegmentTitleMap(
|
|
3184
|
-
return
|
|
3881
|
+
const titleMap = buildSegmentTitleMap(doc);
|
|
3882
|
+
return doc.audio.segments.map((seg, i) => ({
|
|
3185
3883
|
title: titleMap.get(i) || seg.name,
|
|
3186
3884
|
startTime: seg.startTime,
|
|
3187
3885
|
duration: seg.duration
|
|
@@ -3214,25 +3912,28 @@ function DocPlayer({
|
|
|
3214
3912
|
}, [renderMode, isDebugMode, seekTo, totalDuration, expandedBlocks, coverBlock]);
|
|
3215
3913
|
const defaultMode = captionsEnabledProp === false ? "off" : captionStyle || "standard";
|
|
3216
3914
|
const [captionMode, setCaptionMode] = useState7(defaultMode);
|
|
3915
|
+
useEffect8(() => {
|
|
3916
|
+
setCaptionMode(defaultMode);
|
|
3917
|
+
}, [defaultMode]);
|
|
3217
3918
|
const captionsEnabled = captionMode !== "off";
|
|
3218
3919
|
const activeCaptionStyle = captionMode === "social" ? "social" : "standard";
|
|
3219
|
-
const setCaptionsEnabled =
|
|
3920
|
+
const setCaptionsEnabled = useCallback6(
|
|
3220
3921
|
(enabled) => {
|
|
3221
3922
|
setCaptionMode(enabled ? captionStyle || "standard" : "off");
|
|
3222
3923
|
onCaptionsToggle?.(enabled);
|
|
3223
3924
|
},
|
|
3224
3925
|
[onCaptionsToggle, captionStyle]
|
|
3225
3926
|
);
|
|
3226
|
-
const cycleCaptionMode =
|
|
3927
|
+
const cycleCaptionMode = useCallback6(() => {
|
|
3227
3928
|
setCaptionMode((prev) => {
|
|
3228
3929
|
const next = prev === "off" ? "standard" : prev === "standard" ? "social" : "off";
|
|
3229
3930
|
onCaptionsToggle?.(next !== "off");
|
|
3230
3931
|
return next;
|
|
3231
3932
|
});
|
|
3232
3933
|
}, [onCaptionsToggle]);
|
|
3233
|
-
const hasCaptions =
|
|
3234
|
-
const segmentTitleMap =
|
|
3235
|
-
const playbackState =
|
|
3934
|
+
const hasCaptions = doc.captions && doc.captions.phrases.length > 0;
|
|
3935
|
+
const segmentTitleMap = useMemo9(() => buildSegmentTitleMap(doc), [doc]);
|
|
3936
|
+
const playbackState = useMemo9(
|
|
3236
3937
|
() => ({
|
|
3237
3938
|
isPlaying,
|
|
3238
3939
|
currentTime,
|
|
@@ -3245,10 +3946,10 @@ function DocPlayer({
|
|
|
3245
3946
|
captionMode,
|
|
3246
3947
|
isFullscreen,
|
|
3247
3948
|
currentSegmentIndex: currentSegment,
|
|
3248
|
-
currentSegmentName: segmentTitleMap.get(currentSegment) ??
|
|
3949
|
+
currentSegmentName: segmentTitleMap.get(currentSegment) ?? doc.audio.segments[currentSegment]?.name ?? null,
|
|
3249
3950
|
currentBlock: currentBlock ?? null
|
|
3250
3951
|
}),
|
|
3251
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps --
|
|
3952
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- doc.audio.segments is stable within a given doc
|
|
3252
3953
|
[
|
|
3253
3954
|
isPlaying,
|
|
3254
3955
|
currentTime,
|
|
@@ -3265,7 +3966,7 @@ function DocPlayer({
|
|
|
3265
3966
|
currentBlock
|
|
3266
3967
|
]
|
|
3267
3968
|
);
|
|
3268
|
-
const playbackActions =
|
|
3969
|
+
const playbackActions = useMemo9(
|
|
3269
3970
|
() => ({
|
|
3270
3971
|
toggle,
|
|
3271
3972
|
restart,
|
|
@@ -3276,7 +3977,7 @@ function DocPlayer({
|
|
|
3276
3977
|
}),
|
|
3277
3978
|
[toggle, restart, seekTo, setCaptionsEnabled, cycleCaptionMode, onFullscreenToggle]
|
|
3278
3979
|
);
|
|
3279
|
-
const slideNavActions =
|
|
3980
|
+
const slideNavActions = useMemo9(
|
|
3280
3981
|
() => ({
|
|
3281
3982
|
nextSlide: () => {
|
|
3282
3983
|
if (currentBlockIndex < expandedBlocks.length - 1) {
|
|
@@ -3308,13 +4009,22 @@ function DocPlayer({
|
|
|
3308
4009
|
}),
|
|
3309
4010
|
[currentBlockIndex, expandedBlocks, seekTo, pause]
|
|
3310
4011
|
);
|
|
3311
|
-
|
|
4012
|
+
const swipeEnabled = isSlideshowMode && !isLinearMode && !renderMode && enableSwipe;
|
|
4013
|
+
const swipe = useSlideSwipe({
|
|
4014
|
+
enabled: swipeEnabled,
|
|
4015
|
+
containerRef,
|
|
4016
|
+
canGoNext: currentBlockIndex < expandedBlocks.length - 1,
|
|
4017
|
+
canGoPrev: currentBlockIndex > 0,
|
|
4018
|
+
onNext: slideNavActions.nextSlide,
|
|
4019
|
+
onPrev: slideNavActions.prevSlide
|
|
4020
|
+
});
|
|
4021
|
+
useEffect8(() => {
|
|
3312
4022
|
onPlaybackStateChange?.(playbackState);
|
|
3313
4023
|
}, [playbackState, onPlaybackStateChange]);
|
|
3314
|
-
|
|
4024
|
+
useEffect8(() => {
|
|
3315
4025
|
onControlsReady?.({ play, pause, ...playbackActions });
|
|
3316
4026
|
}, [play, pause, playbackActions, onControlsReady]);
|
|
3317
|
-
const getBlockTitle =
|
|
4027
|
+
const getBlockTitle = useCallback6((block) => {
|
|
3318
4028
|
const docBlock = block;
|
|
3319
4029
|
if (isTemplateBlock2(docBlock)) {
|
|
3320
4030
|
const props = docBlock;
|
|
@@ -3338,7 +4048,7 @@ function DocPlayer({
|
|
|
3338
4048
|
}
|
|
3339
4049
|
return block.id.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
3340
4050
|
}, []);
|
|
3341
|
-
const blockMarkers =
|
|
4051
|
+
const blockMarkers = useMemo9(() => {
|
|
3342
4052
|
if (!totalDuration || !expandedBlocks.length) return [];
|
|
3343
4053
|
let prevSegment = -1;
|
|
3344
4054
|
return expandedBlocks.map((block, index) => {
|
|
@@ -3353,13 +4063,13 @@ function DocPlayer({
|
|
|
3353
4063
|
};
|
|
3354
4064
|
});
|
|
3355
4065
|
}, [expandedBlocks, totalDuration, getBlockTitle]);
|
|
3356
|
-
|
|
4066
|
+
useEffect8(() => {
|
|
3357
4067
|
if (blockMarkers.length > 0) {
|
|
3358
4068
|
onBlockMarkers?.(blockMarkers);
|
|
3359
4069
|
}
|
|
3360
4070
|
}, [blockMarkers, onBlockMarkers]);
|
|
3361
4071
|
expandedBlocksLenRef.current = expandedBlocks.length;
|
|
3362
|
-
const handleKeyDown =
|
|
4072
|
+
const handleKeyDown = useCallback6(
|
|
3363
4073
|
(e) => {
|
|
3364
4074
|
const activeEl = document.activeElement;
|
|
3365
4075
|
if (activeEl && (activeEl.tagName === "INPUT" || activeEl.tagName === "TEXTAREA" || activeEl.tagName === "SELECT")) {
|
|
@@ -3405,13 +4115,13 @@ function DocPlayer({
|
|
|
3405
4115
|
},
|
|
3406
4116
|
[isSlideshowMode, isLinearMode, toggle, seekTo, slideNavActions]
|
|
3407
4117
|
);
|
|
3408
|
-
|
|
4118
|
+
useEffect8(() => {
|
|
3409
4119
|
if (renderMode) return;
|
|
3410
4120
|
window.addEventListener("keydown", handleKeyDown);
|
|
3411
4121
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
3412
4122
|
}, [handleKeyDown, renderMode]);
|
|
3413
4123
|
if (isLinearMode) {
|
|
3414
|
-
return /* @__PURE__ */
|
|
4124
|
+
return /* @__PURE__ */ jsx20(
|
|
3415
4125
|
"div",
|
|
3416
4126
|
{
|
|
3417
4127
|
ref: containerRef,
|
|
@@ -3422,10 +4132,10 @@ function DocPlayer({
|
|
|
3422
4132
|
height: "100%",
|
|
3423
4133
|
overflow: "hidden"
|
|
3424
4134
|
},
|
|
3425
|
-
children: /* @__PURE__ */
|
|
4135
|
+
children: /* @__PURE__ */ jsx20(
|
|
3426
4136
|
LinearDocView,
|
|
3427
4137
|
{
|
|
3428
|
-
doc
|
|
4138
|
+
doc,
|
|
3429
4139
|
basePath,
|
|
3430
4140
|
viewport: activeViewport,
|
|
3431
4141
|
theme,
|
|
@@ -3435,24 +4145,38 @@ function DocPlayer({
|
|
|
3435
4145
|
}
|
|
3436
4146
|
);
|
|
3437
4147
|
}
|
|
3438
|
-
return /* @__PURE__ */
|
|
4148
|
+
return /* @__PURE__ */ jsxs13(
|
|
3439
4149
|
"div",
|
|
3440
4150
|
{
|
|
3441
4151
|
ref: containerRef,
|
|
3442
|
-
className: "doc-player"
|
|
4152
|
+
className: `doc-player${swipeEnabled ? " doc-player--swipe" : ""}${swipe.phase === "dragging" ? " doc-player--grabbing" : ""}`,
|
|
3443
4153
|
onClick: handleContainerClick,
|
|
4154
|
+
onPointerDown: swipe.onPointerDown,
|
|
3444
4155
|
style: {
|
|
3445
4156
|
position: "relative",
|
|
3446
4157
|
width: "100%",
|
|
3447
4158
|
aspectRatio: `${activeViewport.width} / ${activeViewport.height}`,
|
|
3448
4159
|
margin: "0 auto",
|
|
3449
4160
|
overflow: "hidden",
|
|
3450
|
-
cursor
|
|
4161
|
+
// Swipe uses the grab/grabbing cursor via CSS classes; let vertical page
|
|
4162
|
+
// scroll through on touch while we own horizontal drags.
|
|
4163
|
+
cursor: renderMode || swipeEnabled ? void 0 : "pointer",
|
|
4164
|
+
touchAction: swipeEnabled ? "pan-y" : void 0
|
|
3451
4165
|
},
|
|
3452
4166
|
children: [
|
|
3453
|
-
/* @__PURE__ */
|
|
3454
|
-
/* @__PURE__ */
|
|
3455
|
-
|
|
4167
|
+
/* @__PURE__ */ jsx20("audio", { ref: audioRef, preload: "auto", muted }),
|
|
4168
|
+
/* @__PURE__ */ jsx20(
|
|
4169
|
+
MediaClipLayer,
|
|
4170
|
+
{
|
|
4171
|
+
schedule: mediaSchedule,
|
|
4172
|
+
currentTime,
|
|
4173
|
+
isPlaying,
|
|
4174
|
+
basePath,
|
|
4175
|
+
renderMode
|
|
4176
|
+
}
|
|
4177
|
+
),
|
|
4178
|
+
/* @__PURE__ */ jsxs13("div", { className: "doc-player__viewport", children: [
|
|
4179
|
+
showCoverBlock && coverBlock && /* @__PURE__ */ jsx20("div", { className: "doc-player__block doc-player__block--cover", children: /* @__PURE__ */ jsx20(
|
|
3456
4180
|
BlockRenderer,
|
|
3457
4181
|
{
|
|
3458
4182
|
block: coverBlock,
|
|
@@ -3462,31 +4186,44 @@ function DocPlayer({
|
|
|
3462
4186
|
viewport: activeViewport
|
|
3463
4187
|
}
|
|
3464
4188
|
) }),
|
|
3465
|
-
!showCoverBlock && previousBlock && isExiting &&
|
|
4189
|
+
!showCoverBlock && previousBlock && isExiting && // Keyed by block id so each block is its own DOM subtree: React never
|
|
4190
|
+
// reconciles one block's layers onto another's (templates reuse layer
|
|
4191
|
+
// ids like `title`/`background`), which would otherwise reuse stale
|
|
4192
|
+
// DOM / skip entrance animations mid-transition.
|
|
4193
|
+
/* @__PURE__ */ jsx20("div", { className: "doc-player__block doc-player__block--previous", children: /* @__PURE__ */ jsx20(
|
|
3466
4194
|
BlockRenderer,
|
|
3467
4195
|
{
|
|
3468
4196
|
block: previousBlock,
|
|
3469
4197
|
blockTime,
|
|
3470
4198
|
basePath,
|
|
3471
4199
|
isExiting: true,
|
|
4200
|
+
transition: currentBlock?.transition,
|
|
3472
4201
|
viewport: activeViewport
|
|
3473
4202
|
}
|
|
3474
|
-
) }),
|
|
3475
|
-
!showCoverBlock && currentBlock && /* @__PURE__ */
|
|
3476
|
-
|
|
4203
|
+
) }, previousBlock.id),
|
|
4204
|
+
!showCoverBlock && currentBlock && /* @__PURE__ */ jsx20(
|
|
4205
|
+
"div",
|
|
3477
4206
|
{
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
4207
|
+
className: `doc-player__block doc-player__block--active${swipe.phase !== "idle" ? ` doc-player__block--${swipe.phase}` : ""}`,
|
|
4208
|
+
style: swipe.phase !== "idle" ? { transform: `translateX(${swipe.offsetPx}px)` } : void 0,
|
|
4209
|
+
children: /* @__PURE__ */ jsx20(
|
|
4210
|
+
BlockRenderer,
|
|
4211
|
+
{
|
|
4212
|
+
block: currentBlock,
|
|
4213
|
+
blockTime,
|
|
4214
|
+
basePath,
|
|
4215
|
+
isEntering,
|
|
4216
|
+
viewport: activeViewport,
|
|
4217
|
+
isPlaying
|
|
4218
|
+
}
|
|
4219
|
+
)
|
|
4220
|
+
},
|
|
4221
|
+
currentBlock.id
|
|
4222
|
+
),
|
|
4223
|
+
hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */ jsx20(
|
|
3487
4224
|
CaptionOverlay,
|
|
3488
4225
|
{
|
|
3489
|
-
captions:
|
|
4226
|
+
captions: doc.captions,
|
|
3490
4227
|
currentTime,
|
|
3491
4228
|
enabled: captionsEnabled && (renderMode || isPlaying || currentTime > 0),
|
|
3492
4229
|
fontSize: 16,
|
|
@@ -3495,7 +4232,7 @@ function DocPlayer({
|
|
|
3495
4232
|
viewport: activeViewport
|
|
3496
4233
|
}
|
|
3497
4234
|
),
|
|
3498
|
-
isDebugMode && /* @__PURE__ */
|
|
4235
|
+
isDebugMode && /* @__PURE__ */ jsxs13(
|
|
3499
4236
|
"div",
|
|
3500
4237
|
{
|
|
3501
4238
|
className: "doc-player__debug",
|
|
@@ -3516,27 +4253,27 @@ function DocPlayer({
|
|
|
3516
4253
|
textAlign: "left"
|
|
3517
4254
|
},
|
|
3518
4255
|
children: [
|
|
3519
|
-
/* @__PURE__ */
|
|
3520
|
-
/* @__PURE__ */
|
|
3521
|
-
/* @__PURE__ */
|
|
4256
|
+
/* @__PURE__ */ jsx20("div", { style: { color: "#ffcc00", fontWeight: "bold", marginBottom: "4px" }, children: "DEBUG MODE" }),
|
|
4257
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4258
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "template:" }),
|
|
3522
4259
|
" ",
|
|
3523
|
-
/* @__PURE__ */
|
|
4260
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#ff6b6b" }, children: currentBlock?.template ?? "raw" })
|
|
3524
4261
|
] }),
|
|
3525
|
-
/* @__PURE__ */
|
|
3526
|
-
/* @__PURE__ */
|
|
4262
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4263
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "block:" }),
|
|
3527
4264
|
" ",
|
|
3528
4265
|
currentBlockIndex + 1,
|
|
3529
4266
|
"/",
|
|
3530
4267
|
expandedBlocks.length,
|
|
3531
4268
|
" ",
|
|
3532
|
-
/* @__PURE__ */
|
|
4269
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3533
4270
|
"(",
|
|
3534
4271
|
currentBlock?.id || "none",
|
|
3535
4272
|
")"
|
|
3536
4273
|
] })
|
|
3537
4274
|
] }),
|
|
3538
|
-
/* @__PURE__ */
|
|
3539
|
-
/* @__PURE__ */
|
|
4275
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4276
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "time:" }),
|
|
3540
4277
|
" ",
|
|
3541
4278
|
currentTime.toFixed(2),
|
|
3542
4279
|
"s /",
|
|
@@ -3544,17 +4281,16 @@ function DocPlayer({
|
|
|
3544
4281
|
totalDuration.toFixed(1),
|
|
3545
4282
|
"s",
|
|
3546
4283
|
" ",
|
|
3547
|
-
/* @__PURE__ */
|
|
4284
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3548
4285
|
"(progress: ",
|
|
3549
4286
|
(docProgress * 100).toFixed(1),
|
|
3550
|
-
"%, scriptDur:",
|
|
3551
|
-
|
|
3552
|
-
script.duration.toFixed(1),
|
|
4287
|
+
"%, scriptDur: ",
|
|
4288
|
+
doc.duration.toFixed(1),
|
|
3553
4289
|
")"
|
|
3554
4290
|
] })
|
|
3555
4291
|
] }),
|
|
3556
|
-
/* @__PURE__ */
|
|
3557
|
-
/* @__PURE__ */
|
|
4292
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4293
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "blockTime:" }),
|
|
3558
4294
|
" ",
|
|
3559
4295
|
blockTime.toFixed(2),
|
|
3560
4296
|
"s /",
|
|
@@ -3562,58 +4298,58 @@ function DocPlayer({
|
|
|
3562
4298
|
(currentBlock?.duration || 0).toFixed(1),
|
|
3563
4299
|
"s"
|
|
3564
4300
|
] }),
|
|
3565
|
-
/* @__PURE__ */
|
|
3566
|
-
/* @__PURE__ */
|
|
4301
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4302
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "segment:" }),
|
|
3567
4303
|
" ",
|
|
3568
4304
|
currentSegment,
|
|
3569
4305
|
"/",
|
|
3570
|
-
|
|
4306
|
+
doc.audio.segments.length - 1,
|
|
3571
4307
|
" ",
|
|
3572
|
-
/* @__PURE__ */
|
|
4308
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3573
4309
|
"(",
|
|
3574
|
-
|
|
4310
|
+
doc.audio.segments[currentSegment]?.name || "none",
|
|
3575
4311
|
")"
|
|
3576
4312
|
] })
|
|
3577
4313
|
] }),
|
|
3578
|
-
/* @__PURE__ */
|
|
3579
|
-
/* @__PURE__ */
|
|
4314
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4315
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "viewport:" }),
|
|
3580
4316
|
" ",
|
|
3581
4317
|
activeViewport.name || `${activeViewport.width}x${activeViewport.height}`,
|
|
3582
4318
|
" ",
|
|
3583
|
-
/* @__PURE__ */
|
|
4319
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3584
4320
|
"(",
|
|
3585
4321
|
orientation,
|
|
3586
4322
|
")"
|
|
3587
4323
|
] })
|
|
3588
4324
|
] }),
|
|
3589
|
-
/* @__PURE__ */
|
|
3590
|
-
/* @__PURE__ */
|
|
4325
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4326
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "playing:" }),
|
|
3591
4327
|
" ",
|
|
3592
|
-
/* @__PURE__ */
|
|
3593
|
-
showCoverBlock && /* @__PURE__ */
|
|
4328
|
+
/* @__PURE__ */ jsx20("span", { style: { color: isPlaying ? "#4ade80" : "#f87171" }, children: isPlaying ? "yes" : "no" }),
|
|
4329
|
+
showCoverBlock && /* @__PURE__ */ jsx20("span", { style: { color: "#60a5fa" }, children: " (cover)" })
|
|
3594
4330
|
] }),
|
|
3595
4331
|
hasCaptions && (() => {
|
|
3596
|
-
const debugPhrase = getCaptionAtTime2(
|
|
4332
|
+
const debugPhrase = getCaptionAtTime2(doc.captions, currentTime);
|
|
3597
4333
|
const debugEnabled = captionsEnabled && (isPlaying || currentTime > 0);
|
|
3598
|
-
return /* @__PURE__ */
|
|
3599
|
-
/* @__PURE__ */
|
|
3600
|
-
/* @__PURE__ */
|
|
4334
|
+
return /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
4335
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4336
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "captions:" }),
|
|
3601
4337
|
" ",
|
|
3602
|
-
|
|
4338
|
+
doc.captions?.phrases.length || 0,
|
|
3603
4339
|
" phrases",
|
|
3604
4340
|
" ",
|
|
3605
|
-
/* @__PURE__ */
|
|
4341
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: captionsEnabled ? "#4ade80" : "#666" }, children: [
|
|
3606
4342
|
"(",
|
|
3607
4343
|
captionsEnabled ? "on" : "off",
|
|
3608
4344
|
")"
|
|
3609
4345
|
] })
|
|
3610
4346
|
] }),
|
|
3611
|
-
/* @__PURE__ */
|
|
3612
|
-
/* @__PURE__ */
|
|
4347
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4348
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "cc.enabled:" }),
|
|
3613
4349
|
" ",
|
|
3614
|
-
/* @__PURE__ */
|
|
4350
|
+
/* @__PURE__ */ jsx20("span", { style: { color: debugEnabled ? "#4ade80" : "#f87171" }, children: String(debugEnabled) }),
|
|
3615
4351
|
" ",
|
|
3616
|
-
/* @__PURE__ */
|
|
4352
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
3617
4353
|
"(playing=",
|
|
3618
4354
|
String(isPlaying),
|
|
3619
4355
|
" t>0=",
|
|
@@ -3621,15 +4357,15 @@ function DocPlayer({
|
|
|
3621
4357
|
")"
|
|
3622
4358
|
] })
|
|
3623
4359
|
] }),
|
|
3624
|
-
/* @__PURE__ */
|
|
3625
|
-
/* @__PURE__ */
|
|
4360
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4361
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "cc.phrase:" }),
|
|
3626
4362
|
" ",
|
|
3627
|
-
/* @__PURE__ */
|
|
4363
|
+
/* @__PURE__ */ jsx20("span", { style: { color: debugPhrase ? "#4ade80" : "#f87171" }, children: debugPhrase ? `"${debugPhrase.text.slice(0, 30)}..."` : "null" })
|
|
3628
4364
|
] }),
|
|
3629
|
-
debugPhrase && /* @__PURE__ */
|
|
3630
|
-
/* @__PURE__ */
|
|
4365
|
+
debugPhrase && /* @__PURE__ */ jsxs13("div", { children: [
|
|
4366
|
+
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "cc.range:" }),
|
|
3631
4367
|
" ",
|
|
3632
|
-
/* @__PURE__ */
|
|
4368
|
+
/* @__PURE__ */ jsxs13("span", { style: { color: "#60a5fa" }, children: [
|
|
3633
4369
|
debugPhrase.startTime.toFixed(2),
|
|
3634
4370
|
"-",
|
|
3635
4371
|
debugPhrase.endTime.toFixed(2)
|
|
@@ -3641,7 +4377,7 @@ function DocPlayer({
|
|
|
3641
4377
|
}
|
|
3642
4378
|
)
|
|
3643
4379
|
] }),
|
|
3644
|
-
!isAvailable && unavailableMessage && /* @__PURE__ */
|
|
4380
|
+
!isAvailable && unavailableMessage && /* @__PURE__ */ jsxs13(
|
|
3645
4381
|
"div",
|
|
3646
4382
|
{
|
|
3647
4383
|
className: "doc-player__unavailable",
|
|
@@ -3662,12 +4398,12 @@ function DocPlayer({
|
|
|
3662
4398
|
zIndex: 50
|
|
3663
4399
|
},
|
|
3664
4400
|
children: [
|
|
3665
|
-
/* @__PURE__ */
|
|
3666
|
-
/* @__PURE__ */
|
|
4401
|
+
/* @__PURE__ */ jsx20("span", { style: { fontSize: "32px" }, children: "\u{1F50A}" }),
|
|
4402
|
+
/* @__PURE__ */ jsx20("span", { children: unavailableMessage })
|
|
3667
4403
|
]
|
|
3668
4404
|
}
|
|
3669
4405
|
),
|
|
3670
|
-
!renderMode && !isSlideshowMode && showControls && /* @__PURE__ */
|
|
4406
|
+
!renderMode && !isSlideshowMode && showControls && /* @__PURE__ */ jsx20(
|
|
3671
4407
|
DocControlsOverlay,
|
|
3672
4408
|
{
|
|
3673
4409
|
state: playbackState,
|
|
@@ -3677,7 +4413,7 @@ function DocPlayer({
|
|
|
3677
4413
|
getBlockTitle
|
|
3678
4414
|
}
|
|
3679
4415
|
),
|
|
3680
|
-
!renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */
|
|
4416
|
+
!renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */ jsx20(
|
|
3681
4417
|
"div",
|
|
3682
4418
|
{
|
|
3683
4419
|
className: "doc-player__scrubber",
|
|
@@ -3692,7 +4428,7 @@ function DocPlayer({
|
|
|
3692
4428
|
alignItems: "center",
|
|
3693
4429
|
zIndex: 100
|
|
3694
4430
|
},
|
|
3695
|
-
children: /* @__PURE__ */
|
|
4431
|
+
children: /* @__PURE__ */ jsx20(
|
|
3696
4432
|
DocProgressBar,
|
|
3697
4433
|
{
|
|
3698
4434
|
state: playbackState,
|
|
@@ -3704,15 +4440,15 @@ function DocPlayer({
|
|
|
3704
4440
|
)
|
|
3705
4441
|
}
|
|
3706
4442
|
),
|
|
3707
|
-
!renderMode && isSlideshowMode && /* @__PURE__ */
|
|
3708
|
-
!isSlideshowMode && tapFeedback && /* @__PURE__ */
|
|
4443
|
+
!renderMode && isSlideshowMode && /* @__PURE__ */ jsx20(DocControlsSlideshow, { state: playbackState, slideNav: slideNavActions }),
|
|
4444
|
+
!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
4445
|
]
|
|
3710
4446
|
}
|
|
3711
4447
|
);
|
|
3712
4448
|
}
|
|
3713
4449
|
|
|
3714
4450
|
// src/DocControlsBottom.tsx
|
|
3715
|
-
import { jsx as
|
|
4451
|
+
import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3716
4452
|
function DocControlsBottom({
|
|
3717
4453
|
state,
|
|
3718
4454
|
actions,
|
|
@@ -3720,32 +4456,32 @@ function DocControlsBottom({
|
|
|
3720
4456
|
expandedBlocks,
|
|
3721
4457
|
getBlockTitle
|
|
3722
4458
|
}) {
|
|
3723
|
-
return /* @__PURE__ */
|
|
3724
|
-
/* @__PURE__ */
|
|
4459
|
+
return /* @__PURE__ */ jsxs14("div", { className: "doc-controls-bottom", children: [
|
|
4460
|
+
/* @__PURE__ */ jsx21(
|
|
3725
4461
|
"button",
|
|
3726
4462
|
{
|
|
3727
4463
|
className: "bottom-ctrl-btn",
|
|
3728
4464
|
onClick: actions.restart,
|
|
3729
4465
|
title: "Restart",
|
|
3730
4466
|
"aria-label": "Restart from beginning",
|
|
3731
|
-
children: /* @__PURE__ */
|
|
4467
|
+
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
4468
|
}
|
|
3733
4469
|
),
|
|
3734
|
-
/* @__PURE__ */
|
|
4470
|
+
/* @__PURE__ */ jsx21(
|
|
3735
4471
|
"button",
|
|
3736
4472
|
{
|
|
3737
4473
|
className: "bottom-ctrl-btn bottom-play-btn",
|
|
3738
4474
|
onClick: actions.toggle,
|
|
3739
4475
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
3740
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
4476
|
+
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
4477
|
}
|
|
3742
4478
|
),
|
|
3743
|
-
/* @__PURE__ */
|
|
4479
|
+
/* @__PURE__ */ jsxs14("span", { className: "bottom-time", children: [
|
|
3744
4480
|
formatTime(state.currentTime),
|
|
3745
4481
|
" / ",
|
|
3746
4482
|
formatTime(state.totalDuration)
|
|
3747
4483
|
] }),
|
|
3748
|
-
/* @__PURE__ */
|
|
4484
|
+
/* @__PURE__ */ jsx21(
|
|
3749
4485
|
DocProgressBar,
|
|
3750
4486
|
{
|
|
3751
4487
|
state,
|
|
@@ -3755,82 +4491,82 @@ function DocControlsBottom({
|
|
|
3755
4491
|
getBlockTitle
|
|
3756
4492
|
}
|
|
3757
4493
|
),
|
|
3758
|
-
/* @__PURE__ */
|
|
4494
|
+
/* @__PURE__ */ jsxs14("span", { className: "bottom-segment", children: [
|
|
3759
4495
|
state.currentBlockIndex + 1,
|
|
3760
4496
|
"/",
|
|
3761
4497
|
state.totalBlocks
|
|
3762
4498
|
] }),
|
|
3763
|
-
state.hasCaptions && /* @__PURE__ */
|
|
4499
|
+
state.hasCaptions && /* @__PURE__ */ jsx21(
|
|
3764
4500
|
"button",
|
|
3765
4501
|
{
|
|
3766
4502
|
className: `bottom-ctrl-btn ${state.captionMode !== "off" ? "bottom-ctrl-btn--active" : ""}`,
|
|
3767
4503
|
onClick: () => actions.cycleCaptionMode(),
|
|
3768
4504
|
title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
|
|
3769
4505
|
"aria-label": "Cycle caption style",
|
|
3770
|
-
children: /* @__PURE__ */
|
|
4506
|
+
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
4507
|
}
|
|
3772
4508
|
)
|
|
3773
4509
|
] });
|
|
3774
4510
|
}
|
|
3775
4511
|
|
|
3776
4512
|
// src/DocControlsSidebar.tsx
|
|
3777
|
-
import { jsx as
|
|
4513
|
+
import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3778
4514
|
function DocControlsSidebar({ state, actions }) {
|
|
3779
|
-
return /* @__PURE__ */
|
|
3780
|
-
/* @__PURE__ */
|
|
4515
|
+
return /* @__PURE__ */ jsxs15("div", { className: "doc-controls-sidebar", children: [
|
|
4516
|
+
/* @__PURE__ */ jsx22(
|
|
3781
4517
|
"button",
|
|
3782
4518
|
{
|
|
3783
4519
|
className: "sidebar-ctrl-btn",
|
|
3784
4520
|
onClick: actions.restart,
|
|
3785
4521
|
title: "Restart",
|
|
3786
4522
|
"aria-label": "Restart from beginning",
|
|
3787
|
-
children: /* @__PURE__ */
|
|
4523
|
+
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
4524
|
}
|
|
3789
4525
|
),
|
|
3790
|
-
/* @__PURE__ */
|
|
4526
|
+
/* @__PURE__ */ jsx22(
|
|
3791
4527
|
"button",
|
|
3792
4528
|
{
|
|
3793
4529
|
className: "sidebar-ctrl-btn sidebar-play-btn",
|
|
3794
4530
|
onClick: actions.toggle,
|
|
3795
4531
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
3796
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
4532
|
+
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
4533
|
}
|
|
3798
4534
|
),
|
|
3799
|
-
/* @__PURE__ */
|
|
3800
|
-
/* @__PURE__ */
|
|
3801
|
-
/* @__PURE__ */
|
|
4535
|
+
/* @__PURE__ */ jsxs15("div", { className: "sidebar-time", children: [
|
|
4536
|
+
/* @__PURE__ */ jsx22("div", { children: formatTime(state.currentTime) }),
|
|
4537
|
+
/* @__PURE__ */ jsx22("div", { className: "sidebar-time-total", children: formatTime(state.totalDuration) })
|
|
3802
4538
|
] }),
|
|
3803
|
-
/* @__PURE__ */
|
|
4539
|
+
/* @__PURE__ */ jsxs15("div", { className: "sidebar-segment", children: [
|
|
3804
4540
|
state.currentBlockIndex + 1,
|
|
3805
4541
|
"/",
|
|
3806
4542
|
state.totalBlocks
|
|
3807
4543
|
] }),
|
|
3808
|
-
state.hasCaptions && /* @__PURE__ */
|
|
4544
|
+
state.hasCaptions && /* @__PURE__ */ jsx22(
|
|
3809
4545
|
"button",
|
|
3810
4546
|
{
|
|
3811
4547
|
className: `sidebar-ctrl-btn ${state.captionMode !== "off" ? "sidebar-ctrl-btn--active" : ""}`,
|
|
3812
4548
|
onClick: () => actions.cycleCaptionMode(),
|
|
3813
4549
|
title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
|
|
3814
4550
|
"aria-label": "Cycle caption style",
|
|
3815
|
-
children: /* @__PURE__ */
|
|
4551
|
+
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
4552
|
}
|
|
3817
4553
|
),
|
|
3818
|
-
actions.toggleFullscreen && /* @__PURE__ */
|
|
4554
|
+
actions.toggleFullscreen && /* @__PURE__ */ jsx22(
|
|
3819
4555
|
"button",
|
|
3820
4556
|
{
|
|
3821
4557
|
className: `sidebar-ctrl-btn ${state.isFullscreen ? "sidebar-ctrl-btn--active" : ""}`,
|
|
3822
4558
|
onClick: actions.toggleFullscreen,
|
|
3823
4559
|
title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
|
|
3824
4560
|
"aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
3825
|
-
children: state.isFullscreen ? /* @__PURE__ */
|
|
4561
|
+
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
4562
|
}
|
|
3827
4563
|
)
|
|
3828
4564
|
] });
|
|
3829
4565
|
}
|
|
3830
4566
|
|
|
3831
4567
|
// src/DocPlayerWithSidebar.tsx
|
|
3832
|
-
import { useRef as
|
|
3833
|
-
import { jsx as
|
|
4568
|
+
import { useRef as useRef8, useState as useState8, useCallback as useCallback7, useEffect as useEffect9 } from "react";
|
|
4569
|
+
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3834
4570
|
var DEFAULT_STATE = {
|
|
3835
4571
|
isPlaying: false,
|
|
3836
4572
|
currentTime: 0,
|
|
@@ -3846,24 +4582,25 @@ var DEFAULT_STATE = {
|
|
|
3846
4582
|
currentBlock: null
|
|
3847
4583
|
};
|
|
3848
4584
|
function DocPlayerWithSidebar({
|
|
3849
|
-
|
|
4585
|
+
doc,
|
|
3850
4586
|
basePath,
|
|
3851
4587
|
autoPlay = false,
|
|
3852
4588
|
onEnded,
|
|
3853
4589
|
onTimeUpdate,
|
|
3854
|
-
|
|
4590
|
+
audioController,
|
|
3855
4591
|
muted,
|
|
3856
4592
|
captionsEnabled,
|
|
3857
4593
|
isFullscreen,
|
|
3858
4594
|
onFullscreenToggle,
|
|
3859
4595
|
forceViewport,
|
|
3860
|
-
onPlayingChange
|
|
4596
|
+
onPlayingChange,
|
|
4597
|
+
theme
|
|
3861
4598
|
}) {
|
|
3862
|
-
const stateRef =
|
|
3863
|
-
const actionsRef =
|
|
3864
|
-
const wasPlayingRef =
|
|
4599
|
+
const stateRef = useRef8(DEFAULT_STATE);
|
|
4600
|
+
const actionsRef = useRef8(null);
|
|
4601
|
+
const wasPlayingRef = useRef8(false);
|
|
3865
4602
|
const [, setTick] = useState8(0);
|
|
3866
|
-
const handleStateChange =
|
|
4603
|
+
const handleStateChange = useCallback7(
|
|
3867
4604
|
(state) => {
|
|
3868
4605
|
stateRef.current = state;
|
|
3869
4606
|
if (onPlayingChange && state.isPlaying !== wasPlayingRef.current) {
|
|
@@ -3873,7 +4610,7 @@ function DocPlayerWithSidebar({
|
|
|
3873
4610
|
},
|
|
3874
4611
|
[onPlayingChange]
|
|
3875
4612
|
);
|
|
3876
|
-
const handleControlsReady =
|
|
4613
|
+
const handleControlsReady = useCallback7(
|
|
3877
4614
|
(controls) => {
|
|
3878
4615
|
const isFirst = !actionsRef.current;
|
|
3879
4616
|
actionsRef.current = controls;
|
|
@@ -3881,22 +4618,23 @@ function DocPlayerWithSidebar({
|
|
|
3881
4618
|
},
|
|
3882
4619
|
[]
|
|
3883
4620
|
);
|
|
3884
|
-
|
|
4621
|
+
useEffect9(() => {
|
|
3885
4622
|
const interval = setInterval(() => {
|
|
3886
4623
|
setTick((t) => t + 1);
|
|
3887
4624
|
}, 250);
|
|
3888
4625
|
return () => clearInterval(interval);
|
|
3889
4626
|
}, []);
|
|
3890
|
-
return /* @__PURE__ */
|
|
3891
|
-
/* @__PURE__ */
|
|
4627
|
+
return /* @__PURE__ */ jsxs16("div", { className: "doc-player-sidebar-layout", children: [
|
|
4628
|
+
/* @__PURE__ */ jsx23("div", { className: "doc-player-sidebar-layout__video", children: /* @__PURE__ */ jsx23(
|
|
3892
4629
|
DocPlayer,
|
|
3893
4630
|
{
|
|
3894
|
-
|
|
4631
|
+
doc,
|
|
4632
|
+
theme,
|
|
3895
4633
|
basePath,
|
|
3896
4634
|
autoPlay,
|
|
3897
4635
|
onEnded,
|
|
3898
4636
|
onTimeUpdate,
|
|
3899
|
-
|
|
4637
|
+
audioController,
|
|
3900
4638
|
muted,
|
|
3901
4639
|
captionsEnabled,
|
|
3902
4640
|
showControls: isFullscreen,
|
|
@@ -3908,42 +4646,21 @@ function DocPlayerWithSidebar({
|
|
|
3908
4646
|
forceViewport
|
|
3909
4647
|
}
|
|
3910
4648
|
) }),
|
|
3911
|
-
actionsRef.current && /* @__PURE__ */
|
|
4649
|
+
actionsRef.current && /* @__PURE__ */ jsx23(DocControlsSidebar, { state: stateRef.current, actions: actionsRef.current })
|
|
3912
4650
|
] });
|
|
3913
4651
|
}
|
|
3914
4652
|
|
|
3915
4653
|
// src/jsonView/useJsonViewTokens.ts
|
|
3916
|
-
import { useMemo as
|
|
3917
|
-
import {
|
|
3918
|
-
applySurface as applySurface3,
|
|
3919
|
-
resolveFontFamily as resolveFontFamily3
|
|
3920
|
-
} from "@bendyline/squisq/schemas";
|
|
3921
|
-
import { DEFAULT_THEME as DEFAULT_THEME3 } from "@bendyline/squisq/doc";
|
|
4654
|
+
import { useMemo as useMemo10 } from "react";
|
|
4655
|
+
import { buildJsonFormTokens, resolveJsonFormTheme } from "@bendyline/squisq/jsonForm";
|
|
3922
4656
|
function useJsonViewTokens(theme, surface) {
|
|
3923
4657
|
const auto = useAutoSurface(surface === "auto");
|
|
3924
4658
|
const effectiveSurface = surface === "auto" ? auto : surface ?? void 0;
|
|
3925
|
-
return
|
|
3926
|
-
const
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
const monoFont = resolveFontFamily3(
|
|
3931
|
-
finalTheme.typography.monoFont,
|
|
3932
|
-
"ui-monospace, Consolas, monospace"
|
|
3933
|
-
);
|
|
3934
|
-
const style = {
|
|
3935
|
-
["--squisq-json-bg"]: finalTheme.colors.background,
|
|
3936
|
-
["--squisq-json-text"]: finalTheme.colors.text,
|
|
3937
|
-
["--squisq-json-muted"]: finalTheme.colors.textMuted,
|
|
3938
|
-
["--squisq-json-primary"]: finalTheme.colors.primary,
|
|
3939
|
-
["--squisq-json-accent"]: finalTheme.colors.secondary,
|
|
3940
|
-
["--squisq-json-border"]: `color-mix(in srgb, ${finalTheme.colors.textMuted} 35%, transparent)`,
|
|
3941
|
-
["--squisq-json-title-font"]: titleFont,
|
|
3942
|
-
["--squisq-json-body-font"]: bodyFont,
|
|
3943
|
-
["--squisq-json-mono-font"]: monoFont,
|
|
3944
|
-
["--squisq-json-radius"]: `${finalTheme.style.borderRadius ?? 8}px`
|
|
3945
|
-
};
|
|
3946
|
-
return { style, theme: finalTheme };
|
|
4659
|
+
return useMemo10(() => {
|
|
4660
|
+
const style = buildJsonFormTokens(theme, effectiveSurface, {
|
|
4661
|
+
prefix: "--squisq-json"
|
|
4662
|
+
});
|
|
4663
|
+
return { style, theme: resolveJsonFormTheme(theme, effectiveSurface) };
|
|
3947
4664
|
}, [theme, effectiveSurface]);
|
|
3948
4665
|
}
|
|
3949
4666
|
|
|
@@ -3955,67 +4672,67 @@ import {
|
|
|
3955
4672
|
} from "@bendyline/squisq/jsonForm";
|
|
3956
4673
|
|
|
3957
4674
|
// src/jsonView/viewers.tsx
|
|
3958
|
-
import { Fragment as
|
|
4675
|
+
import { Fragment as Fragment4, useMemo as useMemo11 } from "react";
|
|
3959
4676
|
import {
|
|
3960
4677
|
arrayItemKind
|
|
3961
4678
|
} from "@bendyline/squisq/jsonForm";
|
|
3962
|
-
import { parseMarkdown } from "@bendyline/squisq/markdown";
|
|
3963
|
-
import { Fragment as
|
|
4679
|
+
import { parseMarkdown as parseMarkdown3 } from "@bendyline/squisq/markdown";
|
|
4680
|
+
import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3964
4681
|
function TextViewer({ value }) {
|
|
3965
4682
|
if (value === void 0 || value === null || value === "") {
|
|
3966
|
-
return /* @__PURE__ */
|
|
4683
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
3967
4684
|
}
|
|
3968
|
-
return /* @__PURE__ */
|
|
4685
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value", children: String(value) });
|
|
3969
4686
|
}
|
|
3970
4687
|
function MultilineViewer({ value }) {
|
|
3971
4688
|
if (value === void 0 || value === null || value === "") {
|
|
3972
|
-
return /* @__PURE__ */
|
|
4689
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
3973
4690
|
}
|
|
3974
|
-
return /* @__PURE__ */
|
|
4691
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value squisq-jv-value--multiline", children: String(value) });
|
|
3975
4692
|
}
|
|
3976
4693
|
function RichTextViewer({ value }) {
|
|
3977
|
-
const nodes =
|
|
4694
|
+
const nodes = useMemo11(() => {
|
|
3978
4695
|
if (typeof value !== "string" || value === "") return null;
|
|
3979
4696
|
try {
|
|
3980
|
-
const doc =
|
|
4697
|
+
const doc = parseMarkdown3(value);
|
|
3981
4698
|
return doc.children;
|
|
3982
4699
|
} catch {
|
|
3983
4700
|
return null;
|
|
3984
4701
|
}
|
|
3985
4702
|
}, [value]);
|
|
3986
|
-
if (!nodes) return /* @__PURE__ */
|
|
3987
|
-
return /* @__PURE__ */
|
|
4703
|
+
if (!nodes) return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4704
|
+
return /* @__PURE__ */ jsx24("div", { className: "squisq-jv-richtext", children: /* @__PURE__ */ jsx24(MarkdownRenderer, { nodes }) });
|
|
3988
4705
|
}
|
|
3989
4706
|
function NumberViewer({ value }) {
|
|
3990
4707
|
if (value === void 0 || value === null) {
|
|
3991
|
-
return /* @__PURE__ */
|
|
4708
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
3992
4709
|
}
|
|
3993
|
-
return /* @__PURE__ */
|
|
4710
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value", children: String(value) });
|
|
3994
4711
|
}
|
|
3995
4712
|
function BooleanViewer({ value }) {
|
|
3996
4713
|
const on = Boolean(value);
|
|
3997
|
-
return /* @__PURE__ */
|
|
4714
|
+
return /* @__PURE__ */ jsx24("span", { className: `squisq-jv-toggle squisq-jv-toggle--${on ? "on" : "off"}`, children: on ? "On" : "Off" });
|
|
3998
4715
|
}
|
|
3999
4716
|
function EnumViewer({ value, schema }) {
|
|
4000
4717
|
if (value === void 0 || value === null || value === "") {
|
|
4001
|
-
return /* @__PURE__ */
|
|
4718
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4002
4719
|
}
|
|
4003
4720
|
const labels = schema.squisq?.enumLabels;
|
|
4004
4721
|
const display = labels && typeof value === "string" ? labels[value] ?? value : String(value);
|
|
4005
|
-
return /* @__PURE__ */
|
|
4722
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value", children: display });
|
|
4006
4723
|
}
|
|
4007
4724
|
function ColorViewer({ value }) {
|
|
4008
4725
|
if (typeof value !== "string" || value === "") {
|
|
4009
|
-
return /* @__PURE__ */
|
|
4726
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4010
4727
|
}
|
|
4011
|
-
return /* @__PURE__ */
|
|
4012
|
-
/* @__PURE__ */
|
|
4013
|
-
/* @__PURE__ */
|
|
4728
|
+
return /* @__PURE__ */ jsxs17("span", { className: "squisq-jv-color", children: [
|
|
4729
|
+
/* @__PURE__ */ jsx24("span", { className: "squisq-jv-color__swatch", style: { background: value }, "aria-hidden": true }),
|
|
4730
|
+
/* @__PURE__ */ jsx24("span", { className: "squisq-jv-color__hex", children: value })
|
|
4014
4731
|
] });
|
|
4015
4732
|
}
|
|
4016
4733
|
function DateViewer({ value, schema }) {
|
|
4017
4734
|
if (typeof value !== "string" || value === "") {
|
|
4018
|
-
return /* @__PURE__ */
|
|
4735
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4019
4736
|
}
|
|
4020
4737
|
const fmt = schema.format;
|
|
4021
4738
|
let display = value;
|
|
@@ -4032,31 +4749,31 @@ function DateViewer({ value, schema }) {
|
|
|
4032
4749
|
}
|
|
4033
4750
|
} catch {
|
|
4034
4751
|
}
|
|
4035
|
-
return /* @__PURE__ */
|
|
4752
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-value", children: display });
|
|
4036
4753
|
}
|
|
4037
4754
|
function ChipBinViewer({ value, schema }) {
|
|
4038
4755
|
if (!Array.isArray(value) || value.length === 0) {
|
|
4039
|
-
return /* @__PURE__ */
|
|
4756
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4040
4757
|
}
|
|
4041
4758
|
const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
|
|
4042
4759
|
const labels = itemSchema?.squisq?.enumLabels;
|
|
4043
|
-
return /* @__PURE__ */
|
|
4760
|
+
return /* @__PURE__ */ jsx24("div", { className: "squisq-jv-chip-bin", children: value.map((item, i) => {
|
|
4044
4761
|
const label = labels && typeof item === "string" ? labels[item] ?? String(item) : String(item);
|
|
4045
|
-
return /* @__PURE__ */
|
|
4762
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-chip", children: label }, i);
|
|
4046
4763
|
}) });
|
|
4047
4764
|
}
|
|
4048
4765
|
function CardStackViewer(props) {
|
|
4049
4766
|
const { value, schema, rootSchema, rootData, pointer, density } = props;
|
|
4050
4767
|
if (!Array.isArray(value) || value.length === 0) {
|
|
4051
|
-
return /* @__PURE__ */
|
|
4768
|
+
return /* @__PURE__ */ jsx24("span", { className: "squisq-jv-empty", children: "No items" });
|
|
4052
4769
|
}
|
|
4053
4770
|
const itemSchema = (Array.isArray(schema.items) ? schema.items[0] : schema.items) ?? {};
|
|
4054
4771
|
const itemLabel = itemSchema.squisq?.itemLabel;
|
|
4055
|
-
return /* @__PURE__ */
|
|
4772
|
+
return /* @__PURE__ */ jsx24("div", { className: "squisq-jv-card-stack", children: value.map((item, i) => {
|
|
4056
4773
|
const title = resolveItemTitle(itemLabel, item, i);
|
|
4057
|
-
return /* @__PURE__ */
|
|
4058
|
-
title ? /* @__PURE__ */
|
|
4059
|
-
/* @__PURE__ */
|
|
4774
|
+
return /* @__PURE__ */ jsxs17("div", { className: "squisq-jv-card", children: [
|
|
4775
|
+
title ? /* @__PURE__ */ jsx24("h4", { className: "squisq-jv-card__title", children: title }) : null,
|
|
4776
|
+
/* @__PURE__ */ jsx24(
|
|
4060
4777
|
RenderNode,
|
|
4061
4778
|
{
|
|
4062
4779
|
value: item,
|
|
@@ -4087,16 +4804,16 @@ function GroupViewer(props) {
|
|
|
4087
4804
|
const help = schema.squisq?.help ?? schema.description;
|
|
4088
4805
|
const obj = (value && typeof value === "object" ? value : {}) ?? {};
|
|
4089
4806
|
const propEntries = Object.entries(schema.properties ?? {});
|
|
4090
|
-
return /* @__PURE__ */
|
|
4091
|
-
title ? /* @__PURE__ */
|
|
4092
|
-
help ? /* @__PURE__ */
|
|
4093
|
-
propEntries.map(([key, propSchema]) => /* @__PURE__ */
|
|
4807
|
+
return /* @__PURE__ */ jsxs17("section", { className: "squisq-jv-group", children: [
|
|
4808
|
+
title ? /* @__PURE__ */ jsx24("h3", { className: "squisq-jv-group__title", children: title }) : null,
|
|
4809
|
+
help ? /* @__PURE__ */ jsx24("p", { className: "squisq-jv-group__help", children: help }) : null,
|
|
4810
|
+
propEntries.map(([key, propSchema]) => /* @__PURE__ */ jsx24(Fragment4, { children: /* @__PURE__ */ jsx24(
|
|
4094
4811
|
RowOrSection,
|
|
4095
4812
|
{
|
|
4096
4813
|
label: propSchema.squisq?.label ?? propSchema.title ?? key,
|
|
4097
4814
|
help: propSchema.squisq?.help ?? propSchema.description,
|
|
4098
4815
|
kindHint: propSchema,
|
|
4099
|
-
children: /* @__PURE__ */
|
|
4816
|
+
children: /* @__PURE__ */ jsx24(
|
|
4100
4817
|
RenderNode,
|
|
4101
4818
|
{
|
|
4102
4819
|
value: obj[key],
|
|
@@ -4120,11 +4837,11 @@ function RowOrSection({
|
|
|
4120
4837
|
}) {
|
|
4121
4838
|
const composite = isCompositeKind(kindHint);
|
|
4122
4839
|
if (composite) {
|
|
4123
|
-
return /* @__PURE__ */
|
|
4840
|
+
return /* @__PURE__ */ jsx24(Fragment5, { children });
|
|
4124
4841
|
}
|
|
4125
|
-
return /* @__PURE__ */
|
|
4126
|
-
/* @__PURE__ */
|
|
4127
|
-
/* @__PURE__ */
|
|
4842
|
+
return /* @__PURE__ */ jsxs17("div", { className: "squisq-jv-row", children: [
|
|
4843
|
+
/* @__PURE__ */ jsx24("div", { className: "squisq-jv-label", title: help, children: label }),
|
|
4844
|
+
/* @__PURE__ */ jsx24("div", { children })
|
|
4128
4845
|
] });
|
|
4129
4846
|
}
|
|
4130
4847
|
function isCompositeKind(schema) {
|
|
@@ -4145,11 +4862,11 @@ function TabsViewer(props) {
|
|
|
4145
4862
|
const matchedIndex = pickMatchingBranch(branches, value);
|
|
4146
4863
|
const branch = branches[matchedIndex];
|
|
4147
4864
|
if (!branch) {
|
|
4148
|
-
return /* @__PURE__ */
|
|
4865
|
+
return /* @__PURE__ */ jsx24(TextViewer, { ...props });
|
|
4149
4866
|
}
|
|
4150
|
-
return /* @__PURE__ */
|
|
4151
|
-
/* @__PURE__ */
|
|
4152
|
-
/* @__PURE__ */
|
|
4867
|
+
return /* @__PURE__ */ jsxs17("div", { className: "squisq-jv-tabs", children: [
|
|
4868
|
+
/* @__PURE__ */ jsx24("span", { className: "squisq-jv-tabs__discriminator", children: branch.squisq?.label ?? branch.title ?? `Option ${matchedIndex + 1}` }),
|
|
4869
|
+
/* @__PURE__ */ jsx24(
|
|
4153
4870
|
RenderNode,
|
|
4154
4871
|
{
|
|
4155
4872
|
value,
|
|
@@ -4213,7 +4930,7 @@ var VIEWERS = {
|
|
|
4213
4930
|
};
|
|
4214
4931
|
|
|
4215
4932
|
// src/jsonView/RenderNode.tsx
|
|
4216
|
-
import { jsx as
|
|
4933
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
4217
4934
|
function RenderNode(props) {
|
|
4218
4935
|
const resolved = resolveRef(props.schema, props.rootSchema) ?? props.schema;
|
|
4219
4936
|
if (resolveFlag(resolved.squisq?.hidden, props.rootData)) return null;
|
|
@@ -4229,18 +4946,18 @@ function RenderNode(props) {
|
|
|
4229
4946
|
};
|
|
4230
4947
|
if (kind === "group" || kind === "card") {
|
|
4231
4948
|
const Group = Viewer;
|
|
4232
|
-
return /* @__PURE__ */
|
|
4949
|
+
return /* @__PURE__ */ jsx25(Group, { ...viewerProps, suppressTitle: props.suppressTopGroupTitle });
|
|
4233
4950
|
}
|
|
4234
|
-
return /* @__PURE__ */
|
|
4951
|
+
return /* @__PURE__ */ jsx25(Viewer, { ...viewerProps });
|
|
4235
4952
|
}
|
|
4236
4953
|
|
|
4237
4954
|
// src/jsonView/JsonView.tsx
|
|
4238
|
-
import { jsx as
|
|
4955
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
4239
4956
|
function JsonView(props) {
|
|
4240
4957
|
const { schema, value, theme, surface, density = "comfortable", className } = props;
|
|
4241
4958
|
const { style } = useJsonViewTokens(theme, surface);
|
|
4242
4959
|
const cls = "squisq-json-view" + (density === "compact" ? " squisq-json-view--compact" : "") + (className ? ` ${className}` : "");
|
|
4243
|
-
return /* @__PURE__ */
|
|
4960
|
+
return /* @__PURE__ */ jsx26("div", { className: cls, style, children: /* @__PURE__ */ jsx26(
|
|
4244
4961
|
RenderNode,
|
|
4245
4962
|
{
|
|
4246
4963
|
value,
|
|
@@ -4269,7 +4986,9 @@ export {
|
|
|
4269
4986
|
LinearDocView,
|
|
4270
4987
|
MapLayer,
|
|
4271
4988
|
MarkdownRenderer,
|
|
4989
|
+
MediaClipLayer,
|
|
4272
4990
|
MediaContext,
|
|
4991
|
+
PathLayer,
|
|
4273
4992
|
ShapeLayer,
|
|
4274
4993
|
SocialCaptionOverlay,
|
|
4275
4994
|
TableLayer,
|
|
@@ -4283,6 +5002,7 @@ export {
|
|
|
4283
5002
|
useAutoSurface,
|
|
4284
5003
|
useDocPlayback,
|
|
4285
5004
|
useMediaProvider,
|
|
5005
|
+
useMediaSchedule,
|
|
4286
5006
|
useMediaUrl,
|
|
4287
5007
|
useViewportOrientation
|
|
4288
5008
|
};
|