@bendyline/squisq-react 1.4.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -3
- package/dist/index.d.ts +174 -27
- package/dist/index.js +1244 -603
- package/dist/index.js.map +1 -1
- package/dist/squisq-player.global.js +54 -37
- package/dist/squisq-player.global.js.map +1 -1
- package/dist/standalone-source.js +1 -1
- package/package.json +2 -2
- package/src/BlockRenderer.tsx +53 -17
- package/src/DocControlsSlideshow.tsx +222 -5
- package/src/DocPlayer.tsx +367 -183
- package/src/DocPlayerWithSidebar.tsx +4 -0
- package/src/DocProgressBar.tsx +40 -1
- package/src/LinearDocView.tsx +135 -62
- package/src/MarkdownRenderer.tsx +40 -97
- package/src/MediaClipLayer.tsx +12 -2
- package/src/__tests__/BlockRenderer.test.tsx +79 -8
- package/src/__tests__/DocControlsSlideshow.test.tsx +94 -1
- package/src/__tests__/DocPlayer.test.tsx +505 -0
- package/src/__tests__/DocProgressBar.test.tsx +28 -2
- package/src/__tests__/LinearDocView.test.tsx +91 -11
- package/src/__tests__/MapLayer.test.tsx +63 -0
- package/src/__tests__/MarkdownRenderer.test.tsx +13 -2
- package/src/__tests__/MediaClipLayer.test.tsx +70 -0
- package/src/__tests__/MediaContext.test.tsx +51 -0
- package/src/__tests__/PathLayer.test.tsx +12 -1
- package/src/__tests__/VideoLayer.test.tsx +94 -0
- package/src/__tests__/fillStyle.test.tsx +3 -2
- package/src/__tests__/standaloneEntry.test.tsx +103 -0
- package/src/__tests__/useAudioSync.test.ts +49 -0
- package/src/__tests__/useDocPlayback.transition.test.ts +48 -5
- package/src/__tests__/useViewportOrientation.test.ts +22 -0
- package/src/hooks/MediaContext.tsx +12 -3
- package/src/hooks/useAudioSync.ts +61 -12
- package/src/hooks/useDocPlayback.ts +40 -12
- package/src/hooks/useViewportOrientation.ts +2 -4
- package/src/index.ts +5 -2
- package/src/layers/MapLayer.tsx +7 -6
- package/src/layers/PathLayer.tsx +20 -11
- package/src/layers/ShapeLayer.tsx +4 -2
- package/src/layers/TextLayer.tsx +4 -3
- package/src/layers/TreeLayer.tsx +167 -0
- package/src/layers/VideoLayer.tsx +20 -6
- package/src/standalone-entry.tsx +91 -14
- package/src/types.ts +13 -13
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/DocPlayer.tsx
|
|
2
|
-
import { Fragment as Fragment3, useRef as
|
|
2
|
+
import { Fragment as Fragment3, useId as useId7, useRef as useRef9, useState as useState9, useEffect as useEffect10, useCallback as useCallback7, useMemo as useMemo9 } from "react";
|
|
3
3
|
import {
|
|
4
|
-
isTemplateBlock as
|
|
4
|
+
isTemplateBlock as isTemplateBlock3,
|
|
5
5
|
getCaptionAtTime as getCaptionAtTime2,
|
|
6
6
|
resolveMediaSchedule,
|
|
7
7
|
getDocPlaybackDuration
|
|
@@ -32,9 +32,15 @@ function useMediaUrl(relativePath, basePath) {
|
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
let cancelled = false;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
setUrl(fallback);
|
|
36
|
+
provider.resolveUrl(safePath).then(
|
|
37
|
+
(resolved) => {
|
|
38
|
+
if (!cancelled) setUrl(resolved);
|
|
39
|
+
},
|
|
40
|
+
() => {
|
|
41
|
+
if (!cancelled) setUrl(fallback);
|
|
42
|
+
}
|
|
43
|
+
);
|
|
38
44
|
return () => {
|
|
39
45
|
cancelled = true;
|
|
40
46
|
};
|
|
@@ -63,7 +69,8 @@ function MediaClipLayer({
|
|
|
63
69
|
currentTime,
|
|
64
70
|
isPlaying,
|
|
65
71
|
basePath,
|
|
66
|
-
renderMode = false
|
|
72
|
+
renderMode = false,
|
|
73
|
+
muted = false
|
|
67
74
|
}) {
|
|
68
75
|
const { renderClips, activeIds } = useMediaSchedule(schedule, currentTime);
|
|
69
76
|
if (renderClips.length === 0) return null;
|
|
@@ -75,7 +82,8 @@ function MediaClipLayer({
|
|
|
75
82
|
currentTime,
|
|
76
83
|
isPlaying,
|
|
77
84
|
basePath,
|
|
78
|
-
renderMode
|
|
85
|
+
renderMode,
|
|
86
|
+
muted
|
|
79
87
|
},
|
|
80
88
|
clip.id
|
|
81
89
|
)) });
|
|
@@ -86,7 +94,8 @@ function MediaClipElement({
|
|
|
86
94
|
currentTime,
|
|
87
95
|
isPlaying,
|
|
88
96
|
basePath,
|
|
89
|
-
renderMode
|
|
97
|
+
renderMode,
|
|
98
|
+
muted
|
|
90
99
|
}) {
|
|
91
100
|
const ref = useRef(null);
|
|
92
101
|
const src = useMediaUrl(clip.src, basePath);
|
|
@@ -111,7 +120,7 @@ function MediaClipElement({
|
|
|
111
120
|
} else {
|
|
112
121
|
el.pause();
|
|
113
122
|
}
|
|
114
|
-
}, [active, currentTime, isPlaying, renderMode, clip.sourceIn, clip.absoluteStart]);
|
|
123
|
+
}, [active, currentTime, isPlaying, renderMode, clip.sourceIn, clip.absoluteStart, src]);
|
|
115
124
|
const isVideo = clip.kind === "video";
|
|
116
125
|
const common = {
|
|
117
126
|
ref,
|
|
@@ -141,13 +150,21 @@ function MediaClipElement({
|
|
|
141
150
|
}
|
|
142
151
|
);
|
|
143
152
|
}
|
|
144
|
-
return /* @__PURE__ */ jsx(
|
|
153
|
+
return /* @__PURE__ */ jsx(
|
|
154
|
+
"audio",
|
|
155
|
+
{
|
|
156
|
+
...common,
|
|
157
|
+
muted: renderMode || muted,
|
|
158
|
+
style: { position: "absolute", width: 0, height: 0 }
|
|
159
|
+
}
|
|
160
|
+
);
|
|
145
161
|
}
|
|
146
162
|
|
|
147
163
|
// src/DocPlayer.tsx
|
|
148
164
|
import { applySurface as applySurface2 } from "@bendyline/squisq/schemas";
|
|
149
165
|
|
|
150
166
|
// src/BlockRenderer.tsx
|
|
167
|
+
import { useId as useId5 } from "react";
|
|
151
168
|
import { resolveTransitionDuration } from "@bendyline/squisq/schemas";
|
|
152
169
|
|
|
153
170
|
// src/layers/ImageLayer.tsx
|
|
@@ -316,7 +333,7 @@ function remapToKenBurns(anim) {
|
|
|
316
333
|
}
|
|
317
334
|
|
|
318
335
|
// src/layers/TextLayer.tsx
|
|
319
|
-
import { useMemo as useMemo3 } from "react";
|
|
336
|
+
import { useId, useMemo as useMemo3 } from "react";
|
|
320
337
|
import { DEFAULT_DOC_FONT } from "@bendyline/squisq/schemas";
|
|
321
338
|
import {
|
|
322
339
|
parseHtmlToNodes,
|
|
@@ -505,6 +522,7 @@ function IconTextLayer({ layer, viewport, blockTime }) {
|
|
|
505
522
|
) });
|
|
506
523
|
}
|
|
507
524
|
function PlainTextLayer({ layer, viewport, blockTime }) {
|
|
525
|
+
const defsId = `${useId().replace(/:/g, "")}-${layer.id}`;
|
|
508
526
|
const { content, position, animation } = layer;
|
|
509
527
|
const { text, style } = content;
|
|
510
528
|
const rawX = resolveValue(position.x, viewport.width);
|
|
@@ -538,13 +556,13 @@ function PlainTextLayer({ layer, viewport, blockTime }) {
|
|
|
538
556
|
fill: style.color,
|
|
539
557
|
...animStyle.style
|
|
540
558
|
};
|
|
541
|
-
const filterId = style.shadow ? `shadow-${
|
|
559
|
+
const filterId = style.shadow ? `shadow-${defsId}` : void 0;
|
|
542
560
|
return /* @__PURE__ */ jsxs2("g", { className: `block-layer block-layer--text ${animStyle.className}`, "data-layer-id": layer.id, children: [
|
|
543
561
|
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
562
|
/* @__PURE__ */ jsx4(
|
|
545
563
|
TextBox,
|
|
546
564
|
{
|
|
547
|
-
layerId:
|
|
565
|
+
layerId: defsId,
|
|
548
566
|
style,
|
|
549
567
|
box: boxWidth != null && boxHeight != null ? {
|
|
550
568
|
x: rawX - anchorAxis(anchor, boxWidth, "x"),
|
|
@@ -746,9 +764,11 @@ function wrapText(text, fontSize, maxWidth) {
|
|
|
746
764
|
}
|
|
747
765
|
|
|
748
766
|
// src/layers/ShapeLayer.tsx
|
|
767
|
+
import { useId as useId2 } from "react";
|
|
749
768
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
750
769
|
function ShapeLayer({ layer, viewport, blockTime }) {
|
|
751
770
|
const { content, position, animation } = layer;
|
|
771
|
+
const defsId = `${useId2().replace(/:/g, "")}-${layer.id}`;
|
|
752
772
|
const rawX = resolveValue(position.x, viewport.width);
|
|
753
773
|
const rawY = resolveValue(position.y, viewport.height);
|
|
754
774
|
const width = position.width ? resolveValue(position.width, viewport.width) : 100;
|
|
@@ -782,12 +802,12 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
782
802
|
);
|
|
783
803
|
}
|
|
784
804
|
const { fill: fillValue, def: fillDef } = resolveFill(
|
|
785
|
-
|
|
805
|
+
defsId,
|
|
786
806
|
fill,
|
|
787
807
|
content.gradient,
|
|
788
808
|
content.pattern
|
|
789
809
|
);
|
|
790
|
-
const { filterAttr, def: filterDef } = resolveShapeFilter(
|
|
810
|
+
const { filterAttr, def: filterDef } = resolveShapeFilter(defsId, content.filter);
|
|
791
811
|
const dash = borderDashArray(content.borderStyle, content.strokeWidth);
|
|
792
812
|
const shapeProps = {
|
|
793
813
|
fill: fillValue,
|
|
@@ -847,6 +867,7 @@ function ShapeLayer({ layer, viewport, blockTime }) {
|
|
|
847
867
|
}
|
|
848
868
|
|
|
849
869
|
// src/layers/PathLayer.tsx
|
|
870
|
+
import { useId as useId3 } from "react";
|
|
850
871
|
import { markerPath, shapePath } from "@bendyline/squisq/doc";
|
|
851
872
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
852
873
|
function effectivePath(layer, viewport) {
|
|
@@ -860,23 +881,28 @@ function effectivePath(layer, viewport) {
|
|
|
860
881
|
const derived = shapePath(content.shapeKind, rawX + anchor.x, rawY + anchor.y, w, h);
|
|
861
882
|
return derived ?? content.d;
|
|
862
883
|
}
|
|
863
|
-
function
|
|
884
|
+
function readLegacyArrow(content) {
|
|
885
|
+
return content.arrow;
|
|
886
|
+
}
|
|
887
|
+
function effectiveMarker(explicit, legacyArrow, end) {
|
|
864
888
|
if (explicit) return explicit;
|
|
865
|
-
const wants =
|
|
889
|
+
const wants = legacyArrow === "both" || legacyArrow === end;
|
|
866
890
|
return wants ? "arrow" : "none";
|
|
867
891
|
}
|
|
868
892
|
function PathLayer({ layer, viewport, blockTime }) {
|
|
869
893
|
const { content, animation, id } = layer;
|
|
894
|
+
const defsId = `${useId3().replace(/:/g, "")}-${id}`;
|
|
870
895
|
const d = effectivePath(layer, viewport);
|
|
871
896
|
const stroke = content.stroke ?? "#1e293b";
|
|
872
897
|
const strokeWidth = content.strokeWidth ?? 2;
|
|
873
|
-
const { fill, def: fillDef } = resolveFill(
|
|
898
|
+
const { fill, def: fillDef } = resolveFill(defsId, content.fill ?? "none", content.gradient);
|
|
874
899
|
const dash = content.borderStyle ? borderDashArray(content.borderStyle, strokeWidth) : content.dasharray;
|
|
875
900
|
const animStyle = getAnimationStyle(animation, blockTime);
|
|
876
|
-
const startId = `marker-start-${
|
|
877
|
-
const endId = `marker-end-${
|
|
878
|
-
const
|
|
879
|
-
const
|
|
901
|
+
const startId = `marker-start-${defsId}`;
|
|
902
|
+
const endId = `marker-end-${defsId}`;
|
|
903
|
+
const legacyArrow = readLegacyArrow(content);
|
|
904
|
+
const start = markerPath(effectiveMarker(content.startMarker, legacyArrow, "start"), "start");
|
|
905
|
+
const end = markerPath(effectiveMarker(content.endMarker, legacyArrow, "end"), "end");
|
|
880
906
|
return /* @__PURE__ */ jsxs4(
|
|
881
907
|
"g",
|
|
882
908
|
{
|
|
@@ -938,7 +964,7 @@ function MarkerDef({
|
|
|
938
964
|
}
|
|
939
965
|
|
|
940
966
|
// src/layers/MapLayer.tsx
|
|
941
|
-
import { useState as useState2, useEffect as useEffect3 } from "react";
|
|
967
|
+
import { useId as useId4, useState as useState2, useEffect as useEffect3 } from "react";
|
|
942
968
|
|
|
943
969
|
// src/utils/mapTileUtils.ts
|
|
944
970
|
var TILE_PROVIDERS = {
|
|
@@ -1124,6 +1150,7 @@ function drawAttribution(ctx, text, width, height) {
|
|
|
1124
1150
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1125
1151
|
function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
1126
1152
|
const { content, position, animation } = layer;
|
|
1153
|
+
const clipId = `map-clip-${useId4().replace(/:/g, "")}-${layer.id}`;
|
|
1127
1154
|
const [mapImageUrl, setMapImageUrl] = useState2(null);
|
|
1128
1155
|
const [isLoading, setIsLoading] = useState2(true);
|
|
1129
1156
|
const [error, setError] = useState2(null);
|
|
@@ -1168,11 +1195,12 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1168
1195
|
cancelled = true;
|
|
1169
1196
|
};
|
|
1170
1197
|
}, [
|
|
1171
|
-
content.center
|
|
1172
|
-
content.center.lng,
|
|
1198
|
+
content.center,
|
|
1173
1199
|
content.zoom,
|
|
1174
1200
|
content.style,
|
|
1175
1201
|
content.staticSrc,
|
|
1202
|
+
content.markers,
|
|
1203
|
+
content.showAttribution,
|
|
1176
1204
|
width,
|
|
1177
1205
|
height,
|
|
1178
1206
|
basePath
|
|
@@ -1237,8 +1265,8 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1237
1265
|
style: animStyle.style,
|
|
1238
1266
|
"data-layer-id": layer.id,
|
|
1239
1267
|
children: [
|
|
1240
|
-
/* @__PURE__ */ jsx7("defs", { children: /* @__PURE__ */ jsx7("clipPath", { id:
|
|
1241
|
-
/* @__PURE__ */ jsx7("g", { clipPath: `url(
|
|
1268
|
+
/* @__PURE__ */ jsx7("defs", { children: /* @__PURE__ */ jsx7("clipPath", { id: clipId, children: /* @__PURE__ */ jsx7("rect", { x: finalX, y: finalY, width, height }) }) }),
|
|
1269
|
+
/* @__PURE__ */ jsx7("g", { clipPath: `url(#${clipId})`, children: /* @__PURE__ */ jsx7(
|
|
1242
1270
|
"image",
|
|
1243
1271
|
{
|
|
1244
1272
|
href: mapImageUrl,
|
|
@@ -1258,6 +1286,7 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1258
1286
|
// src/layers/VideoLayer.tsx
|
|
1259
1287
|
import { useRef as useRef2, useEffect as useEffect4 } from "react";
|
|
1260
1288
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1289
|
+
var VIDEO_SYNC_DRIFT_SECONDS = 0.2;
|
|
1261
1290
|
function VideoLayer({ layer, basePath, viewport, blockTime, isPlaying }) {
|
|
1262
1291
|
const { content, position } = layer;
|
|
1263
1292
|
const videoRef = useRef2(null);
|
|
@@ -1297,16 +1326,22 @@ function VideoLayer({ layer, basePath, viewport, blockTime, isPlaying }) {
|
|
|
1297
1326
|
video.removeEventListener("timeupdate", handleTimeUpdate);
|
|
1298
1327
|
video.pause();
|
|
1299
1328
|
};
|
|
1300
|
-
}, [
|
|
1329
|
+
}, [src, content.clipStart, content.clipEnd]);
|
|
1301
1330
|
useEffect4(() => {
|
|
1302
1331
|
const video = videoRef.current;
|
|
1303
1332
|
if (!video || !hasStartedRef.current) return;
|
|
1333
|
+
const targetTime = gated ? content.clipStart : Math.min(content.clipEnd, content.clipStart + Math.max(0, blockTime - startAt));
|
|
1334
|
+
if (Math.abs(video.currentTime - targetTime) > VIDEO_SYNC_DRIFT_SECONDS) {
|
|
1335
|
+
video.currentTime = targetTime;
|
|
1336
|
+
}
|
|
1304
1337
|
if (gated) {
|
|
1305
1338
|
video.pause();
|
|
1306
|
-
video.currentTime = content.clipStart;
|
|
1307
1339
|
return;
|
|
1308
1340
|
}
|
|
1309
|
-
if (
|
|
1341
|
+
if (targetTime >= content.clipEnd) {
|
|
1342
|
+
video.pause();
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1310
1345
|
if (isPlaying) {
|
|
1311
1346
|
const playPromise = video.play();
|
|
1312
1347
|
if (playPromise) {
|
|
@@ -1316,7 +1351,7 @@ function VideoLayer({ layer, basePath, viewport, blockTime, isPlaying }) {
|
|
|
1316
1351
|
} else {
|
|
1317
1352
|
video.pause();
|
|
1318
1353
|
}
|
|
1319
|
-
}, [isPlaying, gated, content.clipStart, content.clipEnd]);
|
|
1354
|
+
}, [isPlaying, gated, blockTime, startAt, src, content.clipStart, content.clipEnd]);
|
|
1320
1355
|
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(
|
|
1321
1356
|
"video",
|
|
1322
1357
|
{
|
|
@@ -1425,9 +1460,137 @@ function TableLayer({ layer, viewport, blockTime }) {
|
|
|
1425
1460
|
) });
|
|
1426
1461
|
}
|
|
1427
1462
|
|
|
1428
|
-
// src/
|
|
1463
|
+
// src/layers/TreeLayer.tsx
|
|
1464
|
+
import { useState as useState3 } from "react";
|
|
1429
1465
|
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1430
|
-
|
|
1466
|
+
function faClass(token, fallback) {
|
|
1467
|
+
const name = token && token.trim() ? token.trim() : fallback;
|
|
1468
|
+
const colon = name.indexOf(":");
|
|
1469
|
+
if (colon > 0) {
|
|
1470
|
+
const family = name.slice(0, colon).replace(/^fa-/, "");
|
|
1471
|
+
return `fa-${family} fa-${name.slice(colon + 1)}`;
|
|
1472
|
+
}
|
|
1473
|
+
return `fa-solid fa-${name}`;
|
|
1474
|
+
}
|
|
1475
|
+
function TreeLayer({ layer, viewport, blockTime }) {
|
|
1476
|
+
const { content, position, animation } = layer;
|
|
1477
|
+
const { items, style } = content;
|
|
1478
|
+
const x = resolveValue(position.x, viewport.width);
|
|
1479
|
+
const y = resolveValue(position.y, viewport.height);
|
|
1480
|
+
const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;
|
|
1481
|
+
const height = position.height ? resolveValue(position.height, viewport.height) : viewport.height;
|
|
1482
|
+
const offset = getAnchorOffset(position.anchor, width, height);
|
|
1483
|
+
const animStyle = animation ? getAnimationStyle(animation, blockTime) : {};
|
|
1484
|
+
return /* @__PURE__ */ jsx10(
|
|
1485
|
+
"foreignObject",
|
|
1486
|
+
{
|
|
1487
|
+
x: x + offset.x,
|
|
1488
|
+
y: y + offset.y,
|
|
1489
|
+
width,
|
|
1490
|
+
height,
|
|
1491
|
+
style: animStyle,
|
|
1492
|
+
children: /* @__PURE__ */ jsx10(
|
|
1493
|
+
"div",
|
|
1494
|
+
{
|
|
1495
|
+
...{ xmlns: "http://www.w3.org/1999/xhtml" },
|
|
1496
|
+
className: "squisq-treelayer",
|
|
1497
|
+
style: {
|
|
1498
|
+
width: `${width}px`,
|
|
1499
|
+
height: `${height}px`,
|
|
1500
|
+
display: "flex",
|
|
1501
|
+
flexDirection: "column",
|
|
1502
|
+
justifyContent: "center",
|
|
1503
|
+
padding: "24px 32px",
|
|
1504
|
+
boxSizing: "border-box",
|
|
1505
|
+
fontFamily: style.fontFamily ?? "system-ui, sans-serif",
|
|
1506
|
+
fontSize: `${style.fontSize}px`,
|
|
1507
|
+
lineHeight: 1.7,
|
|
1508
|
+
overflow: "hidden"
|
|
1509
|
+
},
|
|
1510
|
+
children: /* @__PURE__ */ jsx10(TreeList, { items, depth: 0, style })
|
|
1511
|
+
}
|
|
1512
|
+
)
|
|
1513
|
+
}
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
function TreeList({
|
|
1517
|
+
items,
|
|
1518
|
+
depth,
|
|
1519
|
+
style
|
|
1520
|
+
}) {
|
|
1521
|
+
return /* @__PURE__ */ jsx10(
|
|
1522
|
+
"ul",
|
|
1523
|
+
{
|
|
1524
|
+
style: {
|
|
1525
|
+
listStyle: "none",
|
|
1526
|
+
margin: 0,
|
|
1527
|
+
padding: 0,
|
|
1528
|
+
paddingLeft: depth === 0 ? 0 : `${style.indentPx}px`,
|
|
1529
|
+
borderLeft: depth === 0 ? "none" : `1px solid ${style.connectorColor}`
|
|
1530
|
+
},
|
|
1531
|
+
children: items.map((item) => /* @__PURE__ */ jsx10(TreeRow, { item, style }, item.id))
|
|
1532
|
+
}
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
1535
|
+
function TreeRow({
|
|
1536
|
+
item,
|
|
1537
|
+
style
|
|
1538
|
+
}) {
|
|
1539
|
+
const hasChildren = item.children.length > 0;
|
|
1540
|
+
const [collapsed, setCollapsed] = useState3(false);
|
|
1541
|
+
const isDir = item.isDir || hasChildren;
|
|
1542
|
+
const iconCls = isDir ? faClass(style.folderIcon, collapsed ? "folder" : "folder-open") : faClass(style.fileIcon, "file");
|
|
1543
|
+
return /* @__PURE__ */ jsxs7("li", { style: { position: "relative" }, children: [
|
|
1544
|
+
/* @__PURE__ */ jsxs7("div", { style: { display: "flex", alignItems: "baseline", gap: "8px", padding: "1px 0" }, children: [
|
|
1545
|
+
hasChildren ? /* @__PURE__ */ jsx10(
|
|
1546
|
+
"button",
|
|
1547
|
+
{
|
|
1548
|
+
type: "button",
|
|
1549
|
+
"aria-label": collapsed ? "Expand" : "Collapse",
|
|
1550
|
+
onClick: () => setCollapsed((c) => !c),
|
|
1551
|
+
style: {
|
|
1552
|
+
flex: "0 0 auto",
|
|
1553
|
+
width: "1em",
|
|
1554
|
+
border: "none",
|
|
1555
|
+
background: "transparent",
|
|
1556
|
+
cursor: "pointer",
|
|
1557
|
+
color: style.connectorColor,
|
|
1558
|
+
padding: 0,
|
|
1559
|
+
fontSize: "0.8em"
|
|
1560
|
+
},
|
|
1561
|
+
children: /* @__PURE__ */ jsx10(
|
|
1562
|
+
"i",
|
|
1563
|
+
{
|
|
1564
|
+
className: `fa-solid ${collapsed ? "fa-chevron-right" : "fa-chevron-down"}`,
|
|
1565
|
+
"aria-hidden": "true"
|
|
1566
|
+
}
|
|
1567
|
+
)
|
|
1568
|
+
}
|
|
1569
|
+
) : /* @__PURE__ */ jsx10("span", { style: { flex: "0 0 auto", width: "1em" } }),
|
|
1570
|
+
/* @__PURE__ */ jsx10(
|
|
1571
|
+
"i",
|
|
1572
|
+
{
|
|
1573
|
+
className: iconCls,
|
|
1574
|
+
"aria-hidden": "true",
|
|
1575
|
+
style: { flex: "0 0 auto", color: style.iconColor, width: "1.2em", textAlign: "center" }
|
|
1576
|
+
}
|
|
1577
|
+
),
|
|
1578
|
+
/* @__PURE__ */ jsx10(
|
|
1579
|
+
"span",
|
|
1580
|
+
{
|
|
1581
|
+
style: { color: isDir ? style.dirColor : style.rowColor, fontWeight: isDir ? 600 : 400 },
|
|
1582
|
+
children: item.label
|
|
1583
|
+
}
|
|
1584
|
+
),
|
|
1585
|
+
item.comment ? /* @__PURE__ */ jsx10("span", { style: { color: style.commentColor, fontSize: "0.85em", fontStyle: "italic" }, children: item.comment }) : null
|
|
1586
|
+
] }),
|
|
1587
|
+
hasChildren && !collapsed ? /* @__PURE__ */ jsx10(TreeList, { items: item.children, depth: 1, style }) : null
|
|
1588
|
+
] });
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
// src/BlockRenderer.tsx
|
|
1592
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1593
|
+
var DEFAULT_VIEWPORT = {
|
|
1431
1594
|
width: 1920,
|
|
1432
1595
|
height: 1080
|
|
1433
1596
|
};
|
|
@@ -1438,12 +1601,13 @@ function BlockRenderer({
|
|
|
1438
1601
|
isEntering = false,
|
|
1439
1602
|
isExiting = false,
|
|
1440
1603
|
transition,
|
|
1441
|
-
viewport =
|
|
1442
|
-
isPlaying
|
|
1604
|
+
viewport = DEFAULT_VIEWPORT,
|
|
1605
|
+
isPlaying,
|
|
1606
|
+
animationsEnabled = true
|
|
1443
1607
|
}) {
|
|
1444
1608
|
let transitionClass = "";
|
|
1445
1609
|
const transitionStyle = {};
|
|
1446
|
-
const activeTransition = transition ?? block.transition;
|
|
1610
|
+
const activeTransition = animationsEnabled ? transition ?? block.transition : void 0;
|
|
1447
1611
|
if (activeTransition && isEntering) {
|
|
1448
1612
|
transitionClass = getTransitionClass(activeTransition.type, true, activeTransition.direction);
|
|
1449
1613
|
transitionStyle["--transition-duration"] = `${resolveTransitionDuration(activeTransition)}s`;
|
|
@@ -1451,8 +1615,9 @@ function BlockRenderer({
|
|
|
1451
1615
|
transitionClass = getTransitionClass(activeTransition.type, false, activeTransition.direction);
|
|
1452
1616
|
transitionStyle["--transition-duration"] = `${resolveTransitionDuration(activeTransition)}s`;
|
|
1453
1617
|
}
|
|
1454
|
-
const
|
|
1455
|
-
|
|
1618
|
+
const instanceId = useId5().replace(/:/g, "");
|
|
1619
|
+
const clipId = `vb-clip-${instanceId}-${block.id}`;
|
|
1620
|
+
return /* @__PURE__ */ jsxs8(
|
|
1456
1621
|
"svg",
|
|
1457
1622
|
{
|
|
1458
1623
|
className: `block-svg ${transitionClass}`,
|
|
@@ -1462,15 +1627,16 @@ function BlockRenderer({
|
|
|
1462
1627
|
overflow: "hidden",
|
|
1463
1628
|
"data-block-id": block.id,
|
|
1464
1629
|
children: [
|
|
1465
|
-
/* @__PURE__ */
|
|
1466
|
-
/* @__PURE__ */
|
|
1630
|
+
/* @__PURE__ */ jsx11("defs", { children: /* @__PURE__ */ jsx11("clipPath", { id: clipId, children: /* @__PURE__ */ jsx11("rect", { x: "0", y: "0", width: viewport.width, height: viewport.height }) }) }),
|
|
1631
|
+
/* @__PURE__ */ jsx11("g", { clipPath: `url(#${clipId})`, children: (block.layers ?? []).map((layer) => /* @__PURE__ */ jsx11(
|
|
1467
1632
|
LayerRenderer,
|
|
1468
1633
|
{
|
|
1469
1634
|
layer,
|
|
1470
1635
|
basePath,
|
|
1471
1636
|
viewport,
|
|
1472
1637
|
blockTime,
|
|
1473
|
-
isPlaying
|
|
1638
|
+
isPlaying,
|
|
1639
|
+
animationsEnabled
|
|
1474
1640
|
},
|
|
1475
1641
|
layer.id
|
|
1476
1642
|
)) })
|
|
@@ -1478,23 +1644,47 @@ function BlockRenderer({
|
|
|
1478
1644
|
}
|
|
1479
1645
|
);
|
|
1480
1646
|
}
|
|
1481
|
-
function LayerRenderer({
|
|
1482
|
-
|
|
1647
|
+
function LayerRenderer({
|
|
1648
|
+
layer,
|
|
1649
|
+
basePath,
|
|
1650
|
+
viewport,
|
|
1651
|
+
blockTime,
|
|
1652
|
+
isPlaying,
|
|
1653
|
+
animationsEnabled
|
|
1654
|
+
}) {
|
|
1655
|
+
const renderedLayer = animationsEnabled || !layer.animation ? layer : { ...layer, animation: void 0 };
|
|
1656
|
+
switch (renderedLayer.type) {
|
|
1483
1657
|
case "image":
|
|
1484
|
-
return /* @__PURE__ */
|
|
1658
|
+
return /* @__PURE__ */ jsx11(
|
|
1659
|
+
ImageLayer,
|
|
1660
|
+
{
|
|
1661
|
+
layer: renderedLayer,
|
|
1662
|
+
basePath,
|
|
1663
|
+
viewport,
|
|
1664
|
+
blockTime
|
|
1665
|
+
}
|
|
1666
|
+
);
|
|
1485
1667
|
case "text":
|
|
1486
|
-
return /* @__PURE__ */
|
|
1668
|
+
return /* @__PURE__ */ jsx11(TextLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1487
1669
|
case "shape":
|
|
1488
|
-
return /* @__PURE__ */
|
|
1670
|
+
return /* @__PURE__ */ jsx11(ShapeLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1489
1671
|
case "path":
|
|
1490
|
-
return /* @__PURE__ */
|
|
1672
|
+
return /* @__PURE__ */ jsx11(PathLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1491
1673
|
case "map":
|
|
1492
|
-
return /* @__PURE__ */
|
|
1674
|
+
return /* @__PURE__ */ jsx11(
|
|
1675
|
+
MapLayer,
|
|
1676
|
+
{
|
|
1677
|
+
layer: renderedLayer,
|
|
1678
|
+
basePath,
|
|
1679
|
+
viewport,
|
|
1680
|
+
blockTime
|
|
1681
|
+
}
|
|
1682
|
+
);
|
|
1493
1683
|
case "video":
|
|
1494
|
-
return /* @__PURE__ */
|
|
1684
|
+
return /* @__PURE__ */ jsx11(
|
|
1495
1685
|
VideoLayer,
|
|
1496
1686
|
{
|
|
1497
|
-
layer,
|
|
1687
|
+
layer: renderedLayer,
|
|
1498
1688
|
basePath,
|
|
1499
1689
|
viewport,
|
|
1500
1690
|
blockTime,
|
|
@@ -1502,9 +1692,11 @@ function LayerRenderer({ layer, basePath, viewport, blockTime, isPlaying }) {
|
|
|
1502
1692
|
}
|
|
1503
1693
|
);
|
|
1504
1694
|
case "table":
|
|
1505
|
-
return /* @__PURE__ */
|
|
1695
|
+
return /* @__PURE__ */ jsx11(TableLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1696
|
+
case "tree":
|
|
1697
|
+
return /* @__PURE__ */ jsx11(TreeLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1506
1698
|
default:
|
|
1507
|
-
console.warn(`Unknown layer type: ${
|
|
1699
|
+
console.warn(`Unknown layer type: ${renderedLayer.type}`);
|
|
1508
1700
|
return null;
|
|
1509
1701
|
}
|
|
1510
1702
|
}
|
|
@@ -1515,7 +1707,7 @@ import { getCaptionAtTime } from "@bendyline/squisq/schemas";
|
|
|
1515
1707
|
// src/SocialCaptionOverlay.tsx
|
|
1516
1708
|
import { useMemo as useMemo4 } from "react";
|
|
1517
1709
|
import { resolveFontFamily } from "@bendyline/squisq/schemas";
|
|
1518
|
-
import { jsx as
|
|
1710
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1519
1711
|
var TARGET_CHUNK_SIZE = 4;
|
|
1520
1712
|
var MIN_CHUNK_SIZE = 2;
|
|
1521
1713
|
var MAX_CHUNK_SIZE = 6;
|
|
@@ -1574,7 +1766,7 @@ function SocialCaptionOverlay({
|
|
|
1574
1766
|
[captions]
|
|
1575
1767
|
);
|
|
1576
1768
|
if (!enabled || chunks.length === 0) {
|
|
1577
|
-
return /* @__PURE__ */
|
|
1769
|
+
return /* @__PURE__ */ jsx12(
|
|
1578
1770
|
"div",
|
|
1579
1771
|
{
|
|
1580
1772
|
className: "social-caption-overlay",
|
|
@@ -1636,7 +1828,7 @@ function SocialCaptionOverlay({
|
|
|
1636
1828
|
const viewportHeight = viewport?.height ?? 720;
|
|
1637
1829
|
const baseFontSize = Math.round(viewportHeight * 0.055);
|
|
1638
1830
|
const fontSize = Math.max(24, Math.min(72, baseFontSize));
|
|
1639
|
-
return /* @__PURE__ */
|
|
1831
|
+
return /* @__PURE__ */ jsx12(
|
|
1640
1832
|
"div",
|
|
1641
1833
|
{
|
|
1642
1834
|
className: "social-caption-overlay",
|
|
@@ -1653,7 +1845,7 @@ function SocialCaptionOverlay({
|
|
|
1653
1845
|
opacity: 1,
|
|
1654
1846
|
transition: "opacity 0.15s ease-in-out"
|
|
1655
1847
|
},
|
|
1656
|
-
children: /* @__PURE__ */
|
|
1848
|
+
children: /* @__PURE__ */ jsx12(
|
|
1657
1849
|
"div",
|
|
1658
1850
|
{
|
|
1659
1851
|
style: {
|
|
@@ -1662,7 +1854,7 @@ function SocialCaptionOverlay({
|
|
|
1662
1854
|
},
|
|
1663
1855
|
children: activeChunk.words.map((word, i) => {
|
|
1664
1856
|
const isActive = i === activeWordIndex;
|
|
1665
|
-
return /* @__PURE__ */
|
|
1857
|
+
return /* @__PURE__ */ jsx12(
|
|
1666
1858
|
"span",
|
|
1667
1859
|
{
|
|
1668
1860
|
style: {
|
|
@@ -1687,7 +1879,7 @@ function SocialCaptionOverlay({
|
|
|
1687
1879
|
}
|
|
1688
1880
|
|
|
1689
1881
|
// src/CaptionOverlay.tsx
|
|
1690
|
-
import { jsx as
|
|
1882
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1691
1883
|
function CaptionOverlay({
|
|
1692
1884
|
captions,
|
|
1693
1885
|
currentTime,
|
|
@@ -1698,7 +1890,7 @@ function CaptionOverlay({
|
|
|
1698
1890
|
viewport
|
|
1699
1891
|
}) {
|
|
1700
1892
|
if (captionStyle === "social") {
|
|
1701
|
-
return /* @__PURE__ */
|
|
1893
|
+
return /* @__PURE__ */ jsx13(
|
|
1702
1894
|
SocialCaptionOverlay,
|
|
1703
1895
|
{
|
|
1704
1896
|
captions,
|
|
@@ -1711,7 +1903,7 @@ function CaptionOverlay({
|
|
|
1711
1903
|
}
|
|
1712
1904
|
const phrase = enabled && captions ? getCaptionAtTime(captions, currentTime) : null;
|
|
1713
1905
|
const captionText = phrase?.text ?? null;
|
|
1714
|
-
return /* @__PURE__ */
|
|
1906
|
+
return /* @__PURE__ */ jsx13(
|
|
1715
1907
|
"div",
|
|
1716
1908
|
{
|
|
1717
1909
|
className: "caption-overlay",
|
|
@@ -1730,7 +1922,7 @@ function CaptionOverlay({
|
|
|
1730
1922
|
padding: "0 4px",
|
|
1731
1923
|
boxSizing: "border-box"
|
|
1732
1924
|
},
|
|
1733
|
-
children: captionText && /* @__PURE__ */
|
|
1925
|
+
children: captionText && /* @__PURE__ */ jsx13(
|
|
1734
1926
|
"div",
|
|
1735
1927
|
{
|
|
1736
1928
|
style: {
|
|
@@ -1740,7 +1932,7 @@ function CaptionOverlay({
|
|
|
1740
1932
|
borderRadius: "4px",
|
|
1741
1933
|
backdropFilter: "blur(4px)"
|
|
1742
1934
|
},
|
|
1743
|
-
children: /* @__PURE__ */
|
|
1935
|
+
children: /* @__PURE__ */ jsx13(
|
|
1744
1936
|
"span",
|
|
1745
1937
|
{
|
|
1746
1938
|
style: {
|
|
@@ -1786,22 +1978,40 @@ function useAutoSurface(enabled) {
|
|
|
1786
1978
|
}
|
|
1787
1979
|
|
|
1788
1980
|
// src/hooks/useAudioSync.ts
|
|
1789
|
-
import { useState as
|
|
1790
|
-
function
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
const [
|
|
1981
|
+
import { useState as useState4, useEffect as useEffect5, useRef as useRef3, useCallback as useCallback2 } from "react";
|
|
1982
|
+
function resolveAudioUrl(src, basePath) {
|
|
1983
|
+
if (!src || /^(?:[a-z][a-z0-9+.-]*:|\/\/|\/)/i.test(src)) return src;
|
|
1984
|
+
if (!basePath) return src;
|
|
1985
|
+
return `${basePath.replace(/\/$/, "")}/${src.replace(/^\//, "")}`;
|
|
1986
|
+
}
|
|
1987
|
+
function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
1988
|
+
const [currentTime, setCurrentTime] = useState4(0);
|
|
1989
|
+
const [isPlaying, setIsPlaying] = useState4(false);
|
|
1990
|
+
const [currentSegment, setCurrentSegment] = useState4(0);
|
|
1991
|
+
const [isEnded, setIsEnded] = useState4(false);
|
|
1992
|
+
const [isAudioReady, setIsAudioReady] = useState4(false);
|
|
1993
|
+
const [totalDuration, setTotalDuration] = useState4(0);
|
|
1797
1994
|
const segmentStarts = useRef3([]);
|
|
1798
1995
|
const pendingSeekTime = useRef3(null);
|
|
1799
1996
|
const shouldPlayAfterLoad = useRef3(false);
|
|
1800
1997
|
const blobUrls = useRef3(/* @__PURE__ */ new Map());
|
|
1801
1998
|
const loadingPromises = useRef3(/* @__PURE__ */ new Map());
|
|
1999
|
+
const abortControllers = useRef3(/* @__PURE__ */ new Set());
|
|
2000
|
+
const loadGeneration = useRef3(0);
|
|
1802
2001
|
const fallbackMode = useRef3(false);
|
|
1803
2002
|
useEffect5(() => {
|
|
1804
|
-
|
|
2003
|
+
loadGeneration.current += 1;
|
|
2004
|
+
pendingSeekTime.current = null;
|
|
2005
|
+
shouldPlayAfterLoad.current = false;
|
|
2006
|
+
fallbackMode.current = false;
|
|
2007
|
+
setCurrentTime(0);
|
|
2008
|
+
setCurrentSegment(0);
|
|
2009
|
+
setIsPlaying(false);
|
|
2010
|
+
setIsEnded(false);
|
|
2011
|
+
setIsAudioReady(false);
|
|
2012
|
+
if (!enabled || !audioTrack?.segments) {
|
|
2013
|
+
segmentStarts.current = [];
|
|
2014
|
+
setTotalDuration(0);
|
|
1805
2015
|
return;
|
|
1806
2016
|
}
|
|
1807
2017
|
let time = 0;
|
|
@@ -1811,27 +2021,35 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1811
2021
|
return start;
|
|
1812
2022
|
});
|
|
1813
2023
|
setTotalDuration(time);
|
|
1814
|
-
}, [audioTrack]);
|
|
2024
|
+
}, [audioTrack, enabled]);
|
|
1815
2025
|
const preloadAudio = useCallback2(
|
|
1816
2026
|
async (src) => {
|
|
1817
|
-
const audioUrl =
|
|
2027
|
+
const audioUrl = resolveAudioUrl(src, basePath);
|
|
1818
2028
|
if (blobUrls.current.has(src)) {
|
|
1819
2029
|
return blobUrls.current.get(src);
|
|
1820
2030
|
}
|
|
1821
2031
|
if (loadingPromises.current.has(src)) {
|
|
1822
2032
|
return loadingPromises.current.get(src);
|
|
1823
2033
|
}
|
|
2034
|
+
const controller = new AbortController();
|
|
2035
|
+
abortControllers.current.add(controller);
|
|
2036
|
+
const generation = loadGeneration.current;
|
|
1824
2037
|
const loadPromise = (async () => {
|
|
1825
2038
|
try {
|
|
1826
|
-
const response = await fetch(audioUrl);
|
|
2039
|
+
const response = await fetch(audioUrl, { signal: controller.signal });
|
|
1827
2040
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1828
2041
|
const blob = await response.blob();
|
|
1829
2042
|
const blobUrl = URL.createObjectURL(blob);
|
|
2043
|
+
if (controller.signal.aborted || generation !== loadGeneration.current) {
|
|
2044
|
+
URL.revokeObjectURL(blobUrl);
|
|
2045
|
+
return audioUrl;
|
|
2046
|
+
}
|
|
1830
2047
|
blobUrls.current.set(src, blobUrl);
|
|
1831
2048
|
return blobUrl;
|
|
1832
2049
|
} catch {
|
|
1833
2050
|
return audioUrl;
|
|
1834
2051
|
} finally {
|
|
2052
|
+
abortControllers.current.delete(controller);
|
|
1835
2053
|
loadingPromises.current.delete(src);
|
|
1836
2054
|
}
|
|
1837
2055
|
})();
|
|
@@ -1841,19 +2059,26 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1841
2059
|
[basePath]
|
|
1842
2060
|
);
|
|
1843
2061
|
useEffect5(() => {
|
|
1844
|
-
if (!audioTrack?.segments) return;
|
|
2062
|
+
if (!enabled || !audioTrack?.segments) return;
|
|
1845
2063
|
audioTrack.segments.forEach((segment) => {
|
|
1846
2064
|
preloadAudio(segment.src);
|
|
1847
2065
|
});
|
|
1848
2066
|
const currentBlobUrls = blobUrls.current;
|
|
2067
|
+
const currentAbortControllers = abortControllers.current;
|
|
2068
|
+
const currentLoadingPromises = loadingPromises.current;
|
|
1849
2069
|
return () => {
|
|
2070
|
+
loadGeneration.current += 1;
|
|
2071
|
+
currentAbortControllers.forEach((controller) => controller.abort());
|
|
2072
|
+
currentAbortControllers.clear();
|
|
2073
|
+
currentLoadingPromises.clear();
|
|
1850
2074
|
currentBlobUrls.forEach((url) => {
|
|
1851
2075
|
URL.revokeObjectURL(url);
|
|
1852
2076
|
});
|
|
1853
2077
|
currentBlobUrls.clear();
|
|
1854
2078
|
};
|
|
1855
|
-
}, [audioTrack, preloadAudio]);
|
|
2079
|
+
}, [audioTrack, preloadAudio, enabled]);
|
|
1856
2080
|
useEffect5(() => {
|
|
2081
|
+
if (!enabled) return;
|
|
1857
2082
|
const audio = audioRef.current;
|
|
1858
2083
|
if (!audio) return;
|
|
1859
2084
|
const handleTimeUpdate = () => {
|
|
@@ -1890,8 +2115,9 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1890
2115
|
audio.removeEventListener("ended", handleEnded);
|
|
1891
2116
|
audio.removeEventListener("error", handleError);
|
|
1892
2117
|
};
|
|
1893
|
-
}, [audioRef, currentSegment, audioTrack]);
|
|
2118
|
+
}, [audioRef, currentSegment, audioTrack, enabled]);
|
|
1894
2119
|
useEffect5(() => {
|
|
2120
|
+
if (!enabled) return;
|
|
1895
2121
|
const audio = audioRef.current;
|
|
1896
2122
|
if (!audio || !audioTrack?.segments) return;
|
|
1897
2123
|
const segment = audioTrack.segments[currentSegment];
|
|
@@ -1913,29 +2139,37 @@ function useAudioSync(audioRef, audioTrack, basePath = "") {
|
|
|
1913
2139
|
const currentSrc = audio.src;
|
|
1914
2140
|
const cachedBlobUrl = blobUrls.current.get(segment.src);
|
|
1915
2141
|
const isSameSource = currentSrc && (currentSrc === cachedBlobUrl || currentSrc.endsWith(segment.src));
|
|
2142
|
+
let cancelled = false;
|
|
2143
|
+
let handleCanPlay = null;
|
|
1916
2144
|
if (!isSameSource) {
|
|
1917
2145
|
const loadAndPlay = async () => {
|
|
1918
2146
|
const blobUrl = await preloadAudio(segment.src);
|
|
1919
|
-
|
|
2147
|
+
if (cancelled) return;
|
|
2148
|
+
handleCanPlay = () => {
|
|
2149
|
+
if (cancelled) return;
|
|
1920
2150
|
setIsAudioReady(true);
|
|
1921
2151
|
applyPendingSeek();
|
|
1922
|
-
audio.removeEventListener("canplay", handleCanPlay);
|
|
2152
|
+
if (handleCanPlay) audio.removeEventListener("canplay", handleCanPlay);
|
|
1923
2153
|
};
|
|
1924
2154
|
audio.addEventListener("canplay", handleCanPlay);
|
|
1925
2155
|
audio.src = blobUrl;
|
|
1926
2156
|
audio.load();
|
|
1927
2157
|
await Promise.resolve();
|
|
1928
2158
|
if (audio.readyState >= 3) {
|
|
1929
|
-
audio.removeEventListener("canplay", handleCanPlay);
|
|
2159
|
+
if (handleCanPlay) audio.removeEventListener("canplay", handleCanPlay);
|
|
1930
2160
|
setIsAudioReady(true);
|
|
1931
2161
|
applyPendingSeek();
|
|
1932
2162
|
}
|
|
1933
2163
|
};
|
|
1934
|
-
loadAndPlay();
|
|
2164
|
+
void loadAndPlay();
|
|
1935
2165
|
} else {
|
|
1936
2166
|
applyPendingSeek();
|
|
1937
2167
|
}
|
|
1938
|
-
|
|
2168
|
+
return () => {
|
|
2169
|
+
cancelled = true;
|
|
2170
|
+
if (handleCanPlay) audio.removeEventListener("canplay", handleCanPlay);
|
|
2171
|
+
};
|
|
2172
|
+
}, [audioRef, currentSegment, audioTrack, preloadAudio, enabled]);
|
|
1939
2173
|
const play = useCallback2(() => {
|
|
1940
2174
|
const audio = audioRef.current;
|
|
1941
2175
|
if (audio) {
|
|
@@ -2072,8 +2306,8 @@ import {
|
|
|
2072
2306
|
resolvePersistentLayers,
|
|
2073
2307
|
VIEWPORT_PRESETS
|
|
2074
2308
|
} from "@bendyline/squisq/doc";
|
|
2075
|
-
function useDocPlayback(script, currentTime,
|
|
2076
|
-
|
|
2309
|
+
function useDocPlayback(script, currentTime, options = {}) {
|
|
2310
|
+
const { viewport = VIEWPORT_PRESETS.landscape, theme, onSeek } = options;
|
|
2077
2311
|
const blocks = useMemo6(() => {
|
|
2078
2312
|
if (!script?.blocks) {
|
|
2079
2313
|
return [];
|
|
@@ -2136,8 +2370,19 @@ function useDocPlayback(script, currentTime, viewport = VIEWPORT_PRESETS.landsca
|
|
|
2136
2370
|
const outgoingBlockRef = useRef4(null);
|
|
2137
2371
|
const activeBlockIdRef = useRef4(null);
|
|
2138
2372
|
const lastRenderedBlockRef = useRef4(null);
|
|
2373
|
+
const suppressOutgoingTargetRef = useRef4(null);
|
|
2374
|
+
const suppressOutgoingForNextBlock = useCallback3((blockId) => {
|
|
2375
|
+
if (activeBlockIdRef.current === blockId) {
|
|
2376
|
+
outgoingBlockRef.current = null;
|
|
2377
|
+
suppressOutgoingTargetRef.current = null;
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
suppressOutgoingTargetRef.current = blockId;
|
|
2381
|
+
}, []);
|
|
2139
2382
|
if (currentBlock && currentBlock.id !== activeBlockIdRef.current) {
|
|
2140
|
-
|
|
2383
|
+
const suppressOutgoing = suppressOutgoingTargetRef.current === currentBlock.id;
|
|
2384
|
+
outgoingBlockRef.current = suppressOutgoing ? null : lastRenderedBlockRef.current;
|
|
2385
|
+
suppressOutgoingTargetRef.current = null;
|
|
2141
2386
|
activeBlockIdRef.current = currentBlock.id;
|
|
2142
2387
|
}
|
|
2143
2388
|
lastRenderedBlockRef.current = currentBlock;
|
|
@@ -2151,10 +2396,10 @@ function useDocPlayback(script, currentTime, viewport = VIEWPORT_PRESETS.landsca
|
|
|
2151
2396
|
if (!script || index < 0 || index >= blocks.length) return;
|
|
2152
2397
|
const targetBlock = blocks[index];
|
|
2153
2398
|
if (targetBlock) {
|
|
2154
|
-
|
|
2399
|
+
onSeek?.(targetBlock.startTime);
|
|
2155
2400
|
}
|
|
2156
2401
|
},
|
|
2157
|
-
[script, blocks]
|
|
2402
|
+
[script, blocks, onSeek]
|
|
2158
2403
|
);
|
|
2159
2404
|
const nextBlock = useCallback3(() => {
|
|
2160
2405
|
if (currentBlockIndex < blocks.length - 1) {
|
|
@@ -2178,13 +2423,14 @@ function useDocPlayback(script, currentTime, viewport = VIEWPORT_PRESETS.landsca
|
|
|
2178
2423
|
nextBlock,
|
|
2179
2424
|
prevBlock,
|
|
2180
2425
|
goToBlock,
|
|
2426
|
+
suppressOutgoingForNextBlock,
|
|
2181
2427
|
/** Expanded blocks (templates converted to full blocks with layers) */
|
|
2182
2428
|
blocks
|
|
2183
2429
|
};
|
|
2184
2430
|
}
|
|
2185
2431
|
|
|
2186
2432
|
// src/hooks/useViewportOrientation.ts
|
|
2187
|
-
import { useState as
|
|
2433
|
+
import { useState as useState5, useEffect as useEffect6, useMemo as useMemo7 } from "react";
|
|
2188
2434
|
import {
|
|
2189
2435
|
VIEWPORT_PRESETS as VIEWPORT_PRESETS2
|
|
2190
2436
|
} from "@bendyline/squisq/doc";
|
|
@@ -2195,7 +2441,7 @@ function getOrientationFromWindow(width, height) {
|
|
|
2195
2441
|
} else if (ratio < 0.83) {
|
|
2196
2442
|
return "portrait";
|
|
2197
2443
|
} else {
|
|
2198
|
-
return "
|
|
2444
|
+
return "square";
|
|
2199
2445
|
}
|
|
2200
2446
|
}
|
|
2201
2447
|
function getViewportForOrientation(orientation) {
|
|
@@ -2210,7 +2456,7 @@ function getViewportForOrientation(orientation) {
|
|
|
2210
2456
|
}
|
|
2211
2457
|
}
|
|
2212
2458
|
function useViewportOrientation() {
|
|
2213
|
-
const [windowSize, setWindowSize] =
|
|
2459
|
+
const [windowSize, setWindowSize] = useState5(() => ({
|
|
2214
2460
|
width: typeof window !== "undefined" ? window.innerWidth : 1920,
|
|
2215
2461
|
height: typeof window !== "undefined" ? window.innerHeight : 1080
|
|
2216
2462
|
}));
|
|
@@ -2246,7 +2492,7 @@ function useViewportOrientation() {
|
|
|
2246
2492
|
}
|
|
2247
2493
|
|
|
2248
2494
|
// src/hooks/useSlideSwipe.ts
|
|
2249
|
-
import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5, useState as
|
|
2495
|
+
import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5, useState as useState6 } from "react";
|
|
2250
2496
|
var DISTANCE_RATIO = 0.3;
|
|
2251
2497
|
var FLICK_VELOCITY = 0.5;
|
|
2252
2498
|
var MIN_FLICK_DISTANCE = 12;
|
|
@@ -2270,8 +2516,8 @@ function decideSwipe({
|
|
|
2270
2516
|
}
|
|
2271
2517
|
function useSlideSwipe(opts) {
|
|
2272
2518
|
const settleMs = opts.settleMs ?? DEFAULT_SETTLE_MS;
|
|
2273
|
-
const [offsetPx, setOffsetPx] =
|
|
2274
|
-
const [phase, setPhase] =
|
|
2519
|
+
const [offsetPx, setOffsetPx] = useState6(0);
|
|
2520
|
+
const [phase, setPhase] = useState6("idle");
|
|
2275
2521
|
const optsRef = useRef5(opts);
|
|
2276
2522
|
optsRef.current = opts;
|
|
2277
2523
|
const dragRef = useRef5(null);
|
|
@@ -2402,7 +2648,7 @@ import {
|
|
|
2402
2648
|
import { parseMarkdown as parseMarkdown2 } from "@bendyline/squisq/markdown";
|
|
2403
2649
|
|
|
2404
2650
|
// src/DocProgressBar.tsx
|
|
2405
|
-
import { useRef as useRef6, useState as
|
|
2651
|
+
import { useRef as useRef6, useState as useState7, useCallback as useCallback5 } from "react";
|
|
2406
2652
|
|
|
2407
2653
|
// src/types.ts
|
|
2408
2654
|
function formatTime(seconds) {
|
|
@@ -2412,7 +2658,7 @@ function formatTime(seconds) {
|
|
|
2412
2658
|
}
|
|
2413
2659
|
|
|
2414
2660
|
// src/DocProgressBar.tsx
|
|
2415
|
-
import { jsx as
|
|
2661
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2416
2662
|
function DocProgressBar({
|
|
2417
2663
|
state,
|
|
2418
2664
|
actions,
|
|
@@ -2421,7 +2667,7 @@ function DocProgressBar({
|
|
|
2421
2667
|
getBlockTitle
|
|
2422
2668
|
}) {
|
|
2423
2669
|
const progressBarRef = useRef6(null);
|
|
2424
|
-
const [hoverPosition, setHoverPosition] =
|
|
2670
|
+
const [hoverPosition, setHoverPosition] = useState7(null);
|
|
2425
2671
|
const playProgress = state.totalDuration > 0 ? Math.max(0, Math.min(1, state.currentTime / state.totalDuration)) : 0;
|
|
2426
2672
|
const handleProgressHover = useCallback5((e) => {
|
|
2427
2673
|
const bar = progressBarRef.current;
|
|
@@ -2434,6 +2680,31 @@ function DocProgressBar({
|
|
|
2434
2680
|
const handleProgressLeave = useCallback5(() => {
|
|
2435
2681
|
setHoverPosition(null);
|
|
2436
2682
|
}, []);
|
|
2683
|
+
const handleProgressKeyDown = useCallback5(
|
|
2684
|
+
(e) => {
|
|
2685
|
+
let next = null;
|
|
2686
|
+
switch (e.key) {
|
|
2687
|
+
case "ArrowLeft":
|
|
2688
|
+
case "ArrowDown":
|
|
2689
|
+
next = state.currentTime - 5;
|
|
2690
|
+
break;
|
|
2691
|
+
case "ArrowRight":
|
|
2692
|
+
case "ArrowUp":
|
|
2693
|
+
next = state.currentTime + 5;
|
|
2694
|
+
break;
|
|
2695
|
+
case "Home":
|
|
2696
|
+
next = 0;
|
|
2697
|
+
break;
|
|
2698
|
+
case "End":
|
|
2699
|
+
next = state.totalDuration;
|
|
2700
|
+
break;
|
|
2701
|
+
}
|
|
2702
|
+
if (next == null) return;
|
|
2703
|
+
e.preventDefault();
|
|
2704
|
+
actions.seekTo(Math.max(0, Math.min(state.totalDuration, next)));
|
|
2705
|
+
},
|
|
2706
|
+
[actions, state.currentTime, state.totalDuration]
|
|
2707
|
+
);
|
|
2437
2708
|
const getBlockAtTimeLocal = useCallback5(
|
|
2438
2709
|
(time) => {
|
|
2439
2710
|
for (let i = expandedBlocks.length - 1; i >= 0; i--) {
|
|
@@ -2446,10 +2717,12 @@ function DocProgressBar({
|
|
|
2446
2717
|
},
|
|
2447
2718
|
[expandedBlocks]
|
|
2448
2719
|
);
|
|
2449
|
-
return /* @__PURE__ */
|
|
2720
|
+
return /* @__PURE__ */ jsxs9(
|
|
2450
2721
|
"div",
|
|
2451
2722
|
{
|
|
2452
2723
|
ref: progressBarRef,
|
|
2724
|
+
role: "group",
|
|
2725
|
+
"aria-label": "Playback timeline",
|
|
2453
2726
|
style: {
|
|
2454
2727
|
flex: 1,
|
|
2455
2728
|
height: "24px",
|
|
@@ -2467,9 +2740,17 @@ function DocProgressBar({
|
|
|
2467
2740
|
onMouseMove: handleProgressHover,
|
|
2468
2741
|
onMouseLeave: handleProgressLeave,
|
|
2469
2742
|
children: [
|
|
2470
|
-
/* @__PURE__ */
|
|
2743
|
+
/* @__PURE__ */ jsx14(
|
|
2471
2744
|
"div",
|
|
2472
2745
|
{
|
|
2746
|
+
role: "slider",
|
|
2747
|
+
tabIndex: 0,
|
|
2748
|
+
"aria-label": "Playback position",
|
|
2749
|
+
"aria-valuemin": 0,
|
|
2750
|
+
"aria-valuemax": state.totalDuration,
|
|
2751
|
+
"aria-valuenow": Math.max(0, Math.min(state.totalDuration, state.currentTime)),
|
|
2752
|
+
"aria-valuetext": `${formatTime(state.currentTime)} of ${formatTime(state.totalDuration)}`,
|
|
2753
|
+
onKeyDown: handleProgressKeyDown,
|
|
2473
2754
|
style: {
|
|
2474
2755
|
position: "absolute",
|
|
2475
2756
|
left: 0,
|
|
@@ -2480,7 +2761,7 @@ function DocProgressBar({
|
|
|
2480
2761
|
}
|
|
2481
2762
|
}
|
|
2482
2763
|
),
|
|
2483
|
-
/* @__PURE__ */
|
|
2764
|
+
/* @__PURE__ */ jsx14(
|
|
2484
2765
|
"div",
|
|
2485
2766
|
{
|
|
2486
2767
|
"data-testid": "doc-progress-fill",
|
|
@@ -2494,9 +2775,10 @@ function DocProgressBar({
|
|
|
2494
2775
|
}
|
|
2495
2776
|
}
|
|
2496
2777
|
),
|
|
2497
|
-
blockMarkers.map((marker, i) => /* @__PURE__ */
|
|
2498
|
-
"
|
|
2778
|
+
blockMarkers.map((marker, i) => /* @__PURE__ */ jsx14(
|
|
2779
|
+
"button",
|
|
2499
2780
|
{
|
|
2781
|
+
type: "button",
|
|
2500
2782
|
style: {
|
|
2501
2783
|
position: "absolute",
|
|
2502
2784
|
left: `${marker.position}%`,
|
|
@@ -2506,11 +2788,13 @@ function DocProgressBar({
|
|
|
2506
2788
|
borderRadius: "50%",
|
|
2507
2789
|
background: marker.index === state.currentBlockIndex ? "#ffffff" : "rgba(255,255,255,0.5)",
|
|
2508
2790
|
border: "2px solid #5b9bd5",
|
|
2791
|
+
padding: 0,
|
|
2509
2792
|
cursor: "pointer",
|
|
2510
2793
|
zIndex: 2,
|
|
2511
2794
|
transition: "transform 0.15s, background 0.15s"
|
|
2512
2795
|
},
|
|
2513
2796
|
title: marker.title,
|
|
2797
|
+
"aria-label": `Seek to ${marker.title}`,
|
|
2514
2798
|
onClick: (e) => {
|
|
2515
2799
|
e.stopPropagation();
|
|
2516
2800
|
actions.seekTo(marker.block.startTime);
|
|
@@ -2524,7 +2808,7 @@ function DocProgressBar({
|
|
|
2524
2808
|
},
|
|
2525
2809
|
`${marker.block.id}-${i}`
|
|
2526
2810
|
)),
|
|
2527
|
-
hoverPosition !== null && /* @__PURE__ */
|
|
2811
|
+
hoverPosition !== null && /* @__PURE__ */ jsxs9(
|
|
2528
2812
|
"div",
|
|
2529
2813
|
{
|
|
2530
2814
|
style: {
|
|
@@ -2541,19 +2825,19 @@ function DocProgressBar({
|
|
|
2541
2825
|
zIndex: 10
|
|
2542
2826
|
},
|
|
2543
2827
|
children: [
|
|
2544
|
-
/* @__PURE__ */
|
|
2828
|
+
/* @__PURE__ */ jsx14("div", { style: { color: "white", fontSize: "12px", fontFamily: "monospace" }, children: formatTime(hoverPosition * state.totalDuration) }),
|
|
2545
2829
|
(() => {
|
|
2546
2830
|
const hoverTime = hoverPosition * state.totalDuration;
|
|
2547
2831
|
const slideInfo = getBlockAtTimeLocal(hoverTime);
|
|
2548
2832
|
if (slideInfo && getBlockTitle) {
|
|
2549
|
-
return /* @__PURE__ */
|
|
2833
|
+
return /* @__PURE__ */ jsx14("div", { style: { color: "rgba(255,255,255,0.7)", fontSize: "11px", marginTop: "2px" }, children: getBlockTitle(slideInfo.block) });
|
|
2550
2834
|
}
|
|
2551
2835
|
return null;
|
|
2552
2836
|
})()
|
|
2553
2837
|
]
|
|
2554
2838
|
}
|
|
2555
2839
|
),
|
|
2556
|
-
hoverPosition !== null && /* @__PURE__ */
|
|
2840
|
+
hoverPosition !== null && /* @__PURE__ */ jsx14(
|
|
2557
2841
|
"div",
|
|
2558
2842
|
{
|
|
2559
2843
|
style: {
|
|
@@ -2575,7 +2859,7 @@ function DocProgressBar({
|
|
|
2575
2859
|
}
|
|
2576
2860
|
|
|
2577
2861
|
// src/DocControlsOverlay.tsx
|
|
2578
|
-
import { jsx as
|
|
2862
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2579
2863
|
function DocControlsOverlay({
|
|
2580
2864
|
state,
|
|
2581
2865
|
actions,
|
|
@@ -2583,7 +2867,7 @@ function DocControlsOverlay({
|
|
|
2583
2867
|
expandedBlocks,
|
|
2584
2868
|
getBlockTitle
|
|
2585
2869
|
}) {
|
|
2586
|
-
return /* @__PURE__ */
|
|
2870
|
+
return /* @__PURE__ */ jsxs10(
|
|
2587
2871
|
"div",
|
|
2588
2872
|
{
|
|
2589
2873
|
className: "doc-player__controls",
|
|
@@ -2600,7 +2884,7 @@ function DocControlsOverlay({
|
|
|
2600
2884
|
zIndex: 100
|
|
2601
2885
|
},
|
|
2602
2886
|
children: [
|
|
2603
|
-
/* @__PURE__ */
|
|
2887
|
+
/* @__PURE__ */ jsx15(
|
|
2604
2888
|
"button",
|
|
2605
2889
|
{
|
|
2606
2890
|
onClick: actions.restart,
|
|
@@ -2616,10 +2900,10 @@ function DocControlsOverlay({
|
|
|
2616
2900
|
},
|
|
2617
2901
|
title: "Restart",
|
|
2618
2902
|
"aria-label": "Restart from beginning",
|
|
2619
|
-
children: /* @__PURE__ */
|
|
2903
|
+
children: /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx15("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" }) })
|
|
2620
2904
|
}
|
|
2621
2905
|
),
|
|
2622
|
-
/* @__PURE__ */
|
|
2906
|
+
/* @__PURE__ */ jsx15(
|
|
2623
2907
|
"button",
|
|
2624
2908
|
{
|
|
2625
2909
|
onClick: actions.toggle,
|
|
@@ -2638,15 +2922,15 @@ function DocControlsOverlay({
|
|
|
2638
2922
|
height: "40px"
|
|
2639
2923
|
},
|
|
2640
2924
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
2641
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
2925
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx15("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx15("path", { d: "M8 5v14l11-7z" }) })
|
|
2642
2926
|
}
|
|
2643
2927
|
),
|
|
2644
|
-
/* @__PURE__ */
|
|
2928
|
+
/* @__PURE__ */ jsxs10("span", { style: { color: "white", fontSize: "12px", minWidth: "80px", fontFamily: "monospace" }, children: [
|
|
2645
2929
|
formatTime(state.currentTime),
|
|
2646
2930
|
" / ",
|
|
2647
2931
|
formatTime(state.totalDuration)
|
|
2648
2932
|
] }),
|
|
2649
|
-
/* @__PURE__ */
|
|
2933
|
+
/* @__PURE__ */ jsx15(
|
|
2650
2934
|
DocProgressBar,
|
|
2651
2935
|
{
|
|
2652
2936
|
state,
|
|
@@ -2656,12 +2940,12 @@ function DocControlsOverlay({
|
|
|
2656
2940
|
getBlockTitle
|
|
2657
2941
|
}
|
|
2658
2942
|
),
|
|
2659
|
-
/* @__PURE__ */
|
|
2943
|
+
/* @__PURE__ */ jsxs10("span", { style: { color: "rgba(255,255,255,0.5)", fontSize: "11px", whiteSpace: "nowrap" }, children: [
|
|
2660
2944
|
state.currentBlockIndex + 1,
|
|
2661
2945
|
"/",
|
|
2662
2946
|
state.totalBlocks
|
|
2663
2947
|
] }),
|
|
2664
|
-
state.hasCaptions && /* @__PURE__ */
|
|
2948
|
+
state.hasCaptions && /* @__PURE__ */ jsxs10(
|
|
2665
2949
|
"button",
|
|
2666
2950
|
{
|
|
2667
2951
|
onClick: () => actions.cycleCaptionMode(),
|
|
@@ -2680,12 +2964,12 @@ function DocControlsOverlay({
|
|
|
2680
2964
|
title: state.captionMode === "off" ? "Captions: Off (click for Standard)" : state.captionMode === "standard" ? "Captions: Standard (click for Social)" : "Captions: Social (click to turn off)",
|
|
2681
2965
|
"aria-label": "Cycle caption style",
|
|
2682
2966
|
children: [
|
|
2683
|
-
/* @__PURE__ */
|
|
2684
|
-
state.captionMode !== "off" && /* @__PURE__ */
|
|
2967
|
+
/* @__PURE__ */ jsx15("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx15("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" }) }),
|
|
2968
|
+
state.captionMode !== "off" && /* @__PURE__ */ jsx15("span", { style: { fontSize: "10px", textTransform: "uppercase", letterSpacing: "0.5px" }, children: state.captionMode === "standard" ? "CC" : "SM" })
|
|
2685
2969
|
]
|
|
2686
2970
|
}
|
|
2687
2971
|
),
|
|
2688
|
-
actions.toggleFullscreen && /* @__PURE__ */
|
|
2972
|
+
actions.toggleFullscreen && /* @__PURE__ */ jsx15(
|
|
2689
2973
|
"button",
|
|
2690
2974
|
{
|
|
2691
2975
|
onClick: actions.toggleFullscreen,
|
|
@@ -2701,7 +2985,7 @@ function DocControlsOverlay({
|
|
|
2701
2985
|
},
|
|
2702
2986
|
title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
|
|
2703
2987
|
"aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
2704
|
-
children: state.isFullscreen ? /* @__PURE__ */
|
|
2988
|
+
children: state.isFullscreen ? /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx15("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx15("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
|
|
2705
2989
|
}
|
|
2706
2990
|
)
|
|
2707
2991
|
]
|
|
@@ -2710,8 +2994,29 @@ function DocControlsOverlay({
|
|
|
2710
2994
|
}
|
|
2711
2995
|
|
|
2712
2996
|
// src/DocControlsSlideshow.tsx
|
|
2713
|
-
import {
|
|
2714
|
-
|
|
2997
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useId as useId6, useLayoutEffect, useRef as useRef7, useState as useState8 } from "react";
|
|
2998
|
+
import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2999
|
+
function DocControlsSlideshow({
|
|
3000
|
+
state,
|
|
3001
|
+
slideNav,
|
|
3002
|
+
slides = [],
|
|
3003
|
+
pickerOpen,
|
|
3004
|
+
onPickerOpenChange
|
|
3005
|
+
}) {
|
|
3006
|
+
const [uncontrolledPickerOpen, setUncontrolledPickerOpen] = useState8(false);
|
|
3007
|
+
const isPickerOpen = pickerOpen ?? uncontrolledPickerOpen;
|
|
3008
|
+
const setPickerOpen = useCallback6(
|
|
3009
|
+
(open) => {
|
|
3010
|
+
if (pickerOpen === void 0) setUncontrolledPickerOpen(open);
|
|
3011
|
+
onPickerOpenChange?.(open);
|
|
3012
|
+
},
|
|
3013
|
+
[pickerOpen, onPickerOpenChange]
|
|
3014
|
+
);
|
|
3015
|
+
const [pickerMaxHeight, setPickerMaxHeight] = useState8(280);
|
|
3016
|
+
const controlsRef = useRef7(null);
|
|
3017
|
+
const triggerRef = useRef7(null);
|
|
3018
|
+
const menuRef = useRef7(null);
|
|
3019
|
+
const menuId = useId6();
|
|
2715
3020
|
const {
|
|
2716
3021
|
currentBlockIndex,
|
|
2717
3022
|
currentSlideLabel,
|
|
@@ -2722,9 +3027,68 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2722
3027
|
const isFirst = currentBlockIndex <= 0;
|
|
2723
3028
|
const isLast = currentBlockIndex >= totalBlocks - 1;
|
|
2724
3029
|
const counterText = totalBlocks > 0 ? currentSlideLabel ?? `${currentSlideNumber ?? currentBlockIndex + 1} / ${totalSlideNumber ?? totalBlocks}` : "\u2014";
|
|
2725
|
-
|
|
3030
|
+
useEffect8(() => {
|
|
3031
|
+
if (!isPickerOpen) return;
|
|
3032
|
+
const handlePointerDown = (event) => {
|
|
3033
|
+
if (!controlsRef.current?.contains(event.target)) setPickerOpen(false);
|
|
3034
|
+
};
|
|
3035
|
+
const handleKeyDown = (event) => {
|
|
3036
|
+
if (event.key !== "Escape") return;
|
|
3037
|
+
event.preventDefault();
|
|
3038
|
+
setPickerOpen(false);
|
|
3039
|
+
triggerRef.current?.focus();
|
|
3040
|
+
};
|
|
3041
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
3042
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
3043
|
+
return () => {
|
|
3044
|
+
document.removeEventListener("pointerdown", handlePointerDown);
|
|
3045
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
3046
|
+
};
|
|
3047
|
+
}, [isPickerOpen, setPickerOpen]);
|
|
3048
|
+
useLayoutEffect(() => {
|
|
3049
|
+
if (!isPickerOpen) return;
|
|
3050
|
+
const controls = controlsRef.current;
|
|
3051
|
+
const player = controls?.closest(".doc-player") ?? controls?.parentElement;
|
|
3052
|
+
if (!controls || !player) return;
|
|
3053
|
+
const updateMaxHeight = () => {
|
|
3054
|
+
const controlsBounds = controls.getBoundingClientRect();
|
|
3055
|
+
const playerBounds = player.getBoundingClientRect();
|
|
3056
|
+
const availableHeight = Math.floor(controlsBounds.top - playerBounds.top - 8 - 16);
|
|
3057
|
+
setPickerMaxHeight(Math.max(72, availableHeight));
|
|
3058
|
+
};
|
|
3059
|
+
updateMaxHeight();
|
|
3060
|
+
const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(updateMaxHeight);
|
|
3061
|
+
resizeObserver?.observe(player);
|
|
3062
|
+
resizeObserver?.observe(controls);
|
|
3063
|
+
window.addEventListener("resize", updateMaxHeight);
|
|
3064
|
+
return () => {
|
|
3065
|
+
resizeObserver?.disconnect();
|
|
3066
|
+
window.removeEventListener("resize", updateMaxHeight);
|
|
3067
|
+
};
|
|
3068
|
+
}, [isPickerOpen]);
|
|
3069
|
+
useEffect8(() => {
|
|
3070
|
+
if (!isPickerOpen) return;
|
|
3071
|
+
menuRef.current?.querySelector('[aria-current="true"]')?.focus();
|
|
3072
|
+
}, [isPickerOpen]);
|
|
3073
|
+
const handleMenuKeyDown = (event) => {
|
|
3074
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
3075
|
+
const items = Array.from(
|
|
3076
|
+
menuRef.current?.querySelectorAll('[role="menuitem"]') ?? []
|
|
3077
|
+
);
|
|
3078
|
+
if (items.length === 0) return;
|
|
3079
|
+
event.preventDefault();
|
|
3080
|
+
const focusedIndex = items.indexOf(document.activeElement);
|
|
3081
|
+
let nextIndex = focusedIndex;
|
|
3082
|
+
if (event.key === "Home") nextIndex = 0;
|
|
3083
|
+
else if (event.key === "End") nextIndex = items.length - 1;
|
|
3084
|
+
else if (event.key === "ArrowDown") nextIndex = (focusedIndex + 1) % items.length;
|
|
3085
|
+
else nextIndex = (focusedIndex - 1 + items.length) % items.length;
|
|
3086
|
+
items[nextIndex]?.focus();
|
|
3087
|
+
};
|
|
3088
|
+
return /* @__PURE__ */ jsxs11(
|
|
2726
3089
|
"div",
|
|
2727
3090
|
{
|
|
3091
|
+
ref: controlsRef,
|
|
2728
3092
|
className: "doc-controls-slideshow",
|
|
2729
3093
|
"data-testid": "slideshow-controls",
|
|
2730
3094
|
style: {
|
|
@@ -2743,7 +3107,7 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2743
3107
|
WebkitBackdropFilter: "blur(8px)"
|
|
2744
3108
|
},
|
|
2745
3109
|
children: [
|
|
2746
|
-
/* @__PURE__ */
|
|
3110
|
+
/* @__PURE__ */ jsx16(
|
|
2747
3111
|
"button",
|
|
2748
3112
|
{
|
|
2749
3113
|
onClick: (e) => {
|
|
@@ -2772,27 +3136,138 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2772
3136
|
onMouseLeave: (e) => {
|
|
2773
3137
|
e.currentTarget.style.background = "none";
|
|
2774
3138
|
},
|
|
2775
|
-
children: /* @__PURE__ */
|
|
3139
|
+
children: /* @__PURE__ */ jsx16("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx16("path", { d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" }) })
|
|
2776
3140
|
}
|
|
2777
3141
|
),
|
|
2778
|
-
/* @__PURE__ */
|
|
2779
|
-
"
|
|
3142
|
+
/* @__PURE__ */ jsx16(
|
|
3143
|
+
"button",
|
|
2780
3144
|
{
|
|
3145
|
+
ref: triggerRef,
|
|
3146
|
+
type: "button",
|
|
2781
3147
|
"data-testid": "slide-counter",
|
|
3148
|
+
"aria-label": `Choose slide, current ${counterText}`,
|
|
3149
|
+
"aria-haspopup": "menu",
|
|
3150
|
+
"aria-expanded": isPickerOpen,
|
|
3151
|
+
"aria-controls": isPickerOpen ? menuId : void 0,
|
|
3152
|
+
title: "Choose slide",
|
|
3153
|
+
disabled: slides.length === 0,
|
|
3154
|
+
onClick: (event) => {
|
|
3155
|
+
event.stopPropagation();
|
|
3156
|
+
setPickerOpen(!isPickerOpen);
|
|
3157
|
+
},
|
|
2782
3158
|
style: {
|
|
3159
|
+
background: isPickerOpen ? "rgba(255,255,255,0.12)" : "none",
|
|
3160
|
+
border: "none",
|
|
2783
3161
|
color: "rgba(255,255,255,0.9)",
|
|
3162
|
+
cursor: slides.length > 0 ? "pointer" : "default",
|
|
2784
3163
|
fontSize: "13px",
|
|
2785
3164
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
2786
3165
|
fontVariantNumeric: "tabular-nums",
|
|
2787
3166
|
minWidth: "48px",
|
|
2788
3167
|
textAlign: "center",
|
|
2789
|
-
padding: "
|
|
2790
|
-
letterSpacing: "0.02em"
|
|
3168
|
+
padding: "6px 4px",
|
|
3169
|
+
letterSpacing: "0.02em",
|
|
3170
|
+
borderRadius: "4px",
|
|
3171
|
+
transition: "background 0.15s"
|
|
2791
3172
|
},
|
|
2792
3173
|
children: counterText
|
|
2793
3174
|
}
|
|
2794
3175
|
),
|
|
2795
|
-
/* @__PURE__ */
|
|
3176
|
+
isPickerOpen && /* @__PURE__ */ jsx16(
|
|
3177
|
+
"div",
|
|
3178
|
+
{
|
|
3179
|
+
ref: menuRef,
|
|
3180
|
+
id: menuId,
|
|
3181
|
+
role: "menu",
|
|
3182
|
+
"aria-label": "Choose a slide",
|
|
3183
|
+
"data-testid": "slide-picker",
|
|
3184
|
+
onClick: (event) => event.stopPropagation(),
|
|
3185
|
+
onKeyDown: handleMenuKeyDown,
|
|
3186
|
+
style: {
|
|
3187
|
+
position: "absolute",
|
|
3188
|
+
right: 0,
|
|
3189
|
+
bottom: "calc(100% + 8px)",
|
|
3190
|
+
width: "min(280px, calc(100vw - 32px))",
|
|
3191
|
+
maxHeight: `${pickerMaxHeight}px`,
|
|
3192
|
+
overflowY: "auto",
|
|
3193
|
+
padding: "6px",
|
|
3194
|
+
background: "rgba(20, 20, 20, 0.94)",
|
|
3195
|
+
border: "1px solid rgba(255,255,255,0.14)",
|
|
3196
|
+
borderRadius: "8px",
|
|
3197
|
+
boxShadow: "0 10px 30px rgba(0,0,0,0.38)",
|
|
3198
|
+
backdropFilter: "blur(12px)",
|
|
3199
|
+
WebkitBackdropFilter: "blur(12px)"
|
|
3200
|
+
},
|
|
3201
|
+
children: slides.map((slide, index) => {
|
|
3202
|
+
const isCurrent = index === currentBlockIndex;
|
|
3203
|
+
return /* @__PURE__ */ jsxs11(
|
|
3204
|
+
"button",
|
|
3205
|
+
{
|
|
3206
|
+
type: "button",
|
|
3207
|
+
role: "menuitem",
|
|
3208
|
+
"aria-current": isCurrent ? "true" : void 0,
|
|
3209
|
+
"data-testid": `slide-picker-item-${index}`,
|
|
3210
|
+
onClick: () => {
|
|
3211
|
+
slideNav.goToSlide(index);
|
|
3212
|
+
setPickerOpen(false);
|
|
3213
|
+
},
|
|
3214
|
+
style: {
|
|
3215
|
+
display: "grid",
|
|
3216
|
+
gridTemplateColumns: "42px minmax(0, 1fr)",
|
|
3217
|
+
alignItems: "center",
|
|
3218
|
+
gap: "8px",
|
|
3219
|
+
width: "100%",
|
|
3220
|
+
padding: "8px 10px",
|
|
3221
|
+
background: isCurrent ? "rgba(255,255,255,0.14)" : "transparent",
|
|
3222
|
+
border: "none",
|
|
3223
|
+
borderRadius: "5px",
|
|
3224
|
+
color: "rgba(255,255,255,0.94)",
|
|
3225
|
+
cursor: "pointer",
|
|
3226
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
3227
|
+
textAlign: "left"
|
|
3228
|
+
},
|
|
3229
|
+
onMouseEnter: (event) => {
|
|
3230
|
+
event.currentTarget.style.background = "rgba(255,255,255,0.1)";
|
|
3231
|
+
},
|
|
3232
|
+
onMouseLeave: (event) => {
|
|
3233
|
+
event.currentTarget.style.background = isCurrent ? "rgba(255,255,255,0.14)" : "transparent";
|
|
3234
|
+
},
|
|
3235
|
+
children: [
|
|
3236
|
+
/* @__PURE__ */ jsx16(
|
|
3237
|
+
"span",
|
|
3238
|
+
{
|
|
3239
|
+
style: {
|
|
3240
|
+
color: isCurrent ? "#fff" : "rgba(255,255,255,0.58)",
|
|
3241
|
+
fontSize: "12px",
|
|
3242
|
+
fontVariantNumeric: "tabular-nums",
|
|
3243
|
+
textAlign: "right"
|
|
3244
|
+
},
|
|
3245
|
+
children: slide.label
|
|
3246
|
+
}
|
|
3247
|
+
),
|
|
3248
|
+
/* @__PURE__ */ jsx16(
|
|
3249
|
+
"span",
|
|
3250
|
+
{
|
|
3251
|
+
style: {
|
|
3252
|
+
minWidth: 0,
|
|
3253
|
+
overflow: "hidden",
|
|
3254
|
+
color: isCurrent ? "#fff" : "rgba(255,255,255,0.82)",
|
|
3255
|
+
fontSize: "13px",
|
|
3256
|
+
lineHeight: 1.3,
|
|
3257
|
+
textOverflow: "ellipsis",
|
|
3258
|
+
whiteSpace: "nowrap"
|
|
3259
|
+
},
|
|
3260
|
+
children: slide.summary
|
|
3261
|
+
}
|
|
3262
|
+
)
|
|
3263
|
+
]
|
|
3264
|
+
},
|
|
3265
|
+
`${slide.id}-${index}`
|
|
3266
|
+
);
|
|
3267
|
+
})
|
|
3268
|
+
}
|
|
3269
|
+
),
|
|
3270
|
+
/* @__PURE__ */ jsx16(
|
|
2796
3271
|
"button",
|
|
2797
3272
|
{
|
|
2798
3273
|
onClick: (e) => {
|
|
@@ -2821,7 +3296,7 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2821
3296
|
onMouseLeave: (e) => {
|
|
2822
3297
|
e.currentTarget.style.background = "none";
|
|
2823
3298
|
},
|
|
2824
|
-
children: /* @__PURE__ */
|
|
3299
|
+
children: /* @__PURE__ */ jsx16("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx16("path", { d: "M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z" }) })
|
|
2825
3300
|
}
|
|
2826
3301
|
)
|
|
2827
3302
|
]
|
|
@@ -2830,18 +3305,18 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2830
3305
|
}
|
|
2831
3306
|
|
|
2832
3307
|
// src/LinearDocView.tsx
|
|
2833
|
-
import { useMemo as useMemo8 } from "react";
|
|
3308
|
+
import { useEffect as useEffect9, useMemo as useMemo8, useRef as useRef8 } from "react";
|
|
2834
3309
|
import {
|
|
2835
3310
|
applySurface,
|
|
2836
3311
|
resolveFontFamily as resolveFontFamily2
|
|
2837
3312
|
} from "@bendyline/squisq/schemas";
|
|
2838
3313
|
import { VIEWPORT_PRESETS as VIEWPORT_PRESETS3 } from "@bendyline/squisq/schemas";
|
|
2839
3314
|
import {
|
|
2840
|
-
|
|
2841
|
-
hasTemplate,
|
|
3315
|
+
materializeBlockLayers,
|
|
2842
3316
|
markdownToDoc,
|
|
2843
3317
|
DEFAULT_THEME as DEFAULT_THEME2,
|
|
2844
|
-
deriveTemplateInputs
|
|
3318
|
+
deriveTemplateInputs,
|
|
3319
|
+
isTemplateBlock as isTemplateBlock2
|
|
2845
3320
|
} from "@bendyline/squisq/doc";
|
|
2846
3321
|
import { extractPlainText, parseMarkdown } from "@bendyline/squisq/markdown";
|
|
2847
3322
|
|
|
@@ -2853,7 +3328,7 @@ import {
|
|
|
2853
3328
|
} from "@bendyline/squisq/markdown";
|
|
2854
3329
|
|
|
2855
3330
|
// src/InlineVideoPlayer.tsx
|
|
2856
|
-
import { jsx as
|
|
3331
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2857
3332
|
function InlineVideoPlayer({
|
|
2858
3333
|
src,
|
|
2859
3334
|
basePath = "",
|
|
@@ -2868,7 +3343,7 @@ function InlineVideoPlayer({
|
|
|
2868
3343
|
const resolvedPoster = useMediaUrl(poster ?? "", basePath);
|
|
2869
3344
|
const posterUrl = poster ? resolvedPoster : void 0;
|
|
2870
3345
|
if (!resolvedSrc) return null;
|
|
2871
|
-
return /* @__PURE__ */
|
|
3346
|
+
return /* @__PURE__ */ jsx17("span", { className: `squisq-inline-video-player ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx17(
|
|
2872
3347
|
"video",
|
|
2873
3348
|
{
|
|
2874
3349
|
src: resolvedSrc,
|
|
@@ -2883,7 +3358,7 @@ function InlineVideoPlayer({
|
|
|
2883
3358
|
}
|
|
2884
3359
|
|
|
2885
3360
|
// src/InlineAudioPlayer.tsx
|
|
2886
|
-
import { jsx as
|
|
3361
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
2887
3362
|
function InlineAudioPlayer({
|
|
2888
3363
|
src,
|
|
2889
3364
|
basePath = "",
|
|
@@ -2893,11 +3368,11 @@ function InlineAudioPlayer({
|
|
|
2893
3368
|
}) {
|
|
2894
3369
|
const resolvedSrc = useMediaUrl(src, basePath);
|
|
2895
3370
|
if (!resolvedSrc) return null;
|
|
2896
|
-
return /* @__PURE__ */
|
|
3371
|
+
return /* @__PURE__ */ jsx18("span", { className: `squisq-inline-audio-player ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx18("audio", { src: resolvedSrc, controls, preload }) });
|
|
2897
3372
|
}
|
|
2898
3373
|
|
|
2899
3374
|
// src/MarkdownRenderer.tsx
|
|
2900
|
-
import { jsx as
|
|
3375
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2901
3376
|
var DEFAULT_CTX = { htmlPolicy: "sanitize" };
|
|
2902
3377
|
function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
2903
3378
|
return nodes.map((node, i) => {
|
|
@@ -2905,28 +3380,28 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
2905
3380
|
switch (node.type) {
|
|
2906
3381
|
case "text": {
|
|
2907
3382
|
if (!node.value.includes("\n")) {
|
|
2908
|
-
return /* @__PURE__ */
|
|
3383
|
+
return /* @__PURE__ */ jsx19(Fragment2, { children: node.value }, key);
|
|
2909
3384
|
}
|
|
2910
3385
|
const parts = node.value.split("\n");
|
|
2911
|
-
return /* @__PURE__ */
|
|
2912
|
-
j > 0 && /* @__PURE__ */
|
|
3386
|
+
return /* @__PURE__ */ jsx19(Fragment2, { children: parts.map((part, j) => /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
3387
|
+
j > 0 && /* @__PURE__ */ jsx19("br", {}),
|
|
2913
3388
|
part
|
|
2914
3389
|
] }, j)) }, key);
|
|
2915
3390
|
}
|
|
2916
3391
|
case "emphasis":
|
|
2917
|
-
return /* @__PURE__ */
|
|
3392
|
+
return /* @__PURE__ */ jsx19("em", { className: "squisq-md-em", children: renderInline(node.children, key, ctx) }, key);
|
|
2918
3393
|
case "strong":
|
|
2919
|
-
return /* @__PURE__ */
|
|
3394
|
+
return /* @__PURE__ */ jsx19("strong", { className: "squisq-md-strong", children: renderInline(node.children, key, ctx) }, key);
|
|
2920
3395
|
case "delete":
|
|
2921
|
-
return /* @__PURE__ */
|
|
3396
|
+
return /* @__PURE__ */ jsx19("del", { className: "squisq-md-del", children: renderInline(node.children, key, ctx) }, key);
|
|
2922
3397
|
case "inlineCode":
|
|
2923
|
-
return /* @__PURE__ */
|
|
3398
|
+
return /* @__PURE__ */ jsx19("code", { className: "squisq-md-inline-code", children: node.value }, key);
|
|
2924
3399
|
case "link": {
|
|
2925
3400
|
const href = sanitizeUrl(node.url, "link", { extraLinkSchemes: ctx.linkSchemes });
|
|
2926
3401
|
if (!href) {
|
|
2927
|
-
return /* @__PURE__ */
|
|
3402
|
+
return /* @__PURE__ */ jsx19("span", { className: "squisq-md-link squisq-md-link--blocked", children: renderInline(node.children, key, ctx) }, key);
|
|
2928
3403
|
}
|
|
2929
|
-
return /* @__PURE__ */
|
|
3404
|
+
return /* @__PURE__ */ jsx19(
|
|
2930
3405
|
"a",
|
|
2931
3406
|
{
|
|
2932
3407
|
className: "squisq-md-link",
|
|
@@ -2940,42 +3415,32 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
2940
3415
|
);
|
|
2941
3416
|
}
|
|
2942
3417
|
case "image":
|
|
2943
|
-
return /* @__PURE__ */
|
|
3418
|
+
return /* @__PURE__ */ jsx19(MdImage, { src: node.url, alt: node.alt ?? "", title: node.title ?? void 0 }, key);
|
|
2944
3419
|
case "break":
|
|
2945
|
-
return /* @__PURE__ */
|
|
3420
|
+
return /* @__PURE__ */ jsx19("br", {}, key);
|
|
2946
3421
|
case "inlineMath":
|
|
2947
|
-
return /* @__PURE__ */
|
|
3422
|
+
return /* @__PURE__ */ jsx19("code", { className: "squisq-md-inline-math", children: node.value }, key);
|
|
2948
3423
|
case "htmlInline":
|
|
2949
3424
|
if (ctx.htmlPolicy === "strip") return null;
|
|
2950
|
-
|
|
2951
|
-
return /* @__PURE__ */ jsx18(
|
|
2952
|
-
"span",
|
|
2953
|
-
{
|
|
2954
|
-
className: "squisq-md-html-inline",
|
|
2955
|
-
dangerouslySetInnerHTML: { __html: node.rawHtml }
|
|
2956
|
-
},
|
|
2957
|
-
key
|
|
2958
|
-
);
|
|
2959
|
-
}
|
|
2960
|
-
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-html-inline", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`) }, key);
|
|
3425
|
+
return /* @__PURE__ */ jsx19("span", { className: "squisq-md-html-inline", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`, ctx) }, key);
|
|
2961
3426
|
case "footnoteReference":
|
|
2962
|
-
return /* @__PURE__ */
|
|
3427
|
+
return /* @__PURE__ */ jsx19("sup", { className: "squisq-md-footnote-ref", children: /* @__PURE__ */ jsxs12("a", { href: `#fn-${node.identifier}`, children: [
|
|
2963
3428
|
"[",
|
|
2964
3429
|
node.label ?? node.identifier,
|
|
2965
3430
|
"]"
|
|
2966
3431
|
] }) }, key);
|
|
2967
3432
|
case "linkReference":
|
|
2968
|
-
return /* @__PURE__ */
|
|
3433
|
+
return /* @__PURE__ */ jsx19("span", { className: "squisq-md-link-ref", children: renderInline(node.children, key, ctx) }, key);
|
|
2969
3434
|
case "imageReference":
|
|
2970
|
-
return /* @__PURE__ */
|
|
3435
|
+
return /* @__PURE__ */ jsxs12("span", { className: "squisq-md-image-ref", children: [
|
|
2971
3436
|
"[",
|
|
2972
3437
|
node.alt ?? node.identifier,
|
|
2973
3438
|
"]"
|
|
2974
3439
|
] }, key);
|
|
2975
3440
|
case "textDirective":
|
|
2976
|
-
return /* @__PURE__ */
|
|
3441
|
+
return /* @__PURE__ */ jsx19("span", { className: "squisq-md-text-directive", "data-directive": node.name, children: renderInline(node.children, key, ctx) }, key);
|
|
2977
3442
|
case "mention":
|
|
2978
|
-
return /* @__PURE__ */
|
|
3443
|
+
return /* @__PURE__ */ jsxs12(
|
|
2979
3444
|
"span",
|
|
2980
3445
|
{
|
|
2981
3446
|
className: "squisq-md-mention mention",
|
|
@@ -2998,61 +3463,51 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
2998
3463
|
function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
2999
3464
|
switch (node.type) {
|
|
3000
3465
|
case "paragraph":
|
|
3001
|
-
return /* @__PURE__ */
|
|
3466
|
+
return /* @__PURE__ */ jsx19("p", { className: "squisq-md-p", children: renderInline(node.children, key, ctx) }, key);
|
|
3002
3467
|
case "heading": {
|
|
3003
3468
|
const Tag = `h${node.depth}`;
|
|
3004
|
-
return /* @__PURE__ */
|
|
3469
|
+
return /* @__PURE__ */ jsx19(Tag, { className: `squisq-md-heading squisq-md-h${node.depth}`, children: renderInline(node.children, key, ctx) }, key);
|
|
3005
3470
|
}
|
|
3006
3471
|
case "blockquote":
|
|
3007
|
-
return /* @__PURE__ */
|
|
3472
|
+
return /* @__PURE__ */ jsx19("blockquote", { className: "squisq-md-blockquote", children: renderBlocks(node.children, key, ctx) }, key);
|
|
3008
3473
|
case "list":
|
|
3009
3474
|
if (node.ordered) {
|
|
3010
|
-
return /* @__PURE__ */
|
|
3475
|
+
return /* @__PURE__ */ jsx19("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);
|
|
3011
3476
|
}
|
|
3012
|
-
return /* @__PURE__ */
|
|
3477
|
+
return /* @__PURE__ */ jsx19("ul", { className: "squisq-md-list squisq-md-ul", children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, ctx)) }, key);
|
|
3013
3478
|
case "code":
|
|
3014
|
-
return /* @__PURE__ */
|
|
3479
|
+
return /* @__PURE__ */ jsx19("pre", { className: "squisq-md-code-block", children: /* @__PURE__ */ jsx19("code", { className: node.lang ? `language-${node.lang}` : void 0, children: node.value }) }, key);
|
|
3015
3480
|
case "thematicBreak":
|
|
3016
|
-
return /* @__PURE__ */
|
|
3481
|
+
return /* @__PURE__ */ jsx19("hr", { className: "squisq-md-hr" }, key);
|
|
3017
3482
|
case "table":
|
|
3018
3483
|
return renderTable(node.children, node.align, key, ctx);
|
|
3019
3484
|
case "htmlBlock":
|
|
3020
3485
|
if (ctx.htmlPolicy === "strip") return null;
|
|
3021
|
-
|
|
3022
|
-
return /* @__PURE__ */ jsx18(
|
|
3023
|
-
"div",
|
|
3024
|
-
{
|
|
3025
|
-
className: "squisq-md-html-block",
|
|
3026
|
-
dangerouslySetInnerHTML: { __html: node.rawHtml }
|
|
3027
|
-
},
|
|
3028
|
-
key
|
|
3029
|
-
);
|
|
3030
|
-
}
|
|
3031
|
-
return /* @__PURE__ */ jsx18("div", { className: "squisq-md-html-block", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`) }, key);
|
|
3486
|
+
return /* @__PURE__ */ jsx19("div", { className: "squisq-md-html-block", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`, ctx) }, key);
|
|
3032
3487
|
case "math":
|
|
3033
|
-
return /* @__PURE__ */
|
|
3488
|
+
return /* @__PURE__ */ jsx19("pre", { className: "squisq-md-math-block", children: /* @__PURE__ */ jsx19("code", { children: node.value }) }, key);
|
|
3034
3489
|
case "definition":
|
|
3035
3490
|
return null;
|
|
3036
3491
|
case "footnoteDefinition":
|
|
3037
|
-
return /* @__PURE__ */
|
|
3038
|
-
/* @__PURE__ */
|
|
3492
|
+
return /* @__PURE__ */ jsxs12("div", { className: "squisq-md-footnote-def", id: `fn-${node.identifier}`, children: [
|
|
3493
|
+
/* @__PURE__ */ jsx19("sup", { children: node.label ?? node.identifier }),
|
|
3039
3494
|
renderBlocks(node.children, key, ctx)
|
|
3040
3495
|
] }, key);
|
|
3041
3496
|
case "containerDirective":
|
|
3042
|
-
return /* @__PURE__ */
|
|
3497
|
+
return /* @__PURE__ */ jsxs12(
|
|
3043
3498
|
"div",
|
|
3044
3499
|
{
|
|
3045
3500
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
3046
3501
|
"data-directive": node.name,
|
|
3047
3502
|
children: [
|
|
3048
|
-
node.label && /* @__PURE__ */
|
|
3503
|
+
node.label && /* @__PURE__ */ jsx19("div", { className: "squisq-md-directive-label", children: node.label }),
|
|
3049
3504
|
renderBlocks(node.children, key, ctx)
|
|
3050
3505
|
]
|
|
3051
3506
|
},
|
|
3052
3507
|
key
|
|
3053
3508
|
);
|
|
3054
3509
|
case "leafDirective":
|
|
3055
|
-
return /* @__PURE__ */
|
|
3510
|
+
return /* @__PURE__ */ jsx19(
|
|
3056
3511
|
"div",
|
|
3057
3512
|
{
|
|
3058
3513
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
@@ -3062,11 +3517,11 @@ function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
|
3062
3517
|
key
|
|
3063
3518
|
);
|
|
3064
3519
|
case "definitionList":
|
|
3065
|
-
return /* @__PURE__ */
|
|
3520
|
+
return /* @__PURE__ */ jsx19("dl", { className: "squisq-md-dl", children: node.children.map((child, i) => {
|
|
3066
3521
|
if (child.type === "definitionTerm") {
|
|
3067
|
-
return /* @__PURE__ */
|
|
3522
|
+
return /* @__PURE__ */ jsx19("dt", { className: "squisq-md-dt", children: renderInline(child.children, `${key}dt${i}`, ctx) }, `${key}dt${i}`);
|
|
3068
3523
|
}
|
|
3069
|
-
return /* @__PURE__ */
|
|
3524
|
+
return /* @__PURE__ */ jsx19("dd", { className: "squisq-md-dd", children: renderBlocks(child.children, `${key}dd${i}`, ctx) }, `${key}dd${i}`);
|
|
3070
3525
|
}) }, key);
|
|
3071
3526
|
default:
|
|
3072
3527
|
return null;
|
|
@@ -3074,15 +3529,15 @@ function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
|
3074
3529
|
}
|
|
3075
3530
|
function renderListItem(item, key, ctx = DEFAULT_CTX) {
|
|
3076
3531
|
const isTask = item.checked !== null && item.checked !== void 0;
|
|
3077
|
-
return /* @__PURE__ */
|
|
3078
|
-
isTask && /* @__PURE__ */
|
|
3532
|
+
return /* @__PURE__ */ jsxs12("li", { className: `squisq-md-li${isTask ? " squisq-md-task" : ""}`, children: [
|
|
3533
|
+
isTask && /* @__PURE__ */ jsx19("input", { type: "checkbox", checked: !!item.checked, readOnly: true, className: "squisq-md-checkbox" }),
|
|
3079
3534
|
renderBlocks(item.children, key, ctx)
|
|
3080
3535
|
] }, key);
|
|
3081
3536
|
}
|
|
3082
3537
|
function renderTable(rows, align, key, ctx = DEFAULT_CTX) {
|
|
3083
3538
|
const [headerRow, ...bodyRows] = rows;
|
|
3084
|
-
return /* @__PURE__ */
|
|
3085
|
-
headerRow && /* @__PURE__ */
|
|
3539
|
+
return /* @__PURE__ */ jsxs12("table", { className: "squisq-md-table", children: [
|
|
3540
|
+
headerRow && /* @__PURE__ */ jsx19("thead", { children: /* @__PURE__ */ jsx19("tr", { children: headerRow.children.map((cell, ci) => /* @__PURE__ */ jsx19(
|
|
3086
3541
|
"th",
|
|
3087
3542
|
{
|
|
3088
3543
|
className: "squisq-md-th",
|
|
@@ -3091,7 +3546,7 @@ function renderTable(rows, align, key, ctx = DEFAULT_CTX) {
|
|
|
3091
3546
|
},
|
|
3092
3547
|
`${key}th${ci}`
|
|
3093
3548
|
)) }) }),
|
|
3094
|
-
bodyRows.length > 0 && /* @__PURE__ */
|
|
3549
|
+
bodyRows.length > 0 && /* @__PURE__ */ jsx19("tbody", { children: bodyRows.map((row, ri) => /* @__PURE__ */ jsx19("tr", { children: row.children.map((cell, ci) => /* @__PURE__ */ jsx19(
|
|
3095
3550
|
"td",
|
|
3096
3551
|
{
|
|
3097
3552
|
className: "squisq-md-td",
|
|
@@ -3109,22 +3564,13 @@ function MdImage({ src, alt, title }) {
|
|
|
3109
3564
|
const safeSrc = sanitizeUrl(src, "media");
|
|
3110
3565
|
const resolved = useMediaUrl(safeSrc ?? "", ".");
|
|
3111
3566
|
if (!safeSrc) return null;
|
|
3112
|
-
return /* @__PURE__ */
|
|
3567
|
+
return /* @__PURE__ */ jsx19("img", { className: "squisq-md-image", src: resolved, alt, title });
|
|
3113
3568
|
}
|
|
3114
3569
|
function resolveHtmlNodes(nodes, htmlPolicy) {
|
|
3115
3570
|
if (htmlPolicy === "strip") return [];
|
|
3116
3571
|
if (htmlPolicy === "trusted") return nodes;
|
|
3117
3572
|
return sanitizeHtmlNodes2(nodes);
|
|
3118
3573
|
}
|
|
3119
|
-
function containsMediaTag(nodes) {
|
|
3120
|
-
for (const node of nodes) {
|
|
3121
|
-
if (node.type !== "htmlElement") continue;
|
|
3122
|
-
const tagName = node.tagName.toLowerCase();
|
|
3123
|
-
if (tagName === "video" || tagName === "audio") return true;
|
|
3124
|
-
if (containsMediaTag(node.children)) return true;
|
|
3125
|
-
}
|
|
3126
|
-
return false;
|
|
3127
|
-
}
|
|
3128
3574
|
var DANGEROUS_HTML_TAGS = /* @__PURE__ */ new Set([
|
|
3129
3575
|
"base",
|
|
3130
3576
|
"embed",
|
|
@@ -3136,18 +3582,6 @@ var DANGEROUS_HTML_TAGS = /* @__PURE__ */ new Set([
|
|
|
3136
3582
|
"style",
|
|
3137
3583
|
"title"
|
|
3138
3584
|
]);
|
|
3139
|
-
function containsDangerousTag(nodes) {
|
|
3140
|
-
for (const node of nodes) {
|
|
3141
|
-
if (node.type !== "htmlElement") continue;
|
|
3142
|
-
if (DANGEROUS_HTML_TAGS.has(node.tagName.toLowerCase())) return true;
|
|
3143
|
-
if (containsDangerousTag(node.children)) return true;
|
|
3144
|
-
}
|
|
3145
|
-
return false;
|
|
3146
|
-
}
|
|
3147
|
-
var DANGEROUS_RAW_HTML_RE = /<\s*\/?\s*(?:base|embed|iframe|link|meta|object|script|style|title)\b/i;
|
|
3148
|
-
function hasDangerousRawHtml(rawHtml) {
|
|
3149
|
-
return DANGEROUS_RAW_HTML_RE.test(rawHtml);
|
|
3150
|
-
}
|
|
3151
3585
|
var PASSTHROUGH_ATTRS = {
|
|
3152
3586
|
// common
|
|
3153
3587
|
class: "className",
|
|
@@ -3166,7 +3600,7 @@ var PASSTHROUGH_ATTRS = {
|
|
|
3166
3600
|
target: "target",
|
|
3167
3601
|
rel: "rel"
|
|
3168
3602
|
};
|
|
3169
|
-
function reactPropsFromAttrs(attrs) {
|
|
3603
|
+
function reactPropsFromAttrs(attrs, ctx) {
|
|
3170
3604
|
const out = {};
|
|
3171
3605
|
for (const [name, value] of Object.entries(attrs)) {
|
|
3172
3606
|
const propName = PASSTHROUGH_ATTRS[name];
|
|
@@ -3175,21 +3609,33 @@ function reactPropsFromAttrs(attrs) {
|
|
|
3175
3609
|
out["data-style"] = value;
|
|
3176
3610
|
continue;
|
|
3177
3611
|
}
|
|
3612
|
+
if (propName === "href") {
|
|
3613
|
+
const href = sanitizeUrl(value, "link", { extraLinkSchemes: ctx.linkSchemes });
|
|
3614
|
+
if (href) out.href = href;
|
|
3615
|
+
continue;
|
|
3616
|
+
}
|
|
3617
|
+
if (propName === "src") {
|
|
3618
|
+
const src = sanitizeUrl(value, "media");
|
|
3619
|
+
if (src) out.src = src;
|
|
3620
|
+
continue;
|
|
3621
|
+
}
|
|
3178
3622
|
out[propName] = value;
|
|
3179
3623
|
}
|
|
3180
3624
|
return out;
|
|
3181
3625
|
}
|
|
3182
|
-
function renderHtmlElement(el, key) {
|
|
3626
|
+
function renderHtmlElement(el, key, ctx) {
|
|
3183
3627
|
const tagName = el.tagName.toLowerCase();
|
|
3184
3628
|
if (DANGEROUS_HTML_TAGS.has(tagName)) return null;
|
|
3185
3629
|
if (tagName === "video") {
|
|
3186
|
-
|
|
3630
|
+
const src = sanitizeUrl(el.attributes.src ?? "", "media") ?? "";
|
|
3631
|
+
const poster = sanitizeUrl(el.attributes.poster ?? "", "media") ?? void 0;
|
|
3632
|
+
return /* @__PURE__ */ jsx19(
|
|
3187
3633
|
InlineVideoPlayer,
|
|
3188
3634
|
{
|
|
3189
|
-
src
|
|
3635
|
+
src,
|
|
3190
3636
|
width: el.attributes.width,
|
|
3191
3637
|
height: el.attributes.height,
|
|
3192
|
-
poster
|
|
3638
|
+
poster,
|
|
3193
3639
|
controls: "controls" in el.attributes,
|
|
3194
3640
|
preload: el.attributes.preload === "none" || el.attributes.preload === "metadata" || el.attributes.preload === "auto" ? el.attributes.preload : void 0
|
|
3195
3641
|
},
|
|
@@ -3197,10 +3643,11 @@ function renderHtmlElement(el, key) {
|
|
|
3197
3643
|
);
|
|
3198
3644
|
}
|
|
3199
3645
|
if (tagName === "audio") {
|
|
3200
|
-
|
|
3646
|
+
const src = sanitizeUrl(el.attributes.src ?? "", "media") ?? "";
|
|
3647
|
+
return /* @__PURE__ */ jsx19(
|
|
3201
3648
|
InlineAudioPlayer,
|
|
3202
3649
|
{
|
|
3203
|
-
src
|
|
3650
|
+
src,
|
|
3204
3651
|
controls: "controls" in el.attributes,
|
|
3205
3652
|
preload: el.attributes.preload === "none" || el.attributes.preload === "metadata" || el.attributes.preload === "auto" ? el.attributes.preload : void 0
|
|
3206
3653
|
},
|
|
@@ -3208,20 +3655,20 @@ function renderHtmlElement(el, key) {
|
|
|
3208
3655
|
);
|
|
3209
3656
|
}
|
|
3210
3657
|
const Tag = tagName;
|
|
3211
|
-
const props = reactPropsFromAttrs(el.attributes);
|
|
3658
|
+
const props = reactPropsFromAttrs(el.attributes, ctx);
|
|
3212
3659
|
if (el.selfClosing) {
|
|
3213
|
-
return /* @__PURE__ */
|
|
3660
|
+
return /* @__PURE__ */ jsx19(Tag, { ...props }, key);
|
|
3214
3661
|
}
|
|
3215
|
-
return /* @__PURE__ */
|
|
3662
|
+
return /* @__PURE__ */ jsx19(Tag, { ...props, children: renderHtmlNodes(el.children, `${key}c`, ctx) }, key);
|
|
3216
3663
|
}
|
|
3217
|
-
function renderHtmlNodes(nodes, keyPrefix) {
|
|
3664
|
+
function renderHtmlNodes(nodes, keyPrefix, ctx = DEFAULT_CTX) {
|
|
3218
3665
|
return nodes.map((node, i) => {
|
|
3219
3666
|
const key = `${keyPrefix}${i}`;
|
|
3220
3667
|
switch (node.type) {
|
|
3221
3668
|
case "htmlElement":
|
|
3222
|
-
return renderHtmlElement(node, key);
|
|
3669
|
+
return renderHtmlElement(node, key, ctx);
|
|
3223
3670
|
case "htmlText":
|
|
3224
|
-
return /* @__PURE__ */
|
|
3671
|
+
return /* @__PURE__ */ jsx19(Fragment2, { children: node.value }, key);
|
|
3225
3672
|
case "htmlComment":
|
|
3226
3673
|
return null;
|
|
3227
3674
|
default:
|
|
@@ -3236,25 +3683,16 @@ function MarkdownRenderer({
|
|
|
3236
3683
|
linkSchemes
|
|
3237
3684
|
}) {
|
|
3238
3685
|
if (!nodes || nodes.length === 0) return null;
|
|
3239
|
-
return /* @__PURE__ */
|
|
3686
|
+
return /* @__PURE__ */ jsx19("div", { className: `squisq-md ${className || ""}`, children: renderBlocks(nodes, "", { htmlPolicy, linkSchemes }) });
|
|
3240
3687
|
}
|
|
3241
3688
|
|
|
3242
3689
|
// src/LinearDocView.tsx
|
|
3243
|
-
import { jsx as
|
|
3244
|
-
var warnedUnknownTemplates = /* @__PURE__ */ new Set();
|
|
3690
|
+
import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3245
3691
|
function isAnnotatedBlock(block) {
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
warnedUnknownTemplates.add(annotation.template);
|
|
3251
|
-
console.warn(
|
|
3252
|
-
`[squisq] Unknown template "${annotation.template}" \u2014 rendering the block as plain markdown.`
|
|
3253
|
-
);
|
|
3254
|
-
}
|
|
3255
|
-
return false;
|
|
3256
|
-
}
|
|
3257
|
-
return true;
|
|
3692
|
+
return !!block.sourceHeading?.templateAnnotation?.template || !block.sourceHeading && isTemplateBlock2(block);
|
|
3693
|
+
}
|
|
3694
|
+
function visualTemplateName(block) {
|
|
3695
|
+
return block.sourceHeading?.templateAnnotation?.template ?? block.template;
|
|
3258
3696
|
}
|
|
3259
3697
|
function countAll(blocks) {
|
|
3260
3698
|
let count = 0;
|
|
@@ -3264,50 +3702,65 @@ function countAll(blocks) {
|
|
|
3264
3702
|
}
|
|
3265
3703
|
return count;
|
|
3266
3704
|
}
|
|
3267
|
-
function BlockSection({
|
|
3705
|
+
function BlockSection({
|
|
3706
|
+
block,
|
|
3707
|
+
basePath,
|
|
3708
|
+
viewport,
|
|
3709
|
+
renderContext,
|
|
3710
|
+
blockIndex,
|
|
3711
|
+
blockIndices,
|
|
3712
|
+
animationsEnabled
|
|
3713
|
+
}) {
|
|
3268
3714
|
const isAnnotated = isAnnotatedBlock(block);
|
|
3269
3715
|
const visualBlock = useMemo8(() => {
|
|
3270
3716
|
if (!isAnnotated) return null;
|
|
3271
|
-
const annotation = block.sourceHeading
|
|
3272
|
-
const
|
|
3273
|
-
const templateBlock = {
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
block.
|
|
3284
|
-
{
|
|
3717
|
+
const annotation = block.sourceHeading?.templateAnnotation;
|
|
3718
|
+
const templateName = visualTemplateName(block) ?? "sectionHeader";
|
|
3719
|
+
const templateBlock = annotation ? (() => {
|
|
3720
|
+
const headingText = extractPlainText(block.sourceHeading);
|
|
3721
|
+
return {
|
|
3722
|
+
id: block.id,
|
|
3723
|
+
template: templateName,
|
|
3724
|
+
startTime: 0,
|
|
3725
|
+
duration: 1,
|
|
3726
|
+
audioSegment: 0,
|
|
3727
|
+
title: headingText,
|
|
3728
|
+
contents: block.contents,
|
|
3729
|
+
children: block.children,
|
|
3730
|
+
...deriveTemplateInputs(templateName, headingText, block.contents, {
|
|
3285
3731
|
placeholders: true
|
|
3286
|
-
}
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3732
|
+
}) ?? {},
|
|
3733
|
+
...annotation.params,
|
|
3734
|
+
...block.templateOverrides
|
|
3735
|
+
};
|
|
3736
|
+
})() : {
|
|
3737
|
+
...block,
|
|
3738
|
+
startTime: block.startTime ?? 0,
|
|
3739
|
+
duration: block.duration ?? 1,
|
|
3740
|
+
audioSegment: block.audioSegment ?? 0,
|
|
3741
|
+
template: templateName
|
|
3290
3742
|
};
|
|
3291
3743
|
const ctx = {
|
|
3292
3744
|
...renderContext,
|
|
3293
3745
|
blockIndex
|
|
3294
3746
|
};
|
|
3295
|
-
const layers =
|
|
3747
|
+
const { layers } = materializeBlockLayers(templateBlock, ctx);
|
|
3296
3748
|
return {
|
|
3297
3749
|
...block,
|
|
3298
3750
|
layers,
|
|
3299
|
-
template:
|
|
3751
|
+
template: templateName
|
|
3300
3752
|
};
|
|
3301
3753
|
}, [block, isAnnotated, renderContext, blockIndex]);
|
|
3302
|
-
return /* @__PURE__ */
|
|
3754
|
+
return /* @__PURE__ */ jsxs13(
|
|
3303
3755
|
"div",
|
|
3304
3756
|
{
|
|
3305
3757
|
className: "squisq-linear-section",
|
|
3306
3758
|
"data-block-id": block.id,
|
|
3307
|
-
"data-
|
|
3759
|
+
"data-block-index": blockIndex,
|
|
3760
|
+
"data-template": isAnnotated ? visualTemplateName(block) : void 0,
|
|
3308
3761
|
children: [
|
|
3309
|
-
block.sourceHeading && !isAnnotated && /* @__PURE__ */
|
|
3310
|
-
isAnnotated && visualBlock && /* @__PURE__ */
|
|
3762
|
+
block.sourceHeading && !isAnnotated && /* @__PURE__ */ jsx20(MarkdownRenderer, { nodes: [block.sourceHeading] }),
|
|
3763
|
+
isAnnotated && visualBlock && /* @__PURE__ */ jsx20("div", { className: "squisq-linear-card", children: /* @__PURE__ */ jsx20(
|
|
3311
3764
|
"div",
|
|
3312
3765
|
{
|
|
3313
3766
|
className: "squisq-linear-card-svg",
|
|
@@ -3317,26 +3770,29 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
3317
3770
|
overflow: "hidden",
|
|
3318
3771
|
marginBottom: "1em"
|
|
3319
3772
|
},
|
|
3320
|
-
children: /* @__PURE__ */
|
|
3773
|
+
children: /* @__PURE__ */ jsx20(
|
|
3321
3774
|
BlockRenderer,
|
|
3322
3775
|
{
|
|
3323
3776
|
block: visualBlock,
|
|
3324
3777
|
blockTime: 0,
|
|
3325
3778
|
basePath,
|
|
3326
|
-
viewport
|
|
3779
|
+
viewport,
|
|
3780
|
+
animationsEnabled
|
|
3327
3781
|
}
|
|
3328
3782
|
)
|
|
3329
3783
|
}
|
|
3330
3784
|
) }),
|
|
3331
|
-
!isAnnotated && block.contents && block.contents.length > 0 && /* @__PURE__ */
|
|
3332
|
-
block.children && block.children.length > 0 && /* @__PURE__ */
|
|
3785
|
+
!isAnnotated && block.contents && block.contents.length > 0 && /* @__PURE__ */ jsx20(MarkdownRenderer, { nodes: block.contents }),
|
|
3786
|
+
block.children && block.children.length > 0 && /* @__PURE__ */ jsx20("div", { className: "squisq-linear-children", children: block.children.map((child, i) => /* @__PURE__ */ jsx20(
|
|
3333
3787
|
BlockSection,
|
|
3334
3788
|
{
|
|
3335
3789
|
block: child,
|
|
3336
3790
|
basePath,
|
|
3337
3791
|
viewport,
|
|
3338
3792
|
renderContext,
|
|
3339
|
-
blockIndex: blockIndex + i + 1
|
|
3793
|
+
blockIndex: blockIndices.get(child) ?? blockIndex + i + 1,
|
|
3794
|
+
blockIndices,
|
|
3795
|
+
animationsEnabled
|
|
3340
3796
|
},
|
|
3341
3797
|
child.id
|
|
3342
3798
|
)) })
|
|
@@ -3352,9 +3808,12 @@ function LinearDocView({
|
|
|
3352
3808
|
className,
|
|
3353
3809
|
theme,
|
|
3354
3810
|
surface,
|
|
3811
|
+
animationsEnabled = true,
|
|
3355
3812
|
thinMargins = false,
|
|
3356
|
-
imageDisplayMode = "inline"
|
|
3813
|
+
imageDisplayMode = "inline",
|
|
3814
|
+
globalKeyboardShortcuts = false
|
|
3357
3815
|
}) {
|
|
3816
|
+
const scrollRef = useRef8(null);
|
|
3358
3817
|
const activeViewport = viewport ?? VIEWPORT_PRESETS3.landscape;
|
|
3359
3818
|
const markdownDoc = useMemo8(
|
|
3360
3819
|
() => !doc && markdown !== void 0 ? markdownToDoc(parseMarkdown(markdown)) : void 0,
|
|
@@ -3365,6 +3824,18 @@ function LinearDocView({
|
|
|
3365
3824
|
() => resolvedDoc ? countAll(resolvedDoc.blocks) : 0,
|
|
3366
3825
|
[resolvedDoc]
|
|
3367
3826
|
);
|
|
3827
|
+
const blockIndices = useMemo8(() => {
|
|
3828
|
+
const indices = /* @__PURE__ */ new Map();
|
|
3829
|
+
let index = 0;
|
|
3830
|
+
const visit = (blocks) => {
|
|
3831
|
+
for (const block of blocks) {
|
|
3832
|
+
indices.set(block, index++);
|
|
3833
|
+
if (block.children) visit(block.children);
|
|
3834
|
+
}
|
|
3835
|
+
};
|
|
3836
|
+
if (resolvedDoc) visit(resolvedDoc.blocks);
|
|
3837
|
+
return indices;
|
|
3838
|
+
}, [resolvedDoc]);
|
|
3368
3839
|
const autoSurface = useAutoSurface(surface === "auto");
|
|
3369
3840
|
const resolvedSurface = surface === "auto" ? autoSurface : surface;
|
|
3370
3841
|
const renderContext = useMemo8(() => {
|
|
@@ -3376,12 +3847,37 @@ function LinearDocView({
|
|
|
3376
3847
|
totalBlocks,
|
|
3377
3848
|
// Theme atmosphere (vignette/grain/gradient persistent layers) shows
|
|
3378
3849
|
// on the inline template cards so they match the player's look.
|
|
3379
|
-
persistentLayers: effectiveTheme.persistentLayers
|
|
3850
|
+
persistentLayers: effectiveTheme.persistentLayers,
|
|
3851
|
+
customTemplates: resolvedDoc?.customTemplates
|
|
3380
3852
|
};
|
|
3381
|
-
}, [activeViewport, totalBlocks, theme, resolvedSurface]);
|
|
3853
|
+
}, [activeViewport, resolvedDoc?.customTemplates, totalBlocks, theme, resolvedSurface]);
|
|
3382
3854
|
const activeTheme = renderContext.theme;
|
|
3855
|
+
useEffect9(() => {
|
|
3856
|
+
if (!globalKeyboardShortcuts) return;
|
|
3857
|
+
const handleKeyDown = (event) => {
|
|
3858
|
+
if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.key !== "ArrowDown" && event.key !== "ArrowUp") {
|
|
3859
|
+
return;
|
|
3860
|
+
}
|
|
3861
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
3862
|
+
if (target?.closest(
|
|
3863
|
+
'input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="textbox"], [role="combobox"], [role="listbox"], [role="menu"], [role="dialog"], [aria-modal="true"], .monaco-editor'
|
|
3864
|
+
)) {
|
|
3865
|
+
return;
|
|
3866
|
+
}
|
|
3867
|
+
const scroller = scrollRef.current;
|
|
3868
|
+
if (!scroller) return;
|
|
3869
|
+
event.preventDefault();
|
|
3870
|
+
const distance = Math.max(64, Math.round(scroller.clientHeight * 0.12));
|
|
3871
|
+
scroller.scrollBy({
|
|
3872
|
+
top: event.key === "ArrowDown" ? distance : -distance,
|
|
3873
|
+
behavior: "smooth"
|
|
3874
|
+
});
|
|
3875
|
+
};
|
|
3876
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
3877
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
3878
|
+
}, [globalKeyboardShortcuts]);
|
|
3383
3879
|
if (!resolvedDoc) {
|
|
3384
|
-
return /* @__PURE__ */
|
|
3880
|
+
return /* @__PURE__ */ jsx20("div", { ref: scrollRef, className: `squisq-linear squisq-linear--empty ${className || ""}` });
|
|
3385
3881
|
}
|
|
3386
3882
|
const bgColor = activeTheme.colors.background;
|
|
3387
3883
|
const textColor = activeTheme.colors.text;
|
|
@@ -3390,9 +3886,10 @@ function LinearDocView({
|
|
|
3390
3886
|
const bodyFont = resolveFontFamily2(activeTheme.typography.bodyFont, "system-ui, sans-serif");
|
|
3391
3887
|
const titleFont = resolveFontFamily2(activeTheme.typography.titleFont, "Georgia, serif");
|
|
3392
3888
|
const lineHt = activeTheme.typography.lineHeight ?? 1.7;
|
|
3393
|
-
return /* @__PURE__ */
|
|
3889
|
+
return /* @__PURE__ */ jsx20(
|
|
3394
3890
|
"div",
|
|
3395
3891
|
{
|
|
3892
|
+
ref: scrollRef,
|
|
3396
3893
|
className: `squisq-linear ${className || ""}`,
|
|
3397
3894
|
style: {
|
|
3398
3895
|
width: "100%",
|
|
@@ -3406,7 +3903,7 @@ function LinearDocView({
|
|
|
3406
3903
|
overflowX: "hidden",
|
|
3407
3904
|
background: bgColor
|
|
3408
3905
|
},
|
|
3409
|
-
children: /* @__PURE__ */
|
|
3906
|
+
children: /* @__PURE__ */ jsxs13(
|
|
3410
3907
|
"div",
|
|
3411
3908
|
{
|
|
3412
3909
|
className: `squisq-linear-content squisq-md${thinMargins ? " squisq-linear-content--thin" : ""}${imageDisplayMode === "thumbnail" ? " squisq-linear-content--thumbnail-images" : ""}`,
|
|
@@ -3431,7 +3928,7 @@ function LinearDocView({
|
|
|
3431
3928
|
"--squisq-linear-bg": bgColor
|
|
3432
3929
|
},
|
|
3433
3930
|
children: [
|
|
3434
|
-
/* @__PURE__ */
|
|
3931
|
+
/* @__PURE__ */ jsx20("style", { children: `
|
|
3435
3932
|
.squisq-linear-content h1,
|
|
3436
3933
|
.squisq-linear-content h2,
|
|
3437
3934
|
.squisq-linear-content h3,
|
|
@@ -3543,14 +4040,16 @@ function LinearDocView({
|
|
|
3543
4040
|
background: color-mix(in srgb, var(--squisq-linear-primary) 8%, transparent);
|
|
3544
4041
|
}
|
|
3545
4042
|
` }),
|
|
3546
|
-
resolvedDoc.blocks.map((block, i) => /* @__PURE__ */
|
|
4043
|
+
resolvedDoc.blocks.map((block, i) => /* @__PURE__ */ jsx20(
|
|
3547
4044
|
BlockSection,
|
|
3548
4045
|
{
|
|
3549
4046
|
block,
|
|
3550
4047
|
basePath,
|
|
3551
4048
|
viewport: activeViewport,
|
|
3552
4049
|
renderContext,
|
|
3553
|
-
blockIndex: i
|
|
4050
|
+
blockIndex: blockIndices.get(block) ?? i,
|
|
4051
|
+
blockIndices,
|
|
4052
|
+
animationsEnabled
|
|
3554
4053
|
},
|
|
3555
4054
|
block.id
|
|
3556
4055
|
))
|
|
@@ -3562,7 +4061,7 @@ function LinearDocView({
|
|
|
3562
4061
|
}
|
|
3563
4062
|
|
|
3564
4063
|
// src/DocPlayer.tsx
|
|
3565
|
-
import { jsx as
|
|
4064
|
+
import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3566
4065
|
var SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
3567
4066
|
"a",
|
|
3568
4067
|
"an",
|
|
@@ -3583,7 +4082,7 @@ var SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
|
3583
4082
|
function buildSegmentTitleMap(doc) {
|
|
3584
4083
|
const map = /* @__PURE__ */ new Map();
|
|
3585
4084
|
for (const block of doc.blocks) {
|
|
3586
|
-
if (
|
|
4085
|
+
if (isTemplateBlock3(block) && block.template === "sectionHeader" && "title" in block) {
|
|
3587
4086
|
const segIdx = block.audioSegment;
|
|
3588
4087
|
if (!map.has(segIdx)) {
|
|
3589
4088
|
map.set(segIdx, block.title);
|
|
@@ -3624,14 +4123,15 @@ function DocPlayer(props) {
|
|
|
3624
4123
|
);
|
|
3625
4124
|
const resolvedDoc = doc ?? markdownDoc;
|
|
3626
4125
|
if (!resolvedDoc) {
|
|
3627
|
-
return /* @__PURE__ */
|
|
4126
|
+
return /* @__PURE__ */ jsx21("div", { className: "doc-player doc-player--empty" });
|
|
3628
4127
|
}
|
|
3629
|
-
return /* @__PURE__ */
|
|
4128
|
+
return /* @__PURE__ */ jsx21(DocPlayerContent, { ...props, doc: resolvedDoc });
|
|
3630
4129
|
}
|
|
3631
4130
|
function DocPlayerContent({
|
|
3632
4131
|
doc,
|
|
3633
4132
|
basePath = ".",
|
|
3634
4133
|
renderMode = false,
|
|
4134
|
+
animationsEnabled = true,
|
|
3635
4135
|
autoPlay = false,
|
|
3636
4136
|
onEnded,
|
|
3637
4137
|
onTimeUpdate,
|
|
@@ -3643,23 +4143,27 @@ function DocPlayerContent({
|
|
|
3643
4143
|
onCaptionsToggle,
|
|
3644
4144
|
onPlaybackStateChange,
|
|
3645
4145
|
onControlsReady,
|
|
4146
|
+
onRenderAPIReady,
|
|
3646
4147
|
isFullscreen = false,
|
|
3647
4148
|
onFullscreenToggle,
|
|
3648
4149
|
onBlockMarkers,
|
|
3649
4150
|
forceViewport,
|
|
3650
4151
|
displayMode = "video",
|
|
3651
4152
|
showCoverSlide = true,
|
|
4153
|
+
coverVisible,
|
|
3652
4154
|
theme,
|
|
3653
4155
|
surface,
|
|
3654
4156
|
captionStyle = "standard",
|
|
3655
|
-
enableSwipe = true
|
|
4157
|
+
enableSwipe = true,
|
|
4158
|
+
globalKeyboardShortcuts = false
|
|
3656
4159
|
}) {
|
|
3657
4160
|
const isSlideshowMode = displayMode === "slideshow";
|
|
3658
4161
|
const isLinearMode = displayMode === "linear";
|
|
3659
|
-
const audioRef =
|
|
3660
|
-
const containerRef =
|
|
3661
|
-
const
|
|
3662
|
-
const
|
|
4162
|
+
const audioRef = useRef9(null);
|
|
4163
|
+
const containerRef = useRef9(null);
|
|
4164
|
+
const playerId = `squisq-player-${useId7().replace(/:/g, "")}`;
|
|
4165
|
+
const [tapFeedback, setTapFeedback] = useState9(null);
|
|
4166
|
+
const tapFeedbackTimer = useRef9();
|
|
3663
4167
|
const { viewport, orientation } = useViewportOrientation();
|
|
3664
4168
|
const activeViewport = forceViewport || (renderMode ? VIEWPORT_PRESETS4.landscape : viewport);
|
|
3665
4169
|
const isDebugMode = useMemo9(() => {
|
|
@@ -3667,9 +4171,9 @@ function DocPlayerContent({
|
|
|
3667
4171
|
const params = new URLSearchParams(window.location.search);
|
|
3668
4172
|
return params.get("debug") === "true";
|
|
3669
4173
|
}, []);
|
|
3670
|
-
const internalAudio = useAudioSync(audioRef, doc.audio, basePath);
|
|
4174
|
+
const internalAudio = useAudioSync(audioRef, doc.audio, basePath, !externalAudioController);
|
|
3671
4175
|
const audio = externalAudioController || internalAudio;
|
|
3672
|
-
|
|
4176
|
+
useEffect10(() => {
|
|
3673
4177
|
if (warnedMissingStyles || !isDevEnvironment()) return;
|
|
3674
4178
|
const el = containerRef.current;
|
|
3675
4179
|
if (!el || typeof getComputedStyle !== "function") return;
|
|
@@ -3698,21 +4202,28 @@ function DocPlayerContent({
|
|
|
3698
4202
|
restart
|
|
3699
4203
|
} = audio;
|
|
3700
4204
|
const mediaSchedule = useMemo9(() => resolveMediaSchedule(doc), [doc]);
|
|
3701
|
-
const currentTimeRef =
|
|
4205
|
+
const currentTimeRef = useRef9(currentTime);
|
|
3702
4206
|
currentTimeRef.current = currentTime;
|
|
3703
|
-
const totalDurationRef =
|
|
4207
|
+
const totalDurationRef = useRef9(totalDuration);
|
|
3704
4208
|
totalDurationRef.current = totalDuration;
|
|
3705
|
-
const expandedBlocksLenRef =
|
|
3706
|
-
const handleContainerClick =
|
|
4209
|
+
const expandedBlocksLenRef = useRef9(0);
|
|
4210
|
+
const handleContainerClick = useCallback7(
|
|
3707
4211
|
(e) => {
|
|
3708
|
-
if (renderMode ||
|
|
4212
|
+
if (renderMode || isLinearMode) return;
|
|
3709
4213
|
const target = e.target;
|
|
4214
|
+
if (isSlideshowMode) {
|
|
4215
|
+
if (!target.closest('button, a, input, textarea, select, [contenteditable="true"]')) {
|
|
4216
|
+
containerRef.current?.focus({ preventScroll: true });
|
|
4217
|
+
}
|
|
4218
|
+
return;
|
|
4219
|
+
}
|
|
3710
4220
|
if (target.closest(
|
|
3711
|
-
|
|
4221
|
+
'button, a, input, textarea, select, [contenteditable="true"], .doc-player__controls, .doc-player__scrubber, .doc-controls-sidebar, .doc-controls-slideshow'
|
|
3712
4222
|
))
|
|
3713
4223
|
return;
|
|
4224
|
+
containerRef.current?.focus({ preventScroll: true });
|
|
3714
4225
|
toggle();
|
|
3715
|
-
const nextState = isPlaying ? "
|
|
4226
|
+
const nextState = isPlaying ? "pause" : "play";
|
|
3716
4227
|
setTapFeedback(nextState);
|
|
3717
4228
|
clearTimeout(tapFeedbackTimer.current);
|
|
3718
4229
|
tapFeedbackTimer.current = setTimeout(() => setTapFeedback(null), 600);
|
|
@@ -3736,8 +4247,13 @@ function DocPlayerContent({
|
|
|
3736
4247
|
docProgress,
|
|
3737
4248
|
nextBlock: _nextBlock,
|
|
3738
4249
|
prevBlock: _prevBlock,
|
|
3739
|
-
blocks: expandedBlocks
|
|
3740
|
-
|
|
4250
|
+
blocks: expandedBlocks,
|
|
4251
|
+
suppressOutgoingForNextBlock
|
|
4252
|
+
} = useDocPlayback(doc, currentTime, {
|
|
4253
|
+
viewport: activeViewport,
|
|
4254
|
+
theme: effectiveTheme,
|
|
4255
|
+
onSeek: seekTo
|
|
4256
|
+
});
|
|
3741
4257
|
const coverBlock = useMemo9(() => {
|
|
3742
4258
|
const startBlockConfig = doc.startBlock;
|
|
3743
4259
|
if (!showCoverSlide) return null;
|
|
@@ -3755,9 +4271,13 @@ function DocPlayerContent({
|
|
|
3755
4271
|
};
|
|
3756
4272
|
}, [doc.startBlock, activeViewport, effectiveTheme, showCoverSlide]);
|
|
3757
4273
|
const hasManagedCover = !!coverBlock;
|
|
3758
|
-
const [slideshowCoverVisible, setSlideshowCoverVisible] =
|
|
3759
|
-
const
|
|
3760
|
-
|
|
4274
|
+
const [slideshowCoverVisible, setSlideshowCoverVisible] = useState9(false);
|
|
4275
|
+
const [isSlideshowPickerOpen, setIsSlideshowPickerOpen] = useState9(false);
|
|
4276
|
+
const slideshowCoverInitKeyRef = useRef9("");
|
|
4277
|
+
useEffect10(() => {
|
|
4278
|
+
slideshowCoverInitKeyRef.current = "";
|
|
4279
|
+
}, [doc]);
|
|
4280
|
+
useEffect10(() => {
|
|
3761
4281
|
const initKey = `${isSlideshowMode}:${hasManagedCover}:${renderMode}`;
|
|
3762
4282
|
if (slideshowCoverInitKeyRef.current === initKey) return;
|
|
3763
4283
|
slideshowCoverInitKeyRef.current = initKey;
|
|
@@ -3768,14 +4288,21 @@ function DocPlayerContent({
|
|
|
3768
4288
|
setSlideshowCoverVisible(false);
|
|
3769
4289
|
}
|
|
3770
4290
|
}, [isSlideshowMode, hasManagedCover, renderMode, pause]);
|
|
3771
|
-
const [coverForced, setCoverForced] =
|
|
3772
|
-
const [coverGraceActive, setCoverGraceActive] =
|
|
3773
|
-
const coverGraceTimer =
|
|
3774
|
-
const coverWasShowing =
|
|
3775
|
-
const hasPlayedOnce =
|
|
4291
|
+
const [coverForced, setCoverForced] = useState9(false);
|
|
4292
|
+
const [coverGraceActive, setCoverGraceActive] = useState9(false);
|
|
4293
|
+
const coverGraceTimer = useRef9();
|
|
4294
|
+
const coverWasShowing = useRef9(false);
|
|
4295
|
+
const hasPlayedOnce = useRef9(false);
|
|
4296
|
+
useEffect10(() => {
|
|
4297
|
+
hasPlayedOnce.current = false;
|
|
4298
|
+
coverWasShowing.current = false;
|
|
4299
|
+
clearTimeout(coverGraceTimer.current);
|
|
4300
|
+
setCoverGraceActive(false);
|
|
4301
|
+
setCoverForced(false);
|
|
4302
|
+
}, [doc]);
|
|
3776
4303
|
const atRest = !!(coverBlock && !isSlideshowMode && !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
3777
4304
|
if (atRest) coverWasShowing.current = true;
|
|
3778
|
-
|
|
4305
|
+
useEffect10(() => {
|
|
3779
4306
|
if (isPlaying && coverWasShowing.current && coverBlock && !renderMode && !isSlideshowMode) {
|
|
3780
4307
|
coverWasShowing.current = false;
|
|
3781
4308
|
hasPlayedOnce.current = true;
|
|
@@ -3783,88 +4310,96 @@ function DocPlayerContent({
|
|
|
3783
4310
|
coverGraceTimer.current = setTimeout(() => setCoverGraceActive(false), 3e3);
|
|
3784
4311
|
}
|
|
3785
4312
|
}, [isPlaying, coverBlock, renderMode, isSlideshowMode]);
|
|
3786
|
-
|
|
4313
|
+
useEffect10(() => () => clearTimeout(coverGraceTimer.current), []);
|
|
3787
4314
|
const showVideoCoverBlock = !isSlideshowMode && !isLinearMode && !!coverBlock && (coverForced || coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
3788
|
-
const
|
|
3789
|
-
const
|
|
4315
|
+
const effectiveSlideshowCoverVisible = coverVisible ?? slideshowCoverVisible;
|
|
4316
|
+
const showSlideshowCover = !!(isSlideshowMode && !isLinearMode && !renderMode && coverBlock && effectiveSlideshowCoverVisible);
|
|
4317
|
+
const showCoverBlock = coverVisible === void 0 ? showVideoCoverBlock || showSlideshowCover : !!coverBlock && coverVisible;
|
|
3790
4318
|
const slideshowHasCover = !!(isSlideshowMode && !renderMode && coverBlock);
|
|
3791
|
-
const slideshowSlideIndex = slideshowHasCover ?
|
|
4319
|
+
const slideshowSlideIndex = slideshowHasCover ? effectiveSlideshowCoverVisible ? 0 : currentBlockIndex + 1 : currentBlockIndex;
|
|
3792
4320
|
const slideshowTotalSlides = slideshowHasCover ? expandedBlocks.length + 1 : expandedBlocks.length;
|
|
3793
|
-
const hasAutoPlayed =
|
|
3794
|
-
|
|
4321
|
+
const hasAutoPlayed = useRef9(false);
|
|
4322
|
+
useEffect10(() => {
|
|
4323
|
+
hasAutoPlayed.current = false;
|
|
4324
|
+
}, [doc]);
|
|
4325
|
+
useEffect10(() => {
|
|
3795
4326
|
if (isAudioReady && autoPlay && !hasAutoPlayed.current) {
|
|
3796
4327
|
hasAutoPlayed.current = true;
|
|
3797
4328
|
play();
|
|
3798
4329
|
}
|
|
3799
4330
|
}, [isAudioReady, autoPlay, play]);
|
|
3800
|
-
|
|
4331
|
+
useEffect10(() => {
|
|
3801
4332
|
onTimeUpdate?.(currentTime);
|
|
3802
4333
|
}, [currentTime, onTimeUpdate]);
|
|
3803
|
-
|
|
4334
|
+
useEffect10(() => {
|
|
3804
4335
|
if (isEnded) {
|
|
3805
4336
|
onEnded?.();
|
|
3806
4337
|
}
|
|
3807
4338
|
}, [isEnded, onEnded]);
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
4339
|
+
const liveRenderAPIRef = useRef9(null);
|
|
4340
|
+
const stableRenderAPIRef = useRef9(null);
|
|
4341
|
+
if (!stableRenderAPIRef.current) {
|
|
4342
|
+
const current = () => {
|
|
4343
|
+
const api = liveRenderAPIRef.current;
|
|
4344
|
+
if (!api) throw new Error("Squisq render API is not currently available.");
|
|
4345
|
+
return api;
|
|
4346
|
+
};
|
|
4347
|
+
stableRenderAPIRef.current = {
|
|
4348
|
+
seekTo: (time) => current().seekTo(time),
|
|
4349
|
+
getDuration: () => current().getDuration(),
|
|
4350
|
+
getBlocks: () => current().getBlocks(),
|
|
4351
|
+
getAudioSegments: () => current().getAudioSegments(),
|
|
4352
|
+
getCaptions: () => current().getCaptions(),
|
|
4353
|
+
getChapters: () => current().getChapters(),
|
|
4354
|
+
showCover: () => current().showCover(),
|
|
4355
|
+
hideCover: () => current().hideCover(),
|
|
4356
|
+
hasCoverBlock: () => current().hasCoverBlock()
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
const stableRenderAPI = stableRenderAPIRef.current;
|
|
4360
|
+
useEffect10(() => {
|
|
4361
|
+
if (!renderMode && !isDebugMode) {
|
|
4362
|
+
liveRenderAPIRef.current = null;
|
|
4363
|
+
return;
|
|
4364
|
+
}
|
|
4365
|
+
const root = containerRef.current;
|
|
4366
|
+
if (!root) {
|
|
4367
|
+
liveRenderAPIRef.current = null;
|
|
4368
|
+
return;
|
|
4369
|
+
}
|
|
4370
|
+
const renderSeekTo = (time) => {
|
|
4371
|
+
seekTo(time);
|
|
4372
|
+
return new Promise((resolve) => {
|
|
4373
|
+
requestAnimationFrame(() => {
|
|
4374
|
+
let blockStartTime = 0;
|
|
4375
|
+
for (let i = expandedBlocks.length - 1; i >= 0; i--) {
|
|
4376
|
+
if (time >= expandedBlocks[i].startTime) {
|
|
4377
|
+
blockStartTime = expandedBlocks[i].startTime;
|
|
4378
|
+
break;
|
|
3821
4379
|
}
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
});
|
|
3832
|
-
const blockElapsed = time - blockStartTime;
|
|
3833
|
-
const videoSeekPromises = [];
|
|
3834
|
-
const activeBlockEl = document.querySelector(".doc-player__block--active");
|
|
3835
|
-
if (activeBlockEl) {
|
|
3836
|
-
const videos = activeBlockEl.querySelectorAll("video[data-clip-start]");
|
|
3837
|
-
videos.forEach((el) => {
|
|
3838
|
-
const video = el;
|
|
3839
|
-
const clipStart = parseFloat(video.dataset.clipStart || "0");
|
|
3840
|
-
const clipEnd = parseFloat(video.dataset.clipEnd || "0");
|
|
3841
|
-
const startAt = parseFloat(video.dataset.startAt || "0");
|
|
3842
|
-
const targetTime = Math.min(
|
|
3843
|
-
clipStart + Math.max(0, blockElapsed - startAt),
|
|
3844
|
-
clipEnd
|
|
3845
|
-
);
|
|
3846
|
-
video.pause();
|
|
3847
|
-
video.currentTime = targetTime;
|
|
3848
|
-
videoSeekPromises.push(
|
|
3849
|
-
new Promise((r) => {
|
|
3850
|
-
if (Math.abs(video.currentTime - targetTime) < 0.1) {
|
|
3851
|
-
r();
|
|
3852
|
-
} else {
|
|
3853
|
-
video.addEventListener("seeked", () => r(), { once: true });
|
|
3854
|
-
setTimeout(r, 200);
|
|
3855
|
-
}
|
|
3856
|
-
})
|
|
3857
|
-
);
|
|
3858
|
-
});
|
|
4380
|
+
}
|
|
4381
|
+
const elapsedMs = (time - blockStartTime) * 1e3;
|
|
4382
|
+
(root.getAnimations?.() ?? []).forEach((anim) => {
|
|
4383
|
+
const target = anim.effect?.target;
|
|
4384
|
+
if (!target) return;
|
|
4385
|
+
if (target.closest(".doc-player__block--active")) {
|
|
4386
|
+
anim.currentTime = Math.max(0, elapsedMs);
|
|
4387
|
+
} else if (target.closest(".doc-player__block--previous")) {
|
|
4388
|
+
anim.currentTime = Math.max(0, elapsedMs);
|
|
3859
4389
|
}
|
|
3860
|
-
|
|
4390
|
+
});
|
|
4391
|
+
const blockElapsed = time - blockStartTime;
|
|
4392
|
+
const videoSeekPromises = [];
|
|
4393
|
+
const activeBlockEl = root.querySelector(".doc-player__block--active");
|
|
4394
|
+
if (activeBlockEl) {
|
|
4395
|
+
const videos = activeBlockEl.querySelectorAll("video[data-clip-start]");
|
|
4396
|
+
videos.forEach((el) => {
|
|
3861
4397
|
const video = el;
|
|
3862
|
-
const
|
|
3863
|
-
const
|
|
3864
|
-
const
|
|
4398
|
+
const clipStart = parseFloat(video.dataset.clipStart || "0");
|
|
4399
|
+
const clipEnd = parseFloat(video.dataset.clipEnd || "0");
|
|
4400
|
+
const startAt = parseFloat(video.dataset.startAt || "0");
|
|
4401
|
+
const targetTime = Math.min(clipStart + Math.max(0, blockElapsed - startAt), clipEnd);
|
|
3865
4402
|
video.pause();
|
|
3866
|
-
if (time < absStart || time >= absEnd) return;
|
|
3867
|
-
const targetTime = sourceIn + (time - absStart);
|
|
3868
4403
|
video.currentTime = targetTime;
|
|
3869
4404
|
videoSeekPromises.push(
|
|
3870
4405
|
new Promise((r) => {
|
|
@@ -3877,82 +4412,111 @@ function DocPlayerContent({
|
|
|
3877
4412
|
})
|
|
3878
4413
|
);
|
|
3879
4414
|
});
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
4415
|
+
}
|
|
4416
|
+
root.querySelectorAll("video[data-clip-id]").forEach((el) => {
|
|
4417
|
+
const video = el;
|
|
4418
|
+
const absStart = parseFloat(video.dataset.absStart || "0");
|
|
4419
|
+
const absEnd = parseFloat(video.dataset.absEnd || "0");
|
|
4420
|
+
const sourceIn = parseFloat(video.dataset.sourceIn || "0");
|
|
4421
|
+
video.pause();
|
|
4422
|
+
if (time < absStart || time >= absEnd) return;
|
|
4423
|
+
const targetTime = sourceIn + (time - absStart);
|
|
4424
|
+
video.currentTime = targetTime;
|
|
4425
|
+
videoSeekPromises.push(
|
|
4426
|
+
new Promise((r) => {
|
|
4427
|
+
if (Math.abs(video.currentTime - targetTime) < 0.1) {
|
|
4428
|
+
r();
|
|
4429
|
+
} else {
|
|
4430
|
+
video.addEventListener("seeked", () => r(), { once: true });
|
|
4431
|
+
setTimeout(r, 200);
|
|
4432
|
+
}
|
|
4433
|
+
})
|
|
4434
|
+
);
|
|
4435
|
+
});
|
|
4436
|
+
Promise.all(videoSeekPromises).then(() => {
|
|
4437
|
+
requestAnimationFrame(() => resolve());
|
|
3883
4438
|
});
|
|
3884
4439
|
});
|
|
3885
|
-
};
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
4440
|
+
});
|
|
4441
|
+
};
|
|
4442
|
+
const getDuration = () => {
|
|
4443
|
+
const mediaDuration = getDocPlaybackDuration(doc);
|
|
4444
|
+
if (totalDuration > 0) return Math.max(totalDuration, mediaDuration);
|
|
4445
|
+
return mediaDuration;
|
|
4446
|
+
};
|
|
4447
|
+
const getBlocks = () => expandedBlocks.map((s) => ({
|
|
4448
|
+
id: s.id,
|
|
4449
|
+
template: s.template ?? "raw",
|
|
4450
|
+
startTime: s.startTime,
|
|
4451
|
+
duration: s.duration
|
|
4452
|
+
}));
|
|
4453
|
+
const getAudioSegments = () => doc.audio.segments.map((seg) => ({
|
|
4454
|
+
src: seg.src,
|
|
4455
|
+
name: seg.name,
|
|
4456
|
+
duration: seg.duration,
|
|
4457
|
+
startTime: seg.startTime
|
|
4458
|
+
}));
|
|
4459
|
+
const getCaptions = () => doc.captions?.phrases?.map((p) => ({
|
|
4460
|
+
text: p.text,
|
|
4461
|
+
startTime: p.startTime,
|
|
4462
|
+
endTime: p.endTime
|
|
4463
|
+
})) || [];
|
|
4464
|
+
const getChapters = () => {
|
|
4465
|
+
const titleMap = buildSegmentTitleMap(doc);
|
|
4466
|
+
return doc.audio.segments.map((seg, i) => ({
|
|
4467
|
+
title: titleMap.get(i) || seg.name,
|
|
4468
|
+
startTime: seg.startTime,
|
|
4469
|
+
duration: seg.duration
|
|
3902
4470
|
}));
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
}
|
|
4471
|
+
};
|
|
4472
|
+
const showCover = () => {
|
|
4473
|
+
setCoverForced(true);
|
|
4474
|
+
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
4475
|
+
};
|
|
4476
|
+
const hideCover = () => {
|
|
4477
|
+
setCoverForced(false);
|
|
4478
|
+
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
4479
|
+
};
|
|
4480
|
+
const hasCoverBlock = () => !!coverBlock;
|
|
4481
|
+
const api = {
|
|
4482
|
+
seekTo: renderSeekTo,
|
|
4483
|
+
getDuration,
|
|
4484
|
+
getBlocks,
|
|
4485
|
+
getAudioSegments,
|
|
4486
|
+
getCaptions,
|
|
4487
|
+
getChapters,
|
|
4488
|
+
showCover,
|
|
4489
|
+
hideCover,
|
|
4490
|
+
hasCoverBlock
|
|
4491
|
+
};
|
|
4492
|
+
liveRenderAPIRef.current = api;
|
|
3926
4493
|
return () => {
|
|
3927
|
-
if (
|
|
3928
|
-
const w = window;
|
|
3929
|
-
delete w.seekTo;
|
|
3930
|
-
delete w.getDuration;
|
|
3931
|
-
delete w.getBlocks;
|
|
3932
|
-
delete w.getAudioSegments;
|
|
3933
|
-
delete w.getCaptions;
|
|
3934
|
-
delete w.getChapters;
|
|
3935
|
-
delete w.showCover;
|
|
3936
|
-
delete w.hideCover;
|
|
3937
|
-
delete w.hasCoverBlock;
|
|
3938
|
-
}
|
|
4494
|
+
if (liveRenderAPIRef.current === api) liveRenderAPIRef.current = null;
|
|
3939
4495
|
};
|
|
3940
|
-
}, [renderMode, isDebugMode, seekTo, totalDuration, expandedBlocks, coverBlock]);
|
|
4496
|
+
}, [renderMode, isDebugMode, seekTo, totalDuration, expandedBlocks, coverBlock, doc]);
|
|
4497
|
+
useEffect10(() => {
|
|
4498
|
+
if (!renderMode && !isDebugMode || !containerRef.current) {
|
|
4499
|
+
onRenderAPIReady?.(null);
|
|
4500
|
+
return;
|
|
4501
|
+
}
|
|
4502
|
+
onRenderAPIReady?.(stableRenderAPI);
|
|
4503
|
+
return () => onRenderAPIReady?.(null);
|
|
4504
|
+
}, [renderMode, isDebugMode, onRenderAPIReady, stableRenderAPI]);
|
|
3941
4505
|
const defaultMode = captionsEnabledProp === false ? "off" : captionStyle || "standard";
|
|
3942
|
-
const [captionMode, setCaptionMode] =
|
|
3943
|
-
|
|
4506
|
+
const [captionMode, setCaptionMode] = useState9(defaultMode);
|
|
4507
|
+
useEffect10(() => {
|
|
3944
4508
|
setCaptionMode(defaultMode);
|
|
3945
4509
|
}, [defaultMode]);
|
|
3946
4510
|
const captionsEnabled = captionMode !== "off";
|
|
3947
4511
|
const activeCaptionStyle = captionMode === "social" ? "social" : "standard";
|
|
3948
|
-
const setCaptionsEnabled =
|
|
4512
|
+
const setCaptionsEnabled = useCallback7(
|
|
3949
4513
|
(enabled) => {
|
|
3950
4514
|
setCaptionMode(enabled ? captionStyle || "standard" : "off");
|
|
3951
4515
|
onCaptionsToggle?.(enabled);
|
|
3952
4516
|
},
|
|
3953
4517
|
[onCaptionsToggle, captionStyle]
|
|
3954
4518
|
);
|
|
3955
|
-
const cycleCaptionMode =
|
|
4519
|
+
const cycleCaptionMode = useCallback7(() => {
|
|
3956
4520
|
setCaptionMode((prev) => {
|
|
3957
4521
|
const next = prev === "off" ? "standard" : prev === "standard" ? "social" : "off";
|
|
3958
4522
|
onCaptionsToggle?.(next !== "off");
|
|
@@ -3966,6 +4530,7 @@ function DocPlayerContent({
|
|
|
3966
4530
|
isPlaying,
|
|
3967
4531
|
currentTime,
|
|
3968
4532
|
totalDuration,
|
|
4533
|
+
isCoverVisible: showCoverBlock,
|
|
3969
4534
|
currentBlockIndex: slideshowSlideIndex,
|
|
3970
4535
|
totalBlocks: slideshowTotalSlides,
|
|
3971
4536
|
docProgress,
|
|
@@ -3985,6 +4550,7 @@ function DocPlayerContent({
|
|
|
3985
4550
|
isPlaying,
|
|
3986
4551
|
currentTime,
|
|
3987
4552
|
totalDuration,
|
|
4553
|
+
showCoverBlock,
|
|
3988
4554
|
slideshowSlideIndex,
|
|
3989
4555
|
slideshowTotalSlides,
|
|
3990
4556
|
docProgress,
|
|
@@ -4078,23 +4644,39 @@ function DocPlayerContent({
|
|
|
4078
4644
|
[currentBlockIndex, expandedBlocks, seekTo, pause, slideshowHasCover, slideshowCoverVisible]
|
|
4079
4645
|
);
|
|
4080
4646
|
const swipeEnabled = isSlideshowMode && !isLinearMode && !renderMode && enableSwipe;
|
|
4647
|
+
const armContextFreeSwipeEntry = useCallback7(
|
|
4648
|
+
(destinationSlideIndex) => {
|
|
4649
|
+
const destinationBlockIndex = destinationSlideIndex - (slideshowHasCover ? 1 : 0);
|
|
4650
|
+
const destinationBlock = expandedBlocks[destinationBlockIndex];
|
|
4651
|
+
if (destinationBlock) suppressOutgoingForNextBlock(destinationBlock.id);
|
|
4652
|
+
},
|
|
4653
|
+
[expandedBlocks, slideshowHasCover, suppressOutgoingForNextBlock]
|
|
4654
|
+
);
|
|
4655
|
+
const handleSwipeNext = useCallback7(() => {
|
|
4656
|
+
armContextFreeSwipeEntry(slideshowSlideIndex + 1);
|
|
4657
|
+
slideNavActions.nextSlide();
|
|
4658
|
+
}, [armContextFreeSwipeEntry, slideshowSlideIndex, slideNavActions]);
|
|
4659
|
+
const handleSwipePrev = useCallback7(() => {
|
|
4660
|
+
armContextFreeSwipeEntry(slideshowSlideIndex - 1);
|
|
4661
|
+
slideNavActions.prevSlide();
|
|
4662
|
+
}, [armContextFreeSwipeEntry, slideshowSlideIndex, slideNavActions]);
|
|
4081
4663
|
const swipe = useSlideSwipe({
|
|
4082
4664
|
enabled: swipeEnabled,
|
|
4083
4665
|
containerRef,
|
|
4084
4666
|
canGoNext: slideshowSlideIndex < slideshowTotalSlides - 1,
|
|
4085
4667
|
canGoPrev: slideshowSlideIndex > 0,
|
|
4086
|
-
onNext:
|
|
4087
|
-
onPrev:
|
|
4668
|
+
onNext: handleSwipeNext,
|
|
4669
|
+
onPrev: handleSwipePrev
|
|
4088
4670
|
});
|
|
4089
|
-
|
|
4671
|
+
useEffect10(() => {
|
|
4090
4672
|
onPlaybackStateChange?.(playbackState);
|
|
4091
4673
|
}, [playbackState, onPlaybackStateChange]);
|
|
4092
|
-
|
|
4674
|
+
useEffect10(() => {
|
|
4093
4675
|
onControlsReady?.({ play, pause, ...playbackActions });
|
|
4094
4676
|
}, [play, pause, playbackActions, onControlsReady]);
|
|
4095
|
-
const getBlockTitle =
|
|
4677
|
+
const getBlockTitle = useCallback7((block) => {
|
|
4096
4678
|
const docBlock = block;
|
|
4097
|
-
if (
|
|
4679
|
+
if (isTemplateBlock3(docBlock)) {
|
|
4098
4680
|
const props = docBlock;
|
|
4099
4681
|
if (typeof props.title === "string") return props.title;
|
|
4100
4682
|
if (typeof props.stat === "string") return props.stat;
|
|
@@ -4116,6 +4698,22 @@ function DocPlayerContent({
|
|
|
4116
4698
|
}
|
|
4117
4699
|
return block.id.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
4118
4700
|
}, []);
|
|
4701
|
+
const slideshowPickerItems = useMemo9(() => {
|
|
4702
|
+
const blockItems = expandedBlocks.map((block, index) => ({
|
|
4703
|
+
id: block.id,
|
|
4704
|
+
label: String(index + 1),
|
|
4705
|
+
summary: getBlockTitle(block)
|
|
4706
|
+
}));
|
|
4707
|
+
if (!slideshowHasCover || !coverBlock) return blockItems;
|
|
4708
|
+
return [
|
|
4709
|
+
{
|
|
4710
|
+
id: "__cover__",
|
|
4711
|
+
label: "Cover",
|
|
4712
|
+
summary: getBlockTitle(coverBlock)
|
|
4713
|
+
},
|
|
4714
|
+
...blockItems
|
|
4715
|
+
];
|
|
4716
|
+
}, [coverBlock, expandedBlocks, getBlockTitle, slideshowHasCover]);
|
|
4119
4717
|
const blockMarkers = useMemo9(() => {
|
|
4120
4718
|
if (!totalDuration || !expandedBlocks.length) return [];
|
|
4121
4719
|
let prevSegment = -1;
|
|
@@ -4131,16 +4729,26 @@ function DocPlayerContent({
|
|
|
4131
4729
|
};
|
|
4132
4730
|
});
|
|
4133
4731
|
}, [expandedBlocks, totalDuration, getBlockTitle]);
|
|
4134
|
-
|
|
4732
|
+
useEffect10(() => {
|
|
4135
4733
|
if (blockMarkers.length > 0) {
|
|
4136
4734
|
onBlockMarkers?.(blockMarkers);
|
|
4137
4735
|
}
|
|
4138
4736
|
}, [blockMarkers, onBlockMarkers]);
|
|
4139
4737
|
expandedBlocksLenRef.current = isSlideshowMode ? slideshowTotalSlides : expandedBlocks.length;
|
|
4140
|
-
const
|
|
4141
|
-
(e) => {
|
|
4142
|
-
|
|
4143
|
-
|
|
4738
|
+
const handleKeyboardShortcut = useCallback7(
|
|
4739
|
+
(e, global) => {
|
|
4740
|
+
if (e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
|
|
4741
|
+
const target = e.target instanceof Element ? e.target : null;
|
|
4742
|
+
const isEditableTarget = !!target?.closest(
|
|
4743
|
+
'input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="textbox"], [role="combobox"], [role="listbox"], [role="slider"], [role="spinbutton"], .monaco-editor'
|
|
4744
|
+
);
|
|
4745
|
+
const isOpenInteractionTarget = !!target?.closest(
|
|
4746
|
+
'[role="menu"], [role="dialog"], [aria-modal="true"]'
|
|
4747
|
+
);
|
|
4748
|
+
const isSlideshowToolbarTarget = isSlideshowMode && !!target?.closest(".doc-controls-slideshow") && !target.closest('[role="menu"]');
|
|
4749
|
+
if (isEditableTarget || global && isOpenInteractionTarget || !global && !!target?.closest(
|
|
4750
|
+
'input, textarea, select, button, a, [contenteditable]:not([contenteditable="false"]), [role="textbox"]'
|
|
4751
|
+
) && !isSlideshowToolbarTarget) {
|
|
4144
4752
|
return;
|
|
4145
4753
|
}
|
|
4146
4754
|
if (isLinearMode) return;
|
|
@@ -4153,10 +4761,13 @@ function DocPlayerContent({
|
|
|
4153
4761
|
slideNavActions.nextSlide();
|
|
4154
4762
|
break;
|
|
4155
4763
|
case "ArrowLeft":
|
|
4156
|
-
case "ArrowUp":
|
|
4157
4764
|
e.preventDefault();
|
|
4158
4765
|
slideNavActions.prevSlide();
|
|
4159
4766
|
break;
|
|
4767
|
+
case "ArrowUp":
|
|
4768
|
+
e.preventDefault();
|
|
4769
|
+
setIsSlideshowPickerOpen(true);
|
|
4770
|
+
break;
|
|
4160
4771
|
case "Home":
|
|
4161
4772
|
e.preventDefault();
|
|
4162
4773
|
slideNavActions.goToSlide(0);
|
|
@@ -4173,9 +4784,11 @@ function DocPlayerContent({
|
|
|
4173
4784
|
toggle();
|
|
4174
4785
|
break;
|
|
4175
4786
|
case "ArrowRight":
|
|
4787
|
+
e.preventDefault();
|
|
4176
4788
|
seekTo(Math.min(currentTimeRef.current + 10, totalDurationRef.current));
|
|
4177
4789
|
break;
|
|
4178
4790
|
case "ArrowLeft":
|
|
4791
|
+
e.preventDefault();
|
|
4179
4792
|
seekTo(Math.max(currentTimeRef.current - 10, 0));
|
|
4180
4793
|
break;
|
|
4181
4794
|
}
|
|
@@ -4183,16 +4796,24 @@ function DocPlayerContent({
|
|
|
4183
4796
|
},
|
|
4184
4797
|
[isSlideshowMode, isLinearMode, toggle, seekTo, slideNavActions]
|
|
4185
4798
|
);
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4799
|
+
const handleKeyDown = useCallback7(
|
|
4800
|
+
(e) => handleKeyboardShortcut(e, false),
|
|
4801
|
+
[handleKeyboardShortcut]
|
|
4802
|
+
);
|
|
4803
|
+
useEffect10(() => {
|
|
4804
|
+
if (!globalKeyboardShortcuts || renderMode || isLinearMode) return;
|
|
4805
|
+
const handleDocumentKeyDown = (event) => {
|
|
4806
|
+
handleKeyboardShortcut(event, true);
|
|
4807
|
+
};
|
|
4808
|
+
document.addEventListener("keydown", handleDocumentKeyDown);
|
|
4809
|
+
return () => document.removeEventListener("keydown", handleDocumentKeyDown);
|
|
4810
|
+
}, [globalKeyboardShortcuts, handleKeyboardShortcut, isLinearMode, renderMode]);
|
|
4191
4811
|
if (isLinearMode) {
|
|
4192
|
-
return /* @__PURE__ */
|
|
4812
|
+
return /* @__PURE__ */ jsx21(
|
|
4193
4813
|
"div",
|
|
4194
4814
|
{
|
|
4195
4815
|
ref: containerRef,
|
|
4816
|
+
"data-player-id": playerId,
|
|
4196
4817
|
className: "doc-player doc-player--linear",
|
|
4197
4818
|
style: {
|
|
4198
4819
|
position: "relative",
|
|
@@ -4200,23 +4821,28 @@ function DocPlayerContent({
|
|
|
4200
4821
|
height: "100%",
|
|
4201
4822
|
overflow: "hidden"
|
|
4202
4823
|
},
|
|
4203
|
-
children: /* @__PURE__ */
|
|
4824
|
+
children: /* @__PURE__ */ jsx21(
|
|
4204
4825
|
LinearDocView,
|
|
4205
4826
|
{
|
|
4206
4827
|
doc,
|
|
4207
4828
|
basePath,
|
|
4208
4829
|
viewport: activeViewport,
|
|
4209
4830
|
theme,
|
|
4210
|
-
surface
|
|
4831
|
+
surface,
|
|
4832
|
+
animationsEnabled
|
|
4211
4833
|
}
|
|
4212
4834
|
)
|
|
4213
4835
|
}
|
|
4214
4836
|
);
|
|
4215
4837
|
}
|
|
4216
|
-
return /* @__PURE__ */
|
|
4838
|
+
return /* @__PURE__ */ jsxs14(
|
|
4217
4839
|
"div",
|
|
4218
4840
|
{
|
|
4219
4841
|
ref: containerRef,
|
|
4842
|
+
"data-player-id": playerId,
|
|
4843
|
+
tabIndex: renderMode ? -1 : 0,
|
|
4844
|
+
"aria-label": "Document player",
|
|
4845
|
+
onKeyDown: renderMode ? void 0 : handleKeyDown,
|
|
4220
4846
|
className: `doc-player${swipeEnabled ? " doc-player--swipe" : ""}${swipe.phase === "dragging" ? " doc-player--grabbing" : ""}`,
|
|
4221
4847
|
onClick: handleContainerClick,
|
|
4222
4848
|
onPointerDown: swipe.onPointerDown,
|
|
@@ -4232,33 +4858,35 @@ function DocPlayerContent({
|
|
|
4232
4858
|
touchAction: swipeEnabled ? "pan-y" : void 0
|
|
4233
4859
|
},
|
|
4234
4860
|
children: [
|
|
4235
|
-
/* @__PURE__ */
|
|
4236
|
-
/* @__PURE__ */
|
|
4861
|
+
/* @__PURE__ */ jsx21("audio", { ref: audioRef, preload: "auto", muted }),
|
|
4862
|
+
/* @__PURE__ */ jsx21(
|
|
4237
4863
|
MediaClipLayer,
|
|
4238
4864
|
{
|
|
4239
4865
|
schedule: mediaSchedule,
|
|
4240
4866
|
currentTime,
|
|
4241
4867
|
isPlaying,
|
|
4242
4868
|
basePath,
|
|
4243
|
-
renderMode
|
|
4869
|
+
renderMode,
|
|
4870
|
+
muted
|
|
4244
4871
|
}
|
|
4245
4872
|
),
|
|
4246
|
-
/* @__PURE__ */
|
|
4247
|
-
showCoverBlock && coverBlock && /* @__PURE__ */
|
|
4873
|
+
/* @__PURE__ */ jsxs14("div", { className: "doc-player__viewport", children: [
|
|
4874
|
+
showCoverBlock && coverBlock && /* @__PURE__ */ jsx21("div", { className: "doc-player__block doc-player__block--cover", children: /* @__PURE__ */ jsx21(
|
|
4248
4875
|
BlockRenderer,
|
|
4249
4876
|
{
|
|
4250
4877
|
block: coverBlock,
|
|
4251
4878
|
blockTime: 0,
|
|
4252
4879
|
basePath,
|
|
4253
4880
|
isEntering: false,
|
|
4254
|
-
viewport: activeViewport
|
|
4881
|
+
viewport: activeViewport,
|
|
4882
|
+
animationsEnabled
|
|
4255
4883
|
}
|
|
4256
4884
|
) }),
|
|
4257
|
-
!showCoverBlock && previousBlock && isExiting && // Keyed by block id so each block is its own DOM subtree: React never
|
|
4885
|
+
animationsEnabled && !showCoverBlock && previousBlock && isExiting && // Keyed by block id so each block is its own DOM subtree: React never
|
|
4258
4886
|
// reconciles one block's layers onto another's (templates reuse layer
|
|
4259
4887
|
// ids like `title`/`background`), which would otherwise reuse stale
|
|
4260
4888
|
// DOM / skip entrance animations mid-transition.
|
|
4261
|
-
/* @__PURE__ */
|
|
4889
|
+
/* @__PURE__ */ jsx21("div", { className: "doc-player__block doc-player__block--previous", children: /* @__PURE__ */ jsx21(
|
|
4262
4890
|
BlockRenderer,
|
|
4263
4891
|
{
|
|
4264
4892
|
block: previousBlock,
|
|
@@ -4266,29 +4894,31 @@ function DocPlayerContent({
|
|
|
4266
4894
|
basePath,
|
|
4267
4895
|
isExiting: true,
|
|
4268
4896
|
transition: currentBlock?.transition,
|
|
4269
|
-
viewport: activeViewport
|
|
4897
|
+
viewport: activeViewport,
|
|
4898
|
+
animationsEnabled
|
|
4270
4899
|
}
|
|
4271
4900
|
) }, previousBlock.id),
|
|
4272
|
-
!showCoverBlock && currentBlock && /* @__PURE__ */
|
|
4901
|
+
!showCoverBlock && currentBlock && /* @__PURE__ */ jsx21(
|
|
4273
4902
|
"div",
|
|
4274
4903
|
{
|
|
4275
4904
|
className: `doc-player__block doc-player__block--active${swipe.phase !== "idle" ? ` doc-player__block--${swipe.phase}` : ""}`,
|
|
4276
4905
|
style: swipe.phase !== "idle" ? { transform: `translateX(${swipe.offsetPx}px)` } : void 0,
|
|
4277
|
-
children: /* @__PURE__ */
|
|
4906
|
+
children: /* @__PURE__ */ jsx21(
|
|
4278
4907
|
BlockRenderer,
|
|
4279
4908
|
{
|
|
4280
4909
|
block: currentBlock,
|
|
4281
4910
|
blockTime,
|
|
4282
4911
|
basePath,
|
|
4283
|
-
isEntering,
|
|
4912
|
+
isEntering: animationsEnabled && isEntering,
|
|
4284
4913
|
viewport: activeViewport,
|
|
4285
|
-
isPlaying
|
|
4914
|
+
isPlaying,
|
|
4915
|
+
animationsEnabled
|
|
4286
4916
|
}
|
|
4287
4917
|
)
|
|
4288
4918
|
},
|
|
4289
4919
|
currentBlock.id
|
|
4290
4920
|
),
|
|
4291
|
-
hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */
|
|
4921
|
+
hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */ jsx21(
|
|
4292
4922
|
CaptionOverlay,
|
|
4293
4923
|
{
|
|
4294
4924
|
captions: doc.captions,
|
|
@@ -4300,7 +4930,7 @@ function DocPlayerContent({
|
|
|
4300
4930
|
viewport: activeViewport
|
|
4301
4931
|
}
|
|
4302
4932
|
),
|
|
4303
|
-
isDebugMode && /* @__PURE__ */
|
|
4933
|
+
isDebugMode && /* @__PURE__ */ jsxs14(
|
|
4304
4934
|
"div",
|
|
4305
4935
|
{
|
|
4306
4936
|
className: "doc-player__debug",
|
|
@@ -4321,27 +4951,27 @@ function DocPlayerContent({
|
|
|
4321
4951
|
textAlign: "left"
|
|
4322
4952
|
},
|
|
4323
4953
|
children: [
|
|
4324
|
-
/* @__PURE__ */
|
|
4325
|
-
/* @__PURE__ */
|
|
4326
|
-
/* @__PURE__ */
|
|
4954
|
+
/* @__PURE__ */ jsx21("div", { style: { color: "#ffcc00", fontWeight: "bold", marginBottom: "4px" }, children: "DEBUG MODE" }),
|
|
4955
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
4956
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "template:" }),
|
|
4327
4957
|
" ",
|
|
4328
|
-
/* @__PURE__ */
|
|
4958
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#ff6b6b" }, children: currentBlock?.template ?? "raw" })
|
|
4329
4959
|
] }),
|
|
4330
|
-
/* @__PURE__ */
|
|
4331
|
-
/* @__PURE__ */
|
|
4960
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
4961
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "block:" }),
|
|
4332
4962
|
" ",
|
|
4333
4963
|
currentBlockIndex + 1,
|
|
4334
4964
|
"/",
|
|
4335
4965
|
expandedBlocks.length,
|
|
4336
4966
|
" ",
|
|
4337
|
-
/* @__PURE__ */
|
|
4967
|
+
/* @__PURE__ */ jsxs14("span", { style: { color: "#666" }, children: [
|
|
4338
4968
|
"(",
|
|
4339
4969
|
currentBlock?.id || "none",
|
|
4340
4970
|
")"
|
|
4341
4971
|
] })
|
|
4342
4972
|
] }),
|
|
4343
|
-
/* @__PURE__ */
|
|
4344
|
-
/* @__PURE__ */
|
|
4973
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
4974
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "time:" }),
|
|
4345
4975
|
" ",
|
|
4346
4976
|
currentTime.toFixed(2),
|
|
4347
4977
|
"s /",
|
|
@@ -4349,7 +4979,7 @@ function DocPlayerContent({
|
|
|
4349
4979
|
totalDuration.toFixed(1),
|
|
4350
4980
|
"s",
|
|
4351
4981
|
" ",
|
|
4352
|
-
/* @__PURE__ */
|
|
4982
|
+
/* @__PURE__ */ jsxs14("span", { style: { color: "#666" }, children: [
|
|
4353
4983
|
"(progress: ",
|
|
4354
4984
|
(docProgress * 100).toFixed(1),
|
|
4355
4985
|
"%, scriptDur: ",
|
|
@@ -4357,8 +4987,8 @@ function DocPlayerContent({
|
|
|
4357
4987
|
")"
|
|
4358
4988
|
] })
|
|
4359
4989
|
] }),
|
|
4360
|
-
/* @__PURE__ */
|
|
4361
|
-
/* @__PURE__ */
|
|
4990
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
4991
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "blockTime:" }),
|
|
4362
4992
|
" ",
|
|
4363
4993
|
blockTime.toFixed(2),
|
|
4364
4994
|
"s /",
|
|
@@ -4366,58 +4996,58 @@ function DocPlayerContent({
|
|
|
4366
4996
|
(currentBlock?.duration || 0).toFixed(1),
|
|
4367
4997
|
"s"
|
|
4368
4998
|
] }),
|
|
4369
|
-
/* @__PURE__ */
|
|
4370
|
-
/* @__PURE__ */
|
|
4999
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
5000
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "segment:" }),
|
|
4371
5001
|
" ",
|
|
4372
5002
|
currentSegment,
|
|
4373
5003
|
"/",
|
|
4374
5004
|
doc.audio.segments.length - 1,
|
|
4375
5005
|
" ",
|
|
4376
|
-
/* @__PURE__ */
|
|
5006
|
+
/* @__PURE__ */ jsxs14("span", { style: { color: "#666" }, children: [
|
|
4377
5007
|
"(",
|
|
4378
5008
|
doc.audio.segments[currentSegment]?.name || "none",
|
|
4379
5009
|
")"
|
|
4380
5010
|
] })
|
|
4381
5011
|
] }),
|
|
4382
|
-
/* @__PURE__ */
|
|
4383
|
-
/* @__PURE__ */
|
|
5012
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
5013
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "viewport:" }),
|
|
4384
5014
|
" ",
|
|
4385
5015
|
activeViewport.name || `${activeViewport.width}x${activeViewport.height}`,
|
|
4386
5016
|
" ",
|
|
4387
|
-
/* @__PURE__ */
|
|
5017
|
+
/* @__PURE__ */ jsxs14("span", { style: { color: "#666" }, children: [
|
|
4388
5018
|
"(",
|
|
4389
5019
|
orientation,
|
|
4390
5020
|
")"
|
|
4391
5021
|
] })
|
|
4392
5022
|
] }),
|
|
4393
|
-
/* @__PURE__ */
|
|
4394
|
-
/* @__PURE__ */
|
|
5023
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
5024
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "playing:" }),
|
|
4395
5025
|
" ",
|
|
4396
|
-
/* @__PURE__ */
|
|
4397
|
-
showCoverBlock && /* @__PURE__ */
|
|
5026
|
+
/* @__PURE__ */ jsx21("span", { style: { color: isPlaying ? "#4ade80" : "#f87171" }, children: isPlaying ? "yes" : "no" }),
|
|
5027
|
+
showCoverBlock && /* @__PURE__ */ jsx21("span", { style: { color: "#60a5fa" }, children: " (cover)" })
|
|
4398
5028
|
] }),
|
|
4399
5029
|
hasCaptions && (() => {
|
|
4400
5030
|
const debugPhrase = getCaptionAtTime2(doc.captions, currentTime);
|
|
4401
5031
|
const debugEnabled = captionsEnabled && (isPlaying || currentTime > 0);
|
|
4402
|
-
return /* @__PURE__ */
|
|
4403
|
-
/* @__PURE__ */
|
|
4404
|
-
/* @__PURE__ */
|
|
5032
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
5033
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
5034
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "captions:" }),
|
|
4405
5035
|
" ",
|
|
4406
5036
|
doc.captions?.phrases.length || 0,
|
|
4407
5037
|
" phrases",
|
|
4408
5038
|
" ",
|
|
4409
|
-
/* @__PURE__ */
|
|
5039
|
+
/* @__PURE__ */ jsxs14("span", { style: { color: captionsEnabled ? "#4ade80" : "#666" }, children: [
|
|
4410
5040
|
"(",
|
|
4411
5041
|
captionsEnabled ? "on" : "off",
|
|
4412
5042
|
")"
|
|
4413
5043
|
] })
|
|
4414
5044
|
] }),
|
|
4415
|
-
/* @__PURE__ */
|
|
4416
|
-
/* @__PURE__ */
|
|
5045
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
5046
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "cc.enabled:" }),
|
|
4417
5047
|
" ",
|
|
4418
|
-
/* @__PURE__ */
|
|
5048
|
+
/* @__PURE__ */ jsx21("span", { style: { color: debugEnabled ? "#4ade80" : "#f87171" }, children: String(debugEnabled) }),
|
|
4419
5049
|
" ",
|
|
4420
|
-
/* @__PURE__ */
|
|
5050
|
+
/* @__PURE__ */ jsxs14("span", { style: { color: "#666" }, children: [
|
|
4421
5051
|
"(playing=",
|
|
4422
5052
|
String(isPlaying),
|
|
4423
5053
|
" t>0=",
|
|
@@ -4425,15 +5055,15 @@ function DocPlayerContent({
|
|
|
4425
5055
|
")"
|
|
4426
5056
|
] })
|
|
4427
5057
|
] }),
|
|
4428
|
-
/* @__PURE__ */
|
|
4429
|
-
/* @__PURE__ */
|
|
5058
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
5059
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "cc.phrase:" }),
|
|
4430
5060
|
" ",
|
|
4431
|
-
/* @__PURE__ */
|
|
5061
|
+
/* @__PURE__ */ jsx21("span", { style: { color: debugPhrase ? "#4ade80" : "#f87171" }, children: debugPhrase ? `"${debugPhrase.text.slice(0, 30)}..."` : "null" })
|
|
4432
5062
|
] }),
|
|
4433
|
-
debugPhrase && /* @__PURE__ */
|
|
4434
|
-
/* @__PURE__ */
|
|
5063
|
+
debugPhrase && /* @__PURE__ */ jsxs14("div", { children: [
|
|
5064
|
+
/* @__PURE__ */ jsx21("span", { style: { color: "#888" }, children: "cc.range:" }),
|
|
4435
5065
|
" ",
|
|
4436
|
-
/* @__PURE__ */
|
|
5066
|
+
/* @__PURE__ */ jsxs14("span", { style: { color: "#60a5fa" }, children: [
|
|
4437
5067
|
debugPhrase.startTime.toFixed(2),
|
|
4438
5068
|
"-",
|
|
4439
5069
|
debugPhrase.endTime.toFixed(2)
|
|
@@ -4445,7 +5075,7 @@ function DocPlayerContent({
|
|
|
4445
5075
|
}
|
|
4446
5076
|
)
|
|
4447
5077
|
] }),
|
|
4448
|
-
!isAvailable && unavailableMessage && /* @__PURE__ */
|
|
5078
|
+
!isAvailable && unavailableMessage && /* @__PURE__ */ jsxs14(
|
|
4449
5079
|
"div",
|
|
4450
5080
|
{
|
|
4451
5081
|
className: "doc-player__unavailable",
|
|
@@ -4466,12 +5096,12 @@ function DocPlayerContent({
|
|
|
4466
5096
|
zIndex: 50
|
|
4467
5097
|
},
|
|
4468
5098
|
children: [
|
|
4469
|
-
/* @__PURE__ */
|
|
4470
|
-
/* @__PURE__ */
|
|
5099
|
+
/* @__PURE__ */ jsx21("span", { style: { fontSize: "32px" }, children: "\u{1F50A}" }),
|
|
5100
|
+
/* @__PURE__ */ jsx21("span", { children: unavailableMessage })
|
|
4471
5101
|
]
|
|
4472
5102
|
}
|
|
4473
5103
|
),
|
|
4474
|
-
!renderMode && !isSlideshowMode && showControls && /* @__PURE__ */
|
|
5104
|
+
!renderMode && !isSlideshowMode && showControls && /* @__PURE__ */ jsx21(
|
|
4475
5105
|
DocControlsOverlay,
|
|
4476
5106
|
{
|
|
4477
5107
|
state: playbackState,
|
|
@@ -4481,7 +5111,7 @@ function DocPlayerContent({
|
|
|
4481
5111
|
getBlockTitle
|
|
4482
5112
|
}
|
|
4483
5113
|
),
|
|
4484
|
-
!renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */
|
|
5114
|
+
!renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */ jsx21(
|
|
4485
5115
|
"div",
|
|
4486
5116
|
{
|
|
4487
5117
|
className: "doc-player__scrubber",
|
|
@@ -4496,7 +5126,7 @@ function DocPlayerContent({
|
|
|
4496
5126
|
alignItems: "center",
|
|
4497
5127
|
zIndex: 100
|
|
4498
5128
|
},
|
|
4499
|
-
children: /* @__PURE__ */
|
|
5129
|
+
children: /* @__PURE__ */ jsx21(
|
|
4500
5130
|
DocProgressBar,
|
|
4501
5131
|
{
|
|
4502
5132
|
state: playbackState,
|
|
@@ -4508,15 +5138,24 @@ function DocPlayerContent({
|
|
|
4508
5138
|
)
|
|
4509
5139
|
}
|
|
4510
5140
|
),
|
|
4511
|
-
!renderMode && isSlideshowMode && /* @__PURE__ */
|
|
4512
|
-
|
|
5141
|
+
!renderMode && isSlideshowMode && showControls && /* @__PURE__ */ jsx21(
|
|
5142
|
+
DocControlsSlideshow,
|
|
5143
|
+
{
|
|
5144
|
+
state: playbackState,
|
|
5145
|
+
slideNav: slideNavActions,
|
|
5146
|
+
slides: slideshowPickerItems,
|
|
5147
|
+
pickerOpen: isSlideshowPickerOpen,
|
|
5148
|
+
onPickerOpenChange: setIsSlideshowPickerOpen
|
|
5149
|
+
}
|
|
5150
|
+
),
|
|
5151
|
+
!isSlideshowMode && tapFeedback && /* @__PURE__ */ jsx21("div", { className: "doc-player__tap-feedback", children: /* @__PURE__ */ jsx21("svg", { viewBox: "0 0 24 24", fill: "white", width: "48", height: "48", children: tapFeedback === "pause" ? /* @__PURE__ */ jsx21("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) : /* @__PURE__ */ jsx21("path", { d: "M8 5v14l11-7z" }) }) }, tapFeedback)
|
|
4513
5152
|
]
|
|
4514
5153
|
}
|
|
4515
5154
|
);
|
|
4516
5155
|
}
|
|
4517
5156
|
|
|
4518
5157
|
// src/DocControlsBottom.tsx
|
|
4519
|
-
import { jsx as
|
|
5158
|
+
import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4520
5159
|
function DocControlsBottom({
|
|
4521
5160
|
state,
|
|
4522
5161
|
actions,
|
|
@@ -4524,32 +5163,32 @@ function DocControlsBottom({
|
|
|
4524
5163
|
expandedBlocks,
|
|
4525
5164
|
getBlockTitle
|
|
4526
5165
|
}) {
|
|
4527
|
-
return /* @__PURE__ */
|
|
4528
|
-
/* @__PURE__ */
|
|
5166
|
+
return /* @__PURE__ */ jsxs15("div", { className: "doc-controls-bottom", children: [
|
|
5167
|
+
/* @__PURE__ */ jsx22(
|
|
4529
5168
|
"button",
|
|
4530
5169
|
{
|
|
4531
5170
|
className: "bottom-ctrl-btn",
|
|
4532
5171
|
onClick: actions.restart,
|
|
4533
5172
|
title: "Restart",
|
|
4534
5173
|
"aria-label": "Restart from beginning",
|
|
4535
|
-
children: /* @__PURE__ */
|
|
5174
|
+
children: /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", 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" }) })
|
|
4536
5175
|
}
|
|
4537
5176
|
),
|
|
4538
|
-
/* @__PURE__ */
|
|
5177
|
+
/* @__PURE__ */ jsx22(
|
|
4539
5178
|
"button",
|
|
4540
5179
|
{
|
|
4541
5180
|
className: "bottom-ctrl-btn bottom-play-btn",
|
|
4542
5181
|
onClick: actions.toggle,
|
|
4543
5182
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
4544
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
5183
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx22("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx22("path", { d: "M8 5v14l11-7z" }) })
|
|
4545
5184
|
}
|
|
4546
5185
|
),
|
|
4547
|
-
/* @__PURE__ */
|
|
5186
|
+
/* @__PURE__ */ jsxs15("span", { className: "bottom-time", children: [
|
|
4548
5187
|
formatTime(state.currentTime),
|
|
4549
5188
|
" / ",
|
|
4550
5189
|
formatTime(state.totalDuration)
|
|
4551
5190
|
] }),
|
|
4552
|
-
/* @__PURE__ */
|
|
5191
|
+
/* @__PURE__ */ jsx22(
|
|
4553
5192
|
DocProgressBar,
|
|
4554
5193
|
{
|
|
4555
5194
|
state,
|
|
@@ -4559,82 +5198,82 @@ function DocControlsBottom({
|
|
|
4559
5198
|
getBlockTitle
|
|
4560
5199
|
}
|
|
4561
5200
|
),
|
|
4562
|
-
/* @__PURE__ */
|
|
5201
|
+
/* @__PURE__ */ jsxs15("span", { className: "bottom-segment", children: [
|
|
4563
5202
|
state.currentBlockIndex + 1,
|
|
4564
5203
|
"/",
|
|
4565
5204
|
state.totalBlocks
|
|
4566
5205
|
] }),
|
|
4567
|
-
state.hasCaptions && /* @__PURE__ */
|
|
5206
|
+
state.hasCaptions && /* @__PURE__ */ jsx22(
|
|
4568
5207
|
"button",
|
|
4569
5208
|
{
|
|
4570
5209
|
className: `bottom-ctrl-btn ${state.captionMode !== "off" ? "bottom-ctrl-btn--active" : ""}`,
|
|
4571
5210
|
onClick: () => actions.cycleCaptionMode(),
|
|
4572
5211
|
title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
|
|
4573
5212
|
"aria-label": "Cycle caption style",
|
|
4574
|
-
children: /* @__PURE__ */
|
|
5213
|
+
children: /* @__PURE__ */ jsx22("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", 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" }) })
|
|
4575
5214
|
}
|
|
4576
5215
|
)
|
|
4577
5216
|
] });
|
|
4578
5217
|
}
|
|
4579
5218
|
|
|
4580
5219
|
// src/DocControlsSidebar.tsx
|
|
4581
|
-
import { jsx as
|
|
5220
|
+
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4582
5221
|
function DocControlsSidebar({ state, actions }) {
|
|
4583
|
-
return /* @__PURE__ */
|
|
4584
|
-
/* @__PURE__ */
|
|
5222
|
+
return /* @__PURE__ */ jsxs16("div", { className: "doc-controls-sidebar", children: [
|
|
5223
|
+
/* @__PURE__ */ jsx23(
|
|
4585
5224
|
"button",
|
|
4586
5225
|
{
|
|
4587
5226
|
className: "sidebar-ctrl-btn",
|
|
4588
5227
|
onClick: actions.restart,
|
|
4589
5228
|
title: "Restart",
|
|
4590
5229
|
"aria-label": "Restart from beginning",
|
|
4591
|
-
children: /* @__PURE__ */
|
|
5230
|
+
children: /* @__PURE__ */ jsx23("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx23("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" }) })
|
|
4592
5231
|
}
|
|
4593
5232
|
),
|
|
4594
|
-
/* @__PURE__ */
|
|
5233
|
+
/* @__PURE__ */ jsx23(
|
|
4595
5234
|
"button",
|
|
4596
5235
|
{
|
|
4597
5236
|
className: "sidebar-ctrl-btn sidebar-play-btn",
|
|
4598
5237
|
onClick: actions.toggle,
|
|
4599
5238
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
4600
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
5239
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx23("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx23("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx23("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx23("path", { d: "M8 5v14l11-7z" }) })
|
|
4601
5240
|
}
|
|
4602
5241
|
),
|
|
4603
|
-
/* @__PURE__ */
|
|
4604
|
-
/* @__PURE__ */
|
|
4605
|
-
/* @__PURE__ */
|
|
5242
|
+
/* @__PURE__ */ jsxs16("div", { className: "sidebar-time", children: [
|
|
5243
|
+
/* @__PURE__ */ jsx23("div", { children: formatTime(state.currentTime) }),
|
|
5244
|
+
/* @__PURE__ */ jsx23("div", { className: "sidebar-time-total", children: formatTime(state.totalDuration) })
|
|
4606
5245
|
] }),
|
|
4607
|
-
/* @__PURE__ */
|
|
5246
|
+
/* @__PURE__ */ jsxs16("div", { className: "sidebar-segment", children: [
|
|
4608
5247
|
state.currentBlockIndex + 1,
|
|
4609
5248
|
"/",
|
|
4610
5249
|
state.totalBlocks
|
|
4611
5250
|
] }),
|
|
4612
|
-
state.hasCaptions && /* @__PURE__ */
|
|
5251
|
+
state.hasCaptions && /* @__PURE__ */ jsx23(
|
|
4613
5252
|
"button",
|
|
4614
5253
|
{
|
|
4615
5254
|
className: `sidebar-ctrl-btn ${state.captionMode !== "off" ? "sidebar-ctrl-btn--active" : ""}`,
|
|
4616
5255
|
onClick: () => actions.cycleCaptionMode(),
|
|
4617
5256
|
title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
|
|
4618
5257
|
"aria-label": "Cycle caption style",
|
|
4619
|
-
children: /* @__PURE__ */
|
|
5258
|
+
children: /* @__PURE__ */ jsx23("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx23("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" }) })
|
|
4620
5259
|
}
|
|
4621
5260
|
),
|
|
4622
|
-
actions.toggleFullscreen && /* @__PURE__ */
|
|
5261
|
+
actions.toggleFullscreen && /* @__PURE__ */ jsx23(
|
|
4623
5262
|
"button",
|
|
4624
5263
|
{
|
|
4625
5264
|
className: `sidebar-ctrl-btn ${state.isFullscreen ? "sidebar-ctrl-btn--active" : ""}`,
|
|
4626
5265
|
onClick: actions.toggleFullscreen,
|
|
4627
5266
|
title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
|
|
4628
5267
|
"aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
4629
|
-
children: state.isFullscreen ? /* @__PURE__ */
|
|
5268
|
+
children: state.isFullscreen ? /* @__PURE__ */ jsx23("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx23("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx23("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx23("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
|
|
4630
5269
|
}
|
|
4631
5270
|
)
|
|
4632
5271
|
] });
|
|
4633
5272
|
}
|
|
4634
5273
|
|
|
4635
5274
|
// src/DocPlayerWithSidebar.tsx
|
|
4636
|
-
import { useRef as
|
|
4637
|
-
import { jsx as
|
|
5275
|
+
import { useRef as useRef10, useState as useState10, useCallback as useCallback8, useEffect as useEffect11 } from "react";
|
|
5276
|
+
import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4638
5277
|
var DEFAULT_STATE = {
|
|
4639
5278
|
isPlaying: false,
|
|
4640
5279
|
currentTime: 0,
|
|
@@ -4656,6 +5295,7 @@ function DocPlayerWithSidebar({
|
|
|
4656
5295
|
onEnded,
|
|
4657
5296
|
onTimeUpdate,
|
|
4658
5297
|
audioController,
|
|
5298
|
+
animationsEnabled = true,
|
|
4659
5299
|
muted,
|
|
4660
5300
|
captionsEnabled,
|
|
4661
5301
|
isFullscreen,
|
|
@@ -4664,11 +5304,11 @@ function DocPlayerWithSidebar({
|
|
|
4664
5304
|
onPlayingChange,
|
|
4665
5305
|
theme
|
|
4666
5306
|
}) {
|
|
4667
|
-
const stateRef =
|
|
4668
|
-
const actionsRef =
|
|
4669
|
-
const wasPlayingRef =
|
|
4670
|
-
const [, setTick] =
|
|
4671
|
-
const handleStateChange =
|
|
5307
|
+
const stateRef = useRef10(DEFAULT_STATE);
|
|
5308
|
+
const actionsRef = useRef10(null);
|
|
5309
|
+
const wasPlayingRef = useRef10(false);
|
|
5310
|
+
const [, setTick] = useState10(0);
|
|
5311
|
+
const handleStateChange = useCallback8(
|
|
4672
5312
|
(state) => {
|
|
4673
5313
|
stateRef.current = state;
|
|
4674
5314
|
if (onPlayingChange && state.isPlaying !== wasPlayingRef.current) {
|
|
@@ -4678,7 +5318,7 @@ function DocPlayerWithSidebar({
|
|
|
4678
5318
|
},
|
|
4679
5319
|
[onPlayingChange]
|
|
4680
5320
|
);
|
|
4681
|
-
const handleControlsReady =
|
|
5321
|
+
const handleControlsReady = useCallback8(
|
|
4682
5322
|
(controls) => {
|
|
4683
5323
|
const isFirst = !actionsRef.current;
|
|
4684
5324
|
actionsRef.current = controls;
|
|
@@ -4686,14 +5326,14 @@ function DocPlayerWithSidebar({
|
|
|
4686
5326
|
},
|
|
4687
5327
|
[]
|
|
4688
5328
|
);
|
|
4689
|
-
|
|
5329
|
+
useEffect11(() => {
|
|
4690
5330
|
const interval = setInterval(() => {
|
|
4691
5331
|
setTick((t) => t + 1);
|
|
4692
5332
|
}, 250);
|
|
4693
5333
|
return () => clearInterval(interval);
|
|
4694
5334
|
}, []);
|
|
4695
|
-
return /* @__PURE__ */
|
|
4696
|
-
/* @__PURE__ */
|
|
5335
|
+
return /* @__PURE__ */ jsxs17("div", { className: "doc-player-sidebar-layout", children: [
|
|
5336
|
+
/* @__PURE__ */ jsx24("div", { className: "doc-player-sidebar-layout__video", children: /* @__PURE__ */ jsx24(
|
|
4697
5337
|
DocPlayer,
|
|
4698
5338
|
{
|
|
4699
5339
|
doc,
|
|
@@ -4703,6 +5343,7 @@ function DocPlayerWithSidebar({
|
|
|
4703
5343
|
onEnded,
|
|
4704
5344
|
onTimeUpdate,
|
|
4705
5345
|
audioController,
|
|
5346
|
+
animationsEnabled,
|
|
4706
5347
|
muted,
|
|
4707
5348
|
captionsEnabled,
|
|
4708
5349
|
showControls: isFullscreen,
|
|
@@ -4714,7 +5355,7 @@ function DocPlayerWithSidebar({
|
|
|
4714
5355
|
forceViewport
|
|
4715
5356
|
}
|
|
4716
5357
|
) }),
|
|
4717
|
-
actionsRef.current && /* @__PURE__ */
|
|
5358
|
+
actionsRef.current && /* @__PURE__ */ jsx24(DocControlsSidebar, { state: stateRef.current, actions: actionsRef.current })
|
|
4718
5359
|
] });
|
|
4719
5360
|
}
|
|
4720
5361
|
|
|
@@ -4745,18 +5386,18 @@ import {
|
|
|
4745
5386
|
arrayItemKind
|
|
4746
5387
|
} from "@bendyline/squisq/jsonForm";
|
|
4747
5388
|
import { parseMarkdown as parseMarkdown3 } from "@bendyline/squisq/markdown";
|
|
4748
|
-
import { Fragment as Fragment5, jsx as
|
|
5389
|
+
import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
4749
5390
|
function TextViewer({ value }) {
|
|
4750
5391
|
if (value === void 0 || value === null || value === "") {
|
|
4751
|
-
return /* @__PURE__ */
|
|
5392
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4752
5393
|
}
|
|
4753
|
-
return /* @__PURE__ */
|
|
5394
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-value", children: String(value) });
|
|
4754
5395
|
}
|
|
4755
5396
|
function MultilineViewer({ value }) {
|
|
4756
5397
|
if (value === void 0 || value === null || value === "") {
|
|
4757
|
-
return /* @__PURE__ */
|
|
5398
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4758
5399
|
}
|
|
4759
|
-
return /* @__PURE__ */
|
|
5400
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-value squisq-jv-value--multiline", children: String(value) });
|
|
4760
5401
|
}
|
|
4761
5402
|
function RichTextViewer({ value }) {
|
|
4762
5403
|
const nodes = useMemo11(() => {
|
|
@@ -4768,39 +5409,39 @@ function RichTextViewer({ value }) {
|
|
|
4768
5409
|
return null;
|
|
4769
5410
|
}
|
|
4770
5411
|
}, [value]);
|
|
4771
|
-
if (!nodes) return /* @__PURE__ */
|
|
4772
|
-
return /* @__PURE__ */
|
|
5412
|
+
if (!nodes) return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
5413
|
+
return /* @__PURE__ */ jsx25("div", { className: "squisq-jv-richtext", children: /* @__PURE__ */ jsx25(MarkdownRenderer, { nodes }) });
|
|
4773
5414
|
}
|
|
4774
5415
|
function NumberViewer({ value }) {
|
|
4775
5416
|
if (value === void 0 || value === null) {
|
|
4776
|
-
return /* @__PURE__ */
|
|
5417
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4777
5418
|
}
|
|
4778
|
-
return /* @__PURE__ */
|
|
5419
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-value", children: String(value) });
|
|
4779
5420
|
}
|
|
4780
5421
|
function BooleanViewer({ value }) {
|
|
4781
5422
|
const on = Boolean(value);
|
|
4782
|
-
return /* @__PURE__ */
|
|
5423
|
+
return /* @__PURE__ */ jsx25("span", { className: `squisq-jv-toggle squisq-jv-toggle--${on ? "on" : "off"}`, children: on ? "On" : "Off" });
|
|
4783
5424
|
}
|
|
4784
5425
|
function EnumViewer({ value, schema }) {
|
|
4785
5426
|
if (value === void 0 || value === null || value === "") {
|
|
4786
|
-
return /* @__PURE__ */
|
|
5427
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4787
5428
|
}
|
|
4788
5429
|
const labels = schema.squisq?.enumLabels;
|
|
4789
5430
|
const display = labels && typeof value === "string" ? labels[value] ?? value : String(value);
|
|
4790
|
-
return /* @__PURE__ */
|
|
5431
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-value", children: display });
|
|
4791
5432
|
}
|
|
4792
5433
|
function ColorViewer({ value }) {
|
|
4793
5434
|
if (typeof value !== "string" || value === "") {
|
|
4794
|
-
return /* @__PURE__ */
|
|
5435
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4795
5436
|
}
|
|
4796
|
-
return /* @__PURE__ */
|
|
4797
|
-
/* @__PURE__ */
|
|
4798
|
-
/* @__PURE__ */
|
|
5437
|
+
return /* @__PURE__ */ jsxs18("span", { className: "squisq-jv-color", children: [
|
|
5438
|
+
/* @__PURE__ */ jsx25("span", { className: "squisq-jv-color__swatch", style: { background: value }, "aria-hidden": true }),
|
|
5439
|
+
/* @__PURE__ */ jsx25("span", { className: "squisq-jv-color__hex", children: value })
|
|
4799
5440
|
] });
|
|
4800
5441
|
}
|
|
4801
5442
|
function DateViewer({ value, schema }) {
|
|
4802
5443
|
if (typeof value !== "string" || value === "") {
|
|
4803
|
-
return /* @__PURE__ */
|
|
5444
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4804
5445
|
}
|
|
4805
5446
|
const fmt = schema.format;
|
|
4806
5447
|
let display = value;
|
|
@@ -4817,31 +5458,31 @@ function DateViewer({ value, schema }) {
|
|
|
4817
5458
|
}
|
|
4818
5459
|
} catch {
|
|
4819
5460
|
}
|
|
4820
|
-
return /* @__PURE__ */
|
|
5461
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-value", children: display });
|
|
4821
5462
|
}
|
|
4822
5463
|
function ChipBinViewer({ value, schema }) {
|
|
4823
5464
|
if (!Array.isArray(value) || value.length === 0) {
|
|
4824
|
-
return /* @__PURE__ */
|
|
5465
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
4825
5466
|
}
|
|
4826
5467
|
const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
|
|
4827
5468
|
const labels = itemSchema?.squisq?.enumLabels;
|
|
4828
|
-
return /* @__PURE__ */
|
|
5469
|
+
return /* @__PURE__ */ jsx25("div", { className: "squisq-jv-chip-bin", children: value.map((item, i) => {
|
|
4829
5470
|
const label = labels && typeof item === "string" ? labels[item] ?? String(item) : String(item);
|
|
4830
|
-
return /* @__PURE__ */
|
|
5471
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-chip", children: label }, i);
|
|
4831
5472
|
}) });
|
|
4832
5473
|
}
|
|
4833
5474
|
function CardStackViewer(props) {
|
|
4834
5475
|
const { value, schema, rootSchema, rootData, pointer, density } = props;
|
|
4835
5476
|
if (!Array.isArray(value) || value.length === 0) {
|
|
4836
|
-
return /* @__PURE__ */
|
|
5477
|
+
return /* @__PURE__ */ jsx25("span", { className: "squisq-jv-empty", children: "No items" });
|
|
4837
5478
|
}
|
|
4838
5479
|
const itemSchema = (Array.isArray(schema.items) ? schema.items[0] : schema.items) ?? {};
|
|
4839
5480
|
const itemLabel = itemSchema.squisq?.itemLabel;
|
|
4840
|
-
return /* @__PURE__ */
|
|
5481
|
+
return /* @__PURE__ */ jsx25("div", { className: "squisq-jv-card-stack", children: value.map((item, i) => {
|
|
4841
5482
|
const title = resolveItemTitle(itemLabel, item, i);
|
|
4842
|
-
return /* @__PURE__ */
|
|
4843
|
-
title ? /* @__PURE__ */
|
|
4844
|
-
/* @__PURE__ */
|
|
5483
|
+
return /* @__PURE__ */ jsxs18("div", { className: "squisq-jv-card", children: [
|
|
5484
|
+
title ? /* @__PURE__ */ jsx25("h4", { className: "squisq-jv-card__title", children: title }) : null,
|
|
5485
|
+
/* @__PURE__ */ jsx25(
|
|
4845
5486
|
RenderNode,
|
|
4846
5487
|
{
|
|
4847
5488
|
value: item,
|
|
@@ -4872,16 +5513,16 @@ function GroupViewer(props) {
|
|
|
4872
5513
|
const help = schema.squisq?.help ?? schema.description;
|
|
4873
5514
|
const obj = (value && typeof value === "object" ? value : {}) ?? {};
|
|
4874
5515
|
const propEntries = Object.entries(schema.properties ?? {});
|
|
4875
|
-
return /* @__PURE__ */
|
|
4876
|
-
title ? /* @__PURE__ */
|
|
4877
|
-
help ? /* @__PURE__ */
|
|
4878
|
-
propEntries.map(([key, propSchema]) => /* @__PURE__ */
|
|
5516
|
+
return /* @__PURE__ */ jsxs18("section", { className: "squisq-jv-group", children: [
|
|
5517
|
+
title ? /* @__PURE__ */ jsx25("h3", { className: "squisq-jv-group__title", children: title }) : null,
|
|
5518
|
+
help ? /* @__PURE__ */ jsx25("p", { className: "squisq-jv-group__help", children: help }) : null,
|
|
5519
|
+
propEntries.map(([key, propSchema]) => /* @__PURE__ */ jsx25(Fragment4, { children: /* @__PURE__ */ jsx25(
|
|
4879
5520
|
RowOrSection,
|
|
4880
5521
|
{
|
|
4881
5522
|
label: propSchema.squisq?.label ?? propSchema.title ?? key,
|
|
4882
5523
|
help: propSchema.squisq?.help ?? propSchema.description,
|
|
4883
5524
|
kindHint: propSchema,
|
|
4884
|
-
children: /* @__PURE__ */
|
|
5525
|
+
children: /* @__PURE__ */ jsx25(
|
|
4885
5526
|
RenderNode,
|
|
4886
5527
|
{
|
|
4887
5528
|
value: obj[key],
|
|
@@ -4905,11 +5546,11 @@ function RowOrSection({
|
|
|
4905
5546
|
}) {
|
|
4906
5547
|
const composite = isCompositeKind(kindHint);
|
|
4907
5548
|
if (composite) {
|
|
4908
|
-
return /* @__PURE__ */
|
|
5549
|
+
return /* @__PURE__ */ jsx25(Fragment5, { children });
|
|
4909
5550
|
}
|
|
4910
|
-
return /* @__PURE__ */
|
|
4911
|
-
/* @__PURE__ */
|
|
4912
|
-
/* @__PURE__ */
|
|
5551
|
+
return /* @__PURE__ */ jsxs18("div", { className: "squisq-jv-row", children: [
|
|
5552
|
+
/* @__PURE__ */ jsx25("div", { className: "squisq-jv-label", title: help, children: label }),
|
|
5553
|
+
/* @__PURE__ */ jsx25("div", { children })
|
|
4913
5554
|
] });
|
|
4914
5555
|
}
|
|
4915
5556
|
function isCompositeKind(schema) {
|
|
@@ -4930,11 +5571,11 @@ function TabsViewer(props) {
|
|
|
4930
5571
|
const matchedIndex = pickMatchingBranch(branches, value);
|
|
4931
5572
|
const branch = branches[matchedIndex];
|
|
4932
5573
|
if (!branch) {
|
|
4933
|
-
return /* @__PURE__ */
|
|
5574
|
+
return /* @__PURE__ */ jsx25(TextViewer, { ...props });
|
|
4934
5575
|
}
|
|
4935
|
-
return /* @__PURE__ */
|
|
4936
|
-
/* @__PURE__ */
|
|
4937
|
-
/* @__PURE__ */
|
|
5576
|
+
return /* @__PURE__ */ jsxs18("div", { className: "squisq-jv-tabs", children: [
|
|
5577
|
+
/* @__PURE__ */ jsx25("span", { className: "squisq-jv-tabs__discriminator", children: branch.squisq?.label ?? branch.title ?? `Option ${matchedIndex + 1}` }),
|
|
5578
|
+
/* @__PURE__ */ jsx25(
|
|
4938
5579
|
RenderNode,
|
|
4939
5580
|
{
|
|
4940
5581
|
value,
|
|
@@ -4998,7 +5639,7 @@ var VIEWERS = {
|
|
|
4998
5639
|
};
|
|
4999
5640
|
|
|
5000
5641
|
// src/jsonView/RenderNode.tsx
|
|
5001
|
-
import { jsx as
|
|
5642
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
5002
5643
|
function RenderNode(props) {
|
|
5003
5644
|
const resolved = resolveRef(props.schema, props.rootSchema) ?? props.schema;
|
|
5004
5645
|
if (resolveFlag(resolved.squisq?.hidden, props.rootData)) return null;
|
|
@@ -5014,18 +5655,18 @@ function RenderNode(props) {
|
|
|
5014
5655
|
};
|
|
5015
5656
|
if (kind === "group" || kind === "card") {
|
|
5016
5657
|
const Group = Viewer;
|
|
5017
|
-
return /* @__PURE__ */
|
|
5658
|
+
return /* @__PURE__ */ jsx26(Group, { ...viewerProps, suppressTitle: props.suppressTopGroupTitle });
|
|
5018
5659
|
}
|
|
5019
|
-
return /* @__PURE__ */
|
|
5660
|
+
return /* @__PURE__ */ jsx26(Viewer, { ...viewerProps });
|
|
5020
5661
|
}
|
|
5021
5662
|
|
|
5022
5663
|
// src/jsonView/JsonView.tsx
|
|
5023
|
-
import { jsx as
|
|
5664
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
5024
5665
|
function JsonView(props) {
|
|
5025
5666
|
const { schema, value, theme, surface, density = "comfortable", className } = props;
|
|
5026
5667
|
const { style } = useJsonViewTokens(theme, surface);
|
|
5027
5668
|
const cls = "squisq-json-view" + (density === "compact" ? " squisq-json-view--compact" : "") + (className ? ` ${className}` : "");
|
|
5028
|
-
return /* @__PURE__ */
|
|
5669
|
+
return /* @__PURE__ */ jsx27("div", { className: cls, style, children: /* @__PURE__ */ jsx27(
|
|
5029
5670
|
RenderNode,
|
|
5030
5671
|
{
|
|
5031
5672
|
value,
|
|
@@ -5061,7 +5702,7 @@ export {
|
|
|
5061
5702
|
SocialCaptionOverlay,
|
|
5062
5703
|
TableLayer,
|
|
5063
5704
|
TextLayer,
|
|
5064
|
-
|
|
5705
|
+
TreeLayer,
|
|
5065
5706
|
VideoLayer,
|
|
5066
5707
|
formatTime,
|
|
5067
5708
|
getAnimationStyle,
|