@bendyline/squisq-react 2.0.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE.md +20 -0
- package/README.md +6 -0
- package/dist/index.d.ts +101 -96
- package/dist/index.js +1483 -782
- package/dist/squisq-player.full.global.js +4165 -0
- package/dist/squisq-player.global.js +561 -168
- package/dist/standalone-source.d.ts +1 -0
- package/dist/standalone-source.js +1 -1
- package/dist/styles/index.css +64 -0
- package/package.json +8 -4
- package/dist/index.js.map +0 -1
- package/dist/squisq-player.css +0 -2
- package/dist/squisq-player.css.map +0 -1
- package/dist/squisq-player.global.js.map +0 -1
- package/src/BlockRenderer.tsx +0 -194
- package/src/CaptionOverlay.tsx +0 -111
- package/src/DocControlsBottom.tsx +0 -109
- package/src/DocControlsOverlay.tsx +0 -190
- package/src/DocControlsSidebar.tsx +0 -113
- package/src/DocControlsSlideshow.tsx +0 -360
- package/src/DocPlayer.tsx +0 -1528
- package/src/DocPlayerWithSidebar.tsx +0 -155
- package/src/DocProgressBar.tsx +0 -257
- package/src/InlineAudioPlayer.tsx +0 -46
- package/src/InlineVideoPlayer.tsx +0 -70
- package/src/LinearDocView.tsx +0 -569
- package/src/MarkdownRenderer.tsx +0 -624
- package/src/MediaClipLayer.tsx +0 -145
- package/src/SocialCaptionOverlay.tsx +0 -255
- package/src/__tests__/BlockRenderer.test.tsx +0 -235
- package/src/__tests__/DocControlsSlideshow.test.tsx +0 -221
- package/src/__tests__/DocPlayer.test.tsx +0 -635
- package/src/__tests__/DocPlayerStylesSentinel.test.tsx +0 -41
- package/src/__tests__/DocProgressBar.test.tsx +0 -102
- package/src/__tests__/JsonView.test.tsx +0 -111
- package/src/__tests__/LinearDocView.test.tsx +0 -426
- package/src/__tests__/MapLayer.test.tsx +0 -63
- package/src/__tests__/MarkdownRenderer.test.tsx +0 -357
- package/src/__tests__/MediaClipLayer.test.tsx +0 -70
- package/src/__tests__/MediaContext.test.tsx +0 -51
- package/src/__tests__/PathLayer.test.tsx +0 -84
- package/src/__tests__/TableLayer.test.tsx +0 -142
- package/src/__tests__/VideoLayer.test.tsx +0 -94
- package/src/__tests__/exports.test.ts +0 -55
- package/src/__tests__/fillStyle.test.tsx +0 -160
- package/src/__tests__/standaloneEntry.test.tsx +0 -103
- package/src/__tests__/transitionStyles.test.ts +0 -125
- package/src/__tests__/useAudioSync.test.ts +0 -49
- package/src/__tests__/useDocPlayback.transition.test.ts +0 -113
- package/src/__tests__/useJsonViewTokens.test.ts +0 -41
- package/src/__tests__/useSlideSwipe.test.ts +0 -81
- package/src/__tests__/useViewportOrientation.test.ts +0 -22
- package/src/hooks/AudioController.ts +0 -114
- package/src/hooks/MediaContext.tsx +0 -97
- package/src/hooks/index.ts +0 -11
- package/src/hooks/useAudioSync.ts +0 -456
- package/src/hooks/useAutoSurface.ts +0 -33
- package/src/hooks/useDocPlayback.ts +0 -265
- package/src/hooks/useMediaSchedule.ts +0 -39
- package/src/hooks/useSlideSwipe.ts +0 -265
- package/src/hooks/useViewportOrientation.ts +0 -115
- package/src/index.ts +0 -70
- package/src/jsonView/JsonView.tsx +0 -51
- package/src/jsonView/RenderNode.tsx +0 -51
- package/src/jsonView/index.ts +0 -2
- package/src/jsonView/json-view.css +0 -206
- package/src/jsonView/useJsonViewTokens.ts +0 -32
- package/src/jsonView/viewers.tsx +0 -343
- package/src/layers/ImageLayer.tsx +0 -297
- package/src/layers/MapLayer.tsx +0 -185
- package/src/layers/PathLayer.tsx +0 -155
- package/src/layers/ShapeLayer.tsx +0 -158
- package/src/layers/TableLayer.tsx +0 -129
- package/src/layers/TextLayer.tsx +0 -571
- package/src/layers/TreeLayer.tsx +0 -167
- package/src/layers/VideoLayer.tsx +0 -171
- package/src/layers/index.ts +0 -6
- package/src/standalone-entry.tsx +0 -337
- package/src/standalone-source.d.ts +0 -10
- package/src/styles/doc-animations.css +0 -2431
- package/src/styles/index.css +0 -7
- package/src/types.ts +0 -183
- package/src/utils/animationUtils.ts +0 -13
- package/src/utils/fillStyle.tsx +0 -148
- package/src/utils/layerUtils.ts +0 -42
- package/src/utils/mapTileUtils.ts +0 -375
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/DocPlayer.tsx
|
|
2
|
-
import { Fragment as
|
|
2
|
+
import { Fragment as Fragment4, useId as useId7, useRef as useRef9, useState as useState10, useEffect as useEffect11, useCallback as useCallback7, useMemo as useMemo10 } from "react";
|
|
3
3
|
import {
|
|
4
4
|
isTemplateBlock as isTemplateBlock3,
|
|
5
5
|
getCaptionAtTime as getCaptionAtTime2,
|
|
@@ -12,18 +12,29 @@ import { useEffect as useEffect2, useRef } from "react";
|
|
|
12
12
|
|
|
13
13
|
// src/hooks/MediaContext.tsx
|
|
14
14
|
import { createContext, useContext, useState, useEffect, useMemo } from "react";
|
|
15
|
+
import {
|
|
16
|
+
DEFAULT_INTERACTIVE_RESOURCE_POLICY,
|
|
17
|
+
isResourceUrlAllowed
|
|
18
|
+
} from "@bendyline/squisq/markdown";
|
|
15
19
|
var MediaContext = createContext(null);
|
|
20
|
+
var ResourcePolicyContext = createContext(
|
|
21
|
+
DEFAULT_INTERACTIVE_RESOURCE_POLICY
|
|
22
|
+
);
|
|
23
|
+
function useResourcePolicy() {
|
|
24
|
+
return useContext(ResourcePolicyContext);
|
|
25
|
+
}
|
|
16
26
|
function useMediaProvider() {
|
|
17
27
|
return useContext(MediaContext);
|
|
18
28
|
}
|
|
19
29
|
function useMediaUrl(relativePath, basePath) {
|
|
20
30
|
const provider = useMediaProvider();
|
|
31
|
+
const resourcePolicy = useResourcePolicy();
|
|
21
32
|
const safePath = typeof relativePath === "string" ? relativePath : "";
|
|
22
|
-
const isAbsolute = !safePath ||
|
|
23
|
-
const fallback = useMemo(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
33
|
+
const isAbsolute = !safePath || /^(?:[a-z][a-z0-9+.-]*:|\/\/|\/)/i.test(safePath);
|
|
34
|
+
const fallback = useMemo(() => {
|
|
35
|
+
const candidate = isAbsolute ? safePath : `${basePath.replace(/\/$/, "")}/${safePath}`;
|
|
36
|
+
return isResourceUrlAllowed(candidate, resourcePolicy) ? candidate : "";
|
|
37
|
+
}, [isAbsolute, safePath, basePath, resourcePolicy]);
|
|
27
38
|
const needsProvider = !isAbsolute && !!provider;
|
|
28
39
|
const [url, setUrl] = useState(fallback);
|
|
29
40
|
useEffect(() => {
|
|
@@ -35,7 +46,9 @@ function useMediaUrl(relativePath, basePath) {
|
|
|
35
46
|
setUrl(fallback);
|
|
36
47
|
provider.resolveUrl(safePath).then(
|
|
37
48
|
(resolved) => {
|
|
38
|
-
if (!cancelled)
|
|
49
|
+
if (!cancelled) {
|
|
50
|
+
setUrl(isResourceUrlAllowed(resolved, resourcePolicy) ? resolved : "");
|
|
51
|
+
}
|
|
39
52
|
},
|
|
40
53
|
() => {
|
|
41
54
|
if (!cancelled) setUrl(fallback);
|
|
@@ -44,7 +57,7 @@ function useMediaUrl(relativePath, basePath) {
|
|
|
44
57
|
return () => {
|
|
45
58
|
cancelled = true;
|
|
46
59
|
};
|
|
47
|
-
}, [needsProvider, provider, safePath, fallback]);
|
|
60
|
+
}, [needsProvider, provider, safePath, fallback, resourcePolicy]);
|
|
48
61
|
return needsProvider ? url : fallback;
|
|
49
62
|
}
|
|
50
63
|
|
|
@@ -1039,8 +1052,13 @@ function MarkerDef({
|
|
|
1039
1052
|
|
|
1040
1053
|
// src/layers/MapLayer.tsx
|
|
1041
1054
|
import { useId as useId4, useState as useState2, useEffect as useEffect3 } from "react";
|
|
1055
|
+
import { ResourcePolicyError as ResourcePolicyError2 } from "@bendyline/squisq/markdown";
|
|
1042
1056
|
|
|
1043
1057
|
// src/utils/mapTileUtils.ts
|
|
1058
|
+
import {
|
|
1059
|
+
isResourceUrlAllowed as isResourceUrlAllowed2,
|
|
1060
|
+
ResourcePolicyError
|
|
1061
|
+
} from "@bendyline/squisq/markdown";
|
|
1044
1062
|
var TILE_PROVIDERS = {
|
|
1045
1063
|
terrain: {
|
|
1046
1064
|
url: "https://tile.opentopomap.org/{z}/{x}/{y}.png",
|
|
@@ -1123,6 +1141,13 @@ async function composeMapImage(options) {
|
|
|
1123
1141
|
const provider = TILE_PROVIDERS[style];
|
|
1124
1142
|
const tileSize = provider.tileSize || 256;
|
|
1125
1143
|
const clampedZoom = Math.min(zoom, provider.maxZoom);
|
|
1144
|
+
const probeUrl = buildTileUrl(provider, 0, 0, clampedZoom);
|
|
1145
|
+
if (!isResourceUrlAllowed2(probeUrl, options.policy)) {
|
|
1146
|
+
throw new ResourcePolicyError(
|
|
1147
|
+
"RESOURCE_BLOCKED",
|
|
1148
|
+
`Map tiles for style "${style}" are blocked by the resource policy (${provider.url}). Composing a map contacts the tile host; supply the map as \`staticSrc\` to render it without any remote request.`
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1126
1151
|
const canvas = document.createElement("canvas");
|
|
1127
1152
|
canvas.width = width;
|
|
1128
1153
|
canvas.height = height;
|
|
@@ -1228,6 +1253,7 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1228
1253
|
const [mapImageUrl, setMapImageUrl] = useState2(null);
|
|
1229
1254
|
const [isLoading, setIsLoading] = useState2(true);
|
|
1230
1255
|
const [error, setError] = useState2(null);
|
|
1256
|
+
const [blockedByPolicy, setBlockedByPolicy] = useState2(false);
|
|
1231
1257
|
const x = resolveValue(position.x, viewport.width);
|
|
1232
1258
|
const y = resolveValue(position.y, viewport.height);
|
|
1233
1259
|
const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;
|
|
@@ -1235,16 +1261,18 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1235
1261
|
const offset = getAnchorOffset(position.anchor, width, height);
|
|
1236
1262
|
const finalX = x + offset.x;
|
|
1237
1263
|
const finalY = y + offset.y;
|
|
1264
|
+
const staticSrc = useMediaUrl(content.staticSrc ?? "", basePath);
|
|
1265
|
+
const resourcePolicy = useResourcePolicy();
|
|
1238
1266
|
useEffect3(() => {
|
|
1239
1267
|
let cancelled = false;
|
|
1240
1268
|
if (content.staticSrc) {
|
|
1241
|
-
|
|
1242
|
-
setMapImageUrl(src);
|
|
1269
|
+
setMapImageUrl(staticSrc || null);
|
|
1243
1270
|
setIsLoading(false);
|
|
1244
1271
|
return;
|
|
1245
1272
|
}
|
|
1246
1273
|
setIsLoading(true);
|
|
1247
1274
|
setError(null);
|
|
1275
|
+
setBlockedByPolicy(false);
|
|
1248
1276
|
composeMapImage({
|
|
1249
1277
|
center: content.center,
|
|
1250
1278
|
zoom: content.zoom,
|
|
@@ -1252,7 +1280,8 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1252
1280
|
width,
|
|
1253
1281
|
height,
|
|
1254
1282
|
markers: content.markers,
|
|
1255
|
-
showAttribution: content.showAttribution !== false
|
|
1283
|
+
showAttribution: content.showAttribution !== false,
|
|
1284
|
+
policy: resourcePolicy
|
|
1256
1285
|
}).then((dataUrl) => {
|
|
1257
1286
|
if (!cancelled) {
|
|
1258
1287
|
setMapImageUrl(dataUrl);
|
|
@@ -1260,8 +1289,10 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1260
1289
|
}
|
|
1261
1290
|
}).catch((err) => {
|
|
1262
1291
|
if (!cancelled) {
|
|
1263
|
-
|
|
1292
|
+
const blocked = err instanceof ResourcePolicyError2;
|
|
1293
|
+
if (!blocked) console.error("Failed to compose map:", err);
|
|
1264
1294
|
setError(err instanceof Error ? err.message : String(err));
|
|
1295
|
+
setBlockedByPolicy(blocked);
|
|
1265
1296
|
setIsLoading(false);
|
|
1266
1297
|
}
|
|
1267
1298
|
});
|
|
@@ -1273,11 +1304,13 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1273
1304
|
content.zoom,
|
|
1274
1305
|
content.style,
|
|
1275
1306
|
content.staticSrc,
|
|
1307
|
+
staticSrc,
|
|
1276
1308
|
content.markers,
|
|
1277
1309
|
content.showAttribution,
|
|
1278
1310
|
width,
|
|
1279
1311
|
height,
|
|
1280
|
-
basePath
|
|
1312
|
+
basePath,
|
|
1313
|
+
resourcePolicy
|
|
1281
1314
|
]);
|
|
1282
1315
|
const animStyle = getAnimationStyle(animation, blockTime);
|
|
1283
1316
|
if (isLoading) {
|
|
@@ -1314,7 +1347,16 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1314
1347
|
style: animStyle.style,
|
|
1315
1348
|
"data-layer-id": layer.id,
|
|
1316
1349
|
children: [
|
|
1317
|
-
/* @__PURE__ */ jsx7(
|
|
1350
|
+
/* @__PURE__ */ jsx7(
|
|
1351
|
+
"rect",
|
|
1352
|
+
{
|
|
1353
|
+
x: finalX,
|
|
1354
|
+
y: finalY,
|
|
1355
|
+
width,
|
|
1356
|
+
height,
|
|
1357
|
+
fill: blockedByPolicy ? "#f3f4f6" : "#fef2f2"
|
|
1358
|
+
}
|
|
1359
|
+
),
|
|
1318
1360
|
/* @__PURE__ */ jsx7(
|
|
1319
1361
|
"text",
|
|
1320
1362
|
{
|
|
@@ -1322,10 +1364,10 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1322
1364
|
y: finalY + height / 2,
|
|
1323
1365
|
textAnchor: "middle",
|
|
1324
1366
|
dominantBaseline: "middle",
|
|
1325
|
-
fill: "#dc2626",
|
|
1367
|
+
fill: blockedByPolicy ? "#6b7280" : "#dc2626",
|
|
1326
1368
|
fontSize: "18",
|
|
1327
1369
|
fontFamily: "system-ui, sans-serif",
|
|
1328
|
-
children: "Map failed to load"
|
|
1370
|
+
children: blockedByPolicy ? "Map unavailable offline" : "Map failed to load"
|
|
1329
1371
|
}
|
|
1330
1372
|
)
|
|
1331
1373
|
]
|
|
@@ -1462,76 +1504,84 @@ function TableLayer({ layer, viewport, blockTime }) {
|
|
|
1462
1504
|
const offset = getAnchorOffset(position.anchor, width, height);
|
|
1463
1505
|
const finalX = x + offset.x;
|
|
1464
1506
|
const finalY = y + offset.y;
|
|
1465
|
-
const animStyle =
|
|
1507
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
1466
1508
|
const cellAlign = (ci) => {
|
|
1467
1509
|
const a = align?.[ci];
|
|
1468
1510
|
return a ? { textAlign: a } : void 0;
|
|
1469
1511
|
};
|
|
1470
1512
|
const borderRadius = style.borderRadius ?? 8;
|
|
1471
|
-
return /* @__PURE__ */ jsx9(
|
|
1472
|
-
"
|
|
1513
|
+
return /* @__PURE__ */ jsx9(
|
|
1514
|
+
"g",
|
|
1473
1515
|
{
|
|
1474
|
-
|
|
1475
|
-
style:
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
alignItems: "center",
|
|
1480
|
-
justifyContent: "center",
|
|
1481
|
-
padding: "16px",
|
|
1482
|
-
boxSizing: "border-box"
|
|
1483
|
-
},
|
|
1484
|
-
children: /* @__PURE__ */ jsxs6(
|
|
1485
|
-
"table",
|
|
1516
|
+
className: `block-layer block-layer--table ${animStyle.className}`,
|
|
1517
|
+
style: animStyle.style,
|
|
1518
|
+
"data-layer-id": layer.id,
|
|
1519
|
+
children: /* @__PURE__ */ jsx9("foreignObject", { x: finalX, y: finalY, width, height, children: /* @__PURE__ */ jsx9(
|
|
1520
|
+
"div",
|
|
1486
1521
|
{
|
|
1522
|
+
...{ xmlns: "http://www.w3.org/1999/xhtml" },
|
|
1487
1523
|
style: {
|
|
1488
|
-
width:
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
border: `1px solid ${style.borderColor}`
|
|
1524
|
+
width: `${width}px`,
|
|
1525
|
+
height: `${height}px`,
|
|
1526
|
+
display: "flex",
|
|
1527
|
+
alignItems: "center",
|
|
1528
|
+
justifyContent: "center",
|
|
1529
|
+
padding: "16px",
|
|
1530
|
+
boxSizing: "border-box"
|
|
1496
1531
|
},
|
|
1497
|
-
children:
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
{
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
...cellAlign(ci)
|
|
1510
|
-
},
|
|
1511
|
-
children: header
|
|
1512
|
-
},
|
|
1513
|
-
ci
|
|
1514
|
-
)) }) }),
|
|
1515
|
-
rows.length > 0 && /* @__PURE__ */ jsx9("tbody", { children: rows.map((row, ri) => /* @__PURE__ */ jsx9("tr", { children: row.map((cell, ci) => /* @__PURE__ */ jsx9(
|
|
1516
|
-
"td",
|
|
1517
|
-
{
|
|
1518
|
-
style: {
|
|
1519
|
-
background: style.cellBackground,
|
|
1520
|
-
color: style.cellColor,
|
|
1521
|
-
padding: "10px 16px",
|
|
1522
|
-
borderBottom: ri < rows.length - 1 ? `1px solid ${style.borderColor}` : void 0,
|
|
1523
|
-
borderRight: ci < row.length - 1 ? `1px solid ${style.borderColor}` : void 0,
|
|
1524
|
-
...cellAlign(ci)
|
|
1525
|
-
},
|
|
1526
|
-
children: cell
|
|
1532
|
+
children: /* @__PURE__ */ jsxs6(
|
|
1533
|
+
"table",
|
|
1534
|
+
{
|
|
1535
|
+
style: {
|
|
1536
|
+
width: "100%",
|
|
1537
|
+
borderCollapse: "separate",
|
|
1538
|
+
borderSpacing: 0,
|
|
1539
|
+
fontSize: `${style.fontSize}px`,
|
|
1540
|
+
fontFamily: style.fontFamily ?? "system-ui, sans-serif",
|
|
1541
|
+
overflow: "hidden",
|
|
1542
|
+
borderRadius: `${borderRadius}px`,
|
|
1543
|
+
border: `1px solid ${style.borderColor}`
|
|
1527
1544
|
},
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1545
|
+
children: [
|
|
1546
|
+
headers.length > 0 && /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsx9("tr", { children: headers.map((header, ci) => /* @__PURE__ */ jsx9(
|
|
1547
|
+
"th",
|
|
1548
|
+
{
|
|
1549
|
+
style: {
|
|
1550
|
+
background: style.headerBackground,
|
|
1551
|
+
color: style.headerColor,
|
|
1552
|
+
fontFamily: style.headerFontFamily ?? style.fontFamily ?? "system-ui, sans-serif",
|
|
1553
|
+
fontWeight: 600,
|
|
1554
|
+
padding: "12px 16px",
|
|
1555
|
+
borderBottom: `2px solid ${style.borderColor}`,
|
|
1556
|
+
borderRight: ci < headers.length - 1 ? `1px solid ${style.borderColor}` : void 0,
|
|
1557
|
+
...cellAlign(ci)
|
|
1558
|
+
},
|
|
1559
|
+
children: header
|
|
1560
|
+
},
|
|
1561
|
+
ci
|
|
1562
|
+
)) }) }),
|
|
1563
|
+
rows.length > 0 && /* @__PURE__ */ jsx9("tbody", { children: rows.map((row, ri) => /* @__PURE__ */ jsx9("tr", { children: row.map((cell, ci) => /* @__PURE__ */ jsx9(
|
|
1564
|
+
"td",
|
|
1565
|
+
{
|
|
1566
|
+
style: {
|
|
1567
|
+
background: style.cellBackground,
|
|
1568
|
+
color: style.cellColor,
|
|
1569
|
+
padding: "10px 16px",
|
|
1570
|
+
borderBottom: ri < rows.length - 1 ? `1px solid ${style.borderColor}` : void 0,
|
|
1571
|
+
borderRight: ci < row.length - 1 ? `1px solid ${style.borderColor}` : void 0,
|
|
1572
|
+
...cellAlign(ci)
|
|
1573
|
+
},
|
|
1574
|
+
children: cell
|
|
1575
|
+
},
|
|
1576
|
+
ci
|
|
1577
|
+
)) }, ri)) })
|
|
1578
|
+
]
|
|
1579
|
+
}
|
|
1580
|
+
)
|
|
1531
1581
|
}
|
|
1532
|
-
)
|
|
1582
|
+
) })
|
|
1533
1583
|
}
|
|
1534
|
-
)
|
|
1584
|
+
);
|
|
1535
1585
|
}
|
|
1536
1586
|
|
|
1537
1587
|
// src/layers/TreeLayer.tsx
|
|
@@ -1554,16 +1604,14 @@ function TreeLayer({ layer, viewport, blockTime }) {
|
|
|
1554
1604
|
const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;
|
|
1555
1605
|
const height = position.height ? resolveValue(position.height, viewport.height) : viewport.height;
|
|
1556
1606
|
const offset = getAnchorOffset(position.anchor, width, height);
|
|
1557
|
-
const animStyle =
|
|
1607
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
1558
1608
|
return /* @__PURE__ */ jsx10(
|
|
1559
|
-
"
|
|
1609
|
+
"g",
|
|
1560
1610
|
{
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
height,
|
|
1565
|
-
style: animStyle,
|
|
1566
|
-
children: /* @__PURE__ */ jsx10(
|
|
1611
|
+
className: `block-layer block-layer--tree ${animStyle.className}`,
|
|
1612
|
+
style: animStyle.style,
|
|
1613
|
+
"data-layer-id": layer.id,
|
|
1614
|
+
children: /* @__PURE__ */ jsx10("foreignObject", { x: x + offset.x, y: y + offset.y, width, height, children: /* @__PURE__ */ jsx10(
|
|
1567
1615
|
"div",
|
|
1568
1616
|
{
|
|
1569
1617
|
...{ xmlns: "http://www.w3.org/1999/xhtml" },
|
|
@@ -1583,7 +1631,7 @@ function TreeLayer({ layer, viewport, blockTime }) {
|
|
|
1583
1631
|
},
|
|
1584
1632
|
children: /* @__PURE__ */ jsx10(TreeList, { items, depth: 0, style })
|
|
1585
1633
|
}
|
|
1586
|
-
)
|
|
1634
|
+
) })
|
|
1587
1635
|
}
|
|
1588
1636
|
);
|
|
1589
1637
|
}
|
|
@@ -1662,8 +1710,146 @@ function TreeRow({
|
|
|
1662
1710
|
] });
|
|
1663
1711
|
}
|
|
1664
1712
|
|
|
1665
|
-
// src/
|
|
1713
|
+
// src/mermaid/MermaidDiagram.tsx
|
|
1714
|
+
import { useEffect as useEffect5, useState as useState4 } from "react";
|
|
1715
|
+
|
|
1716
|
+
// src/mermaid/mermaidRuntime.ts
|
|
1717
|
+
var mermaidPromise = null;
|
|
1718
|
+
async function loadMermaid() {
|
|
1719
|
+
if (!mermaidPromise) {
|
|
1720
|
+
mermaidPromise = import("mermaid").then(({ default: mermaid }) => {
|
|
1721
|
+
mermaid.initialize({
|
|
1722
|
+
startOnLoad: false,
|
|
1723
|
+
securityLevel: "strict",
|
|
1724
|
+
suppressErrorRendering: true,
|
|
1725
|
+
deterministicIds: false,
|
|
1726
|
+
logLevel: "error"
|
|
1727
|
+
});
|
|
1728
|
+
return mermaid;
|
|
1729
|
+
}).catch((error) => {
|
|
1730
|
+
mermaidPromise = null;
|
|
1731
|
+
throw error;
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
return mermaidPromise;
|
|
1735
|
+
}
|
|
1736
|
+
function enqueue(run) {
|
|
1737
|
+
const host = globalThis;
|
|
1738
|
+
const previous = host.__squisqMermaidWorkQueue ?? Promise.resolve();
|
|
1739
|
+
const result = previous.then(run, run);
|
|
1740
|
+
host.__squisqMermaidWorkQueue = result.then(
|
|
1741
|
+
() => void 0,
|
|
1742
|
+
() => void 0
|
|
1743
|
+
);
|
|
1744
|
+
return result;
|
|
1745
|
+
}
|
|
1746
|
+
function renderMermaidSvg(id, source, container) {
|
|
1747
|
+
return enqueue(async () => {
|
|
1748
|
+
const mermaid = await loadMermaid();
|
|
1749
|
+
const { svg, diagramType } = await mermaid.render(id, source, container);
|
|
1750
|
+
return { svg, diagramType };
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
function mermaidRenderErrorMessage(error) {
|
|
1754
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
1755
|
+
return raw.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean).slice(0, 6).join("\n") || "Unknown Mermaid rendering error.";
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
// src/mermaid/MermaidDiagram.tsx
|
|
1666
1759
|
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1760
|
+
var renderSequence = 0;
|
|
1761
|
+
function MermaidDiagram({
|
|
1762
|
+
source,
|
|
1763
|
+
className,
|
|
1764
|
+
ariaLabel = "Mermaid diagram"
|
|
1765
|
+
}) {
|
|
1766
|
+
const [svg, setSvg] = useState4("");
|
|
1767
|
+
const [error, setError] = useState4("");
|
|
1768
|
+
const [rendering, setRendering] = useState4(true);
|
|
1769
|
+
useEffect5(() => {
|
|
1770
|
+
let current = true;
|
|
1771
|
+
const id = `squisq-rich-mermaid-${++renderSequence}`;
|
|
1772
|
+
setRendering(true);
|
|
1773
|
+
setError("");
|
|
1774
|
+
void renderMermaidSvg(id, source).then((result) => {
|
|
1775
|
+
if (!current) return;
|
|
1776
|
+
setSvg(result.svg);
|
|
1777
|
+
}).catch((caught) => {
|
|
1778
|
+
if (!current) return;
|
|
1779
|
+
setSvg("");
|
|
1780
|
+
setError(mermaidRenderErrorMessage(caught));
|
|
1781
|
+
}).finally(() => {
|
|
1782
|
+
if (current) setRendering(false);
|
|
1783
|
+
});
|
|
1784
|
+
return () => {
|
|
1785
|
+
current = false;
|
|
1786
|
+
};
|
|
1787
|
+
}, [source]);
|
|
1788
|
+
const classes = ["squisq-mermaid-rendered", className].filter(Boolean).join(" ");
|
|
1789
|
+
return /* @__PURE__ */ jsxs8("div", { className: classes, role: "img", "aria-label": ariaLabel, children: [
|
|
1790
|
+
rendering && /* @__PURE__ */ jsx11("div", { className: "squisq-mermaid-render-status", children: "Rendering diagram\u2026" }),
|
|
1791
|
+
!rendering && error && /* @__PURE__ */ jsxs8("div", { className: "squisq-mermaid-render-error", role: "alert", children: [
|
|
1792
|
+
/* @__PURE__ */ jsx11("strong", { children: "Mermaid could not render this diagram." }),
|
|
1793
|
+
/* @__PURE__ */ jsx11("pre", { children: error })
|
|
1794
|
+
] }),
|
|
1795
|
+
!rendering && !error && svg && /* @__PURE__ */ jsx11(
|
|
1796
|
+
"div",
|
|
1797
|
+
{
|
|
1798
|
+
className: "squisq-mermaid-render-svg",
|
|
1799
|
+
dangerouslySetInnerHTML: { __html: svg }
|
|
1800
|
+
}
|
|
1801
|
+
)
|
|
1802
|
+
] });
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// src/layers/MermaidLayer.tsx
|
|
1806
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1807
|
+
function MermaidLayer({ layer, viewport, blockTime }) {
|
|
1808
|
+
const { position, content, animation } = layer;
|
|
1809
|
+
const width = resolveValue(position.width ?? viewport.width, viewport.width);
|
|
1810
|
+
const height = resolveValue(position.height ?? viewport.height, viewport.height);
|
|
1811
|
+
let x = resolveValue(position.x, viewport.width);
|
|
1812
|
+
let y = resolveValue(position.y, viewport.height);
|
|
1813
|
+
const anchor = position.anchor ?? "top-left";
|
|
1814
|
+
if (anchor === "center") {
|
|
1815
|
+
x -= width / 2;
|
|
1816
|
+
y -= height / 2;
|
|
1817
|
+
} else {
|
|
1818
|
+
if (anchor.endsWith("right")) x -= width;
|
|
1819
|
+
if (anchor.startsWith("bottom")) y -= height;
|
|
1820
|
+
}
|
|
1821
|
+
const animStyle = getAnimationStyle(animation, blockTime);
|
|
1822
|
+
const panelStyle = {
|
|
1823
|
+
boxSizing: "border-box",
|
|
1824
|
+
width: "100%",
|
|
1825
|
+
height: "100%",
|
|
1826
|
+
padding: content.padding ?? 16,
|
|
1827
|
+
borderRadius: 16,
|
|
1828
|
+
background: content.background,
|
|
1829
|
+
color: content.foreground,
|
|
1830
|
+
overflow: "hidden"
|
|
1831
|
+
};
|
|
1832
|
+
return /* @__PURE__ */ jsx12(
|
|
1833
|
+
"g",
|
|
1834
|
+
{
|
|
1835
|
+
className: `block-layer block-layer--mermaid ${animStyle.className}`,
|
|
1836
|
+
"data-layer-id": layer.id,
|
|
1837
|
+
style: animStyle.style,
|
|
1838
|
+
children: /* @__PURE__ */ jsx12("foreignObject", { x, y, width, height, children: /* @__PURE__ */ jsx12(
|
|
1839
|
+
"div",
|
|
1840
|
+
{
|
|
1841
|
+
...{ xmlns: "http://www.w3.org/1999/xhtml" },
|
|
1842
|
+
className: "squisq-mermaid-layer-frame",
|
|
1843
|
+
style: panelStyle,
|
|
1844
|
+
children: /* @__PURE__ */ jsx12(MermaidDiagram, { source: content.source, ariaLabel: "Mermaid diagram on slide" })
|
|
1845
|
+
}
|
|
1846
|
+
) })
|
|
1847
|
+
}
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// src/BlockRenderer.tsx
|
|
1852
|
+
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1667
1853
|
var DEFAULT_VIEWPORT = {
|
|
1668
1854
|
width: 1920,
|
|
1669
1855
|
height: 1080
|
|
@@ -1691,7 +1877,7 @@ function BlockRenderer({
|
|
|
1691
1877
|
}
|
|
1692
1878
|
const instanceId = useId5().replace(/:/g, "");
|
|
1693
1879
|
const clipId = `vb-clip-${instanceId}-${block.id}`;
|
|
1694
|
-
return /* @__PURE__ */
|
|
1880
|
+
return /* @__PURE__ */ jsxs9(
|
|
1695
1881
|
"svg",
|
|
1696
1882
|
{
|
|
1697
1883
|
className: `block-svg ${transitionClass}`,
|
|
@@ -1701,8 +1887,8 @@ function BlockRenderer({
|
|
|
1701
1887
|
overflow: "hidden",
|
|
1702
1888
|
"data-block-id": block.id,
|
|
1703
1889
|
children: [
|
|
1704
|
-
/* @__PURE__ */
|
|
1705
|
-
/* @__PURE__ */
|
|
1890
|
+
/* @__PURE__ */ jsx13("defs", { children: /* @__PURE__ */ jsx13("clipPath", { id: clipId, children: /* @__PURE__ */ jsx13("rect", { x: "0", y: "0", width: viewport.width, height: viewport.height }) }) }),
|
|
1891
|
+
/* @__PURE__ */ jsx13("g", { clipPath: `url(#${clipId})`, children: (block.layers ?? []).map((layer) => /* @__PURE__ */ jsx13(
|
|
1706
1892
|
LayerRenderer,
|
|
1707
1893
|
{
|
|
1708
1894
|
layer,
|
|
@@ -1729,7 +1915,7 @@ function LayerRenderer({
|
|
|
1729
1915
|
const renderedLayer = animationsEnabled || !layer.animation ? layer : { ...layer, animation: void 0 };
|
|
1730
1916
|
switch (renderedLayer.type) {
|
|
1731
1917
|
case "image":
|
|
1732
|
-
return /* @__PURE__ */
|
|
1918
|
+
return /* @__PURE__ */ jsx13(
|
|
1733
1919
|
ImageLayer,
|
|
1734
1920
|
{
|
|
1735
1921
|
layer: renderedLayer,
|
|
@@ -1740,13 +1926,13 @@ function LayerRenderer({
|
|
|
1740
1926
|
}
|
|
1741
1927
|
);
|
|
1742
1928
|
case "text":
|
|
1743
|
-
return /* @__PURE__ */
|
|
1929
|
+
return /* @__PURE__ */ jsx13(TextLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1744
1930
|
case "shape":
|
|
1745
|
-
return /* @__PURE__ */
|
|
1931
|
+
return /* @__PURE__ */ jsx13(ShapeLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1746
1932
|
case "path":
|
|
1747
|
-
return /* @__PURE__ */
|
|
1933
|
+
return /* @__PURE__ */ jsx13(PathLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1748
1934
|
case "map":
|
|
1749
|
-
return /* @__PURE__ */
|
|
1935
|
+
return /* @__PURE__ */ jsx13(
|
|
1750
1936
|
MapLayer,
|
|
1751
1937
|
{
|
|
1752
1938
|
layer: renderedLayer,
|
|
@@ -1756,7 +1942,7 @@ function LayerRenderer({
|
|
|
1756
1942
|
}
|
|
1757
1943
|
);
|
|
1758
1944
|
case "video":
|
|
1759
|
-
return /* @__PURE__ */
|
|
1945
|
+
return /* @__PURE__ */ jsx13(
|
|
1760
1946
|
VideoLayer,
|
|
1761
1947
|
{
|
|
1762
1948
|
layer: renderedLayer,
|
|
@@ -1767,9 +1953,11 @@ function LayerRenderer({
|
|
|
1767
1953
|
}
|
|
1768
1954
|
);
|
|
1769
1955
|
case "table":
|
|
1770
|
-
return /* @__PURE__ */
|
|
1956
|
+
return /* @__PURE__ */ jsx13(TableLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1771
1957
|
case "tree":
|
|
1772
|
-
return /* @__PURE__ */
|
|
1958
|
+
return /* @__PURE__ */ jsx13(TreeLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1959
|
+
case "mermaid":
|
|
1960
|
+
return /* @__PURE__ */ jsx13(MermaidLayer, { layer: renderedLayer, viewport, blockTime });
|
|
1773
1961
|
default:
|
|
1774
1962
|
console.warn(`Unknown layer type: ${renderedLayer.type}`);
|
|
1775
1963
|
return null;
|
|
@@ -1782,7 +1970,7 @@ import { getCaptionAtTime } from "@bendyline/squisq/schemas";
|
|
|
1782
1970
|
// src/SocialCaptionOverlay.tsx
|
|
1783
1971
|
import { useMemo as useMemo4 } from "react";
|
|
1784
1972
|
import { resolveFontFamily } from "@bendyline/squisq/schemas";
|
|
1785
|
-
import { jsx as
|
|
1973
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1786
1974
|
var TARGET_CHUNK_SIZE = 4;
|
|
1787
1975
|
var MIN_CHUNK_SIZE = 2;
|
|
1788
1976
|
var MAX_CHUNK_SIZE = 6;
|
|
@@ -1841,7 +2029,7 @@ function SocialCaptionOverlay({
|
|
|
1841
2029
|
[captions]
|
|
1842
2030
|
);
|
|
1843
2031
|
if (!enabled || chunks.length === 0) {
|
|
1844
|
-
return /* @__PURE__ */
|
|
2032
|
+
return /* @__PURE__ */ jsx14(
|
|
1845
2033
|
"div",
|
|
1846
2034
|
{
|
|
1847
2035
|
className: "social-caption-overlay",
|
|
@@ -1903,7 +2091,7 @@ function SocialCaptionOverlay({
|
|
|
1903
2091
|
const viewportHeight = viewport?.height ?? 720;
|
|
1904
2092
|
const baseFontSize = Math.round(viewportHeight * 0.055);
|
|
1905
2093
|
const fontSize = Math.max(24, Math.min(72, baseFontSize));
|
|
1906
|
-
return /* @__PURE__ */
|
|
2094
|
+
return /* @__PURE__ */ jsx14(
|
|
1907
2095
|
"div",
|
|
1908
2096
|
{
|
|
1909
2097
|
className: "social-caption-overlay",
|
|
@@ -1920,7 +2108,7 @@ function SocialCaptionOverlay({
|
|
|
1920
2108
|
opacity: 1,
|
|
1921
2109
|
transition: "opacity 0.15s ease-in-out"
|
|
1922
2110
|
},
|
|
1923
|
-
children: /* @__PURE__ */
|
|
2111
|
+
children: /* @__PURE__ */ jsx14(
|
|
1924
2112
|
"div",
|
|
1925
2113
|
{
|
|
1926
2114
|
style: {
|
|
@@ -1929,7 +2117,7 @@ function SocialCaptionOverlay({
|
|
|
1929
2117
|
},
|
|
1930
2118
|
children: activeChunk.words.map((word, i) => {
|
|
1931
2119
|
const isActive = i === activeWordIndex;
|
|
1932
|
-
return /* @__PURE__ */
|
|
2120
|
+
return /* @__PURE__ */ jsx14(
|
|
1933
2121
|
"span",
|
|
1934
2122
|
{
|
|
1935
2123
|
style: {
|
|
@@ -1954,7 +2142,7 @@ function SocialCaptionOverlay({
|
|
|
1954
2142
|
}
|
|
1955
2143
|
|
|
1956
2144
|
// src/CaptionOverlay.tsx
|
|
1957
|
-
import { jsx as
|
|
2145
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
1958
2146
|
function CaptionOverlay({
|
|
1959
2147
|
captions,
|
|
1960
2148
|
currentTime,
|
|
@@ -1965,7 +2153,7 @@ function CaptionOverlay({
|
|
|
1965
2153
|
viewport
|
|
1966
2154
|
}) {
|
|
1967
2155
|
if (captionStyle === "social") {
|
|
1968
|
-
return /* @__PURE__ */
|
|
2156
|
+
return /* @__PURE__ */ jsx15(
|
|
1969
2157
|
SocialCaptionOverlay,
|
|
1970
2158
|
{
|
|
1971
2159
|
captions,
|
|
@@ -1978,7 +2166,7 @@ function CaptionOverlay({
|
|
|
1978
2166
|
}
|
|
1979
2167
|
const phrase = enabled && captions ? getCaptionAtTime(captions, currentTime) : null;
|
|
1980
2168
|
const captionText = phrase?.text ?? null;
|
|
1981
|
-
return /* @__PURE__ */
|
|
2169
|
+
return /* @__PURE__ */ jsx15(
|
|
1982
2170
|
"div",
|
|
1983
2171
|
{
|
|
1984
2172
|
className: "caption-overlay",
|
|
@@ -1997,7 +2185,7 @@ function CaptionOverlay({
|
|
|
1997
2185
|
padding: "0 4px",
|
|
1998
2186
|
boxSizing: "border-box"
|
|
1999
2187
|
},
|
|
2000
|
-
children: captionText && /* @__PURE__ */
|
|
2188
|
+
children: captionText && /* @__PURE__ */ jsx15(
|
|
2001
2189
|
"div",
|
|
2002
2190
|
{
|
|
2003
2191
|
style: {
|
|
@@ -2007,7 +2195,7 @@ function CaptionOverlay({
|
|
|
2007
2195
|
borderRadius: "4px",
|
|
2008
2196
|
backdropFilter: "blur(4px)"
|
|
2009
2197
|
},
|
|
2010
|
-
children: /* @__PURE__ */
|
|
2198
|
+
children: /* @__PURE__ */ jsx15(
|
|
2011
2199
|
"span",
|
|
2012
2200
|
{
|
|
2013
2201
|
style: {
|
|
@@ -2053,55 +2241,109 @@ function useAutoSurface(enabled) {
|
|
|
2053
2241
|
}
|
|
2054
2242
|
|
|
2055
2243
|
// src/hooks/useAudioSync.ts
|
|
2056
|
-
import { useState as
|
|
2244
|
+
import { useState as useState5, useEffect as useEffect6, useRef as useRef3, useCallback as useCallback2 } from "react";
|
|
2245
|
+
import { fetchResourceBytes, isResourceUrlAllowed as isResourceUrlAllowed3 } from "@bendyline/squisq/markdown";
|
|
2246
|
+
|
|
2247
|
+
// src/hooks/AudioController.ts
|
|
2248
|
+
function calculateSegmentTiming(segments) {
|
|
2249
|
+
if (!segments?.length) {
|
|
2250
|
+
return { segmentStarts: [], totalDuration: 0 };
|
|
2251
|
+
}
|
|
2252
|
+
let time = 0;
|
|
2253
|
+
const segmentStarts = segments.map((seg) => {
|
|
2254
|
+
const start = time;
|
|
2255
|
+
time += seg.duration;
|
|
2256
|
+
return start;
|
|
2257
|
+
});
|
|
2258
|
+
return { segmentStarts, totalDuration: time };
|
|
2259
|
+
}
|
|
2260
|
+
function findSegmentAtTime(time, segments, segmentStarts) {
|
|
2261
|
+
if (!segments?.length) {
|
|
2262
|
+
return { segmentIndex: 0, segmentStart: 0 };
|
|
2263
|
+
}
|
|
2264
|
+
let segmentIndex = 0;
|
|
2265
|
+
let segmentStart = 0;
|
|
2266
|
+
for (let i = 0; i < segments.length; i++) {
|
|
2267
|
+
const segEnd = segmentStarts[i] + segments[i].duration;
|
|
2268
|
+
if (time < segEnd) {
|
|
2269
|
+
segmentIndex = i;
|
|
2270
|
+
segmentStart = segmentStarts[i];
|
|
2271
|
+
break;
|
|
2272
|
+
}
|
|
2273
|
+
if (i === segments.length - 1) {
|
|
2274
|
+
segmentIndex = i;
|
|
2275
|
+
segmentStart = segmentStarts[i];
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
return { segmentIndex, segmentStart };
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
// src/hooks/useAudioSync.ts
|
|
2282
|
+
var SEEK_COMPLETION_TIMEOUT_MS = 15e3;
|
|
2283
|
+
var UNLOADABLE_MESSAGE = "Audio could not be loaded. Check that the media file is available.";
|
|
2057
2284
|
function resolveAudioUrl(src, basePath) {
|
|
2058
2285
|
if (!src || /^(?:[a-z][a-z0-9+.-]*:|\/\/|\/)/i.test(src)) return src;
|
|
2059
2286
|
if (!basePath) return src;
|
|
2060
2287
|
return `${basePath.replace(/\/$/, "")}/${src.replace(/^\//, "")}`;
|
|
2061
2288
|
}
|
|
2062
|
-
function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
2063
|
-
const
|
|
2064
|
-
const [
|
|
2065
|
-
const [
|
|
2066
|
-
const [
|
|
2067
|
-
const [
|
|
2068
|
-
const [
|
|
2289
|
+
function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true, mode = "media") {
|
|
2290
|
+
const resourcePolicy = useResourcePolicy();
|
|
2291
|
+
const [currentTime, setCurrentTime] = useState5(0);
|
|
2292
|
+
const [isPlaying, setIsPlaying] = useState5(false);
|
|
2293
|
+
const [currentSegment, setCurrentSegment] = useState5(0);
|
|
2294
|
+
const [isEnded, setIsEnded] = useState5(false);
|
|
2295
|
+
const [isAudioReady, setIsAudioReady] = useState5(false);
|
|
2296
|
+
const [totalDuration, setTotalDuration] = useState5(0);
|
|
2297
|
+
const [isAvailable, setIsAvailable] = useState5(true);
|
|
2298
|
+
const [unavailableMessage, setUnavailableMessage] = useState5();
|
|
2069
2299
|
const segmentStarts = useRef3([]);
|
|
2070
2300
|
const pendingSeekTime = useRef3(null);
|
|
2301
|
+
const pendingSeekCompletion = useRef3(null);
|
|
2071
2302
|
const shouldPlayAfterLoad = useRef3(false);
|
|
2072
2303
|
const blobUrls = useRef3(/* @__PURE__ */ new Map());
|
|
2073
2304
|
const loadingPromises = useRef3(/* @__PURE__ */ new Map());
|
|
2074
2305
|
const abortControllers = useRef3(/* @__PURE__ */ new Set());
|
|
2075
2306
|
const loadGeneration = useRef3(0);
|
|
2307
|
+
const activeSegmentSrc = useRef3(void 0);
|
|
2308
|
+
activeSegmentSrc.current = audioTrack?.segments[currentSegment]?.src;
|
|
2309
|
+
const boundSource = useRef3(null);
|
|
2310
|
+
const currentSegmentRef = useRef3(0);
|
|
2311
|
+
currentSegmentRef.current = currentSegment;
|
|
2076
2312
|
const fallbackMode = useRef3(false);
|
|
2077
|
-
|
|
2313
|
+
useEffect6(() => {
|
|
2078
2314
|
loadGeneration.current += 1;
|
|
2079
2315
|
pendingSeekTime.current = null;
|
|
2316
|
+
pendingSeekCompletion.current?.();
|
|
2317
|
+
pendingSeekCompletion.current = null;
|
|
2080
2318
|
shouldPlayAfterLoad.current = false;
|
|
2081
2319
|
fallbackMode.current = false;
|
|
2320
|
+
boundSource.current = null;
|
|
2082
2321
|
setCurrentTime(0);
|
|
2083
2322
|
setCurrentSegment(0);
|
|
2084
2323
|
setIsPlaying(false);
|
|
2085
2324
|
setIsEnded(false);
|
|
2086
2325
|
setIsAudioReady(false);
|
|
2326
|
+
setIsAvailable(true);
|
|
2327
|
+
setUnavailableMessage(void 0);
|
|
2087
2328
|
if (!enabled || !audioTrack?.segments) {
|
|
2088
2329
|
segmentStarts.current = [];
|
|
2089
2330
|
setTotalDuration(0);
|
|
2090
2331
|
return;
|
|
2091
2332
|
}
|
|
2092
|
-
|
|
2093
|
-
segmentStarts.current =
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
});
|
|
2098
|
-
setTotalDuration(time);
|
|
2099
|
-
}, [audioTrack, enabled]);
|
|
2333
|
+
const timing = calculateSegmentTiming(audioTrack.segments);
|
|
2334
|
+
segmentStarts.current = timing.segmentStarts;
|
|
2335
|
+
setTotalDuration(timing.totalDuration);
|
|
2336
|
+
if (mode === "synthetic") setIsAudioReady(true);
|
|
2337
|
+
}, [audioTrack, enabled, mode]);
|
|
2100
2338
|
const preloadAudio = useCallback2(
|
|
2101
2339
|
async (src) => {
|
|
2102
2340
|
const audioUrl = resolveAudioUrl(src, basePath);
|
|
2341
|
+
if (!isResourceUrlAllowed3(audioUrl, resourcePolicy)) return "";
|
|
2103
2342
|
if (blobUrls.current.has(src)) {
|
|
2104
|
-
|
|
2343
|
+
const cached = blobUrls.current.get(src);
|
|
2344
|
+
blobUrls.current.delete(src);
|
|
2345
|
+
blobUrls.current.set(src, cached);
|
|
2346
|
+
return cached;
|
|
2105
2347
|
}
|
|
2106
2348
|
if (loadingPromises.current.has(src)) {
|
|
2107
2349
|
return loadingPromises.current.get(src);
|
|
@@ -2111,33 +2353,44 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2111
2353
|
const generation = loadGeneration.current;
|
|
2112
2354
|
const loadPromise = (async () => {
|
|
2113
2355
|
try {
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
|
|
2356
|
+
const resource = await fetchResourceBytes(audioUrl, {
|
|
2357
|
+
policy: resourcePolicy,
|
|
2358
|
+
signal: controller.signal,
|
|
2359
|
+
contentTypePrefixes: ["audio/", "video/", "application/octet-stream"]
|
|
2360
|
+
});
|
|
2361
|
+
const blob = new Blob([resource.bytes.slice().buffer], {
|
|
2362
|
+
type: resource.contentType || "application/octet-stream"
|
|
2363
|
+
});
|
|
2117
2364
|
const blobUrl = URL.createObjectURL(blob);
|
|
2118
2365
|
if (controller.signal.aborted || generation !== loadGeneration.current) {
|
|
2119
2366
|
URL.revokeObjectURL(blobUrl);
|
|
2120
2367
|
return audioUrl;
|
|
2121
2368
|
}
|
|
2122
2369
|
blobUrls.current.set(src, blobUrl);
|
|
2370
|
+
while (blobUrls.current.size > 2) {
|
|
2371
|
+
const oldest = [...blobUrls.current.entries()].find(([key]) => key !== activeSegmentSrc.current) ?? blobUrls.current.entries().next().value;
|
|
2372
|
+
if (!oldest) break;
|
|
2373
|
+
blobUrls.current.delete(oldest[0]);
|
|
2374
|
+
URL.revokeObjectURL(oldest[1]);
|
|
2375
|
+
}
|
|
2123
2376
|
return blobUrl;
|
|
2124
2377
|
} catch {
|
|
2125
|
-
return
|
|
2126
|
-
} finally {
|
|
2127
|
-
abortControllers.current.delete(controller);
|
|
2128
|
-
loadingPromises.current.delete(src);
|
|
2378
|
+
return "";
|
|
2129
2379
|
}
|
|
2130
2380
|
})();
|
|
2131
2381
|
loadingPromises.current.set(src, loadPromise);
|
|
2382
|
+
void loadPromise.then(() => {
|
|
2383
|
+
abortControllers.current.delete(controller);
|
|
2384
|
+
if (loadingPromises.current.get(src) === loadPromise) {
|
|
2385
|
+
loadingPromises.current.delete(src);
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2132
2388
|
return loadPromise;
|
|
2133
2389
|
},
|
|
2134
|
-
[basePath]
|
|
2390
|
+
[basePath, resourcePolicy]
|
|
2135
2391
|
);
|
|
2136
|
-
|
|
2392
|
+
useEffect6(() => {
|
|
2137
2393
|
if (!enabled || !audioTrack?.segments) return;
|
|
2138
|
-
audioTrack.segments.forEach((segment) => {
|
|
2139
|
-
preloadAudio(segment.src);
|
|
2140
|
-
});
|
|
2141
2394
|
const currentBlobUrls = blobUrls.current;
|
|
2142
2395
|
const currentAbortControllers = abortControllers.current;
|
|
2143
2396
|
const currentLoadingPromises = loadingPromises.current;
|
|
@@ -2152,8 +2405,13 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2152
2405
|
currentBlobUrls.clear();
|
|
2153
2406
|
};
|
|
2154
2407
|
}, [audioTrack, preloadAudio, enabled]);
|
|
2155
|
-
|
|
2156
|
-
if (!enabled) return;
|
|
2408
|
+
useEffect6(() => {
|
|
2409
|
+
if (!enabled || mode === "synthetic") return;
|
|
2410
|
+
const segment = audioTrack?.segments[currentSegment];
|
|
2411
|
+
if (segment) void preloadAudio(segment.src);
|
|
2412
|
+
}, [audioTrack, currentSegment, enabled, mode, preloadAudio]);
|
|
2413
|
+
useEffect6(() => {
|
|
2414
|
+
if (!enabled || mode === "synthetic") return;
|
|
2157
2415
|
const audio = audioRef.current;
|
|
2158
2416
|
if (!audio) return;
|
|
2159
2417
|
const handleTimeUpdate = () => {
|
|
@@ -2168,6 +2426,16 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2168
2426
|
const handlePause = () => setIsPlaying(false);
|
|
2169
2427
|
const handleError = () => {
|
|
2170
2428
|
setIsAudioReady(true);
|
|
2429
|
+
setIsPlaying(false);
|
|
2430
|
+
setIsAvailable(false);
|
|
2431
|
+
setUnavailableMessage(UNLOADABLE_MESSAGE);
|
|
2432
|
+
shouldPlayAfterLoad.current = false;
|
|
2433
|
+
if (pendingSeekTime.current !== null) {
|
|
2434
|
+
setCurrentTime(pendingSeekTime.current);
|
|
2435
|
+
pendingSeekTime.current = null;
|
|
2436
|
+
}
|
|
2437
|
+
pendingSeekCompletion.current?.();
|
|
2438
|
+
pendingSeekCompletion.current = null;
|
|
2171
2439
|
};
|
|
2172
2440
|
const handleEnded = () => {
|
|
2173
2441
|
if (audioTrack && currentSegment < audioTrack.segments.length - 1) {
|
|
@@ -2190,9 +2458,9 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2190
2458
|
audio.removeEventListener("ended", handleEnded);
|
|
2191
2459
|
audio.removeEventListener("error", handleError);
|
|
2192
2460
|
};
|
|
2193
|
-
}, [audioRef, currentSegment, audioTrack, enabled]);
|
|
2194
|
-
|
|
2195
|
-
if (!enabled) return;
|
|
2461
|
+
}, [audioRef, currentSegment, audioTrack, enabled, mode]);
|
|
2462
|
+
useEffect6(() => {
|
|
2463
|
+
if (!enabled || mode === "synthetic") return;
|
|
2196
2464
|
const audio = audioRef.current;
|
|
2197
2465
|
if (!audio || !audioTrack?.segments) return;
|
|
2198
2466
|
const segment = audioTrack.segments[currentSegment];
|
|
@@ -2204,22 +2472,48 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2204
2472
|
audio.currentTime = Math.max(0, segmentTime);
|
|
2205
2473
|
setCurrentTime(pendingSeekTime.current);
|
|
2206
2474
|
pendingSeekTime.current = null;
|
|
2475
|
+
pendingSeekCompletion.current?.();
|
|
2476
|
+
pendingSeekCompletion.current = null;
|
|
2207
2477
|
}
|
|
2208
2478
|
if (shouldPlayAfterLoad.current) {
|
|
2209
|
-
audio.play().catch(() => {
|
|
2479
|
+
audio.play().catch((error) => {
|
|
2480
|
+
setIsPlaying(false);
|
|
2481
|
+
if (!(error instanceof Error && error.name === "NotAllowedError")) {
|
|
2482
|
+
setIsAvailable(false);
|
|
2483
|
+
setUnavailableMessage(
|
|
2484
|
+
"Audio playback failed. Check that the media file is supported and available."
|
|
2485
|
+
);
|
|
2486
|
+
}
|
|
2210
2487
|
});
|
|
2211
2488
|
shouldPlayAfterLoad.current = false;
|
|
2212
2489
|
}
|
|
2213
2490
|
};
|
|
2214
2491
|
const currentSrc = audio.src;
|
|
2215
|
-
const
|
|
2216
|
-
const isSameSource = currentSrc &&
|
|
2492
|
+
const bound = boundSource.current;
|
|
2493
|
+
const isSameSource = !!currentSrc && bound?.key === segment.src && bound.url === currentSrc;
|
|
2217
2494
|
let cancelled = false;
|
|
2218
2495
|
let handleCanPlay = null;
|
|
2219
2496
|
if (!isSameSource) {
|
|
2220
2497
|
const loadAndPlay = async () => {
|
|
2221
2498
|
const blobUrl = await preloadAudio(segment.src);
|
|
2222
2499
|
if (cancelled) return;
|
|
2500
|
+
if (!blobUrl) {
|
|
2501
|
+
audio.removeAttribute("src");
|
|
2502
|
+
audio.load();
|
|
2503
|
+
boundSource.current = null;
|
|
2504
|
+
shouldPlayAfterLoad.current = false;
|
|
2505
|
+
setIsAudioReady(true);
|
|
2506
|
+
setIsPlaying(false);
|
|
2507
|
+
setIsAvailable(false);
|
|
2508
|
+
setUnavailableMessage(UNLOADABLE_MESSAGE);
|
|
2509
|
+
if (pendingSeekTime.current !== null) {
|
|
2510
|
+
setCurrentTime(pendingSeekTime.current);
|
|
2511
|
+
pendingSeekTime.current = null;
|
|
2512
|
+
}
|
|
2513
|
+
pendingSeekCompletion.current?.();
|
|
2514
|
+
pendingSeekCompletion.current = null;
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2223
2517
|
handleCanPlay = () => {
|
|
2224
2518
|
if (cancelled) return;
|
|
2225
2519
|
setIsAudioReady(true);
|
|
@@ -2228,6 +2522,7 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2228
2522
|
};
|
|
2229
2523
|
audio.addEventListener("canplay", handleCanPlay);
|
|
2230
2524
|
audio.src = blobUrl;
|
|
2525
|
+
boundSource.current = { key: segment.src, url: audio.src };
|
|
2231
2526
|
audio.load();
|
|
2232
2527
|
await Promise.resolve();
|
|
2233
2528
|
if (audio.readyState >= 3) {
|
|
@@ -2244,22 +2539,86 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2244
2539
|
cancelled = true;
|
|
2245
2540
|
if (handleCanPlay) audio.removeEventListener("canplay", handleCanPlay);
|
|
2246
2541
|
};
|
|
2247
|
-
}, [audioRef, currentSegment, audioTrack, preloadAudio, enabled]);
|
|
2248
|
-
const
|
|
2542
|
+
}, [audioRef, currentSegment, audioTrack, preloadAudio, enabled, mode]);
|
|
2543
|
+
const seekTo = useCallback2(
|
|
2544
|
+
async (time) => {
|
|
2545
|
+
const audio = audioRef.current;
|
|
2546
|
+
if (!audioTrack?.segments) return;
|
|
2547
|
+
const clampedTime = totalDuration > 0 ? Math.max(0, Math.min(time, totalDuration)) : Math.max(0, time);
|
|
2548
|
+
const starts = segmentStarts.current.length === audioTrack.segments.length ? segmentStarts.current : calculateSegmentTiming(audioTrack.segments).segmentStarts;
|
|
2549
|
+
const { segmentIndex, segmentStart } = findSegmentAtTime(
|
|
2550
|
+
clampedTime,
|
|
2551
|
+
audioTrack.segments,
|
|
2552
|
+
starts
|
|
2553
|
+
);
|
|
2554
|
+
const wasPlaying = mode === "synthetic" ? isPlaying : !audio?.paused;
|
|
2555
|
+
setIsEnded(false);
|
|
2556
|
+
if (!audio && mode === "media") {
|
|
2557
|
+
setCurrentSegment(segmentIndex);
|
|
2558
|
+
setCurrentTime(clampedTime);
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
if (segmentIndex !== currentSegmentRef.current) {
|
|
2562
|
+
pendingSeekTime.current = clampedTime;
|
|
2563
|
+
shouldPlayAfterLoad.current = wasPlaying;
|
|
2564
|
+
const completion = new Promise((resolve) => {
|
|
2565
|
+
pendingSeekCompletion.current?.();
|
|
2566
|
+
let settled = false;
|
|
2567
|
+
const settle = () => {
|
|
2568
|
+
if (settled) return;
|
|
2569
|
+
settled = true;
|
|
2570
|
+
clearTimeout(timer);
|
|
2571
|
+
if (pendingSeekCompletion.current === settle) pendingSeekCompletion.current = null;
|
|
2572
|
+
resolve();
|
|
2573
|
+
};
|
|
2574
|
+
const timer = setTimeout(settle, SEEK_COMPLETION_TIMEOUT_MS);
|
|
2575
|
+
pendingSeekCompletion.current = settle;
|
|
2576
|
+
});
|
|
2577
|
+
setCurrentSegment(segmentIndex);
|
|
2578
|
+
if (mode === "synthetic") {
|
|
2579
|
+
setCurrentTime(clampedTime);
|
|
2580
|
+
pendingSeekTime.current = null;
|
|
2581
|
+
pendingSeekCompletion.current?.();
|
|
2582
|
+
pendingSeekCompletion.current = null;
|
|
2583
|
+
}
|
|
2584
|
+
await completion;
|
|
2585
|
+
} else {
|
|
2586
|
+
const segmentTime = clampedTime - segmentStart;
|
|
2587
|
+
if (audio && mode === "media") audio.currentTime = Math.max(0, segmentTime);
|
|
2588
|
+
setCurrentTime(clampedTime);
|
|
2589
|
+
}
|
|
2590
|
+
},
|
|
2591
|
+
[audioRef, audioTrack, isPlaying, mode, totalDuration]
|
|
2592
|
+
);
|
|
2593
|
+
const play = useCallback2(async () => {
|
|
2594
|
+
if (mode === "synthetic") {
|
|
2595
|
+
fallbackMode.current = true;
|
|
2596
|
+
setIsPlaying(true);
|
|
2597
|
+
return;
|
|
2598
|
+
}
|
|
2249
2599
|
const audio = audioRef.current;
|
|
2250
2600
|
if (audio) {
|
|
2251
2601
|
if (isEnded) {
|
|
2252
|
-
|
|
2253
|
-
setIsEnded(false);
|
|
2602
|
+
await seekTo(0);
|
|
2254
2603
|
}
|
|
2255
|
-
|
|
2604
|
+
try {
|
|
2605
|
+
await audio.play();
|
|
2256
2606
|
fallbackMode.current = false;
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2607
|
+
setIsAvailable(true);
|
|
2608
|
+
setUnavailableMessage(void 0);
|
|
2609
|
+
} catch (error) {
|
|
2610
|
+
fallbackMode.current = false;
|
|
2611
|
+
setIsPlaying(false);
|
|
2612
|
+
const name = error instanceof Error ? error.name : "";
|
|
2613
|
+
if (name !== "NotAllowedError") {
|
|
2614
|
+
setIsAvailable(false);
|
|
2615
|
+
setUnavailableMessage(
|
|
2616
|
+
"Audio playback failed. Check that the media file is supported and available."
|
|
2617
|
+
);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2261
2620
|
}
|
|
2262
|
-
}, [audioRef, isEnded]);
|
|
2621
|
+
}, [audioRef, isEnded, mode, seekTo]);
|
|
2263
2622
|
const pause = useCallback2(() => {
|
|
2264
2623
|
const audio = audioRef.current;
|
|
2265
2624
|
if (audio) {
|
|
@@ -2269,45 +2628,13 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2269
2628
|
}, [audioRef]);
|
|
2270
2629
|
const toggle = useCallback2(() => {
|
|
2271
2630
|
const audio = audioRef.current;
|
|
2272
|
-
if (!audio) return;
|
|
2631
|
+
if (!audio && mode === "media") return;
|
|
2273
2632
|
if (!isPlaying) {
|
|
2274
2633
|
play();
|
|
2275
2634
|
} else {
|
|
2276
2635
|
pause();
|
|
2277
2636
|
}
|
|
2278
|
-
}, [audioRef, isPlaying, play, pause]);
|
|
2279
|
-
const seekTo = useCallback2(
|
|
2280
|
-
(time) => {
|
|
2281
|
-
const audio = audioRef.current;
|
|
2282
|
-
if (!audio || !audioTrack?.segments) return;
|
|
2283
|
-
const clampedTime = totalDuration > 0 ? Math.max(0, Math.min(time, totalDuration)) : Math.max(0, time);
|
|
2284
|
-
let segmentIndex = 0;
|
|
2285
|
-
let segmentStart = 0;
|
|
2286
|
-
for (let i = 0; i < audioTrack.segments.length; i++) {
|
|
2287
|
-
const segEnd = segmentStart + audioTrack.segments[i].duration;
|
|
2288
|
-
if (clampedTime < segEnd) {
|
|
2289
|
-
segmentIndex = i;
|
|
2290
|
-
break;
|
|
2291
|
-
}
|
|
2292
|
-
segmentStart = segEnd;
|
|
2293
|
-
if (i === audioTrack.segments.length - 1) {
|
|
2294
|
-
segmentIndex = i;
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2297
|
-
const wasPlaying = !audio.paused;
|
|
2298
|
-
setIsEnded(false);
|
|
2299
|
-
if (segmentIndex !== currentSegment) {
|
|
2300
|
-
pendingSeekTime.current = clampedTime;
|
|
2301
|
-
shouldPlayAfterLoad.current = wasPlaying;
|
|
2302
|
-
setCurrentSegment(segmentIndex);
|
|
2303
|
-
} else {
|
|
2304
|
-
const segmentTime = clampedTime - segmentStart;
|
|
2305
|
-
audio.currentTime = Math.max(0, segmentTime);
|
|
2306
|
-
setCurrentTime(clampedTime);
|
|
2307
|
-
}
|
|
2308
|
-
},
|
|
2309
|
-
[audioRef, audioTrack, currentSegment, totalDuration]
|
|
2310
|
-
);
|
|
2637
|
+
}, [audioRef, isPlaying, mode, play, pause]);
|
|
2311
2638
|
const skipToSegment = useCallback2(
|
|
2312
2639
|
(index) => {
|
|
2313
2640
|
if (!audioTrack?.segments || index < 0 || index >= audioTrack.segments.length) {
|
|
@@ -2319,11 +2646,10 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2319
2646
|
[audioTrack]
|
|
2320
2647
|
);
|
|
2321
2648
|
const restart = useCallback2(async () => {
|
|
2322
|
-
seekTo(0);
|
|
2323
|
-
await
|
|
2324
|
-
play();
|
|
2649
|
+
await seekTo(0);
|
|
2650
|
+
await play();
|
|
2325
2651
|
}, [seekTo, play]);
|
|
2326
|
-
|
|
2652
|
+
useEffect6(() => {
|
|
2327
2653
|
if (!isPlaying || !fallbackMode.current || !totalDuration) return;
|
|
2328
2654
|
let lastTime = performance.now();
|
|
2329
2655
|
let raf;
|
|
@@ -2354,13 +2680,13 @@ function useAudioSync(audioRef, audioTrack, basePath = "", enabled = true) {
|
|
|
2354
2680
|
totalDuration,
|
|
2355
2681
|
isEnded,
|
|
2356
2682
|
isReady: isAudioReady,
|
|
2357
|
-
isAvailable
|
|
2358
|
-
|
|
2683
|
+
isAvailable,
|
|
2684
|
+
unavailableMessage,
|
|
2359
2685
|
// Actions
|
|
2360
|
-
play
|
|
2686
|
+
play,
|
|
2361
2687
|
pause: async () => pause(),
|
|
2362
2688
|
toggle: async () => toggle(),
|
|
2363
|
-
seekTo
|
|
2689
|
+
seekTo,
|
|
2364
2690
|
skipToSegment: async (index) => skipToSegment(index),
|
|
2365
2691
|
restart
|
|
2366
2692
|
};
|
|
@@ -2505,7 +2831,7 @@ function useDocPlayback(script, currentTime, options = {}) {
|
|
|
2505
2831
|
}
|
|
2506
2832
|
|
|
2507
2833
|
// src/hooks/useViewportOrientation.ts
|
|
2508
|
-
import { useState as
|
|
2834
|
+
import { useState as useState6, useEffect as useEffect7, useMemo as useMemo7 } from "react";
|
|
2509
2835
|
import {
|
|
2510
2836
|
VIEWPORT_PRESETS as VIEWPORT_PRESETS2
|
|
2511
2837
|
} from "@bendyline/squisq/doc";
|
|
@@ -2531,11 +2857,11 @@ function getViewportForOrientation(orientation) {
|
|
|
2531
2857
|
}
|
|
2532
2858
|
}
|
|
2533
2859
|
function useViewportOrientation() {
|
|
2534
|
-
const [windowSize, setWindowSize] =
|
|
2860
|
+
const [windowSize, setWindowSize] = useState6(() => ({
|
|
2535
2861
|
width: typeof window !== "undefined" ? window.innerWidth : 1920,
|
|
2536
2862
|
height: typeof window !== "undefined" ? window.innerHeight : 1080
|
|
2537
2863
|
}));
|
|
2538
|
-
|
|
2864
|
+
useEffect7(() => {
|
|
2539
2865
|
if (typeof window === "undefined") return;
|
|
2540
2866
|
const handleResize = () => {
|
|
2541
2867
|
setWindowSize({
|
|
@@ -2567,7 +2893,7 @@ function useViewportOrientation() {
|
|
|
2567
2893
|
}
|
|
2568
2894
|
|
|
2569
2895
|
// src/hooks/useSlideSwipe.ts
|
|
2570
|
-
import { useCallback as useCallback4, useEffect as
|
|
2896
|
+
import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef5, useState as useState7 } from "react";
|
|
2571
2897
|
var DISTANCE_RATIO = 0.3;
|
|
2572
2898
|
var FLICK_VELOCITY = 0.5;
|
|
2573
2899
|
var MIN_FLICK_DISTANCE = 12;
|
|
@@ -2591,8 +2917,8 @@ function decideSwipe({
|
|
|
2591
2917
|
}
|
|
2592
2918
|
function useSlideSwipe(opts) {
|
|
2593
2919
|
const settleMs = opts.settleMs ?? DEFAULT_SETTLE_MS;
|
|
2594
|
-
const [offsetPx, setOffsetPx] =
|
|
2595
|
-
const [phase, setPhase] =
|
|
2920
|
+
const [offsetPx, setOffsetPx] = useState7(0);
|
|
2921
|
+
const [phase, setPhase] = useState7("idle");
|
|
2596
2922
|
const optsRef = useRef5(opts);
|
|
2597
2923
|
optsRef.current = opts;
|
|
2598
2924
|
const dragRef = useRef5(null);
|
|
@@ -2630,7 +2956,7 @@ function useSlideSwipe(opts) {
|
|
|
2630
2956
|
setPhase("dragging");
|
|
2631
2957
|
setOffsetPx(0);
|
|
2632
2958
|
}, []);
|
|
2633
|
-
|
|
2959
|
+
useEffect8(() => {
|
|
2634
2960
|
function currentWidth() {
|
|
2635
2961
|
return optsRef.current.containerRef.current?.getBoundingClientRect().width ?? 0;
|
|
2636
2962
|
}
|
|
@@ -2700,7 +3026,7 @@ function useSlideSwipe(opts) {
|
|
|
2700
3026
|
window.removeEventListener("pointercancel", onCancel);
|
|
2701
3027
|
};
|
|
2702
3028
|
}, [settleMs]);
|
|
2703
|
-
|
|
3029
|
+
useEffect8(() => {
|
|
2704
3030
|
if (!opts.enabled) {
|
|
2705
3031
|
dragRef.current = null;
|
|
2706
3032
|
clearPending();
|
|
@@ -2708,7 +3034,7 @@ function useSlideSwipe(opts) {
|
|
|
2708
3034
|
setOffsetPx(0);
|
|
2709
3035
|
}
|
|
2710
3036
|
}, [opts.enabled, clearPending]);
|
|
2711
|
-
|
|
3037
|
+
useEffect8(() => clearPending, [clearPending]);
|
|
2712
3038
|
return { offsetPx, phase, onPointerDown };
|
|
2713
3039
|
}
|
|
2714
3040
|
|
|
@@ -2717,13 +3043,13 @@ import {
|
|
|
2717
3043
|
expandCoverBlock,
|
|
2718
3044
|
createTemplateContext,
|
|
2719
3045
|
markdownToDoc as markdownToDoc2,
|
|
2720
|
-
DEFAULT_THEME as
|
|
2721
|
-
VIEWPORT_PRESETS as
|
|
3046
|
+
DEFAULT_THEME as DEFAULT_THEME4,
|
|
3047
|
+
VIEWPORT_PRESETS as VIEWPORT_PRESETS5
|
|
2722
3048
|
} from "@bendyline/squisq/doc";
|
|
2723
3049
|
import { parseMarkdown as parseMarkdown2 } from "@bendyline/squisq/markdown";
|
|
2724
3050
|
|
|
2725
3051
|
// src/DocProgressBar.tsx
|
|
2726
|
-
import { useRef as useRef6, useState as
|
|
3052
|
+
import { useRef as useRef6, useState as useState8, useCallback as useCallback5 } from "react";
|
|
2727
3053
|
|
|
2728
3054
|
// src/types.ts
|
|
2729
3055
|
function formatTime(seconds) {
|
|
@@ -2733,7 +3059,7 @@ function formatTime(seconds) {
|
|
|
2733
3059
|
}
|
|
2734
3060
|
|
|
2735
3061
|
// src/DocProgressBar.tsx
|
|
2736
|
-
import { jsx as
|
|
3062
|
+
import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2737
3063
|
function DocProgressBar({
|
|
2738
3064
|
state,
|
|
2739
3065
|
actions,
|
|
@@ -2742,7 +3068,7 @@ function DocProgressBar({
|
|
|
2742
3068
|
getBlockTitle
|
|
2743
3069
|
}) {
|
|
2744
3070
|
const progressBarRef = useRef6(null);
|
|
2745
|
-
const [hoverPosition, setHoverPosition] =
|
|
3071
|
+
const [hoverPosition, setHoverPosition] = useState8(null);
|
|
2746
3072
|
const playProgress = state.totalDuration > 0 ? Math.max(0, Math.min(1, state.currentTime / state.totalDuration)) : 0;
|
|
2747
3073
|
const handleProgressHover = useCallback5((e) => {
|
|
2748
3074
|
const bar = progressBarRef.current;
|
|
@@ -2792,7 +3118,7 @@ function DocProgressBar({
|
|
|
2792
3118
|
},
|
|
2793
3119
|
[expandedBlocks]
|
|
2794
3120
|
);
|
|
2795
|
-
return /* @__PURE__ */
|
|
3121
|
+
return /* @__PURE__ */ jsxs10(
|
|
2796
3122
|
"div",
|
|
2797
3123
|
{
|
|
2798
3124
|
ref: progressBarRef,
|
|
@@ -2815,7 +3141,7 @@ function DocProgressBar({
|
|
|
2815
3141
|
onMouseMove: handleProgressHover,
|
|
2816
3142
|
onMouseLeave: handleProgressLeave,
|
|
2817
3143
|
children: [
|
|
2818
|
-
/* @__PURE__ */
|
|
3144
|
+
/* @__PURE__ */ jsx16(
|
|
2819
3145
|
"div",
|
|
2820
3146
|
{
|
|
2821
3147
|
role: "slider",
|
|
@@ -2836,7 +3162,7 @@ function DocProgressBar({
|
|
|
2836
3162
|
}
|
|
2837
3163
|
}
|
|
2838
3164
|
),
|
|
2839
|
-
/* @__PURE__ */
|
|
3165
|
+
/* @__PURE__ */ jsx16(
|
|
2840
3166
|
"div",
|
|
2841
3167
|
{
|
|
2842
3168
|
"data-testid": "doc-progress-fill",
|
|
@@ -2850,7 +3176,7 @@ function DocProgressBar({
|
|
|
2850
3176
|
}
|
|
2851
3177
|
}
|
|
2852
3178
|
),
|
|
2853
|
-
blockMarkers.map((marker, i) => /* @__PURE__ */
|
|
3179
|
+
blockMarkers.map((marker, i) => /* @__PURE__ */ jsx16(
|
|
2854
3180
|
"button",
|
|
2855
3181
|
{
|
|
2856
3182
|
type: "button",
|
|
@@ -2883,7 +3209,7 @@ function DocProgressBar({
|
|
|
2883
3209
|
},
|
|
2884
3210
|
`${marker.block.id}-${i}`
|
|
2885
3211
|
)),
|
|
2886
|
-
hoverPosition !== null && /* @__PURE__ */
|
|
3212
|
+
hoverPosition !== null && /* @__PURE__ */ jsxs10(
|
|
2887
3213
|
"div",
|
|
2888
3214
|
{
|
|
2889
3215
|
style: {
|
|
@@ -2900,19 +3226,19 @@ function DocProgressBar({
|
|
|
2900
3226
|
zIndex: 10
|
|
2901
3227
|
},
|
|
2902
3228
|
children: [
|
|
2903
|
-
/* @__PURE__ */
|
|
3229
|
+
/* @__PURE__ */ jsx16("div", { style: { color: "white", fontSize: "12px", fontFamily: "monospace" }, children: formatTime(hoverPosition * state.totalDuration) }),
|
|
2904
3230
|
(() => {
|
|
2905
3231
|
const hoverTime = hoverPosition * state.totalDuration;
|
|
2906
3232
|
const slideInfo = getBlockAtTimeLocal(hoverTime);
|
|
2907
3233
|
if (slideInfo && getBlockTitle) {
|
|
2908
|
-
return /* @__PURE__ */
|
|
3234
|
+
return /* @__PURE__ */ jsx16("div", { style: { color: "rgba(255,255,255,0.7)", fontSize: "11px", marginTop: "2px" }, children: getBlockTitle(slideInfo.block) });
|
|
2909
3235
|
}
|
|
2910
3236
|
return null;
|
|
2911
3237
|
})()
|
|
2912
3238
|
]
|
|
2913
3239
|
}
|
|
2914
3240
|
),
|
|
2915
|
-
hoverPosition !== null && /* @__PURE__ */
|
|
3241
|
+
hoverPosition !== null && /* @__PURE__ */ jsx16(
|
|
2916
3242
|
"div",
|
|
2917
3243
|
{
|
|
2918
3244
|
style: {
|
|
@@ -2934,7 +3260,7 @@ function DocProgressBar({
|
|
|
2934
3260
|
}
|
|
2935
3261
|
|
|
2936
3262
|
// src/DocControlsOverlay.tsx
|
|
2937
|
-
import { jsx as
|
|
3263
|
+
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2938
3264
|
function DocControlsOverlay({
|
|
2939
3265
|
state,
|
|
2940
3266
|
actions,
|
|
@@ -2942,7 +3268,7 @@ function DocControlsOverlay({
|
|
|
2942
3268
|
expandedBlocks,
|
|
2943
3269
|
getBlockTitle
|
|
2944
3270
|
}) {
|
|
2945
|
-
return /* @__PURE__ */
|
|
3271
|
+
return /* @__PURE__ */ jsxs11(
|
|
2946
3272
|
"div",
|
|
2947
3273
|
{
|
|
2948
3274
|
className: "doc-player__controls",
|
|
@@ -2959,7 +3285,7 @@ function DocControlsOverlay({
|
|
|
2959
3285
|
zIndex: 100
|
|
2960
3286
|
},
|
|
2961
3287
|
children: [
|
|
2962
|
-
/* @__PURE__ */
|
|
3288
|
+
/* @__PURE__ */ jsx17(
|
|
2963
3289
|
"button",
|
|
2964
3290
|
{
|
|
2965
3291
|
onClick: actions.restart,
|
|
@@ -2975,10 +3301,10 @@ function DocControlsOverlay({
|
|
|
2975
3301
|
},
|
|
2976
3302
|
title: "Restart",
|
|
2977
3303
|
"aria-label": "Restart from beginning",
|
|
2978
|
-
children: /* @__PURE__ */
|
|
3304
|
+
children: /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx17("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" }) })
|
|
2979
3305
|
}
|
|
2980
3306
|
),
|
|
2981
|
-
/* @__PURE__ */
|
|
3307
|
+
/* @__PURE__ */ jsx17(
|
|
2982
3308
|
"button",
|
|
2983
3309
|
{
|
|
2984
3310
|
onClick: actions.toggle,
|
|
@@ -2997,15 +3323,15 @@ function DocControlsOverlay({
|
|
|
2997
3323
|
height: "40px"
|
|
2998
3324
|
},
|
|
2999
3325
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
3000
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
3326
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx17("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx17("path", { d: "M8 5v14l11-7z" }) })
|
|
3001
3327
|
}
|
|
3002
3328
|
),
|
|
3003
|
-
/* @__PURE__ */
|
|
3329
|
+
/* @__PURE__ */ jsxs11("span", { style: { color: "white", fontSize: "12px", minWidth: "80px", fontFamily: "monospace" }, children: [
|
|
3004
3330
|
formatTime(state.currentTime),
|
|
3005
3331
|
" / ",
|
|
3006
3332
|
formatTime(state.totalDuration)
|
|
3007
3333
|
] }),
|
|
3008
|
-
/* @__PURE__ */
|
|
3334
|
+
/* @__PURE__ */ jsx17(
|
|
3009
3335
|
DocProgressBar,
|
|
3010
3336
|
{
|
|
3011
3337
|
state,
|
|
@@ -3015,12 +3341,12 @@ function DocControlsOverlay({
|
|
|
3015
3341
|
getBlockTitle
|
|
3016
3342
|
}
|
|
3017
3343
|
),
|
|
3018
|
-
/* @__PURE__ */
|
|
3344
|
+
/* @__PURE__ */ jsxs11("span", { style: { color: "rgba(255,255,255,0.5)", fontSize: "11px", whiteSpace: "nowrap" }, children: [
|
|
3019
3345
|
state.currentBlockIndex + 1,
|
|
3020
3346
|
"/",
|
|
3021
3347
|
state.totalBlocks
|
|
3022
3348
|
] }),
|
|
3023
|
-
state.hasCaptions && /* @__PURE__ */
|
|
3349
|
+
state.hasCaptions && /* @__PURE__ */ jsxs11(
|
|
3024
3350
|
"button",
|
|
3025
3351
|
{
|
|
3026
3352
|
onClick: () => actions.cycleCaptionMode(),
|
|
@@ -3039,12 +3365,12 @@ function DocControlsOverlay({
|
|
|
3039
3365
|
title: state.captionMode === "off" ? "Captions: Off (click for Standard)" : state.captionMode === "standard" ? "Captions: Standard (click for Social)" : "Captions: Social (click to turn off)",
|
|
3040
3366
|
"aria-label": "Cycle caption style",
|
|
3041
3367
|
children: [
|
|
3042
|
-
/* @__PURE__ */
|
|
3043
|
-
state.captionMode !== "off" && /* @__PURE__ */
|
|
3368
|
+
/* @__PURE__ */ jsx17("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx17("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" }) }),
|
|
3369
|
+
state.captionMode !== "off" && /* @__PURE__ */ jsx17("span", { style: { fontSize: "10px", textTransform: "uppercase", letterSpacing: "0.5px" }, children: state.captionMode === "standard" ? "CC" : "SM" })
|
|
3044
3370
|
]
|
|
3045
3371
|
}
|
|
3046
3372
|
),
|
|
3047
|
-
actions.toggleFullscreen && /* @__PURE__ */
|
|
3373
|
+
actions.toggleFullscreen && /* @__PURE__ */ jsx17(
|
|
3048
3374
|
"button",
|
|
3049
3375
|
{
|
|
3050
3376
|
onClick: actions.toggleFullscreen,
|
|
@@ -3060,7 +3386,7 @@ function DocControlsOverlay({
|
|
|
3060
3386
|
},
|
|
3061
3387
|
title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
|
|
3062
3388
|
"aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
3063
|
-
children: state.isFullscreen ? /* @__PURE__ */
|
|
3389
|
+
children: state.isFullscreen ? /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx17("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx17("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
|
|
3064
3390
|
}
|
|
3065
3391
|
)
|
|
3066
3392
|
]
|
|
@@ -3069,8 +3395,8 @@ function DocControlsOverlay({
|
|
|
3069
3395
|
}
|
|
3070
3396
|
|
|
3071
3397
|
// src/DocControlsSlideshow.tsx
|
|
3072
|
-
import { useCallback as useCallback6, useEffect as
|
|
3073
|
-
import { jsx as
|
|
3398
|
+
import { useCallback as useCallback6, useEffect as useEffect9, useId as useId6, useLayoutEffect, useRef as useRef7, useState as useState9 } from "react";
|
|
3399
|
+
import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3074
3400
|
function DocControlsSlideshow({
|
|
3075
3401
|
state,
|
|
3076
3402
|
slideNav,
|
|
@@ -3078,7 +3404,7 @@ function DocControlsSlideshow({
|
|
|
3078
3404
|
pickerOpen,
|
|
3079
3405
|
onPickerOpenChange
|
|
3080
3406
|
}) {
|
|
3081
|
-
const [uncontrolledPickerOpen, setUncontrolledPickerOpen] =
|
|
3407
|
+
const [uncontrolledPickerOpen, setUncontrolledPickerOpen] = useState9(false);
|
|
3082
3408
|
const isPickerOpen = pickerOpen ?? uncontrolledPickerOpen;
|
|
3083
3409
|
const setPickerOpen = useCallback6(
|
|
3084
3410
|
(open) => {
|
|
@@ -3087,7 +3413,7 @@ function DocControlsSlideshow({
|
|
|
3087
3413
|
},
|
|
3088
3414
|
[pickerOpen, onPickerOpenChange]
|
|
3089
3415
|
);
|
|
3090
|
-
const [pickerMaxHeight, setPickerMaxHeight] =
|
|
3416
|
+
const [pickerMaxHeight, setPickerMaxHeight] = useState9(280);
|
|
3091
3417
|
const controlsRef = useRef7(null);
|
|
3092
3418
|
const triggerRef = useRef7(null);
|
|
3093
3419
|
const menuRef = useRef7(null);
|
|
@@ -3102,7 +3428,7 @@ function DocControlsSlideshow({
|
|
|
3102
3428
|
const isFirst = currentBlockIndex <= 0;
|
|
3103
3429
|
const isLast = currentBlockIndex >= totalBlocks - 1;
|
|
3104
3430
|
const counterText = totalBlocks > 0 ? currentSlideLabel ?? `${currentSlideNumber ?? currentBlockIndex + 1} / ${totalSlideNumber ?? totalBlocks}` : "\u2014";
|
|
3105
|
-
|
|
3431
|
+
useEffect9(() => {
|
|
3106
3432
|
if (!isPickerOpen) return;
|
|
3107
3433
|
const handlePointerDown = (event) => {
|
|
3108
3434
|
if (!controlsRef.current?.contains(event.target)) setPickerOpen(false);
|
|
@@ -3141,7 +3467,7 @@ function DocControlsSlideshow({
|
|
|
3141
3467
|
window.removeEventListener("resize", updateMaxHeight);
|
|
3142
3468
|
};
|
|
3143
3469
|
}, [isPickerOpen]);
|
|
3144
|
-
|
|
3470
|
+
useEffect9(() => {
|
|
3145
3471
|
if (!isPickerOpen) return;
|
|
3146
3472
|
menuRef.current?.querySelector('[aria-current="true"]')?.focus();
|
|
3147
3473
|
}, [isPickerOpen]);
|
|
@@ -3160,7 +3486,7 @@ function DocControlsSlideshow({
|
|
|
3160
3486
|
else nextIndex = (focusedIndex - 1 + items.length) % items.length;
|
|
3161
3487
|
items[nextIndex]?.focus();
|
|
3162
3488
|
};
|
|
3163
|
-
return /* @__PURE__ */
|
|
3489
|
+
return /* @__PURE__ */ jsxs12(
|
|
3164
3490
|
"div",
|
|
3165
3491
|
{
|
|
3166
3492
|
ref: controlsRef,
|
|
@@ -3182,7 +3508,7 @@ function DocControlsSlideshow({
|
|
|
3182
3508
|
WebkitBackdropFilter: "blur(8px)"
|
|
3183
3509
|
},
|
|
3184
3510
|
children: [
|
|
3185
|
-
/* @__PURE__ */
|
|
3511
|
+
/* @__PURE__ */ jsx18(
|
|
3186
3512
|
"button",
|
|
3187
3513
|
{
|
|
3188
3514
|
onClick: (e) => {
|
|
@@ -3211,10 +3537,10 @@ function DocControlsSlideshow({
|
|
|
3211
3537
|
onMouseLeave: (e) => {
|
|
3212
3538
|
e.currentTarget.style.background = "none";
|
|
3213
3539
|
},
|
|
3214
|
-
children: /* @__PURE__ */
|
|
3540
|
+
children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx18("path", { d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" }) })
|
|
3215
3541
|
}
|
|
3216
3542
|
),
|
|
3217
|
-
/* @__PURE__ */
|
|
3543
|
+
/* @__PURE__ */ jsx18(
|
|
3218
3544
|
"button",
|
|
3219
3545
|
{
|
|
3220
3546
|
ref: triggerRef,
|
|
@@ -3248,7 +3574,7 @@ function DocControlsSlideshow({
|
|
|
3248
3574
|
children: counterText
|
|
3249
3575
|
}
|
|
3250
3576
|
),
|
|
3251
|
-
isPickerOpen && /* @__PURE__ */
|
|
3577
|
+
isPickerOpen && /* @__PURE__ */ jsx18(
|
|
3252
3578
|
"div",
|
|
3253
3579
|
{
|
|
3254
3580
|
ref: menuRef,
|
|
@@ -3275,7 +3601,7 @@ function DocControlsSlideshow({
|
|
|
3275
3601
|
},
|
|
3276
3602
|
children: slides.map((slide, index) => {
|
|
3277
3603
|
const isCurrent = index === currentBlockIndex;
|
|
3278
|
-
return /* @__PURE__ */
|
|
3604
|
+
return /* @__PURE__ */ jsxs12(
|
|
3279
3605
|
"button",
|
|
3280
3606
|
{
|
|
3281
3607
|
type: "button",
|
|
@@ -3308,7 +3634,7 @@ function DocControlsSlideshow({
|
|
|
3308
3634
|
event.currentTarget.style.background = isCurrent ? "rgba(255,255,255,0.14)" : "transparent";
|
|
3309
3635
|
},
|
|
3310
3636
|
children: [
|
|
3311
|
-
/* @__PURE__ */
|
|
3637
|
+
/* @__PURE__ */ jsx18(
|
|
3312
3638
|
"span",
|
|
3313
3639
|
{
|
|
3314
3640
|
style: {
|
|
@@ -3320,7 +3646,7 @@ function DocControlsSlideshow({
|
|
|
3320
3646
|
children: slide.label
|
|
3321
3647
|
}
|
|
3322
3648
|
),
|
|
3323
|
-
/* @__PURE__ */
|
|
3649
|
+
/* @__PURE__ */ jsx18(
|
|
3324
3650
|
"span",
|
|
3325
3651
|
{
|
|
3326
3652
|
style: {
|
|
@@ -3342,7 +3668,7 @@ function DocControlsSlideshow({
|
|
|
3342
3668
|
})
|
|
3343
3669
|
}
|
|
3344
3670
|
),
|
|
3345
|
-
/* @__PURE__ */
|
|
3671
|
+
/* @__PURE__ */ jsx18(
|
|
3346
3672
|
"button",
|
|
3347
3673
|
{
|
|
3348
3674
|
onClick: (e) => {
|
|
@@ -3371,7 +3697,7 @@ function DocControlsSlideshow({
|
|
|
3371
3697
|
onMouseLeave: (e) => {
|
|
3372
3698
|
e.currentTarget.style.background = "none";
|
|
3373
3699
|
},
|
|
3374
|
-
children: /* @__PURE__ */
|
|
3700
|
+
children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx18("path", { d: "M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z" }) })
|
|
3375
3701
|
}
|
|
3376
3702
|
)
|
|
3377
3703
|
]
|
|
@@ -3380,20 +3706,85 @@ function DocControlsSlideshow({
|
|
|
3380
3706
|
}
|
|
3381
3707
|
|
|
3382
3708
|
// src/LinearDocView.tsx
|
|
3383
|
-
import { useEffect as
|
|
3709
|
+
import { useEffect as useEffect10, useMemo as useMemo9, useRef as useRef8 } from "react";
|
|
3384
3710
|
import {
|
|
3385
3711
|
applySurface,
|
|
3386
3712
|
resolveFontFamily as resolveFontFamily2
|
|
3387
3713
|
} from "@bendyline/squisq/schemas";
|
|
3388
|
-
import { VIEWPORT_PRESETS as
|
|
3714
|
+
import { VIEWPORT_PRESETS as VIEWPORT_PRESETS4 } from "@bendyline/squisq/schemas";
|
|
3389
3715
|
import {
|
|
3390
|
-
materializeBlockLayers,
|
|
3391
3716
|
markdownToDoc,
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3717
|
+
materializePageSections,
|
|
3718
|
+
resolvePageStyle as resolvePageStyle2,
|
|
3719
|
+
buildPageCssVars,
|
|
3720
|
+
pageStyleDataAttributes,
|
|
3721
|
+
PAGE_BASE_CSS,
|
|
3722
|
+
DEFAULT_THEME as DEFAULT_THEME3
|
|
3395
3723
|
} from "@bendyline/squisq/doc";
|
|
3396
|
-
import {
|
|
3724
|
+
import { parseMarkdown } from "@bendyline/squisq/markdown";
|
|
3725
|
+
|
|
3726
|
+
// src/page/PageViewContext.ts
|
|
3727
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
3728
|
+
import { DEFAULT_THEME as DEFAULT_THEME2, resolvePageStyle } from "@bendyline/squisq/doc";
|
|
3729
|
+
import { VIEWPORT_PRESETS as VIEWPORT_PRESETS3 } from "@bendyline/squisq/schemas";
|
|
3730
|
+
var defaultValue = {
|
|
3731
|
+
theme: DEFAULT_THEME2,
|
|
3732
|
+
pageStyle: resolvePageStyle(DEFAULT_THEME2),
|
|
3733
|
+
basePath: "/",
|
|
3734
|
+
viewport: VIEWPORT_PRESETS3.landscape,
|
|
3735
|
+
renderContext: { theme: DEFAULT_THEME2 },
|
|
3736
|
+
animationsEnabled: true,
|
|
3737
|
+
imageDisplayMode: "inline"
|
|
3738
|
+
};
|
|
3739
|
+
var PageViewContext = createContext2(defaultValue);
|
|
3740
|
+
function usePageView() {
|
|
3741
|
+
return useContext2(PageViewContext);
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3744
|
+
// src/page/CanvasSection.tsx
|
|
3745
|
+
import { useMemo as useMemo8 } from "react";
|
|
3746
|
+
import { materializeBlockLayers } from "@bendyline/squisq/doc";
|
|
3747
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
3748
|
+
function CanvasSection({ section }) {
|
|
3749
|
+
const { renderContext, basePath, animationsEnabled } = usePageView();
|
|
3750
|
+
const media = section.slots.media;
|
|
3751
|
+
const canvas = media?.type === "canvas" ? media : void 0;
|
|
3752
|
+
const visualBlock = useMemo8(() => {
|
|
3753
|
+
if (!canvas) return null;
|
|
3754
|
+
const { layers } = materializeBlockLayers(canvas.block, {
|
|
3755
|
+
...renderContext,
|
|
3756
|
+
blockIndex: section.index
|
|
3757
|
+
});
|
|
3758
|
+
return { ...canvas.block, layers };
|
|
3759
|
+
}, [canvas, renderContext, section.index]);
|
|
3760
|
+
if (!canvas || !visualBlock) return null;
|
|
3761
|
+
const aspect = canvas.aspect;
|
|
3762
|
+
const frame = section.hints?.frame;
|
|
3763
|
+
const frameClass = frame === "terminal" ? " squisq-page-canvas--terminal squisq-page-canvas--framed" : " squisq-page-canvas--framed";
|
|
3764
|
+
return /* @__PURE__ */ jsx19(
|
|
3765
|
+
"div",
|
|
3766
|
+
{
|
|
3767
|
+
className: `squisq-page-canvas${frameClass}`,
|
|
3768
|
+
style: {
|
|
3769
|
+
aspectRatio: `${aspect.width} / ${aspect.height}`,
|
|
3770
|
+
// Clamp tall canvases; derive the width cap from the aspect so a
|
|
3771
|
+
// portrait viewport doesn't produce a page-height skyscraper.
|
|
3772
|
+
maxHeight: "min(70vh, 720px)",
|
|
3773
|
+
maxWidth: `calc(min(70vh, 720px) * ${(aspect.width / Math.max(1, aspect.height)).toFixed(4)})`
|
|
3774
|
+
},
|
|
3775
|
+
children: /* @__PURE__ */ jsx19(
|
|
3776
|
+
BlockRenderer,
|
|
3777
|
+
{
|
|
3778
|
+
block: visualBlock,
|
|
3779
|
+
blockTime: 0,
|
|
3780
|
+
basePath,
|
|
3781
|
+
viewport: { width: aspect.width, height: aspect.height },
|
|
3782
|
+
animationsEnabled
|
|
3783
|
+
}
|
|
3784
|
+
)
|
|
3785
|
+
}
|
|
3786
|
+
);
|
|
3787
|
+
}
|
|
3397
3788
|
|
|
3398
3789
|
// src/MarkdownRenderer.tsx
|
|
3399
3790
|
import { Fragment as Fragment2 } from "react";
|
|
@@ -3403,7 +3794,7 @@ import {
|
|
|
3403
3794
|
} from "@bendyline/squisq/markdown";
|
|
3404
3795
|
|
|
3405
3796
|
// src/InlineVideoPlayer.tsx
|
|
3406
|
-
import { jsx as
|
|
3797
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
3407
3798
|
function InlineVideoPlayer({
|
|
3408
3799
|
src,
|
|
3409
3800
|
basePath = "",
|
|
@@ -3418,7 +3809,7 @@ function InlineVideoPlayer({
|
|
|
3418
3809
|
const resolvedPoster = useMediaUrl(poster ?? "", basePath);
|
|
3419
3810
|
const posterUrl = poster ? resolvedPoster : void 0;
|
|
3420
3811
|
if (!resolvedSrc) return null;
|
|
3421
|
-
return /* @__PURE__ */
|
|
3812
|
+
return /* @__PURE__ */ jsx20("span", { className: `squisq-inline-video-player ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx20(
|
|
3422
3813
|
"video",
|
|
3423
3814
|
{
|
|
3424
3815
|
src: resolvedSrc,
|
|
@@ -3433,7 +3824,7 @@ function InlineVideoPlayer({
|
|
|
3433
3824
|
}
|
|
3434
3825
|
|
|
3435
3826
|
// src/InlineAudioPlayer.tsx
|
|
3436
|
-
import { jsx as
|
|
3827
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
3437
3828
|
function InlineAudioPlayer({
|
|
3438
3829
|
src,
|
|
3439
3830
|
basePath = "",
|
|
@@ -3443,11 +3834,11 @@ function InlineAudioPlayer({
|
|
|
3443
3834
|
}) {
|
|
3444
3835
|
const resolvedSrc = useMediaUrl(src, basePath);
|
|
3445
3836
|
if (!resolvedSrc) return null;
|
|
3446
|
-
return /* @__PURE__ */
|
|
3837
|
+
return /* @__PURE__ */ jsx21("span", { className: `squisq-inline-audio-player ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx21("audio", { src: resolvedSrc, controls, preload }) });
|
|
3447
3838
|
}
|
|
3448
3839
|
|
|
3449
3840
|
// src/MarkdownRenderer.tsx
|
|
3450
|
-
import { jsx as
|
|
3841
|
+
import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3451
3842
|
var DEFAULT_CTX = { htmlPolicy: "sanitize" };
|
|
3452
3843
|
function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
3453
3844
|
return nodes.map((node, i) => {
|
|
@@ -3455,28 +3846,28 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
3455
3846
|
switch (node.type) {
|
|
3456
3847
|
case "text": {
|
|
3457
3848
|
if (!node.value.includes("\n")) {
|
|
3458
|
-
return /* @__PURE__ */
|
|
3849
|
+
return /* @__PURE__ */ jsx22(Fragment2, { children: node.value }, key);
|
|
3459
3850
|
}
|
|
3460
3851
|
const parts = node.value.split("\n");
|
|
3461
|
-
return /* @__PURE__ */
|
|
3462
|
-
j > 0 && /* @__PURE__ */
|
|
3852
|
+
return /* @__PURE__ */ jsx22(Fragment2, { children: parts.map((part, j) => /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
3853
|
+
j > 0 && /* @__PURE__ */ jsx22("br", {}),
|
|
3463
3854
|
part
|
|
3464
3855
|
] }, j)) }, key);
|
|
3465
3856
|
}
|
|
3466
3857
|
case "emphasis":
|
|
3467
|
-
return /* @__PURE__ */
|
|
3858
|
+
return /* @__PURE__ */ jsx22("em", { className: "squisq-md-em", children: renderInline(node.children, key, ctx) }, key);
|
|
3468
3859
|
case "strong":
|
|
3469
|
-
return /* @__PURE__ */
|
|
3860
|
+
return /* @__PURE__ */ jsx22("strong", { className: "squisq-md-strong", children: renderInline(node.children, key, ctx) }, key);
|
|
3470
3861
|
case "delete":
|
|
3471
|
-
return /* @__PURE__ */
|
|
3862
|
+
return /* @__PURE__ */ jsx22("del", { className: "squisq-md-del", children: renderInline(node.children, key, ctx) }, key);
|
|
3472
3863
|
case "inlineCode":
|
|
3473
|
-
return /* @__PURE__ */
|
|
3864
|
+
return /* @__PURE__ */ jsx22("code", { className: "squisq-md-inline-code", children: node.value }, key);
|
|
3474
3865
|
case "link": {
|
|
3475
3866
|
const href = sanitizeUrl(node.url, "link", { extraLinkSchemes: ctx.linkSchemes });
|
|
3476
3867
|
if (!href) {
|
|
3477
|
-
return /* @__PURE__ */
|
|
3868
|
+
return /* @__PURE__ */ jsx22("span", { className: "squisq-md-link squisq-md-link--blocked", children: renderInline(node.children, key, ctx) }, key);
|
|
3478
3869
|
}
|
|
3479
|
-
return /* @__PURE__ */
|
|
3870
|
+
return /* @__PURE__ */ jsx22(
|
|
3480
3871
|
"a",
|
|
3481
3872
|
{
|
|
3482
3873
|
className: "squisq-md-link",
|
|
@@ -3490,32 +3881,32 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
3490
3881
|
);
|
|
3491
3882
|
}
|
|
3492
3883
|
case "image":
|
|
3493
|
-
return /* @__PURE__ */
|
|
3884
|
+
return /* @__PURE__ */ jsx22(MdImage, { src: node.url, alt: node.alt ?? "", title: node.title ?? void 0 }, key);
|
|
3494
3885
|
case "break":
|
|
3495
|
-
return /* @__PURE__ */
|
|
3886
|
+
return /* @__PURE__ */ jsx22("br", {}, key);
|
|
3496
3887
|
case "inlineMath":
|
|
3497
|
-
return /* @__PURE__ */
|
|
3888
|
+
return /* @__PURE__ */ jsx22("code", { className: "squisq-md-inline-math", children: node.value }, key);
|
|
3498
3889
|
case "htmlInline":
|
|
3499
3890
|
if (ctx.htmlPolicy === "strip") return null;
|
|
3500
|
-
return /* @__PURE__ */
|
|
3891
|
+
return /* @__PURE__ */ jsx22("span", { className: "squisq-md-html-inline", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`, ctx) }, key);
|
|
3501
3892
|
case "footnoteReference":
|
|
3502
|
-
return /* @__PURE__ */
|
|
3893
|
+
return /* @__PURE__ */ jsx22("sup", { className: "squisq-md-footnote-ref", children: /* @__PURE__ */ jsxs13("a", { href: `#fn-${node.identifier}`, children: [
|
|
3503
3894
|
"[",
|
|
3504
3895
|
node.label ?? node.identifier,
|
|
3505
3896
|
"]"
|
|
3506
3897
|
] }) }, key);
|
|
3507
3898
|
case "linkReference":
|
|
3508
|
-
return /* @__PURE__ */
|
|
3899
|
+
return /* @__PURE__ */ jsx22("span", { className: "squisq-md-link-ref", children: renderInline(node.children, key, ctx) }, key);
|
|
3509
3900
|
case "imageReference":
|
|
3510
|
-
return /* @__PURE__ */
|
|
3901
|
+
return /* @__PURE__ */ jsxs13("span", { className: "squisq-md-image-ref", children: [
|
|
3511
3902
|
"[",
|
|
3512
3903
|
node.alt ?? node.identifier,
|
|
3513
3904
|
"]"
|
|
3514
3905
|
] }, key);
|
|
3515
3906
|
case "textDirective":
|
|
3516
|
-
return /* @__PURE__ */
|
|
3907
|
+
return /* @__PURE__ */ jsx22("span", { className: "squisq-md-text-directive", "data-directive": node.name, children: renderInline(node.children, key, ctx) }, key);
|
|
3517
3908
|
case "mention":
|
|
3518
|
-
return /* @__PURE__ */
|
|
3909
|
+
return /* @__PURE__ */ jsxs13(
|
|
3519
3910
|
"span",
|
|
3520
3911
|
{
|
|
3521
3912
|
className: "squisq-md-mention mention",
|
|
@@ -3538,51 +3929,62 @@ function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
|
3538
3929
|
function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
3539
3930
|
switch (node.type) {
|
|
3540
3931
|
case "paragraph":
|
|
3541
|
-
return /* @__PURE__ */
|
|
3932
|
+
return /* @__PURE__ */ jsx22("p", { className: "squisq-md-p", children: renderInline(node.children, key, ctx) }, key);
|
|
3542
3933
|
case "heading": {
|
|
3543
3934
|
const Tag = `h${node.depth}`;
|
|
3544
|
-
return /* @__PURE__ */
|
|
3935
|
+
return /* @__PURE__ */ jsx22(Tag, { className: `squisq-md-heading squisq-md-h${node.depth}`, children: renderInline(node.children, key, ctx) }, key);
|
|
3545
3936
|
}
|
|
3546
3937
|
case "blockquote":
|
|
3547
|
-
return /* @__PURE__ */
|
|
3938
|
+
return /* @__PURE__ */ jsx22("blockquote", { className: "squisq-md-blockquote", children: renderBlocks(node.children, key, ctx) }, key);
|
|
3548
3939
|
case "list":
|
|
3549
3940
|
if (node.ordered) {
|
|
3550
|
-
return /* @__PURE__ */
|
|
3941
|
+
return /* @__PURE__ */ jsx22("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);
|
|
3551
3942
|
}
|
|
3552
|
-
return /* @__PURE__ */
|
|
3943
|
+
return /* @__PURE__ */ jsx22("ul", { className: "squisq-md-list squisq-md-ul", children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, ctx)) }, key);
|
|
3553
3944
|
case "code":
|
|
3554
|
-
|
|
3945
|
+
if (node.lang?.trim().toLowerCase() === "mermaid") {
|
|
3946
|
+
return /* @__PURE__ */ jsx22(
|
|
3947
|
+
MermaidDiagram,
|
|
3948
|
+
{
|
|
3949
|
+
source: node.value,
|
|
3950
|
+
className: "squisq-md-mermaid",
|
|
3951
|
+
ariaLabel: "Mermaid diagram"
|
|
3952
|
+
},
|
|
3953
|
+
key
|
|
3954
|
+
);
|
|
3955
|
+
}
|
|
3956
|
+
return /* @__PURE__ */ jsx22("pre", { className: "squisq-md-code-block", children: /* @__PURE__ */ jsx22("code", { className: node.lang ? `language-${node.lang}` : void 0, children: node.value }) }, key);
|
|
3555
3957
|
case "thematicBreak":
|
|
3556
|
-
return /* @__PURE__ */
|
|
3958
|
+
return /* @__PURE__ */ jsx22("hr", { className: "squisq-md-hr" }, key);
|
|
3557
3959
|
case "table":
|
|
3558
3960
|
return renderTable(node.children, node.align, key, ctx);
|
|
3559
3961
|
case "htmlBlock":
|
|
3560
3962
|
if (ctx.htmlPolicy === "strip") return null;
|
|
3561
|
-
return /* @__PURE__ */
|
|
3963
|
+
return /* @__PURE__ */ jsx22("div", { className: "squisq-md-html-block", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`, ctx) }, key);
|
|
3562
3964
|
case "math":
|
|
3563
|
-
return /* @__PURE__ */
|
|
3965
|
+
return /* @__PURE__ */ jsx22("pre", { className: "squisq-md-math-block", children: /* @__PURE__ */ jsx22("code", { children: node.value }) }, key);
|
|
3564
3966
|
case "definition":
|
|
3565
3967
|
return null;
|
|
3566
3968
|
case "footnoteDefinition":
|
|
3567
|
-
return /* @__PURE__ */
|
|
3568
|
-
/* @__PURE__ */
|
|
3969
|
+
return /* @__PURE__ */ jsxs13("div", { className: "squisq-md-footnote-def", id: `fn-${node.identifier}`, children: [
|
|
3970
|
+
/* @__PURE__ */ jsx22("sup", { children: node.label ?? node.identifier }),
|
|
3569
3971
|
renderBlocks(node.children, key, ctx)
|
|
3570
3972
|
] }, key);
|
|
3571
3973
|
case "containerDirective":
|
|
3572
|
-
return /* @__PURE__ */
|
|
3974
|
+
return /* @__PURE__ */ jsxs13(
|
|
3573
3975
|
"div",
|
|
3574
3976
|
{
|
|
3575
3977
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
3576
3978
|
"data-directive": node.name,
|
|
3577
3979
|
children: [
|
|
3578
|
-
node.label && /* @__PURE__ */
|
|
3980
|
+
node.label && /* @__PURE__ */ jsx22("div", { className: "squisq-md-directive-label", children: node.label }),
|
|
3579
3981
|
renderBlocks(node.children, key, ctx)
|
|
3580
3982
|
]
|
|
3581
3983
|
},
|
|
3582
3984
|
key
|
|
3583
3985
|
);
|
|
3584
3986
|
case "leafDirective":
|
|
3585
|
-
return /* @__PURE__ */
|
|
3987
|
+
return /* @__PURE__ */ jsx22(
|
|
3586
3988
|
"div",
|
|
3587
3989
|
{
|
|
3588
3990
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
@@ -3592,11 +3994,11 @@ function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
|
3592
3994
|
key
|
|
3593
3995
|
);
|
|
3594
3996
|
case "definitionList":
|
|
3595
|
-
return /* @__PURE__ */
|
|
3997
|
+
return /* @__PURE__ */ jsx22("dl", { className: "squisq-md-dl", children: node.children.map((child, i) => {
|
|
3596
3998
|
if (child.type === "definitionTerm") {
|
|
3597
|
-
return /* @__PURE__ */
|
|
3999
|
+
return /* @__PURE__ */ jsx22("dt", { className: "squisq-md-dt", children: renderInline(child.children, `${key}dt${i}`, ctx) }, `${key}dt${i}`);
|
|
3598
4000
|
}
|
|
3599
|
-
return /* @__PURE__ */
|
|
4001
|
+
return /* @__PURE__ */ jsx22("dd", { className: "squisq-md-dd", children: renderBlocks(child.children, `${key}dd${i}`, ctx) }, `${key}dd${i}`);
|
|
3600
4002
|
}) }, key);
|
|
3601
4003
|
default:
|
|
3602
4004
|
return null;
|
|
@@ -3604,15 +4006,15 @@ function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
|
3604
4006
|
}
|
|
3605
4007
|
function renderListItem(item, key, ctx = DEFAULT_CTX) {
|
|
3606
4008
|
const isTask = item.checked !== null && item.checked !== void 0;
|
|
3607
|
-
return /* @__PURE__ */
|
|
3608
|
-
isTask && /* @__PURE__ */
|
|
4009
|
+
return /* @__PURE__ */ jsxs13("li", { className: `squisq-md-li${isTask ? " squisq-md-task" : ""}`, children: [
|
|
4010
|
+
isTask && /* @__PURE__ */ jsx22("input", { type: "checkbox", checked: !!item.checked, readOnly: true, className: "squisq-md-checkbox" }),
|
|
3609
4011
|
renderBlocks(item.children, key, ctx)
|
|
3610
4012
|
] }, key);
|
|
3611
4013
|
}
|
|
3612
4014
|
function renderTable(rows, align, key, ctx = DEFAULT_CTX) {
|
|
3613
4015
|
const [headerRow, ...bodyRows] = rows;
|
|
3614
|
-
return /* @__PURE__ */
|
|
3615
|
-
headerRow && /* @__PURE__ */
|
|
4016
|
+
return /* @__PURE__ */ jsxs13("table", { className: "squisq-md-table", children: [
|
|
4017
|
+
headerRow && /* @__PURE__ */ jsx22("thead", { children: /* @__PURE__ */ jsx22("tr", { children: headerRow.children.map((cell, ci) => /* @__PURE__ */ jsx22(
|
|
3616
4018
|
"th",
|
|
3617
4019
|
{
|
|
3618
4020
|
className: "squisq-md-th",
|
|
@@ -3621,7 +4023,7 @@ function renderTable(rows, align, key, ctx = DEFAULT_CTX) {
|
|
|
3621
4023
|
},
|
|
3622
4024
|
`${key}th${ci}`
|
|
3623
4025
|
)) }) }),
|
|
3624
|
-
bodyRows.length > 0 && /* @__PURE__ */
|
|
4026
|
+
bodyRows.length > 0 && /* @__PURE__ */ jsx22("tbody", { children: bodyRows.map((row, ri) => /* @__PURE__ */ jsx22("tr", { children: row.children.map((cell, ci) => /* @__PURE__ */ jsx22(
|
|
3625
4027
|
"td",
|
|
3626
4028
|
{
|
|
3627
4029
|
className: "squisq-md-td",
|
|
@@ -3639,7 +4041,7 @@ function MdImage({ src, alt, title }) {
|
|
|
3639
4041
|
const safeSrc = sanitizeUrl(src, "media");
|
|
3640
4042
|
const resolved = useMediaUrl(safeSrc ?? "", ".");
|
|
3641
4043
|
if (!safeSrc) return null;
|
|
3642
|
-
return /* @__PURE__ */
|
|
4044
|
+
return /* @__PURE__ */ jsx22("img", { className: "squisq-md-image", src: resolved, alt, title });
|
|
3643
4045
|
}
|
|
3644
4046
|
function resolveHtmlNodes(nodes, htmlPolicy) {
|
|
3645
4047
|
if (htmlPolicy === "strip") return [];
|
|
@@ -3675,6 +4077,7 @@ var PASSTHROUGH_ATTRS = {
|
|
|
3675
4077
|
target: "target",
|
|
3676
4078
|
rel: "rel"
|
|
3677
4079
|
};
|
|
4080
|
+
var RE_USED_TARGETS = /* @__PURE__ */ new Set(["", "_self", "_parent", "_top"]);
|
|
3678
4081
|
function reactPropsFromAttrs(attrs, ctx) {
|
|
3679
4082
|
const out = {};
|
|
3680
4083
|
for (const [name, value] of Object.entries(attrs)) {
|
|
@@ -3696,6 +4099,12 @@ function reactPropsFromAttrs(attrs, ctx) {
|
|
|
3696
4099
|
}
|
|
3697
4100
|
out[propName] = value;
|
|
3698
4101
|
}
|
|
4102
|
+
if (typeof out.target === "string" && !RE_USED_TARGETS.has(out.target.toLowerCase())) {
|
|
4103
|
+
const rel = typeof out.rel === "string" ? out.rel : "";
|
|
4104
|
+
const tokens = rel.split(/\s+/).filter((t) => t && t.toLowerCase() !== "opener");
|
|
4105
|
+
if (!tokens.some((t) => t.toLowerCase() === "noopener")) tokens.push("noopener");
|
|
4106
|
+
out.rel = tokens.join(" ");
|
|
4107
|
+
}
|
|
3699
4108
|
return out;
|
|
3700
4109
|
}
|
|
3701
4110
|
function renderHtmlElement(el, key, ctx) {
|
|
@@ -3704,7 +4113,7 @@ function renderHtmlElement(el, key, ctx) {
|
|
|
3704
4113
|
if (tagName === "video") {
|
|
3705
4114
|
const src = sanitizeUrl(el.attributes.src ?? "", "media") ?? "";
|
|
3706
4115
|
const poster = sanitizeUrl(el.attributes.poster ?? "", "media") ?? void 0;
|
|
3707
|
-
return /* @__PURE__ */
|
|
4116
|
+
return /* @__PURE__ */ jsx22(
|
|
3708
4117
|
InlineVideoPlayer,
|
|
3709
4118
|
{
|
|
3710
4119
|
src,
|
|
@@ -3719,7 +4128,7 @@ function renderHtmlElement(el, key, ctx) {
|
|
|
3719
4128
|
}
|
|
3720
4129
|
if (tagName === "audio") {
|
|
3721
4130
|
const src = sanitizeUrl(el.attributes.src ?? "", "media") ?? "";
|
|
3722
|
-
return /* @__PURE__ */
|
|
4131
|
+
return /* @__PURE__ */ jsx22(
|
|
3723
4132
|
InlineAudioPlayer,
|
|
3724
4133
|
{
|
|
3725
4134
|
src,
|
|
@@ -3732,9 +4141,9 @@ function renderHtmlElement(el, key, ctx) {
|
|
|
3732
4141
|
const Tag = tagName;
|
|
3733
4142
|
const props = reactPropsFromAttrs(el.attributes, ctx);
|
|
3734
4143
|
if (el.selfClosing) {
|
|
3735
|
-
return /* @__PURE__ */
|
|
4144
|
+
return /* @__PURE__ */ jsx22(Tag, { ...props }, key);
|
|
3736
4145
|
}
|
|
3737
|
-
return /* @__PURE__ */
|
|
4146
|
+
return /* @__PURE__ */ jsx22(Tag, { ...props, children: renderHtmlNodes(el.children, `${key}c`, ctx) }, key);
|
|
3738
4147
|
}
|
|
3739
4148
|
function renderHtmlNodes(nodes, keyPrefix, ctx = DEFAULT_CTX) {
|
|
3740
4149
|
return nodes.map((node, i) => {
|
|
@@ -3743,7 +4152,7 @@ function renderHtmlNodes(nodes, keyPrefix, ctx = DEFAULT_CTX) {
|
|
|
3743
4152
|
case "htmlElement":
|
|
3744
4153
|
return renderHtmlElement(node, key, ctx);
|
|
3745
4154
|
case "htmlText":
|
|
3746
|
-
return /* @__PURE__ */
|
|
4155
|
+
return /* @__PURE__ */ jsx22(Fragment2, { children: node.value }, key);
|
|
3747
4156
|
case "htmlComment":
|
|
3748
4157
|
return null;
|
|
3749
4158
|
default:
|
|
@@ -3758,123 +4167,362 @@ function MarkdownRenderer({
|
|
|
3758
4167
|
linkSchemes
|
|
3759
4168
|
}) {
|
|
3760
4169
|
if (!nodes || nodes.length === 0) return null;
|
|
3761
|
-
return /* @__PURE__ */
|
|
4170
|
+
return /* @__PURE__ */ jsx22("div", { className: `squisq-md ${className || ""}`, children: renderBlocks(nodes, "", { htmlPolicy, linkSchemes }) });
|
|
3762
4171
|
}
|
|
3763
4172
|
|
|
3764
|
-
// src/
|
|
3765
|
-
import {
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
}
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
4173
|
+
// src/page/sections.tsx
|
|
4174
|
+
import { sanitizeUrl as sanitizeUrl2 } from "@bendyline/squisq/markdown";
|
|
4175
|
+
import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4176
|
+
function PageImg({
|
|
4177
|
+
media,
|
|
4178
|
+
eager,
|
|
4179
|
+
className
|
|
4180
|
+
}) {
|
|
4181
|
+
const { basePath } = usePageView();
|
|
4182
|
+
const safeSrc = sanitizeUrl2(media.src, "media");
|
|
4183
|
+
const resolved = useMediaUrl(safeSrc ?? "", basePath);
|
|
4184
|
+
if (!safeSrc) return null;
|
|
4185
|
+
return /* @__PURE__ */ jsx23(
|
|
4186
|
+
"img",
|
|
4187
|
+
{
|
|
4188
|
+
className,
|
|
4189
|
+
src: resolved,
|
|
4190
|
+
alt: media.alt,
|
|
4191
|
+
loading: eager ? "eager" : "lazy",
|
|
4192
|
+
decoding: "async",
|
|
4193
|
+
width: media.width,
|
|
4194
|
+
height: media.height
|
|
4195
|
+
}
|
|
4196
|
+
);
|
|
4197
|
+
}
|
|
4198
|
+
function BackdropVideo({ media }) {
|
|
4199
|
+
const { basePath } = usePageView();
|
|
4200
|
+
const safeSrc = sanitizeUrl2(media.src, "media");
|
|
4201
|
+
const resolvedSrc = useMediaUrl(safeSrc ?? "", basePath);
|
|
4202
|
+
const safePoster = media.posterSrc ? sanitizeUrl2(media.posterSrc, "media") : void 0;
|
|
4203
|
+
const resolvedPoster = useMediaUrl(safePoster ?? "", basePath);
|
|
4204
|
+
if (!safeSrc) return null;
|
|
4205
|
+
return /* @__PURE__ */ jsx23(
|
|
4206
|
+
"video",
|
|
4207
|
+
{
|
|
4208
|
+
src: resolvedSrc,
|
|
4209
|
+
poster: safePoster ? resolvedPoster : void 0,
|
|
4210
|
+
muted: true,
|
|
4211
|
+
loop: true,
|
|
4212
|
+
autoPlay: true,
|
|
4213
|
+
playsInline: true,
|
|
4214
|
+
preload: "metadata"
|
|
4215
|
+
}
|
|
4216
|
+
);
|
|
4217
|
+
}
|
|
4218
|
+
function Eyebrow({ section }) {
|
|
4219
|
+
if (!section.slots.eyebrow) return null;
|
|
4220
|
+
return /* @__PURE__ */ jsx23("span", { className: "squisq-page-eyebrow", children: section.slots.eyebrow });
|
|
4221
|
+
}
|
|
4222
|
+
function MediaCredit({ media }) {
|
|
4223
|
+
if (!media || media.type === "canvas" || !media.credit) return null;
|
|
4224
|
+
return /* @__PURE__ */ jsxs14("span", { className: "squisq-page-media-credit", children: [
|
|
4225
|
+
media.credit,
|
|
4226
|
+
media.license ? ` \xB7 ${media.license}` : ""
|
|
4227
|
+
] });
|
|
4228
|
+
}
|
|
4229
|
+
function HeroSection({ section }) {
|
|
4230
|
+
const { pageStyle } = usePageView();
|
|
4231
|
+
const media = section.slots.media;
|
|
4232
|
+
const image = media?.type === "image" ? media : void 0;
|
|
4233
|
+
const heroStyle = pageStyle.tokens.heroStyle;
|
|
4234
|
+
const body = /* @__PURE__ */ jsxs14("div", { className: "squisq-page-hero-body", children: [
|
|
4235
|
+
/* @__PURE__ */ jsx23(Eyebrow, { section }),
|
|
4236
|
+
/* @__PURE__ */ jsx23("h1", { className: "squisq-page-hero-title", children: section.slots.title }),
|
|
4237
|
+
section.slots.subtitle && /* @__PURE__ */ jsx23("p", { className: "squisq-page-hero-subtitle", children: section.slots.subtitle })
|
|
4238
|
+
] });
|
|
4239
|
+
if (image && heroStyle === "split") {
|
|
4240
|
+
return /* @__PURE__ */ jsxs14("div", { className: "squisq-page-hero-grid", children: [
|
|
4241
|
+
body,
|
|
4242
|
+
/* @__PURE__ */ jsxs14("div", { className: "squisq-page-hero-media squisq-page-media-frame", children: [
|
|
4243
|
+
/* @__PURE__ */ jsx23(PageImg, { media: image, eager: true }),
|
|
4244
|
+
/* @__PURE__ */ jsx23(MediaCredit, { media: image })
|
|
4245
|
+
] })
|
|
4246
|
+
] });
|
|
3777
4247
|
}
|
|
3778
|
-
return
|
|
4248
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
4249
|
+
image && section.background === "media" && /* @__PURE__ */ jsx23("div", { className: "squisq-page-hero-backdrop", children: /* @__PURE__ */ jsx23(PageImg, { media: image, eager: true }) }),
|
|
4250
|
+
body
|
|
4251
|
+
] });
|
|
3779
4252
|
}
|
|
3780
|
-
function
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
4253
|
+
function BannerSection({ section }) {
|
|
4254
|
+
const media = section.slots.media;
|
|
4255
|
+
const image = media?.type === "image" ? media : void 0;
|
|
4256
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
4257
|
+
image && section.background === "media" && /* @__PURE__ */ jsx23("div", { className: "squisq-page-hero-backdrop", children: /* @__PURE__ */ jsx23(PageImg, { media: image }) }),
|
|
4258
|
+
/* @__PURE__ */ jsxs14("div", { className: "squisq-page-banner-body", children: [
|
|
4259
|
+
/* @__PURE__ */ jsx23(Eyebrow, { section }),
|
|
4260
|
+
/* @__PURE__ */ jsx23("h2", { className: "squisq-page-section-title squisq-page-banner-title", children: section.slots.title })
|
|
4261
|
+
] })
|
|
4262
|
+
] });
|
|
4263
|
+
}
|
|
4264
|
+
function StatBandSection({ section }) {
|
|
4265
|
+
const items = section.slots.items ?? [];
|
|
4266
|
+
const comparison = section.variant === "comparison";
|
|
4267
|
+
const numericValues = items.map(
|
|
4268
|
+
(item) => typeof item.value === "number" ? item.value : Number.parseFloat(String(item.value ?? ""))
|
|
4269
|
+
);
|
|
4270
|
+
const maxValue = Math.max(1, ...numericValues.filter((value) => Number.isFinite(value)));
|
|
4271
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
4272
|
+
/* @__PURE__ */ jsx23("div", { className: "squisq-page-stats", children: items.map((item, i) => /* @__PURE__ */ jsxs14("div", { className: `squisq-page-stat${comparison ? " squisq-page-stat--bars" : ""}`, children: [
|
|
4273
|
+
/* @__PURE__ */ jsx23("span", { className: "squisq-page-stat-value", children: item.value }),
|
|
4274
|
+
comparison && Number.isFinite(numericValues[i]) && /* @__PURE__ */ jsx23(
|
|
4275
|
+
"div",
|
|
4276
|
+
{
|
|
4277
|
+
className: "squisq-page-stat-bar",
|
|
4278
|
+
style: { width: `${Math.max(4, numericValues[i] / maxValue * 100)}%` }
|
|
4279
|
+
}
|
|
4280
|
+
),
|
|
4281
|
+
item.title && /* @__PURE__ */ jsx23("span", { className: "squisq-page-stat-title", children: item.title }),
|
|
4282
|
+
item.body && /* @__PURE__ */ jsx23("span", { className: "squisq-page-stat-body", children: item.body })
|
|
4283
|
+
] }, i)) }),
|
|
4284
|
+
section.slots.meta && /* @__PURE__ */ jsx23("div", { className: "squisq-page-stat-meta", children: section.slots.meta })
|
|
4285
|
+
] });
|
|
4286
|
+
}
|
|
4287
|
+
function QuoteBandSection({ section }) {
|
|
4288
|
+
const media = section.slots.media;
|
|
4289
|
+
const backdrop = section.background === "media" && media && media.type !== "canvas";
|
|
4290
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
4291
|
+
backdrop && /* @__PURE__ */ jsx23("div", { className: "squisq-page-quote-backdrop", children: media.type === "image" ? /* @__PURE__ */ jsx23(PageImg, { media }) : /* @__PURE__ */ jsx23(BackdropVideo, { media }) }),
|
|
4292
|
+
/* @__PURE__ */ jsxs14("figure", { className: "squisq-page-quote-figure", style: { margin: 0 }, children: [
|
|
4293
|
+
/* @__PURE__ */ jsx23("blockquote", { className: "squisq-page-quote", children: section.slots.body?.text }),
|
|
4294
|
+
section.slots.attribution && /* @__PURE__ */ jsx23("figcaption", { className: "squisq-page-quote-attribution", children: section.slots.attribution })
|
|
4295
|
+
] })
|
|
4296
|
+
] });
|
|
4297
|
+
}
|
|
4298
|
+
function FeatureSplitSection({
|
|
4299
|
+
section,
|
|
4300
|
+
flip
|
|
3788
4301
|
}) {
|
|
3789
|
-
const
|
|
3790
|
-
const
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
4302
|
+
const media = section.slots.media;
|
|
4303
|
+
const image = media?.type === "image" ? media : void 0;
|
|
4304
|
+
const mediaRight = flip ? section.variant !== "media-right" : section.variant === "media-right";
|
|
4305
|
+
return /* @__PURE__ */ jsxs14("div", { className: `squisq-page-feature${mediaRight ? " squisq-page-feature--media-right" : ""}`, children: [
|
|
4306
|
+
/* @__PURE__ */ jsxs14("div", { className: "squisq-page-feature-media squisq-page-media-frame", children: [
|
|
4307
|
+
image && /* @__PURE__ */ jsx23(PageImg, { media: image }),
|
|
4308
|
+
/* @__PURE__ */ jsx23(MediaCredit, { media: image })
|
|
4309
|
+
] }),
|
|
4310
|
+
/* @__PURE__ */ jsxs14("div", { className: "squisq-page-feature-text", children: [
|
|
4311
|
+
/* @__PURE__ */ jsx23(Eyebrow, { section }),
|
|
4312
|
+
section.slots.title && /* @__PURE__ */ jsx23("h3", { className: "squisq-page-section-title squisq-page-feature-title", children: section.slots.title }),
|
|
4313
|
+
section.slots.body?.text && /* @__PURE__ */ jsx23("p", { className: "squisq-page-feature-body", children: section.slots.body.text })
|
|
4314
|
+
] })
|
|
4315
|
+
] });
|
|
4316
|
+
}
|
|
4317
|
+
function MediaFigureSection({ section }) {
|
|
4318
|
+
const { basePath } = usePageView();
|
|
4319
|
+
const media = section.slots.media;
|
|
4320
|
+
if (!media || media.type === "canvas") return null;
|
|
4321
|
+
return /* @__PURE__ */ jsxs14("figure", { className: "squisq-page-figure", children: [
|
|
4322
|
+
/* @__PURE__ */ jsx23("div", { className: "squisq-page-media-frame", children: media.type === "image" ? /* @__PURE__ */ jsx23(PageImg, { media }) : /* @__PURE__ */ jsx23(InlineVideoPlayer, { src: media.src, poster: media.posterSrc, basePath }) }),
|
|
4323
|
+
(section.slots.caption || media.credit) && /* @__PURE__ */ jsxs14("figcaption", { children: [
|
|
4324
|
+
section.slots.caption,
|
|
4325
|
+
media.credit && /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
4326
|
+
section.slots.caption ? " " : "",
|
|
4327
|
+
/* @__PURE__ */ jsx23(MediaCredit, { media })
|
|
4328
|
+
] })
|
|
4329
|
+
] })
|
|
4330
|
+
] });
|
|
4331
|
+
}
|
|
4332
|
+
function GallerySection({ section }) {
|
|
4333
|
+
const items = section.slots.items ?? [];
|
|
4334
|
+
if (items.length === 0) {
|
|
4335
|
+
return /* @__PURE__ */ jsxs14("div", { className: "squisq-page-gallery-empty", children: [
|
|
4336
|
+
section.slots.title && /* @__PURE__ */ jsx23("h2", { className: "squisq-page-section-title", children: section.slots.title }),
|
|
4337
|
+
section.slots.body?.text && /* @__PURE__ */ jsx23("p", { children: section.slots.body.text }),
|
|
4338
|
+
section.slots.caption && /* @__PURE__ */ jsx23("div", { className: "squisq-page-gallery-caption", children: section.slots.caption })
|
|
4339
|
+
] });
|
|
4340
|
+
}
|
|
4341
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
4342
|
+
/* @__PURE__ */ jsx23("div", { className: "squisq-page-gallery", children: items.map(
|
|
4343
|
+
(item, i) => item.media?.type === "image" ? /* @__PURE__ */ jsx23("div", { className: "squisq-page-media-frame", children: /* @__PURE__ */ jsx23(PageImg, { media: item.media }) }, i) : null
|
|
4344
|
+
) }),
|
|
4345
|
+
section.slots.caption && /* @__PURE__ */ jsx23("div", { className: "squisq-page-gallery-caption", children: section.slots.caption })
|
|
4346
|
+
] });
|
|
4347
|
+
}
|
|
4348
|
+
function CalloutSection({ section }) {
|
|
4349
|
+
const media = section.slots.media;
|
|
4350
|
+
const image = media?.type === "image" ? media : void 0;
|
|
4351
|
+
const framing = section.hints?.framing;
|
|
4352
|
+
return /* @__PURE__ */ jsxs14(
|
|
3830
4353
|
"div",
|
|
3831
4354
|
{
|
|
3832
|
-
className: "squisq-
|
|
3833
|
-
"data-
|
|
3834
|
-
"data-block-index": blockIndex,
|
|
3835
|
-
"data-template": isAnnotated ? visualTemplateName(block) : void 0,
|
|
4355
|
+
className: "squisq-page-callout",
|
|
4356
|
+
"data-hint-framing": typeof framing === "string" ? framing : void 0,
|
|
3836
4357
|
children: [
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
{
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
aspectRatio: `${viewport.width} / ${viewport.height}`,
|
|
3845
|
-
overflow: "hidden",
|
|
3846
|
-
marginBottom: "1em"
|
|
3847
|
-
},
|
|
3848
|
-
children: /* @__PURE__ */ jsx20(
|
|
3849
|
-
BlockRenderer,
|
|
3850
|
-
{
|
|
3851
|
-
block: visualBlock,
|
|
3852
|
-
blockTime: 0,
|
|
3853
|
-
basePath,
|
|
3854
|
-
viewport,
|
|
3855
|
-
animationsEnabled
|
|
3856
|
-
}
|
|
3857
|
-
)
|
|
3858
|
-
}
|
|
3859
|
-
) }),
|
|
3860
|
-
!isAnnotated && block.contents && block.contents.length > 0 && /* @__PURE__ */ jsx20(MarkdownRenderer, { nodes: block.contents }),
|
|
3861
|
-
block.children && block.children.length > 0 && /* @__PURE__ */ jsx20("div", { className: "squisq-linear-children", children: block.children.map((child, i) => /* @__PURE__ */ jsx20(
|
|
3862
|
-
BlockSection,
|
|
3863
|
-
{
|
|
3864
|
-
block: child,
|
|
3865
|
-
basePath,
|
|
3866
|
-
viewport,
|
|
3867
|
-
renderContext,
|
|
3868
|
-
blockIndex: blockIndices.get(child) ?? blockIndex + i + 1,
|
|
3869
|
-
blockIndices,
|
|
3870
|
-
animationsEnabled
|
|
3871
|
-
},
|
|
3872
|
-
child.id
|
|
3873
|
-
)) })
|
|
4358
|
+
image && /* @__PURE__ */ jsx23("div", { className: "squisq-page-media-frame", children: /* @__PURE__ */ jsx23(PageImg, { media: image }) }),
|
|
4359
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
4360
|
+
/* @__PURE__ */ jsx23(Eyebrow, { section }),
|
|
4361
|
+
section.slots.title && /* @__PURE__ */ jsx23("h3", { className: "squisq-page-callout-title", children: section.slots.title }),
|
|
4362
|
+
section.slots.body?.text && /* @__PURE__ */ jsx23("p", { className: "squisq-page-callout-body", children: section.slots.body.text }),
|
|
4363
|
+
section.slots.meta && /* @__PURE__ */ jsx23("div", { className: "squisq-page-callout-meta", children: section.slots.meta })
|
|
4364
|
+
] })
|
|
3874
4365
|
]
|
|
3875
4366
|
}
|
|
3876
4367
|
);
|
|
3877
4368
|
}
|
|
4369
|
+
function CardGridSection({ section }) {
|
|
4370
|
+
const { theme } = usePageView();
|
|
4371
|
+
const items = section.slots.items ?? [];
|
|
4372
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
4373
|
+
section.slots.title && /* @__PURE__ */ jsx23("h2", { className: "squisq-page-section-title squisq-page-cards-title", children: section.slots.title }),
|
|
4374
|
+
/* @__PURE__ */ jsx23("div", { className: "squisq-page-cards", children: items.map((item, i) => {
|
|
4375
|
+
const scheme = item.accentScheme ? theme.colorSchemes?.[item.accentScheme] : void 0;
|
|
4376
|
+
return /* @__PURE__ */ jsxs14(
|
|
4377
|
+
"div",
|
|
4378
|
+
{
|
|
4379
|
+
className: "squisq-page-card",
|
|
4380
|
+
style: scheme ? { "--squisq-page-card-accent": scheme.accent } : void 0,
|
|
4381
|
+
children: [
|
|
4382
|
+
item.title && /* @__PURE__ */ jsx23("h3", { className: "squisq-page-card-title", children: item.title }),
|
|
4383
|
+
item.body && /* @__PURE__ */ jsx23("p", { className: "squisq-page-card-body", children: item.body })
|
|
4384
|
+
]
|
|
4385
|
+
},
|
|
4386
|
+
i
|
|
4387
|
+
);
|
|
4388
|
+
}) })
|
|
4389
|
+
] });
|
|
4390
|
+
}
|
|
4391
|
+
function ItemListSection({ section }) {
|
|
4392
|
+
const items = section.slots.items ?? [];
|
|
4393
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
4394
|
+
section.slots.title && /* @__PURE__ */ jsx23("h2", { className: "squisq-page-section-title squisq-page-items-title", children: section.slots.title }),
|
|
4395
|
+
/* @__PURE__ */ jsx23("ol", { className: "squisq-page-items", children: items.map((item, i) => /* @__PURE__ */ jsx23("li", { children: item.body }, i)) })
|
|
4396
|
+
] });
|
|
4397
|
+
}
|
|
4398
|
+
function TimelineRailSection({ section }) {
|
|
4399
|
+
const milestone = section.slots.milestone;
|
|
4400
|
+
if (!milestone) return null;
|
|
4401
|
+
return /* @__PURE__ */ jsxs14("div", { className: "squisq-page-milestone", "data-mood": milestone.mood, children: [
|
|
4402
|
+
/* @__PURE__ */ jsx23("div", { className: "squisq-page-milestone-date", children: milestone.date }),
|
|
4403
|
+
/* @__PURE__ */ jsx23("div", { className: "squisq-page-milestone-rail", "aria-hidden": "true" }),
|
|
4404
|
+
/* @__PURE__ */ jsxs14("div", { className: "squisq-page-milestone-body", children: [
|
|
4405
|
+
milestone.description,
|
|
4406
|
+
milestone.footer && /* @__PURE__ */ jsx23("div", { className: "squisq-page-milestone-footer", children: milestone.footer })
|
|
4407
|
+
] })
|
|
4408
|
+
] });
|
|
4409
|
+
}
|
|
4410
|
+
function TableSection({ section }) {
|
|
4411
|
+
const table = section.slots.table;
|
|
4412
|
+
if (!table) return null;
|
|
4413
|
+
const alignFor = (i) => table.align?.[i] ?? void 0;
|
|
4414
|
+
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
4415
|
+
section.slots.title && /* @__PURE__ */ jsx23("h2", { className: "squisq-page-section-title squisq-page-table-title", children: section.slots.title }),
|
|
4416
|
+
/* @__PURE__ */ jsx23("div", { className: "squisq-page-table-scroll", children: /* @__PURE__ */ jsxs14("table", { className: "squisq-page-table", children: [
|
|
4417
|
+
/* @__PURE__ */ jsx23("thead", { children: /* @__PURE__ */ jsx23("tr", { children: table.headers.map((header, i) => /* @__PURE__ */ jsx23("th", { style: { textAlign: alignFor(i) }, children: header }, i)) }) }),
|
|
4418
|
+
/* @__PURE__ */ jsx23("tbody", { children: table.rows.map((row, r) => /* @__PURE__ */ jsx23("tr", { children: row.map((cell, c) => /* @__PURE__ */ jsx23("td", { style: { textAlign: alignFor(c) }, children: cell }, c)) }, r)) })
|
|
4419
|
+
] }) })
|
|
4420
|
+
] });
|
|
4421
|
+
}
|
|
4422
|
+
function ProseSection({ section, block }) {
|
|
4423
|
+
const heading = section.slots.title !== void 0 && block?.sourceHeading ? /* @__PURE__ */ jsx23(MarkdownRenderer, { nodes: [block.sourceHeading] }) : null;
|
|
4424
|
+
const bodyNodes = section.slots.body?.markdown ?? block?.contents;
|
|
4425
|
+
return /* @__PURE__ */ jsxs14("div", { className: "squisq-page-prose", children: [
|
|
4426
|
+
heading,
|
|
4427
|
+
bodyNodes && bodyNodes.length > 0 && /* @__PURE__ */ jsx23(MarkdownRenderer, { nodes: bodyNodes })
|
|
4428
|
+
] });
|
|
4429
|
+
}
|
|
4430
|
+
function FooterSection({ section }) {
|
|
4431
|
+
return /* @__PURE__ */ jsxs14("div", { children: [
|
|
4432
|
+
/* @__PURE__ */ jsx23(Eyebrow, { section }),
|
|
4433
|
+
section.slots.title && /* @__PURE__ */ jsx23("h2", { className: "squisq-page-section-title", children: section.slots.title }),
|
|
4434
|
+
section.slots.body?.text && /* @__PURE__ */ jsx23("p", { children: section.slots.body.text })
|
|
4435
|
+
] });
|
|
4436
|
+
}
|
|
4437
|
+
|
|
4438
|
+
// src/page/PageSectionView.tsx
|
|
4439
|
+
import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4440
|
+
function heroHasBackdrop(section, heroStyle) {
|
|
4441
|
+
return section.kind === "hero" && section.background === "media" && section.slots.media?.type === "image" && heroStyle !== "split";
|
|
4442
|
+
}
|
|
4443
|
+
function accentVars(section) {
|
|
4444
|
+
const scheme = section.accent.scheme;
|
|
4445
|
+
if (!scheme) return void 0;
|
|
4446
|
+
return {
|
|
4447
|
+
"--squisq-page-accent": scheme.accent,
|
|
4448
|
+
"--squisq-page-accent-bg": scheme.bg,
|
|
4449
|
+
"--squisq-page-accent-text": scheme.text
|
|
4450
|
+
};
|
|
4451
|
+
}
|
|
4452
|
+
function sectionBody(entry, featureFlip) {
|
|
4453
|
+
const { section, block } = entry;
|
|
4454
|
+
switch (section.kind) {
|
|
4455
|
+
case "hero":
|
|
4456
|
+
return /* @__PURE__ */ jsx24(HeroSection, { section, block });
|
|
4457
|
+
case "banner":
|
|
4458
|
+
return /* @__PURE__ */ jsx24(BannerSection, { section, block });
|
|
4459
|
+
case "stat-band":
|
|
4460
|
+
return /* @__PURE__ */ jsx24(StatBandSection, { section, block });
|
|
4461
|
+
case "quote-band":
|
|
4462
|
+
return /* @__PURE__ */ jsx24(QuoteBandSection, { section, block });
|
|
4463
|
+
case "feature-split":
|
|
4464
|
+
return /* @__PURE__ */ jsx24(FeatureSplitSection, { section, block, flip: featureFlip });
|
|
4465
|
+
case "media-figure":
|
|
4466
|
+
return /* @__PURE__ */ jsx24(MediaFigureSection, { section, block });
|
|
4467
|
+
case "gallery":
|
|
4468
|
+
return /* @__PURE__ */ jsx24(GallerySection, { section, block });
|
|
4469
|
+
case "callout":
|
|
4470
|
+
return /* @__PURE__ */ jsx24(CalloutSection, { section, block });
|
|
4471
|
+
case "card-grid":
|
|
4472
|
+
return /* @__PURE__ */ jsx24(CardGridSection, { section, block });
|
|
4473
|
+
case "item-list":
|
|
4474
|
+
return /* @__PURE__ */ jsx24(ItemListSection, { section, block });
|
|
4475
|
+
case "timeline-rail":
|
|
4476
|
+
return /* @__PURE__ */ jsx24(TimelineRailSection, { section, block });
|
|
4477
|
+
case "table-section":
|
|
4478
|
+
return /* @__PURE__ */ jsx24(TableSection, { section, block });
|
|
4479
|
+
case "canvas-embed":
|
|
4480
|
+
return /* @__PURE__ */ jsx24(CanvasSection, { section });
|
|
4481
|
+
case "footer":
|
|
4482
|
+
return /* @__PURE__ */ jsx24(FooterSection, { section, block });
|
|
4483
|
+
case "prose":
|
|
4484
|
+
default:
|
|
4485
|
+
return /* @__PURE__ */ jsx24(ProseSection, { section, block });
|
|
4486
|
+
}
|
|
4487
|
+
}
|
|
4488
|
+
function PageSectionView({ entry, featureFlip, isLeadProse }) {
|
|
4489
|
+
const { pageStyle } = usePageView();
|
|
4490
|
+
const { section } = entry;
|
|
4491
|
+
const richContent = section.slots.richContent?.markdown;
|
|
4492
|
+
const classes = [
|
|
4493
|
+
"squisq-linear-section",
|
|
4494
|
+
"squisq-page-section",
|
|
4495
|
+
`squisq-page-section--${section.kind}`,
|
|
4496
|
+
`squisq-page-section--bg-${section.background}`,
|
|
4497
|
+
`squisq-page-section--em-${section.emphasis}`
|
|
4498
|
+
];
|
|
4499
|
+
if (section.variant) classes.push(`squisq-page-section--v-${section.variant}`);
|
|
4500
|
+
if (heroHasBackdrop(section, pageStyle.tokens.heroStyle)) {
|
|
4501
|
+
classes.push("squisq-page-section--hero-media");
|
|
4502
|
+
}
|
|
4503
|
+
const hintAttrs = {};
|
|
4504
|
+
if (section.hints?.vignette) hintAttrs["data-hint-vignette"] = "";
|
|
4505
|
+
if (section.hints?.dropCap && isLeadProse) hintAttrs["data-hint-drop-cap"] = "";
|
|
4506
|
+
return /* @__PURE__ */ jsx24(
|
|
4507
|
+
"section",
|
|
4508
|
+
{
|
|
4509
|
+
className: classes.join(" "),
|
|
4510
|
+
"data-section-kind": section.kind,
|
|
4511
|
+
"data-block-id": section.blockId,
|
|
4512
|
+
"data-block-index": section.index,
|
|
4513
|
+
"data-template": section.template,
|
|
4514
|
+
style: accentVars(section),
|
|
4515
|
+
...hintAttrs,
|
|
4516
|
+
children: /* @__PURE__ */ jsxs15("div", { className: "squisq-page-section-inner", children: [
|
|
4517
|
+
sectionBody(entry, featureFlip),
|
|
4518
|
+
richContent && richContent.length > 0 && /* @__PURE__ */ jsx24("div", { className: "squisq-page-rich-content", children: /* @__PURE__ */ jsx24(MarkdownRenderer, { nodes: richContent }) })
|
|
4519
|
+
] })
|
|
4520
|
+
}
|
|
4521
|
+
);
|
|
4522
|
+
}
|
|
4523
|
+
|
|
4524
|
+
// src/LinearDocView.tsx
|
|
4525
|
+
import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3878
4526
|
function LinearDocView({
|
|
3879
4527
|
doc,
|
|
3880
4528
|
markdown,
|
|
@@ -3886,48 +4534,82 @@ function LinearDocView({
|
|
|
3886
4534
|
animationsEnabled = true,
|
|
3887
4535
|
thinMargins = false,
|
|
3888
4536
|
imageDisplayMode = "inline",
|
|
3889
|
-
globalKeyboardShortcuts = false
|
|
4537
|
+
globalKeyboardShortcuts = false,
|
|
4538
|
+
showCover = true,
|
|
4539
|
+
transformPage
|
|
3890
4540
|
}) {
|
|
3891
4541
|
const scrollRef = useRef8(null);
|
|
3892
|
-
const activeViewport = viewport ??
|
|
3893
|
-
const markdownDoc =
|
|
4542
|
+
const activeViewport = viewport ?? VIEWPORT_PRESETS4.landscape;
|
|
4543
|
+
const markdownDoc = useMemo9(
|
|
3894
4544
|
() => !doc && markdown !== void 0 ? markdownToDoc(parseMarkdown(markdown)) : void 0,
|
|
3895
4545
|
[doc, markdown]
|
|
3896
4546
|
);
|
|
3897
4547
|
const resolvedDoc = doc ?? markdownDoc;
|
|
3898
|
-
const totalBlocks = useMemo8(
|
|
3899
|
-
() => resolvedDoc ? countAll(resolvedDoc.blocks) : 0,
|
|
3900
|
-
[resolvedDoc]
|
|
3901
|
-
);
|
|
3902
|
-
const blockIndices = useMemo8(() => {
|
|
3903
|
-
const indices = /* @__PURE__ */ new Map();
|
|
3904
|
-
let index = 0;
|
|
3905
|
-
const visit = (blocks) => {
|
|
3906
|
-
for (const block of blocks) {
|
|
3907
|
-
indices.set(block, index++);
|
|
3908
|
-
if (block.children) visit(block.children);
|
|
3909
|
-
}
|
|
3910
|
-
};
|
|
3911
|
-
if (resolvedDoc) visit(resolvedDoc.blocks);
|
|
3912
|
-
return indices;
|
|
3913
|
-
}, [resolvedDoc]);
|
|
3914
4548
|
const autoSurface = useAutoSurface(surface === "auto");
|
|
3915
4549
|
const resolvedSurface = surface === "auto" ? autoSurface : surface;
|
|
3916
|
-
const
|
|
3917
|
-
const baseTheme = theme ??
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
4550
|
+
const activeTheme = useMemo9(() => {
|
|
4551
|
+
const baseTheme = theme ?? DEFAULT_THEME3;
|
|
4552
|
+
return resolvedSurface ? applySurface(baseTheme, resolvedSurface) : baseTheme;
|
|
4553
|
+
}, [theme, resolvedSurface]);
|
|
4554
|
+
const pageStyle = useMemo9(
|
|
4555
|
+
() => resolvePageStyle2(activeTheme, transformPage),
|
|
4556
|
+
[activeTheme, transformPage]
|
|
4557
|
+
);
|
|
4558
|
+
const sections = useMemo9(() => {
|
|
4559
|
+
if (!resolvedDoc) return [];
|
|
4560
|
+
return materializePageSections(resolvedDoc, {
|
|
4561
|
+
theme: activeTheme,
|
|
3921
4562
|
viewport: activeViewport,
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
4563
|
+
customTemplates: resolvedDoc.customTemplates,
|
|
4564
|
+
cover: showCover !== false ? resolvedDoc.startBlock : false,
|
|
4565
|
+
transformPage
|
|
4566
|
+
});
|
|
4567
|
+
}, [resolvedDoc, activeTheme, activeViewport, showCover, transformPage]);
|
|
4568
|
+
const renderContext = useMemo9(
|
|
4569
|
+
() => ({
|
|
4570
|
+
theme: activeTheme,
|
|
4571
|
+
viewport: activeViewport,
|
|
4572
|
+
totalBlocks: sections.length,
|
|
4573
|
+
persistentLayers: activeTheme.persistentLayers,
|
|
3926
4574
|
customTemplates: resolvedDoc?.customTemplates
|
|
3927
|
-
}
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
4575
|
+
}),
|
|
4576
|
+
[activeTheme, activeViewport, sections.length, resolvedDoc?.customTemplates]
|
|
4577
|
+
);
|
|
4578
|
+
const pageContext = useMemo9(
|
|
4579
|
+
() => ({
|
|
4580
|
+
theme: activeTheme,
|
|
4581
|
+
pageStyle,
|
|
4582
|
+
basePath,
|
|
4583
|
+
viewport: activeViewport,
|
|
4584
|
+
renderContext,
|
|
4585
|
+
animationsEnabled,
|
|
4586
|
+
imageDisplayMode
|
|
4587
|
+
}),
|
|
4588
|
+
[
|
|
4589
|
+
activeTheme,
|
|
4590
|
+
pageStyle,
|
|
4591
|
+
basePath,
|
|
4592
|
+
activeViewport,
|
|
4593
|
+
renderContext,
|
|
4594
|
+
animationsEnabled,
|
|
4595
|
+
imageDisplayMode
|
|
4596
|
+
]
|
|
4597
|
+
);
|
|
4598
|
+
const { featureOrdinals, leadProseIndex } = useMemo9(() => {
|
|
4599
|
+
const ordinals = /* @__PURE__ */ new Map();
|
|
4600
|
+
let featureCount = 0;
|
|
4601
|
+
let firstProse = -1;
|
|
4602
|
+
for (const entry of sections) {
|
|
4603
|
+
if (entry.section.kind === "feature-split") {
|
|
4604
|
+
ordinals.set(entry.section.index, featureCount++);
|
|
4605
|
+
}
|
|
4606
|
+
if (firstProse < 0 && entry.section.kind === "prose") {
|
|
4607
|
+
firstProse = entry.section.index;
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
return { featureOrdinals: ordinals, leadProseIndex: firstProse };
|
|
4611
|
+
}, [sections]);
|
|
4612
|
+
useEffect10(() => {
|
|
3931
4613
|
if (!globalKeyboardShortcuts) return;
|
|
3932
4614
|
const handleKeyDown = (event) => {
|
|
3933
4615
|
if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.key !== "ArrowDown" && event.key !== "ArrowUp") {
|
|
@@ -3952,7 +4634,7 @@ function LinearDocView({
|
|
|
3952
4634
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
3953
4635
|
}, [globalKeyboardShortcuts]);
|
|
3954
4636
|
if (!resolvedDoc) {
|
|
3955
|
-
return /* @__PURE__ */
|
|
4637
|
+
return /* @__PURE__ */ jsx25("div", { ref: scrollRef, className: `squisq-linear squisq-linear--empty ${className || ""}` });
|
|
3956
4638
|
}
|
|
3957
4639
|
const bgColor = activeTheme.colors.background;
|
|
3958
4640
|
const textColor = activeTheme.colors.text;
|
|
@@ -3960,12 +4642,22 @@ function LinearDocView({
|
|
|
3960
4642
|
const primaryColor = activeTheme.colors.primary;
|
|
3961
4643
|
const bodyFont = resolveFontFamily2(activeTheme.typography.bodyFont, "system-ui, sans-serif");
|
|
3962
4644
|
const titleFont = resolveFontFamily2(activeTheme.typography.titleFont, "Georgia, serif");
|
|
3963
|
-
const lineHt = activeTheme.typography.lineHeight ?? 1.
|
|
3964
|
-
|
|
4645
|
+
const lineHt = activeTheme.typography.lineHeight ?? 1.65;
|
|
4646
|
+
const contentClasses = [
|
|
4647
|
+
"squisq-linear-content",
|
|
4648
|
+
"squisq-md",
|
|
4649
|
+
"squisq-page",
|
|
4650
|
+
thinMargins ? "squisq-linear-content--thin squisq-page--thin" : "",
|
|
4651
|
+
imageDisplayMode === "thumbnail" ? "squisq-linear-content--thumbnail-images squisq-page--thumbnail-images" : ""
|
|
4652
|
+
].filter(Boolean).join(" ");
|
|
4653
|
+
return /* @__PURE__ */ jsx25(
|
|
3965
4654
|
"div",
|
|
3966
4655
|
{
|
|
3967
4656
|
ref: scrollRef,
|
|
3968
4657
|
className: `squisq-linear ${className || ""}`,
|
|
4658
|
+
role: "region",
|
|
4659
|
+
"aria-label": "Document page",
|
|
4660
|
+
tabIndex: thinMargins ? void 0 : 0,
|
|
3969
4661
|
style: {
|
|
3970
4662
|
width: "100%",
|
|
3971
4663
|
// Thin-margins mode is the "embedded in someone else's container"
|
|
@@ -3976,25 +4668,26 @@ function LinearDocView({
|
|
|
3976
4668
|
height: thinMargins ? "auto" : "100%",
|
|
3977
4669
|
overflowY: thinMargins ? "visible" : "auto",
|
|
3978
4670
|
overflowX: "hidden",
|
|
3979
|
-
background: bgColor
|
|
4671
|
+
background: bgColor,
|
|
4672
|
+
// Container queries drive the responsive page layout — the page
|
|
4673
|
+
// often lives inside editor panes / chat bubbles where viewport
|
|
4674
|
+
// media queries lie about the available width.
|
|
4675
|
+
containerType: "inline-size",
|
|
4676
|
+
containerName: "squisq-page"
|
|
3980
4677
|
},
|
|
3981
|
-
children: /* @__PURE__ */
|
|
4678
|
+
children: /* @__PURE__ */ jsxs16(
|
|
3982
4679
|
"div",
|
|
3983
4680
|
{
|
|
3984
|
-
className:
|
|
4681
|
+
className: contentClasses,
|
|
4682
|
+
...pageStyleDataAttributes(pageStyle),
|
|
3985
4683
|
style: {
|
|
3986
|
-
// Thin-margins mode drops the 720px reading column + generous
|
|
3987
|
-
// page padding (right for standalone docs) in favor of a tight
|
|
3988
|
-
// layout that hugs its container (right for chat bubbles and
|
|
3989
|
-
// sidebar previews).
|
|
3990
|
-
maxWidth: thinMargins ? "none" : "720px",
|
|
3991
|
-
margin: thinMargins ? "0" : "0 auto",
|
|
3992
|
-
padding: thinMargins ? "0" : "24px 16px",
|
|
3993
4684
|
lineHeight: lineHt,
|
|
3994
4685
|
fontSize: "16px",
|
|
3995
4686
|
fontFamily: bodyFont,
|
|
3996
4687
|
color: textColor,
|
|
3997
|
-
|
|
4688
|
+
...buildPageCssVars(activeTheme, pageStyle),
|
|
4689
|
+
// Legacy custom properties kept for one release so host CSS
|
|
4690
|
+
// and MarkdownRenderer content keyed on them keep working.
|
|
3998
4691
|
"--squisq-linear-title-font": titleFont,
|
|
3999
4692
|
"--squisq-linear-body-font": bodyFont,
|
|
4000
4693
|
"--squisq-linear-text": textColor,
|
|
@@ -4003,134 +4696,20 @@ function LinearDocView({
|
|
|
4003
4696
|
"--squisq-linear-bg": bgColor
|
|
4004
4697
|
},
|
|
4005
4698
|
children: [
|
|
4006
|
-
/* @__PURE__ */
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
.squisq-linear-content h3 { font-size: 1.25em; }
|
|
4021
|
-
.squisq-linear-content p {
|
|
4022
|
-
margin-bottom: 0.75em;
|
|
4023
|
-
}
|
|
4024
|
-
.squisq-linear-content p + p {
|
|
4025
|
-
margin-top: 1.25em;
|
|
4026
|
-
}
|
|
4027
|
-
.squisq-linear-content ul,
|
|
4028
|
-
.squisq-linear-content ol {
|
|
4029
|
-
padding-left: 2em;
|
|
4030
|
-
margin-bottom: 0.75em;
|
|
4031
|
-
}
|
|
4032
|
-
.squisq-linear-content li {
|
|
4033
|
-
margin-bottom: 0.3em;
|
|
4034
|
-
}
|
|
4035
|
-
.squisq-linear-content a {
|
|
4036
|
-
/* Blend the theme's primary toward the body text color so
|
|
4037
|
-
links stay theme-flavored but always have enough contrast
|
|
4038
|
-
against the background. Some themes (e.g. Gezellig) pick a
|
|
4039
|
-
mid-tone primary that's nearly invisible on a dark page
|
|
4040
|
-
without this lift. */
|
|
4041
|
-
color: color-mix(in srgb, var(--squisq-linear-primary) 65%, var(--squisq-linear-text));
|
|
4042
|
-
text-decoration: underline;
|
|
4043
|
-
text-decoration-thickness: 1px;
|
|
4044
|
-
text-underline-offset: 2px;
|
|
4045
|
-
}
|
|
4046
|
-
.squisq-linear-content a:hover {
|
|
4047
|
-
color: var(--squisq-linear-primary);
|
|
4048
|
-
}
|
|
4049
|
-
.squisq-linear-content code {
|
|
4050
|
-
color: var(--squisq-linear-primary);
|
|
4051
|
-
font-size: 0.9em;
|
|
4052
|
-
padding: 0.15em 0.3em;
|
|
4053
|
-
border-radius: 3px;
|
|
4054
|
-
background: rgba(128, 128, 128, 0.15);
|
|
4055
|
-
}
|
|
4056
|
-
.squisq-linear-content pre {
|
|
4057
|
-
padding: 1em;
|
|
4058
|
-
border-radius: 6px;
|
|
4059
|
-
background: rgba(0, 0, 0, 0.2);
|
|
4060
|
-
overflow-x: auto;
|
|
4061
|
-
margin-bottom: 0.75em;
|
|
4062
|
-
}
|
|
4063
|
-
.squisq-linear-content pre code {
|
|
4064
|
-
padding: 0;
|
|
4065
|
-
background: none;
|
|
4066
|
-
}
|
|
4067
|
-
.squisq-linear-content blockquote {
|
|
4068
|
-
border-left: 3px solid var(--squisq-linear-muted);
|
|
4069
|
-
color: var(--squisq-linear-muted);
|
|
4070
|
-
padding-left: 1em;
|
|
4071
|
-
margin-left: 0;
|
|
4072
|
-
margin-bottom: 0.75em;
|
|
4073
|
-
}
|
|
4074
|
-
.squisq-linear-content hr {
|
|
4075
|
-
border: none;
|
|
4076
|
-
border-top: 1px solid var(--squisq-linear-muted);
|
|
4077
|
-
margin: 1.5em 0;
|
|
4078
|
-
}
|
|
4079
|
-
.squisq-linear-content img {
|
|
4080
|
-
max-width: 100%;
|
|
4081
|
-
height: auto;
|
|
4082
|
-
border-radius: 6px;
|
|
4083
|
-
margin: 0.5em 0;
|
|
4084
|
-
}
|
|
4085
|
-
.squisq-linear-content--thumbnail-images img {
|
|
4086
|
-
max-width: 100px;
|
|
4087
|
-
max-height: 100px;
|
|
4088
|
-
width: auto;
|
|
4089
|
-
height: auto;
|
|
4090
|
-
object-fit: contain;
|
|
4091
|
-
display: block;
|
|
4092
|
-
}
|
|
4093
|
-
.squisq-linear-content strong {
|
|
4094
|
-
font-weight: 700;
|
|
4095
|
-
}
|
|
4096
|
-
.squisq-linear-content em {
|
|
4097
|
-
font-style: italic;
|
|
4098
|
-
}
|
|
4099
|
-
.squisq-linear-content table {
|
|
4100
|
-
width: 100%;
|
|
4101
|
-
border-collapse: collapse;
|
|
4102
|
-
margin: 1em 0;
|
|
4103
|
-
font-size: 0.95em;
|
|
4104
|
-
}
|
|
4105
|
-
.squisq-linear-content thead th {
|
|
4106
|
-
background: var(--squisq-linear-primary);
|
|
4107
|
-
color: var(--squisq-linear-bg);
|
|
4108
|
-
font-family: var(--squisq-linear-title-font);
|
|
4109
|
-
font-weight: 600;
|
|
4110
|
-
padding: 10px 14px;
|
|
4111
|
-
text-align: left;
|
|
4112
|
-
}
|
|
4113
|
-
.squisq-linear-content tbody td {
|
|
4114
|
-
padding: 8px 14px;
|
|
4115
|
-
border-bottom: 1px solid color-mix(in srgb, var(--squisq-linear-muted) 30%, transparent);
|
|
4116
|
-
}
|
|
4117
|
-
.squisq-linear-content tbody tr:hover {
|
|
4118
|
-
background: color-mix(in srgb, var(--squisq-linear-primary) 8%, transparent);
|
|
4119
|
-
}
|
|
4120
|
-
` }),
|
|
4121
|
-
resolvedDoc.blocks.map((block, i) => /* @__PURE__ */ jsx20(
|
|
4122
|
-
BlockSection,
|
|
4123
|
-
{
|
|
4124
|
-
block,
|
|
4125
|
-
basePath,
|
|
4126
|
-
viewport: activeViewport,
|
|
4127
|
-
renderContext,
|
|
4128
|
-
blockIndex: blockIndices.get(block) ?? i,
|
|
4129
|
-
blockIndices,
|
|
4130
|
-
animationsEnabled
|
|
4131
|
-
},
|
|
4132
|
-
block.id
|
|
4133
|
-
))
|
|
4699
|
+
/* @__PURE__ */ jsx25("style", { "data-squisq-page": "", children: PAGE_BASE_CSS }),
|
|
4700
|
+
/* @__PURE__ */ jsx25(PageViewContext.Provider, { value: pageContext, children: sections.map((entry) => {
|
|
4701
|
+
const ordinal = featureOrdinals.get(entry.section.index) ?? 0;
|
|
4702
|
+
const flip = !!entry.section.hints?.alternate && ordinal % 2 === 1;
|
|
4703
|
+
return /* @__PURE__ */ jsx25(
|
|
4704
|
+
PageSectionView,
|
|
4705
|
+
{
|
|
4706
|
+
entry,
|
|
4707
|
+
featureFlip: flip,
|
|
4708
|
+
isLeadProse: entry.section.index === leadProseIndex
|
|
4709
|
+
},
|
|
4710
|
+
`${entry.section.blockId}-${entry.section.index}`
|
|
4711
|
+
);
|
|
4712
|
+
}) })
|
|
4134
4713
|
]
|
|
4135
4714
|
}
|
|
4136
4715
|
)
|
|
@@ -4138,8 +4717,8 @@ function LinearDocView({
|
|
|
4138
4717
|
);
|
|
4139
4718
|
}
|
|
4140
4719
|
|
|
4141
|
-
// src/
|
|
4142
|
-
import {
|
|
4720
|
+
// src/docPlayer/segmentTitles.ts
|
|
4721
|
+
import { isTemplateBlock as isTemplateBlock2 } from "@bendyline/squisq/schemas";
|
|
4143
4722
|
var SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
4144
4723
|
"a",
|
|
4145
4724
|
"an",
|
|
@@ -4160,31 +4739,30 @@ var SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
|
4160
4739
|
function buildSegmentTitleMap(doc) {
|
|
4161
4740
|
const map = /* @__PURE__ */ new Map();
|
|
4162
4741
|
for (const block of doc.blocks) {
|
|
4163
|
-
if (
|
|
4164
|
-
const
|
|
4165
|
-
if (!map.has(
|
|
4166
|
-
map.set(segIdx, block.title);
|
|
4167
|
-
}
|
|
4742
|
+
if (isTemplateBlock2(block) && block.template === "sectionHeader" && "title" in block) {
|
|
4743
|
+
const segmentIndex = block.audioSegment;
|
|
4744
|
+
if (!map.has(segmentIndex)) map.set(segmentIndex, block.title);
|
|
4168
4745
|
}
|
|
4169
4746
|
}
|
|
4170
|
-
for (let
|
|
4171
|
-
if (
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
map.set(i, titled);
|
|
4183
|
-
}
|
|
4747
|
+
for (let index = 0; index < doc.audio.segments.length; index++) {
|
|
4748
|
+
if (map.has(index)) continue;
|
|
4749
|
+
const name = doc.audio.segments[index].name;
|
|
4750
|
+
if (name.includes("intro")) {
|
|
4751
|
+
map.set(index, "Introduction");
|
|
4752
|
+
} else if (name.includes("flight-context")) {
|
|
4753
|
+
map.set(index, "Flight Context");
|
|
4754
|
+
} else {
|
|
4755
|
+
const title = name.split("-").map(
|
|
4756
|
+
(word, wordIndex) => wordIndex === 0 || !SMALL_WORDS.has(word) ? word.charAt(0).toUpperCase() + word.slice(1) : word
|
|
4757
|
+
).join(" ");
|
|
4758
|
+
map.set(index, title);
|
|
4184
4759
|
}
|
|
4185
4760
|
}
|
|
4186
4761
|
return map;
|
|
4187
4762
|
}
|
|
4763
|
+
|
|
4764
|
+
// src/DocPlayer.tsx
|
|
4765
|
+
import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4188
4766
|
function isDevEnvironment() {
|
|
4189
4767
|
try {
|
|
4190
4768
|
return typeof process !== "undefined" && process.env.NODE_ENV !== "production";
|
|
@@ -4195,15 +4773,15 @@ function isDevEnvironment() {
|
|
|
4195
4773
|
var warnedMissingStyles = false;
|
|
4196
4774
|
function DocPlayer(props) {
|
|
4197
4775
|
const { doc, markdown } = props;
|
|
4198
|
-
const markdownDoc =
|
|
4776
|
+
const markdownDoc = useMemo10(
|
|
4199
4777
|
() => !doc && markdown !== void 0 ? markdownToDoc2(parseMarkdown2(markdown)) : void 0,
|
|
4200
4778
|
[doc, markdown]
|
|
4201
4779
|
);
|
|
4202
4780
|
const resolvedDoc = doc ?? markdownDoc;
|
|
4203
4781
|
if (!resolvedDoc) {
|
|
4204
|
-
return /* @__PURE__ */
|
|
4782
|
+
return /* @__PURE__ */ jsx26("div", { className: "doc-player doc-player--empty" });
|
|
4205
4783
|
}
|
|
4206
|
-
return /* @__PURE__ */
|
|
4784
|
+
return /* @__PURE__ */ jsx26(DocPlayerContent, { ...props, doc: resolvedDoc });
|
|
4207
4785
|
}
|
|
4208
4786
|
function DocPlayerContent({
|
|
4209
4787
|
doc,
|
|
@@ -4214,6 +4792,7 @@ function DocPlayerContent({
|
|
|
4214
4792
|
onEnded,
|
|
4215
4793
|
onTimeUpdate,
|
|
4216
4794
|
audioController: externalAudioController,
|
|
4795
|
+
audioMode = "media",
|
|
4217
4796
|
showControls = true,
|
|
4218
4797
|
showScrubber = false,
|
|
4219
4798
|
muted = false,
|
|
@@ -4240,18 +4819,24 @@ function DocPlayerContent({
|
|
|
4240
4819
|
const audioRef = useRef9(null);
|
|
4241
4820
|
const containerRef = useRef9(null);
|
|
4242
4821
|
const playerId = `squisq-player-${useId7().replace(/:/g, "")}`;
|
|
4243
|
-
const [tapFeedback, setTapFeedback] =
|
|
4822
|
+
const [tapFeedback, setTapFeedback] = useState10(null);
|
|
4244
4823
|
const tapFeedbackTimer = useRef9();
|
|
4245
4824
|
const { viewport, orientation } = useViewportOrientation();
|
|
4246
|
-
const activeViewport = forceViewport || (renderMode ?
|
|
4247
|
-
const isDebugMode =
|
|
4825
|
+
const activeViewport = forceViewport || (renderMode ? VIEWPORT_PRESETS5.landscape : viewport);
|
|
4826
|
+
const isDebugMode = useMemo10(() => {
|
|
4248
4827
|
if (typeof window === "undefined") return false;
|
|
4249
4828
|
const params = new URLSearchParams(window.location.search);
|
|
4250
4829
|
return params.get("debug") === "true";
|
|
4251
4830
|
}, []);
|
|
4252
|
-
const internalAudio = useAudioSync(
|
|
4831
|
+
const internalAudio = useAudioSync(
|
|
4832
|
+
audioRef,
|
|
4833
|
+
doc.audio,
|
|
4834
|
+
basePath,
|
|
4835
|
+
!externalAudioController,
|
|
4836
|
+
audioMode
|
|
4837
|
+
);
|
|
4253
4838
|
const audio = externalAudioController || internalAudio;
|
|
4254
|
-
|
|
4839
|
+
useEffect11(() => {
|
|
4255
4840
|
if (warnedMissingStyles || !isDevEnvironment()) return;
|
|
4256
4841
|
const el = containerRef.current;
|
|
4257
4842
|
if (!el || typeof getComputedStyle !== "function") return;
|
|
@@ -4279,7 +4864,7 @@ function DocPlayerContent({
|
|
|
4279
4864
|
skipToSegment: _skipToSegment,
|
|
4280
4865
|
restart
|
|
4281
4866
|
} = audio;
|
|
4282
|
-
const mediaSchedule =
|
|
4867
|
+
const mediaSchedule = useMemo10(() => resolveMediaSchedule(doc), [doc]);
|
|
4283
4868
|
const currentTimeRef = useRef9(currentTime);
|
|
4284
4869
|
currentTimeRef.current = currentTime;
|
|
4285
4870
|
const totalDurationRef = useRef9(totalDuration);
|
|
@@ -4310,8 +4895,8 @@ function DocPlayerContent({
|
|
|
4310
4895
|
);
|
|
4311
4896
|
const autoSurface = useAutoSurface(surface === "auto");
|
|
4312
4897
|
const resolvedSurface = surface === "auto" ? autoSurface : surface;
|
|
4313
|
-
const effectiveTheme =
|
|
4314
|
-
const base = theme ??
|
|
4898
|
+
const effectiveTheme = useMemo10(() => {
|
|
4899
|
+
const base = theme ?? DEFAULT_THEME4;
|
|
4315
4900
|
return resolvedSurface ? applySurface2(base, resolvedSurface) : base;
|
|
4316
4901
|
}, [theme, resolvedSurface]);
|
|
4317
4902
|
const {
|
|
@@ -4332,7 +4917,7 @@ function DocPlayerContent({
|
|
|
4332
4917
|
theme: effectiveTheme,
|
|
4333
4918
|
onSeek: seekTo
|
|
4334
4919
|
});
|
|
4335
|
-
const coverBlock =
|
|
4920
|
+
const coverBlock = useMemo10(() => {
|
|
4336
4921
|
const startBlockConfig = doc.startBlock;
|
|
4337
4922
|
if (!showCoverSlide) return null;
|
|
4338
4923
|
if (!startBlockConfig) return null;
|
|
@@ -4349,13 +4934,13 @@ function DocPlayerContent({
|
|
|
4349
4934
|
};
|
|
4350
4935
|
}, [doc.startBlock, activeViewport, effectiveTheme, showCoverSlide]);
|
|
4351
4936
|
const hasManagedCover = !!coverBlock;
|
|
4352
|
-
const [slideshowCoverVisible, setSlideshowCoverVisible] =
|
|
4353
|
-
const [isSlideshowPickerOpen, setIsSlideshowPickerOpen] =
|
|
4937
|
+
const [slideshowCoverVisible, setSlideshowCoverVisible] = useState10(false);
|
|
4938
|
+
const [isSlideshowPickerOpen, setIsSlideshowPickerOpen] = useState10(false);
|
|
4354
4939
|
const slideshowCoverInitKeyRef = useRef9("");
|
|
4355
|
-
|
|
4940
|
+
useEffect11(() => {
|
|
4356
4941
|
slideshowCoverInitKeyRef.current = "";
|
|
4357
4942
|
}, [doc]);
|
|
4358
|
-
|
|
4943
|
+
useEffect11(() => {
|
|
4359
4944
|
const initKey = `${isSlideshowMode}:${hasManagedCover}:${renderMode}`;
|
|
4360
4945
|
if (slideshowCoverInitKeyRef.current === initKey) return;
|
|
4361
4946
|
slideshowCoverInitKeyRef.current = initKey;
|
|
@@ -4366,12 +4951,12 @@ function DocPlayerContent({
|
|
|
4366
4951
|
setSlideshowCoverVisible(false);
|
|
4367
4952
|
}
|
|
4368
4953
|
}, [isSlideshowMode, hasManagedCover, renderMode, pause]);
|
|
4369
|
-
const [coverForced, setCoverForced] =
|
|
4370
|
-
const [coverGraceActive, setCoverGraceActive] =
|
|
4954
|
+
const [coverForced, setCoverForced] = useState10(false);
|
|
4955
|
+
const [coverGraceActive, setCoverGraceActive] = useState10(false);
|
|
4371
4956
|
const coverGraceTimer = useRef9();
|
|
4372
4957
|
const coverWasShowing = useRef9(false);
|
|
4373
4958
|
const hasPlayedOnce = useRef9(false);
|
|
4374
|
-
|
|
4959
|
+
useEffect11(() => {
|
|
4375
4960
|
hasPlayedOnce.current = false;
|
|
4376
4961
|
coverWasShowing.current = false;
|
|
4377
4962
|
clearTimeout(coverGraceTimer.current);
|
|
@@ -4380,7 +4965,7 @@ function DocPlayerContent({
|
|
|
4380
4965
|
}, [doc]);
|
|
4381
4966
|
const atRest = !!(coverBlock && !isSlideshowMode && !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
4382
4967
|
if (atRest) coverWasShowing.current = true;
|
|
4383
|
-
|
|
4968
|
+
useEffect11(() => {
|
|
4384
4969
|
if (isPlaying && coverWasShowing.current && coverBlock && !renderMode && !isSlideshowMode) {
|
|
4385
4970
|
coverWasShowing.current = false;
|
|
4386
4971
|
hasPlayedOnce.current = true;
|
|
@@ -4388,7 +4973,8 @@ function DocPlayerContent({
|
|
|
4388
4973
|
coverGraceTimer.current = setTimeout(() => setCoverGraceActive(false), 3e3);
|
|
4389
4974
|
}
|
|
4390
4975
|
}, [isPlaying, coverBlock, renderMode, isSlideshowMode]);
|
|
4391
|
-
|
|
4976
|
+
useEffect11(() => () => clearTimeout(coverGraceTimer.current), []);
|
|
4977
|
+
useEffect11(() => () => clearTimeout(tapFeedbackTimer.current), []);
|
|
4392
4978
|
const showVideoCoverBlock = !isSlideshowMode && !isLinearMode && !!coverBlock && (coverForced || coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
4393
4979
|
const effectiveSlideshowCoverVisible = coverVisible ?? slideshowCoverVisible;
|
|
4394
4980
|
const showSlideshowCover = !!(isSlideshowMode && !isLinearMode && !renderMode && coverBlock && effectiveSlideshowCoverVisible);
|
|
@@ -4397,19 +4983,19 @@ function DocPlayerContent({
|
|
|
4397
4983
|
const slideshowSlideIndex = slideshowHasCover ? effectiveSlideshowCoverVisible ? 0 : currentBlockIndex + 1 : currentBlockIndex;
|
|
4398
4984
|
const slideshowTotalSlides = slideshowHasCover ? expandedBlocks.length + 1 : expandedBlocks.length;
|
|
4399
4985
|
const hasAutoPlayed = useRef9(false);
|
|
4400
|
-
|
|
4986
|
+
useEffect11(() => {
|
|
4401
4987
|
hasAutoPlayed.current = false;
|
|
4402
4988
|
}, [doc]);
|
|
4403
|
-
|
|
4989
|
+
useEffect11(() => {
|
|
4404
4990
|
if (isAudioReady && autoPlay && !hasAutoPlayed.current) {
|
|
4405
4991
|
hasAutoPlayed.current = true;
|
|
4406
4992
|
play();
|
|
4407
4993
|
}
|
|
4408
4994
|
}, [isAudioReady, autoPlay, play]);
|
|
4409
|
-
|
|
4995
|
+
useEffect11(() => {
|
|
4410
4996
|
onTimeUpdate?.(currentTime);
|
|
4411
4997
|
}, [currentTime, onTimeUpdate]);
|
|
4412
|
-
|
|
4998
|
+
useEffect11(() => {
|
|
4413
4999
|
if (isEnded) {
|
|
4414
5000
|
onEnded?.();
|
|
4415
5001
|
}
|
|
@@ -4435,7 +5021,7 @@ function DocPlayerContent({
|
|
|
4435
5021
|
};
|
|
4436
5022
|
}
|
|
4437
5023
|
const stableRenderAPI = stableRenderAPIRef.current;
|
|
4438
|
-
|
|
5024
|
+
useEffect11(() => {
|
|
4439
5025
|
if (!renderMode && !isDebugMode) {
|
|
4440
5026
|
liveRenderAPIRef.current = null;
|
|
4441
5027
|
return;
|
|
@@ -4572,7 +5158,7 @@ function DocPlayerContent({
|
|
|
4572
5158
|
if (liveRenderAPIRef.current === api) liveRenderAPIRef.current = null;
|
|
4573
5159
|
};
|
|
4574
5160
|
}, [renderMode, isDebugMode, seekTo, totalDuration, expandedBlocks, coverBlock, doc]);
|
|
4575
|
-
|
|
5161
|
+
useEffect11(() => {
|
|
4576
5162
|
if (!renderMode && !isDebugMode || !containerRef.current) {
|
|
4577
5163
|
onRenderAPIReady?.(null);
|
|
4578
5164
|
return;
|
|
@@ -4581,8 +5167,8 @@ function DocPlayerContent({
|
|
|
4581
5167
|
return () => onRenderAPIReady?.(null);
|
|
4582
5168
|
}, [renderMode, isDebugMode, onRenderAPIReady, stableRenderAPI]);
|
|
4583
5169
|
const defaultMode = captionsEnabledProp === false ? "off" : captionStyle || "standard";
|
|
4584
|
-
const [captionMode, setCaptionMode] =
|
|
4585
|
-
|
|
5170
|
+
const [captionMode, setCaptionMode] = useState10(defaultMode);
|
|
5171
|
+
useEffect11(() => {
|
|
4586
5172
|
setCaptionMode(defaultMode);
|
|
4587
5173
|
}, [defaultMode]);
|
|
4588
5174
|
const captionsEnabled = captionMode !== "off";
|
|
@@ -4602,8 +5188,8 @@ function DocPlayerContent({
|
|
|
4602
5188
|
});
|
|
4603
5189
|
}, [onCaptionsToggle]);
|
|
4604
5190
|
const hasCaptions = doc.captions && doc.captions.phrases.length > 0;
|
|
4605
|
-
const segmentTitleMap =
|
|
4606
|
-
const playbackState =
|
|
5191
|
+
const segmentTitleMap = useMemo10(() => buildSegmentTitleMap(doc), [doc]);
|
|
5192
|
+
const playbackState = useMemo10(
|
|
4607
5193
|
() => ({
|
|
4608
5194
|
isPlaying,
|
|
4609
5195
|
currentTime,
|
|
@@ -4646,7 +5232,7 @@ function DocPlayerContent({
|
|
|
4646
5232
|
expandedBlocks.length
|
|
4647
5233
|
]
|
|
4648
5234
|
);
|
|
4649
|
-
const playbackActions =
|
|
5235
|
+
const playbackActions = useMemo10(
|
|
4650
5236
|
() => ({
|
|
4651
5237
|
toggle,
|
|
4652
5238
|
restart,
|
|
@@ -4657,7 +5243,7 @@ function DocPlayerContent({
|
|
|
4657
5243
|
}),
|
|
4658
5244
|
[toggle, restart, seekTo, setCaptionsEnabled, cycleCaptionMode, onFullscreenToggle]
|
|
4659
5245
|
);
|
|
4660
|
-
const slideNavActions =
|
|
5246
|
+
const slideNavActions = useMemo10(
|
|
4661
5247
|
() => ({
|
|
4662
5248
|
nextSlide: () => {
|
|
4663
5249
|
if (slideshowHasCover && slideshowCoverVisible) {
|
|
@@ -4746,10 +5332,10 @@ function DocPlayerContent({
|
|
|
4746
5332
|
onNext: handleSwipeNext,
|
|
4747
5333
|
onPrev: handleSwipePrev
|
|
4748
5334
|
});
|
|
4749
|
-
|
|
5335
|
+
useEffect11(() => {
|
|
4750
5336
|
onPlaybackStateChange?.(playbackState);
|
|
4751
5337
|
}, [playbackState, onPlaybackStateChange]);
|
|
4752
|
-
|
|
5338
|
+
useEffect11(() => {
|
|
4753
5339
|
onControlsReady?.({ play, pause, ...playbackActions });
|
|
4754
5340
|
}, [play, pause, playbackActions, onControlsReady]);
|
|
4755
5341
|
const getBlockTitle = useCallback7((block) => {
|
|
@@ -4776,7 +5362,7 @@ function DocPlayerContent({
|
|
|
4776
5362
|
}
|
|
4777
5363
|
return block.id.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
4778
5364
|
}, []);
|
|
4779
|
-
const slideshowPickerItems =
|
|
5365
|
+
const slideshowPickerItems = useMemo10(() => {
|
|
4780
5366
|
const blockItems = expandedBlocks.map((block, index) => ({
|
|
4781
5367
|
id: block.id,
|
|
4782
5368
|
label: String(index + 1),
|
|
@@ -4792,7 +5378,7 @@ function DocPlayerContent({
|
|
|
4792
5378
|
...blockItems
|
|
4793
5379
|
];
|
|
4794
5380
|
}, [coverBlock, expandedBlocks, getBlockTitle, slideshowHasCover]);
|
|
4795
|
-
const blockMarkers =
|
|
5381
|
+
const blockMarkers = useMemo10(() => {
|
|
4796
5382
|
if (!totalDuration || !expandedBlocks.length) return [];
|
|
4797
5383
|
let prevSegment = -1;
|
|
4798
5384
|
return expandedBlocks.map((block, index) => {
|
|
@@ -4807,7 +5393,7 @@ function DocPlayerContent({
|
|
|
4807
5393
|
};
|
|
4808
5394
|
});
|
|
4809
5395
|
}, [expandedBlocks, totalDuration, getBlockTitle]);
|
|
4810
|
-
|
|
5396
|
+
useEffect11(() => {
|
|
4811
5397
|
if (blockMarkers.length > 0) {
|
|
4812
5398
|
onBlockMarkers?.(blockMarkers);
|
|
4813
5399
|
}
|
|
@@ -4830,6 +5416,12 @@ function DocPlayerContent({
|
|
|
4830
5416
|
return;
|
|
4831
5417
|
}
|
|
4832
5418
|
if (isLinearMode) return;
|
|
5419
|
+
if (e.key === "f" || e.key === "F") {
|
|
5420
|
+
if (!onFullscreenToggle) return;
|
|
5421
|
+
e.preventDefault();
|
|
5422
|
+
onFullscreenToggle();
|
|
5423
|
+
return;
|
|
5424
|
+
}
|
|
4833
5425
|
if (isSlideshowMode) {
|
|
4834
5426
|
switch (e.key) {
|
|
4835
5427
|
case "ArrowRight":
|
|
@@ -4872,13 +5464,13 @@ function DocPlayerContent({
|
|
|
4872
5464
|
}
|
|
4873
5465
|
}
|
|
4874
5466
|
},
|
|
4875
|
-
[isSlideshowMode, isLinearMode, toggle, seekTo, slideNavActions]
|
|
5467
|
+
[isSlideshowMode, isLinearMode, toggle, seekTo, slideNavActions, onFullscreenToggle]
|
|
4876
5468
|
);
|
|
4877
5469
|
const handleKeyDown = useCallback7(
|
|
4878
5470
|
(e) => handleKeyboardShortcut(e, false),
|
|
4879
5471
|
[handleKeyboardShortcut]
|
|
4880
5472
|
);
|
|
4881
|
-
|
|
5473
|
+
useEffect11(() => {
|
|
4882
5474
|
if (!globalKeyboardShortcuts || renderMode || isLinearMode) return;
|
|
4883
5475
|
const handleDocumentKeyDown = (event) => {
|
|
4884
5476
|
handleKeyboardShortcut(event, true);
|
|
@@ -4887,7 +5479,7 @@ function DocPlayerContent({
|
|
|
4887
5479
|
return () => document.removeEventListener("keydown", handleDocumentKeyDown);
|
|
4888
5480
|
}, [globalKeyboardShortcuts, handleKeyboardShortcut, isLinearMode, renderMode]);
|
|
4889
5481
|
if (isLinearMode) {
|
|
4890
|
-
return /* @__PURE__ */
|
|
5482
|
+
return /* @__PURE__ */ jsx26(
|
|
4891
5483
|
"div",
|
|
4892
5484
|
{
|
|
4893
5485
|
ref: containerRef,
|
|
@@ -4899,7 +5491,7 @@ function DocPlayerContent({
|
|
|
4899
5491
|
height: "100%",
|
|
4900
5492
|
overflow: "hidden"
|
|
4901
5493
|
},
|
|
4902
|
-
children: /* @__PURE__ */
|
|
5494
|
+
children: /* @__PURE__ */ jsx26(
|
|
4903
5495
|
LinearDocView,
|
|
4904
5496
|
{
|
|
4905
5497
|
doc,
|
|
@@ -4913,7 +5505,7 @@ function DocPlayerContent({
|
|
|
4913
5505
|
}
|
|
4914
5506
|
);
|
|
4915
5507
|
}
|
|
4916
|
-
return /* @__PURE__ */
|
|
5508
|
+
return /* @__PURE__ */ jsxs17(
|
|
4917
5509
|
"div",
|
|
4918
5510
|
{
|
|
4919
5511
|
ref: containerRef,
|
|
@@ -4936,8 +5528,8 @@ function DocPlayerContent({
|
|
|
4936
5528
|
touchAction: swipeEnabled ? "pan-y" : void 0
|
|
4937
5529
|
},
|
|
4938
5530
|
children: [
|
|
4939
|
-
/* @__PURE__ */
|
|
4940
|
-
/* @__PURE__ */
|
|
5531
|
+
/* @__PURE__ */ jsx26("audio", { ref: audioRef, preload: "auto", muted }),
|
|
5532
|
+
/* @__PURE__ */ jsx26(
|
|
4941
5533
|
MediaClipLayer,
|
|
4942
5534
|
{
|
|
4943
5535
|
schedule: mediaSchedule,
|
|
@@ -4948,8 +5540,8 @@ function DocPlayerContent({
|
|
|
4948
5540
|
muted
|
|
4949
5541
|
}
|
|
4950
5542
|
),
|
|
4951
|
-
/* @__PURE__ */
|
|
4952
|
-
showCoverBlock && coverBlock && /* @__PURE__ */
|
|
5543
|
+
/* @__PURE__ */ jsxs17("div", { className: "doc-player__viewport", children: [
|
|
5544
|
+
showCoverBlock && coverBlock && /* @__PURE__ */ jsx26("div", { className: "doc-player__block doc-player__block--cover", children: /* @__PURE__ */ jsx26(
|
|
4953
5545
|
BlockRenderer,
|
|
4954
5546
|
{
|
|
4955
5547
|
block: coverBlock,
|
|
@@ -4964,7 +5556,7 @@ function DocPlayerContent({
|
|
|
4964
5556
|
// reconciles one block's layers onto another's (templates reuse layer
|
|
4965
5557
|
// ids like `title`/`background`), which would otherwise reuse stale
|
|
4966
5558
|
// DOM / skip entrance animations mid-transition.
|
|
4967
|
-
/* @__PURE__ */
|
|
5559
|
+
/* @__PURE__ */ jsx26("div", { className: "doc-player__block doc-player__block--previous", children: /* @__PURE__ */ jsx26(
|
|
4968
5560
|
BlockRenderer,
|
|
4969
5561
|
{
|
|
4970
5562
|
block: previousBlock,
|
|
@@ -4976,12 +5568,12 @@ function DocPlayerContent({
|
|
|
4976
5568
|
animationsEnabled
|
|
4977
5569
|
}
|
|
4978
5570
|
) }, previousBlock.id),
|
|
4979
|
-
!showCoverBlock && currentBlock && /* @__PURE__ */
|
|
5571
|
+
!showCoverBlock && currentBlock && /* @__PURE__ */ jsx26(
|
|
4980
5572
|
"div",
|
|
4981
5573
|
{
|
|
4982
5574
|
className: `doc-player__block doc-player__block--active${swipe.phase !== "idle" ? ` doc-player__block--${swipe.phase}` : ""}`,
|
|
4983
5575
|
style: swipe.phase !== "idle" ? { transform: `translateX(${swipe.offsetPx}px)` } : void 0,
|
|
4984
|
-
children: /* @__PURE__ */
|
|
5576
|
+
children: /* @__PURE__ */ jsx26(
|
|
4985
5577
|
BlockRenderer,
|
|
4986
5578
|
{
|
|
4987
5579
|
block: currentBlock,
|
|
@@ -4996,7 +5588,7 @@ function DocPlayerContent({
|
|
|
4996
5588
|
},
|
|
4997
5589
|
currentBlock.id
|
|
4998
5590
|
),
|
|
4999
|
-
hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */
|
|
5591
|
+
hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */ jsx26(
|
|
5000
5592
|
CaptionOverlay,
|
|
5001
5593
|
{
|
|
5002
5594
|
captions: doc.captions,
|
|
@@ -5008,7 +5600,7 @@ function DocPlayerContent({
|
|
|
5008
5600
|
viewport: activeViewport
|
|
5009
5601
|
}
|
|
5010
5602
|
),
|
|
5011
|
-
isDebugMode && /* @__PURE__ */
|
|
5603
|
+
isDebugMode && /* @__PURE__ */ jsxs17(
|
|
5012
5604
|
"div",
|
|
5013
5605
|
{
|
|
5014
5606
|
className: "doc-player__debug",
|
|
@@ -5029,27 +5621,27 @@ function DocPlayerContent({
|
|
|
5029
5621
|
textAlign: "left"
|
|
5030
5622
|
},
|
|
5031
5623
|
children: [
|
|
5032
|
-
/* @__PURE__ */
|
|
5033
|
-
/* @__PURE__ */
|
|
5034
|
-
/* @__PURE__ */
|
|
5624
|
+
/* @__PURE__ */ jsx26("div", { style: { color: "#ffcc00", fontWeight: "bold", marginBottom: "4px" }, children: "DEBUG MODE" }),
|
|
5625
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5626
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "template:" }),
|
|
5035
5627
|
" ",
|
|
5036
|
-
/* @__PURE__ */
|
|
5628
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#ff6b6b" }, children: currentBlock?.template ?? "raw" })
|
|
5037
5629
|
] }),
|
|
5038
|
-
/* @__PURE__ */
|
|
5039
|
-
/* @__PURE__ */
|
|
5630
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5631
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "block:" }),
|
|
5040
5632
|
" ",
|
|
5041
5633
|
currentBlockIndex + 1,
|
|
5042
5634
|
"/",
|
|
5043
5635
|
expandedBlocks.length,
|
|
5044
5636
|
" ",
|
|
5045
|
-
/* @__PURE__ */
|
|
5637
|
+
/* @__PURE__ */ jsxs17("span", { style: { color: "#666" }, children: [
|
|
5046
5638
|
"(",
|
|
5047
5639
|
currentBlock?.id || "none",
|
|
5048
5640
|
")"
|
|
5049
5641
|
] })
|
|
5050
5642
|
] }),
|
|
5051
|
-
/* @__PURE__ */
|
|
5052
|
-
/* @__PURE__ */
|
|
5643
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5644
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "time:" }),
|
|
5053
5645
|
" ",
|
|
5054
5646
|
currentTime.toFixed(2),
|
|
5055
5647
|
"s /",
|
|
@@ -5057,7 +5649,7 @@ function DocPlayerContent({
|
|
|
5057
5649
|
totalDuration.toFixed(1),
|
|
5058
5650
|
"s",
|
|
5059
5651
|
" ",
|
|
5060
|
-
/* @__PURE__ */
|
|
5652
|
+
/* @__PURE__ */ jsxs17("span", { style: { color: "#666" }, children: [
|
|
5061
5653
|
"(progress: ",
|
|
5062
5654
|
(docProgress * 100).toFixed(1),
|
|
5063
5655
|
"%, scriptDur: ",
|
|
@@ -5065,8 +5657,8 @@ function DocPlayerContent({
|
|
|
5065
5657
|
")"
|
|
5066
5658
|
] })
|
|
5067
5659
|
] }),
|
|
5068
|
-
/* @__PURE__ */
|
|
5069
|
-
/* @__PURE__ */
|
|
5660
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5661
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "blockTime:" }),
|
|
5070
5662
|
" ",
|
|
5071
5663
|
blockTime.toFixed(2),
|
|
5072
5664
|
"s /",
|
|
@@ -5074,58 +5666,58 @@ function DocPlayerContent({
|
|
|
5074
5666
|
(currentBlock?.duration || 0).toFixed(1),
|
|
5075
5667
|
"s"
|
|
5076
5668
|
] }),
|
|
5077
|
-
/* @__PURE__ */
|
|
5078
|
-
/* @__PURE__ */
|
|
5669
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5670
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "segment:" }),
|
|
5079
5671
|
" ",
|
|
5080
5672
|
currentSegment,
|
|
5081
5673
|
"/",
|
|
5082
5674
|
doc.audio.segments.length - 1,
|
|
5083
5675
|
" ",
|
|
5084
|
-
/* @__PURE__ */
|
|
5676
|
+
/* @__PURE__ */ jsxs17("span", { style: { color: "#666" }, children: [
|
|
5085
5677
|
"(",
|
|
5086
5678
|
doc.audio.segments[currentSegment]?.name || "none",
|
|
5087
5679
|
")"
|
|
5088
5680
|
] })
|
|
5089
5681
|
] }),
|
|
5090
|
-
/* @__PURE__ */
|
|
5091
|
-
/* @__PURE__ */
|
|
5682
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5683
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "viewport:" }),
|
|
5092
5684
|
" ",
|
|
5093
5685
|
activeViewport.name || `${activeViewport.width}x${activeViewport.height}`,
|
|
5094
5686
|
" ",
|
|
5095
|
-
/* @__PURE__ */
|
|
5687
|
+
/* @__PURE__ */ jsxs17("span", { style: { color: "#666" }, children: [
|
|
5096
5688
|
"(",
|
|
5097
5689
|
orientation,
|
|
5098
5690
|
")"
|
|
5099
5691
|
] })
|
|
5100
5692
|
] }),
|
|
5101
|
-
/* @__PURE__ */
|
|
5102
|
-
/* @__PURE__ */
|
|
5693
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5694
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "playing:" }),
|
|
5103
5695
|
" ",
|
|
5104
|
-
/* @__PURE__ */
|
|
5105
|
-
showCoverBlock && /* @__PURE__ */
|
|
5696
|
+
/* @__PURE__ */ jsx26("span", { style: { color: isPlaying ? "#4ade80" : "#f87171" }, children: isPlaying ? "yes" : "no" }),
|
|
5697
|
+
showCoverBlock && /* @__PURE__ */ jsx26("span", { style: { color: "#60a5fa" }, children: " (cover)" })
|
|
5106
5698
|
] }),
|
|
5107
5699
|
hasCaptions && (() => {
|
|
5108
5700
|
const debugPhrase = getCaptionAtTime2(doc.captions, currentTime);
|
|
5109
5701
|
const debugEnabled = captionsEnabled && (isPlaying || currentTime > 0);
|
|
5110
|
-
return /* @__PURE__ */
|
|
5111
|
-
/* @__PURE__ */
|
|
5112
|
-
/* @__PURE__ */
|
|
5702
|
+
return /* @__PURE__ */ jsxs17(Fragment4, { children: [
|
|
5703
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5704
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "captions:" }),
|
|
5113
5705
|
" ",
|
|
5114
5706
|
doc.captions?.phrases.length || 0,
|
|
5115
5707
|
" phrases",
|
|
5116
5708
|
" ",
|
|
5117
|
-
/* @__PURE__ */
|
|
5709
|
+
/* @__PURE__ */ jsxs17("span", { style: { color: captionsEnabled ? "#4ade80" : "#666" }, children: [
|
|
5118
5710
|
"(",
|
|
5119
5711
|
captionsEnabled ? "on" : "off",
|
|
5120
5712
|
")"
|
|
5121
5713
|
] })
|
|
5122
5714
|
] }),
|
|
5123
|
-
/* @__PURE__ */
|
|
5124
|
-
/* @__PURE__ */
|
|
5715
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5716
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "cc.enabled:" }),
|
|
5125
5717
|
" ",
|
|
5126
|
-
/* @__PURE__ */
|
|
5718
|
+
/* @__PURE__ */ jsx26("span", { style: { color: debugEnabled ? "#4ade80" : "#f87171" }, children: String(debugEnabled) }),
|
|
5127
5719
|
" ",
|
|
5128
|
-
/* @__PURE__ */
|
|
5720
|
+
/* @__PURE__ */ jsxs17("span", { style: { color: "#666" }, children: [
|
|
5129
5721
|
"(playing=",
|
|
5130
5722
|
String(isPlaying),
|
|
5131
5723
|
" t>0=",
|
|
@@ -5133,15 +5725,15 @@ function DocPlayerContent({
|
|
|
5133
5725
|
")"
|
|
5134
5726
|
] })
|
|
5135
5727
|
] }),
|
|
5136
|
-
/* @__PURE__ */
|
|
5137
|
-
/* @__PURE__ */
|
|
5728
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
5729
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "cc.phrase:" }),
|
|
5138
5730
|
" ",
|
|
5139
|
-
/* @__PURE__ */
|
|
5731
|
+
/* @__PURE__ */ jsx26("span", { style: { color: debugPhrase ? "#4ade80" : "#f87171" }, children: debugPhrase ? `"${debugPhrase.text.slice(0, 30)}..."` : "null" })
|
|
5140
5732
|
] }),
|
|
5141
|
-
debugPhrase && /* @__PURE__ */
|
|
5142
|
-
/* @__PURE__ */
|
|
5733
|
+
debugPhrase && /* @__PURE__ */ jsxs17("div", { children: [
|
|
5734
|
+
/* @__PURE__ */ jsx26("span", { style: { color: "#888" }, children: "cc.range:" }),
|
|
5143
5735
|
" ",
|
|
5144
|
-
/* @__PURE__ */
|
|
5736
|
+
/* @__PURE__ */ jsxs17("span", { style: { color: "#60a5fa" }, children: [
|
|
5145
5737
|
debugPhrase.startTime.toFixed(2),
|
|
5146
5738
|
"-",
|
|
5147
5739
|
debugPhrase.endTime.toFixed(2)
|
|
@@ -5153,7 +5745,7 @@ function DocPlayerContent({
|
|
|
5153
5745
|
}
|
|
5154
5746
|
)
|
|
5155
5747
|
] }),
|
|
5156
|
-
!isAvailable && unavailableMessage && /* @__PURE__ */
|
|
5748
|
+
!isAvailable && unavailableMessage && /* @__PURE__ */ jsxs17(
|
|
5157
5749
|
"div",
|
|
5158
5750
|
{
|
|
5159
5751
|
className: "doc-player__unavailable",
|
|
@@ -5174,12 +5766,12 @@ function DocPlayerContent({
|
|
|
5174
5766
|
zIndex: 50
|
|
5175
5767
|
},
|
|
5176
5768
|
children: [
|
|
5177
|
-
/* @__PURE__ */
|
|
5178
|
-
/* @__PURE__ */
|
|
5769
|
+
/* @__PURE__ */ jsx26("span", { style: { fontSize: "32px" }, children: "\u{1F50A}" }),
|
|
5770
|
+
/* @__PURE__ */ jsx26("span", { children: unavailableMessage })
|
|
5179
5771
|
]
|
|
5180
5772
|
}
|
|
5181
5773
|
),
|
|
5182
|
-
!renderMode && !isSlideshowMode && showControls && /* @__PURE__ */
|
|
5774
|
+
!renderMode && !isSlideshowMode && showControls && /* @__PURE__ */ jsx26(
|
|
5183
5775
|
DocControlsOverlay,
|
|
5184
5776
|
{
|
|
5185
5777
|
state: playbackState,
|
|
@@ -5189,7 +5781,7 @@ function DocPlayerContent({
|
|
|
5189
5781
|
getBlockTitle
|
|
5190
5782
|
}
|
|
5191
5783
|
),
|
|
5192
|
-
!renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */
|
|
5784
|
+
!renderMode && !isSlideshowMode && !showControls && showScrubber && /* @__PURE__ */ jsx26(
|
|
5193
5785
|
"div",
|
|
5194
5786
|
{
|
|
5195
5787
|
className: "doc-player__scrubber",
|
|
@@ -5204,7 +5796,7 @@ function DocPlayerContent({
|
|
|
5204
5796
|
alignItems: "center",
|
|
5205
5797
|
zIndex: 100
|
|
5206
5798
|
},
|
|
5207
|
-
children: /* @__PURE__ */
|
|
5799
|
+
children: /* @__PURE__ */ jsx26(
|
|
5208
5800
|
DocProgressBar,
|
|
5209
5801
|
{
|
|
5210
5802
|
state: playbackState,
|
|
@@ -5216,7 +5808,7 @@ function DocPlayerContent({
|
|
|
5216
5808
|
)
|
|
5217
5809
|
}
|
|
5218
5810
|
),
|
|
5219
|
-
!renderMode && isSlideshowMode && showControls && /* @__PURE__ */
|
|
5811
|
+
!renderMode && isSlideshowMode && showControls && /* @__PURE__ */ jsx26(
|
|
5220
5812
|
DocControlsSlideshow,
|
|
5221
5813
|
{
|
|
5222
5814
|
state: playbackState,
|
|
@@ -5226,14 +5818,14 @@ function DocPlayerContent({
|
|
|
5226
5818
|
onPickerOpenChange: setIsSlideshowPickerOpen
|
|
5227
5819
|
}
|
|
5228
5820
|
),
|
|
5229
|
-
!isSlideshowMode && tapFeedback && /* @__PURE__ */
|
|
5821
|
+
!isSlideshowMode && tapFeedback && /* @__PURE__ */ jsx26("div", { className: "doc-player__tap-feedback", children: /* @__PURE__ */ jsx26("svg", { viewBox: "0 0 24 24", fill: "white", width: "48", height: "48", children: tapFeedback === "pause" ? /* @__PURE__ */ jsx26("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) : /* @__PURE__ */ jsx26("path", { d: "M8 5v14l11-7z" }) }) }, tapFeedback)
|
|
5230
5822
|
]
|
|
5231
5823
|
}
|
|
5232
5824
|
);
|
|
5233
5825
|
}
|
|
5234
5826
|
|
|
5235
5827
|
// src/DocControlsBottom.tsx
|
|
5236
|
-
import { jsx as
|
|
5828
|
+
import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
5237
5829
|
function DocControlsBottom({
|
|
5238
5830
|
state,
|
|
5239
5831
|
actions,
|
|
@@ -5241,32 +5833,32 @@ function DocControlsBottom({
|
|
|
5241
5833
|
expandedBlocks,
|
|
5242
5834
|
getBlockTitle
|
|
5243
5835
|
}) {
|
|
5244
|
-
return /* @__PURE__ */
|
|
5245
|
-
/* @__PURE__ */
|
|
5836
|
+
return /* @__PURE__ */ jsxs18("div", { className: "doc-controls-bottom", children: [
|
|
5837
|
+
/* @__PURE__ */ jsx27(
|
|
5246
5838
|
"button",
|
|
5247
5839
|
{
|
|
5248
5840
|
className: "bottom-ctrl-btn",
|
|
5249
5841
|
onClick: actions.restart,
|
|
5250
5842
|
title: "Restart",
|
|
5251
5843
|
"aria-label": "Restart from beginning",
|
|
5252
|
-
children: /* @__PURE__ */
|
|
5844
|
+
children: /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx27("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" }) })
|
|
5253
5845
|
}
|
|
5254
5846
|
),
|
|
5255
|
-
/* @__PURE__ */
|
|
5847
|
+
/* @__PURE__ */ jsx27(
|
|
5256
5848
|
"button",
|
|
5257
5849
|
{
|
|
5258
5850
|
className: "bottom-ctrl-btn bottom-play-btn",
|
|
5259
5851
|
onClick: actions.toggle,
|
|
5260
5852
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
5261
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
5853
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx27("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx27("path", { d: "M8 5v14l11-7z" }) })
|
|
5262
5854
|
}
|
|
5263
5855
|
),
|
|
5264
|
-
/* @__PURE__ */
|
|
5856
|
+
/* @__PURE__ */ jsxs18("span", { className: "bottom-time", children: [
|
|
5265
5857
|
formatTime(state.currentTime),
|
|
5266
5858
|
" / ",
|
|
5267
5859
|
formatTime(state.totalDuration)
|
|
5268
5860
|
] }),
|
|
5269
|
-
/* @__PURE__ */
|
|
5861
|
+
/* @__PURE__ */ jsx27(
|
|
5270
5862
|
DocProgressBar,
|
|
5271
5863
|
{
|
|
5272
5864
|
state,
|
|
@@ -5276,82 +5868,92 @@ function DocControlsBottom({
|
|
|
5276
5868
|
getBlockTitle
|
|
5277
5869
|
}
|
|
5278
5870
|
),
|
|
5279
|
-
/* @__PURE__ */
|
|
5871
|
+
/* @__PURE__ */ jsxs18("span", { className: "bottom-segment", children: [
|
|
5280
5872
|
state.currentBlockIndex + 1,
|
|
5281
5873
|
"/",
|
|
5282
5874
|
state.totalBlocks
|
|
5283
5875
|
] }),
|
|
5284
|
-
state.hasCaptions && /* @__PURE__ */
|
|
5876
|
+
state.hasCaptions && /* @__PURE__ */ jsx27(
|
|
5285
5877
|
"button",
|
|
5286
5878
|
{
|
|
5287
5879
|
className: `bottom-ctrl-btn ${state.captionMode !== "off" ? "bottom-ctrl-btn--active" : ""}`,
|
|
5288
5880
|
onClick: () => actions.cycleCaptionMode(),
|
|
5289
5881
|
title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
|
|
5290
5882
|
"aria-label": "Cycle caption style",
|
|
5291
|
-
children: /* @__PURE__ */
|
|
5883
|
+
children: /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx27("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" }) })
|
|
5884
|
+
}
|
|
5885
|
+
),
|
|
5886
|
+
actions.toggleFullscreen && /* @__PURE__ */ jsx27(
|
|
5887
|
+
"button",
|
|
5888
|
+
{
|
|
5889
|
+
className: `bottom-ctrl-btn ${state.isFullscreen ? "bottom-ctrl-btn--active" : ""}`,
|
|
5890
|
+
onClick: actions.toggleFullscreen,
|
|
5891
|
+
title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
|
|
5892
|
+
"aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
5893
|
+
children: state.isFullscreen ? /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx27("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx27("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
|
|
5292
5894
|
}
|
|
5293
5895
|
)
|
|
5294
5896
|
] });
|
|
5295
5897
|
}
|
|
5296
5898
|
|
|
5297
5899
|
// src/DocControlsSidebar.tsx
|
|
5298
|
-
import { jsx as
|
|
5900
|
+
import { jsx as jsx28, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
5299
5901
|
function DocControlsSidebar({ state, actions }) {
|
|
5300
|
-
return /* @__PURE__ */
|
|
5301
|
-
/* @__PURE__ */
|
|
5902
|
+
return /* @__PURE__ */ jsxs19("div", { className: "doc-controls-sidebar", children: [
|
|
5903
|
+
/* @__PURE__ */ jsx28(
|
|
5302
5904
|
"button",
|
|
5303
5905
|
{
|
|
5304
5906
|
className: "sidebar-ctrl-btn",
|
|
5305
5907
|
onClick: actions.restart,
|
|
5306
5908
|
title: "Restart",
|
|
5307
5909
|
"aria-label": "Restart from beginning",
|
|
5308
|
-
children: /* @__PURE__ */
|
|
5910
|
+
children: /* @__PURE__ */ jsx28("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx28("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" }) })
|
|
5309
5911
|
}
|
|
5310
5912
|
),
|
|
5311
|
-
/* @__PURE__ */
|
|
5913
|
+
/* @__PURE__ */ jsx28(
|
|
5312
5914
|
"button",
|
|
5313
5915
|
{
|
|
5314
5916
|
className: "sidebar-ctrl-btn sidebar-play-btn",
|
|
5315
5917
|
onClick: actions.toggle,
|
|
5316
5918
|
"aria-label": state.isPlaying ? "Pause" : "Play",
|
|
5317
|
-
children: state.isPlaying ? /* @__PURE__ */
|
|
5919
|
+
children: state.isPlaying ? /* @__PURE__ */ jsx28("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx28("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }) }) : /* @__PURE__ */ jsx28("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "22", height: "22", children: /* @__PURE__ */ jsx28("path", { d: "M8 5v14l11-7z" }) })
|
|
5318
5920
|
}
|
|
5319
5921
|
),
|
|
5320
|
-
/* @__PURE__ */
|
|
5321
|
-
/* @__PURE__ */
|
|
5322
|
-
/* @__PURE__ */
|
|
5922
|
+
/* @__PURE__ */ jsxs19("div", { className: "sidebar-time", children: [
|
|
5923
|
+
/* @__PURE__ */ jsx28("div", { children: formatTime(state.currentTime) }),
|
|
5924
|
+
/* @__PURE__ */ jsx28("div", { className: "sidebar-time-total", children: formatTime(state.totalDuration) })
|
|
5323
5925
|
] }),
|
|
5324
|
-
/* @__PURE__ */
|
|
5926
|
+
/* @__PURE__ */ jsxs19("div", { className: "sidebar-segment", children: [
|
|
5325
5927
|
state.currentBlockIndex + 1,
|
|
5326
5928
|
"/",
|
|
5327
5929
|
state.totalBlocks
|
|
5328
5930
|
] }),
|
|
5329
|
-
state.hasCaptions && /* @__PURE__ */
|
|
5931
|
+
state.hasCaptions && /* @__PURE__ */ jsx28(
|
|
5330
5932
|
"button",
|
|
5331
5933
|
{
|
|
5332
5934
|
className: `sidebar-ctrl-btn ${state.captionMode !== "off" ? "sidebar-ctrl-btn--active" : ""}`,
|
|
5333
5935
|
onClick: () => actions.cycleCaptionMode(),
|
|
5334
5936
|
title: state.captionMode === "off" ? "Captions: Off" : state.captionMode === "standard" ? "Captions: Standard" : "Captions: Social",
|
|
5335
5937
|
"aria-label": "Cycle caption style",
|
|
5336
|
-
children: /* @__PURE__ */
|
|
5938
|
+
children: /* @__PURE__ */ jsx28("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx28("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" }) })
|
|
5337
5939
|
}
|
|
5338
5940
|
),
|
|
5339
|
-
actions.toggleFullscreen && /* @__PURE__ */
|
|
5941
|
+
actions.toggleFullscreen && /* @__PURE__ */ jsx28(
|
|
5340
5942
|
"button",
|
|
5341
5943
|
{
|
|
5342
5944
|
className: `sidebar-ctrl-btn ${state.isFullscreen ? "sidebar-ctrl-btn--active" : ""}`,
|
|
5343
5945
|
onClick: actions.toggleFullscreen,
|
|
5344
5946
|
title: state.isFullscreen ? "Exit fullscreen (F)" : "Fullscreen (F)",
|
|
5345
5947
|
"aria-label": state.isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
5346
|
-
children: state.isFullscreen ? /* @__PURE__ */
|
|
5948
|
+
children: state.isFullscreen ? /* @__PURE__ */ jsx28("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx28("path", { d: "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" }) }) : /* @__PURE__ */ jsx28("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "20", height: "20", children: /* @__PURE__ */ jsx28("path", { d: "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" }) })
|
|
5347
5949
|
}
|
|
5348
5950
|
)
|
|
5349
5951
|
] });
|
|
5350
5952
|
}
|
|
5351
5953
|
|
|
5352
5954
|
// src/DocPlayerWithSidebar.tsx
|
|
5353
|
-
import { useRef as useRef10, useState as
|
|
5354
|
-
import { jsx as
|
|
5955
|
+
import { useRef as useRef10, useState as useState11, useCallback as useCallback8, useEffect as useEffect12 } from "react";
|
|
5956
|
+
import { jsx as jsx29, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
5355
5957
|
var DEFAULT_STATE = {
|
|
5356
5958
|
isPlaying: false,
|
|
5357
5959
|
currentTime: 0,
|
|
@@ -5385,7 +5987,7 @@ function DocPlayerWithSidebar({
|
|
|
5385
5987
|
const stateRef = useRef10(DEFAULT_STATE);
|
|
5386
5988
|
const actionsRef = useRef10(null);
|
|
5387
5989
|
const wasPlayingRef = useRef10(false);
|
|
5388
|
-
const [, setTick] =
|
|
5990
|
+
const [, setTick] = useState11(0);
|
|
5389
5991
|
const handleStateChange = useCallback8(
|
|
5390
5992
|
(state) => {
|
|
5391
5993
|
stateRef.current = state;
|
|
@@ -5393,6 +5995,7 @@ function DocPlayerWithSidebar({
|
|
|
5393
5995
|
wasPlayingRef.current = state.isPlaying;
|
|
5394
5996
|
onPlayingChange(state.isPlaying);
|
|
5395
5997
|
}
|
|
5998
|
+
if (!state.isPlaying) setTick((t) => t + 1);
|
|
5396
5999
|
},
|
|
5397
6000
|
[onPlayingChange]
|
|
5398
6001
|
);
|
|
@@ -5404,14 +6007,15 @@ function DocPlayerWithSidebar({
|
|
|
5404
6007
|
},
|
|
5405
6008
|
[]
|
|
5406
6009
|
);
|
|
5407
|
-
|
|
6010
|
+
useEffect12(() => {
|
|
5408
6011
|
const interval = setInterval(() => {
|
|
6012
|
+
if (!stateRef.current.isPlaying) return;
|
|
5409
6013
|
setTick((t) => t + 1);
|
|
5410
6014
|
}, 250);
|
|
5411
6015
|
return () => clearInterval(interval);
|
|
5412
6016
|
}, []);
|
|
5413
|
-
return /* @__PURE__ */
|
|
5414
|
-
/* @__PURE__ */
|
|
6017
|
+
return /* @__PURE__ */ jsxs20("div", { className: "doc-player-sidebar-layout", children: [
|
|
6018
|
+
/* @__PURE__ */ jsx29("div", { className: "doc-player-sidebar-layout__video", children: /* @__PURE__ */ jsx29(
|
|
5415
6019
|
DocPlayer,
|
|
5416
6020
|
{
|
|
5417
6021
|
doc,
|
|
@@ -5433,17 +6037,106 @@ function DocPlayerWithSidebar({
|
|
|
5433
6037
|
forceViewport
|
|
5434
6038
|
}
|
|
5435
6039
|
) }),
|
|
5436
|
-
actionsRef.current && /* @__PURE__ */
|
|
6040
|
+
actionsRef.current && /* @__PURE__ */ jsx29(DocControlsSidebar, { state: stateRef.current, actions: actionsRef.current })
|
|
5437
6041
|
] });
|
|
5438
6042
|
}
|
|
5439
6043
|
|
|
6044
|
+
// src/hooks/useModalDialog.ts
|
|
6045
|
+
import { useEffect as useEffect13, useRef as useRef11 } from "react";
|
|
6046
|
+
var FOCUSABLE_SELECTOR = [
|
|
6047
|
+
"a[href]",
|
|
6048
|
+
"button:not([disabled])",
|
|
6049
|
+
"input:not([disabled])",
|
|
6050
|
+
"select:not([disabled])",
|
|
6051
|
+
"textarea:not([disabled])",
|
|
6052
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
6053
|
+
].join(",");
|
|
6054
|
+
function isolateModal(root) {
|
|
6055
|
+
const snapshots = [];
|
|
6056
|
+
let branch = root;
|
|
6057
|
+
while (branch?.parentElement) {
|
|
6058
|
+
for (const sibling of branch.parentElement.children) {
|
|
6059
|
+
if (sibling === branch || !(sibling instanceof HTMLElement)) continue;
|
|
6060
|
+
snapshots.push({
|
|
6061
|
+
element: sibling,
|
|
6062
|
+
inert: sibling.inert === true,
|
|
6063
|
+
ariaHidden: sibling.getAttribute("aria-hidden")
|
|
6064
|
+
});
|
|
6065
|
+
sibling.inert = true;
|
|
6066
|
+
sibling.setAttribute("aria-hidden", "true");
|
|
6067
|
+
}
|
|
6068
|
+
branch = branch.parentElement;
|
|
6069
|
+
if (branch === document.body) break;
|
|
6070
|
+
}
|
|
6071
|
+
return snapshots;
|
|
6072
|
+
}
|
|
6073
|
+
function useModalDialog({
|
|
6074
|
+
rootRef,
|
|
6075
|
+
dialogRef,
|
|
6076
|
+
initialFocusRef,
|
|
6077
|
+
returnFocusRef,
|
|
6078
|
+
onClose
|
|
6079
|
+
}) {
|
|
6080
|
+
const onCloseRef = useRef11(onClose);
|
|
6081
|
+
onCloseRef.current = onClose;
|
|
6082
|
+
useEffect13(() => {
|
|
6083
|
+
const root = rootRef.current;
|
|
6084
|
+
const dialog = dialogRef.current;
|
|
6085
|
+
if (!root || !dialog) return;
|
|
6086
|
+
const previousFocus = returnFocusRef?.current ?? (document.activeElement instanceof HTMLElement ? document.activeElement : null);
|
|
6087
|
+
const inertSnapshots = isolateModal(root);
|
|
6088
|
+
const activeWithinDialog = document.activeElement instanceof HTMLElement && dialog.contains(document.activeElement) ? document.activeElement : null;
|
|
6089
|
+
const focusTarget = initialFocusRef?.current ?? activeWithinDialog ?? dialog.querySelector(FOCUSABLE_SELECTOR) ?? dialog;
|
|
6090
|
+
focusTarget.focus();
|
|
6091
|
+
if (focusTarget instanceof HTMLInputElement || focusTarget instanceof HTMLTextAreaElement) {
|
|
6092
|
+
focusTarget.select();
|
|
6093
|
+
}
|
|
6094
|
+
const handleKeyDown = (event) => {
|
|
6095
|
+
if (event.key === "Escape") {
|
|
6096
|
+
event.preventDefault();
|
|
6097
|
+
event.stopPropagation();
|
|
6098
|
+
onCloseRef.current();
|
|
6099
|
+
return;
|
|
6100
|
+
}
|
|
6101
|
+
if (event.key !== "Tab") return;
|
|
6102
|
+
const focusable = [...dialog.querySelectorAll(FOCUSABLE_SELECTOR)].filter(
|
|
6103
|
+
(element) => !element.hidden && element.getAttribute("aria-hidden") !== "true" && element.getAttribute("aria-disabled") !== "true"
|
|
6104
|
+
);
|
|
6105
|
+
if (focusable.length === 0) {
|
|
6106
|
+
event.preventDefault();
|
|
6107
|
+
dialog.focus();
|
|
6108
|
+
return;
|
|
6109
|
+
}
|
|
6110
|
+
const first = focusable[0];
|
|
6111
|
+
const last = focusable[focusable.length - 1];
|
|
6112
|
+
if (event.shiftKey && (document.activeElement === first || !dialog.contains(document.activeElement))) {
|
|
6113
|
+
event.preventDefault();
|
|
6114
|
+
last.focus();
|
|
6115
|
+
} else if (!event.shiftKey && (document.activeElement === last || !dialog.contains(document.activeElement))) {
|
|
6116
|
+
event.preventDefault();
|
|
6117
|
+
first.focus();
|
|
6118
|
+
}
|
|
6119
|
+
};
|
|
6120
|
+
document.addEventListener("keydown", handleKeyDown, true);
|
|
6121
|
+
return () => {
|
|
6122
|
+
document.removeEventListener("keydown", handleKeyDown, true);
|
|
6123
|
+
for (const snapshot of inertSnapshots) {
|
|
6124
|
+
snapshot.element.inert = snapshot.inert;
|
|
6125
|
+
if (snapshot.ariaHidden === null) snapshot.element.removeAttribute("aria-hidden");
|
|
6126
|
+
else snapshot.element.setAttribute("aria-hidden", snapshot.ariaHidden);
|
|
6127
|
+
}
|
|
6128
|
+
if (previousFocus?.isConnected) previousFocus.focus();
|
|
6129
|
+
};
|
|
6130
|
+
}, [dialogRef, initialFocusRef, returnFocusRef, rootRef]);
|
|
6131
|
+
}
|
|
6132
|
+
|
|
5440
6133
|
// src/jsonView/useJsonViewTokens.ts
|
|
5441
|
-
import { useMemo as
|
|
6134
|
+
import { useMemo as useMemo11 } from "react";
|
|
5442
6135
|
import { buildJsonFormTokens, resolveJsonFormTheme } from "@bendyline/squisq/jsonForm";
|
|
5443
6136
|
function useJsonViewTokens(theme, surface) {
|
|
5444
6137
|
const auto = useAutoSurface(surface === "auto");
|
|
5445
6138
|
const effectiveSurface = surface === "auto" ? auto : surface ?? void 0;
|
|
5446
|
-
return
|
|
6139
|
+
return useMemo11(() => {
|
|
5447
6140
|
const style = buildJsonFormTokens(theme, effectiveSurface, {
|
|
5448
6141
|
prefix: "--squisq-json"
|
|
5449
6142
|
});
|
|
@@ -5459,26 +6152,26 @@ import {
|
|
|
5459
6152
|
} from "@bendyline/squisq/jsonForm";
|
|
5460
6153
|
|
|
5461
6154
|
// src/jsonView/viewers.tsx
|
|
5462
|
-
import { Fragment as
|
|
6155
|
+
import { Fragment as Fragment5, useMemo as useMemo12 } from "react";
|
|
5463
6156
|
import {
|
|
5464
6157
|
arrayItemKind
|
|
5465
6158
|
} from "@bendyline/squisq/jsonForm";
|
|
5466
6159
|
import { parseMarkdown as parseMarkdown3 } from "@bendyline/squisq/markdown";
|
|
5467
|
-
import { Fragment as
|
|
6160
|
+
import { Fragment as Fragment6, jsx as jsx30, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
5468
6161
|
function TextViewer({ value }) {
|
|
5469
6162
|
if (value === void 0 || value === null || value === "") {
|
|
5470
|
-
return /* @__PURE__ */
|
|
6163
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
5471
6164
|
}
|
|
5472
|
-
return /* @__PURE__ */
|
|
6165
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-value", children: String(value) });
|
|
5473
6166
|
}
|
|
5474
6167
|
function MultilineViewer({ value }) {
|
|
5475
6168
|
if (value === void 0 || value === null || value === "") {
|
|
5476
|
-
return /* @__PURE__ */
|
|
6169
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
5477
6170
|
}
|
|
5478
|
-
return /* @__PURE__ */
|
|
6171
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-value squisq-jv-value--multiline", children: String(value) });
|
|
5479
6172
|
}
|
|
5480
6173
|
function RichTextViewer({ value }) {
|
|
5481
|
-
const nodes =
|
|
6174
|
+
const nodes = useMemo12(() => {
|
|
5482
6175
|
if (typeof value !== "string" || value === "") return null;
|
|
5483
6176
|
try {
|
|
5484
6177
|
const doc = parseMarkdown3(value);
|
|
@@ -5487,39 +6180,39 @@ function RichTextViewer({ value }) {
|
|
|
5487
6180
|
return null;
|
|
5488
6181
|
}
|
|
5489
6182
|
}, [value]);
|
|
5490
|
-
if (!nodes) return /* @__PURE__ */
|
|
5491
|
-
return /* @__PURE__ */
|
|
6183
|
+
if (!nodes) return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
6184
|
+
return /* @__PURE__ */ jsx30("div", { className: "squisq-jv-richtext", children: /* @__PURE__ */ jsx30(MarkdownRenderer, { nodes }) });
|
|
5492
6185
|
}
|
|
5493
6186
|
function NumberViewer({ value }) {
|
|
5494
6187
|
if (value === void 0 || value === null) {
|
|
5495
|
-
return /* @__PURE__ */
|
|
6188
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
5496
6189
|
}
|
|
5497
|
-
return /* @__PURE__ */
|
|
6190
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-value", children: String(value) });
|
|
5498
6191
|
}
|
|
5499
6192
|
function BooleanViewer({ value }) {
|
|
5500
6193
|
const on = Boolean(value);
|
|
5501
|
-
return /* @__PURE__ */
|
|
6194
|
+
return /* @__PURE__ */ jsx30("span", { className: `squisq-jv-toggle squisq-jv-toggle--${on ? "on" : "off"}`, children: on ? "On" : "Off" });
|
|
5502
6195
|
}
|
|
5503
6196
|
function EnumViewer({ value, schema }) {
|
|
5504
6197
|
if (value === void 0 || value === null || value === "") {
|
|
5505
|
-
return /* @__PURE__ */
|
|
6198
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
5506
6199
|
}
|
|
5507
6200
|
const labels = schema.squisq?.enumLabels;
|
|
5508
6201
|
const display = labels && typeof value === "string" ? labels[value] ?? value : String(value);
|
|
5509
|
-
return /* @__PURE__ */
|
|
6202
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-value", children: display });
|
|
5510
6203
|
}
|
|
5511
6204
|
function ColorViewer({ value }) {
|
|
5512
6205
|
if (typeof value !== "string" || value === "") {
|
|
5513
|
-
return /* @__PURE__ */
|
|
6206
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
5514
6207
|
}
|
|
5515
|
-
return /* @__PURE__ */
|
|
5516
|
-
/* @__PURE__ */
|
|
5517
|
-
/* @__PURE__ */
|
|
6208
|
+
return /* @__PURE__ */ jsxs21("span", { className: "squisq-jv-color", children: [
|
|
6209
|
+
/* @__PURE__ */ jsx30("span", { className: "squisq-jv-color__swatch", style: { background: value }, "aria-hidden": true }),
|
|
6210
|
+
/* @__PURE__ */ jsx30("span", { className: "squisq-jv-color__hex", children: value })
|
|
5518
6211
|
] });
|
|
5519
6212
|
}
|
|
5520
6213
|
function DateViewer({ value, schema }) {
|
|
5521
6214
|
if (typeof value !== "string" || value === "") {
|
|
5522
|
-
return /* @__PURE__ */
|
|
6215
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
5523
6216
|
}
|
|
5524
6217
|
const fmt = schema.format;
|
|
5525
6218
|
let display = value;
|
|
@@ -5536,31 +6229,31 @@ function DateViewer({ value, schema }) {
|
|
|
5536
6229
|
}
|
|
5537
6230
|
} catch {
|
|
5538
6231
|
}
|
|
5539
|
-
return /* @__PURE__ */
|
|
6232
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-value", children: display });
|
|
5540
6233
|
}
|
|
5541
6234
|
function ChipBinViewer({ value, schema }) {
|
|
5542
6235
|
if (!Array.isArray(value) || value.length === 0) {
|
|
5543
|
-
return /* @__PURE__ */
|
|
6236
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-empty", children: "\u2014" });
|
|
5544
6237
|
}
|
|
5545
6238
|
const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
|
|
5546
6239
|
const labels = itemSchema?.squisq?.enumLabels;
|
|
5547
|
-
return /* @__PURE__ */
|
|
6240
|
+
return /* @__PURE__ */ jsx30("div", { className: "squisq-jv-chip-bin", children: value.map((item, i) => {
|
|
5548
6241
|
const label = labels && typeof item === "string" ? labels[item] ?? String(item) : String(item);
|
|
5549
|
-
return /* @__PURE__ */
|
|
6242
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-chip", children: label }, i);
|
|
5550
6243
|
}) });
|
|
5551
6244
|
}
|
|
5552
6245
|
function CardStackViewer(props) {
|
|
5553
6246
|
const { value, schema, rootSchema, rootData, pointer, density } = props;
|
|
5554
6247
|
if (!Array.isArray(value) || value.length === 0) {
|
|
5555
|
-
return /* @__PURE__ */
|
|
6248
|
+
return /* @__PURE__ */ jsx30("span", { className: "squisq-jv-empty", children: "No items" });
|
|
5556
6249
|
}
|
|
5557
6250
|
const itemSchema = (Array.isArray(schema.items) ? schema.items[0] : schema.items) ?? {};
|
|
5558
6251
|
const itemLabel = itemSchema.squisq?.itemLabel;
|
|
5559
|
-
return /* @__PURE__ */
|
|
6252
|
+
return /* @__PURE__ */ jsx30("div", { className: "squisq-jv-card-stack", children: value.map((item, i) => {
|
|
5560
6253
|
const title = resolveItemTitle(itemLabel, item, i);
|
|
5561
|
-
return /* @__PURE__ */
|
|
5562
|
-
title ? /* @__PURE__ */
|
|
5563
|
-
/* @__PURE__ */
|
|
6254
|
+
return /* @__PURE__ */ jsxs21("div", { className: "squisq-jv-card", children: [
|
|
6255
|
+
title ? /* @__PURE__ */ jsx30("h4", { className: "squisq-jv-card__title", children: title }) : null,
|
|
6256
|
+
/* @__PURE__ */ jsx30(
|
|
5564
6257
|
RenderNode,
|
|
5565
6258
|
{
|
|
5566
6259
|
value: item,
|
|
@@ -5591,16 +6284,16 @@ function GroupViewer(props) {
|
|
|
5591
6284
|
const help = schema.squisq?.help ?? schema.description;
|
|
5592
6285
|
const obj = (value && typeof value === "object" ? value : {}) ?? {};
|
|
5593
6286
|
const propEntries = Object.entries(schema.properties ?? {});
|
|
5594
|
-
return /* @__PURE__ */
|
|
5595
|
-
title ? /* @__PURE__ */
|
|
5596
|
-
help ? /* @__PURE__ */
|
|
5597
|
-
propEntries.map(([key, propSchema]) => /* @__PURE__ */
|
|
6287
|
+
return /* @__PURE__ */ jsxs21("section", { className: "squisq-jv-group", children: [
|
|
6288
|
+
title ? /* @__PURE__ */ jsx30("h3", { className: "squisq-jv-group__title", children: title }) : null,
|
|
6289
|
+
help ? /* @__PURE__ */ jsx30("p", { className: "squisq-jv-group__help", children: help }) : null,
|
|
6290
|
+
propEntries.map(([key, propSchema]) => /* @__PURE__ */ jsx30(Fragment5, { children: /* @__PURE__ */ jsx30(
|
|
5598
6291
|
RowOrSection,
|
|
5599
6292
|
{
|
|
5600
6293
|
label: propSchema.squisq?.label ?? propSchema.title ?? key,
|
|
5601
6294
|
help: propSchema.squisq?.help ?? propSchema.description,
|
|
5602
6295
|
kindHint: propSchema,
|
|
5603
|
-
children: /* @__PURE__ */
|
|
6296
|
+
children: /* @__PURE__ */ jsx30(
|
|
5604
6297
|
RenderNode,
|
|
5605
6298
|
{
|
|
5606
6299
|
value: obj[key],
|
|
@@ -5624,11 +6317,11 @@ function RowOrSection({
|
|
|
5624
6317
|
}) {
|
|
5625
6318
|
const composite = isCompositeKind(kindHint);
|
|
5626
6319
|
if (composite) {
|
|
5627
|
-
return /* @__PURE__ */
|
|
6320
|
+
return /* @__PURE__ */ jsx30(Fragment6, { children });
|
|
5628
6321
|
}
|
|
5629
|
-
return /* @__PURE__ */
|
|
5630
|
-
/* @__PURE__ */
|
|
5631
|
-
/* @__PURE__ */
|
|
6322
|
+
return /* @__PURE__ */ jsxs21("div", { className: "squisq-jv-row", children: [
|
|
6323
|
+
/* @__PURE__ */ jsx30("div", { className: "squisq-jv-label", title: help, children: label }),
|
|
6324
|
+
/* @__PURE__ */ jsx30("div", { children })
|
|
5632
6325
|
] });
|
|
5633
6326
|
}
|
|
5634
6327
|
function isCompositeKind(schema) {
|
|
@@ -5649,11 +6342,11 @@ function TabsViewer(props) {
|
|
|
5649
6342
|
const matchedIndex = pickMatchingBranch(branches, value);
|
|
5650
6343
|
const branch = branches[matchedIndex];
|
|
5651
6344
|
if (!branch) {
|
|
5652
|
-
return /* @__PURE__ */
|
|
6345
|
+
return /* @__PURE__ */ jsx30(TextViewer, { ...props });
|
|
5653
6346
|
}
|
|
5654
|
-
return /* @__PURE__ */
|
|
5655
|
-
/* @__PURE__ */
|
|
5656
|
-
/* @__PURE__ */
|
|
6347
|
+
return /* @__PURE__ */ jsxs21("div", { className: "squisq-jv-tabs", children: [
|
|
6348
|
+
/* @__PURE__ */ jsx30("span", { className: "squisq-jv-tabs__discriminator", children: branch.squisq?.label ?? branch.title ?? `Option ${matchedIndex + 1}` }),
|
|
6349
|
+
/* @__PURE__ */ jsx30(
|
|
5657
6350
|
RenderNode,
|
|
5658
6351
|
{
|
|
5659
6352
|
value,
|
|
@@ -5717,7 +6410,7 @@ var VIEWERS = {
|
|
|
5717
6410
|
};
|
|
5718
6411
|
|
|
5719
6412
|
// src/jsonView/RenderNode.tsx
|
|
5720
|
-
import { jsx as
|
|
6413
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
5721
6414
|
function RenderNode(props) {
|
|
5722
6415
|
const resolved = resolveRef(props.schema, props.rootSchema) ?? props.schema;
|
|
5723
6416
|
if (resolveFlag(resolved.squisq?.hidden, props.rootData)) return null;
|
|
@@ -5733,18 +6426,18 @@ function RenderNode(props) {
|
|
|
5733
6426
|
};
|
|
5734
6427
|
if (kind === "group" || kind === "card") {
|
|
5735
6428
|
const Group = Viewer;
|
|
5736
|
-
return /* @__PURE__ */
|
|
6429
|
+
return /* @__PURE__ */ jsx31(Group, { ...viewerProps, suppressTitle: props.suppressTopGroupTitle });
|
|
5737
6430
|
}
|
|
5738
|
-
return /* @__PURE__ */
|
|
6431
|
+
return /* @__PURE__ */ jsx31(Viewer, { ...viewerProps });
|
|
5739
6432
|
}
|
|
5740
6433
|
|
|
5741
6434
|
// src/jsonView/JsonView.tsx
|
|
5742
|
-
import { jsx as
|
|
6435
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
5743
6436
|
function JsonView(props) {
|
|
5744
6437
|
const { schema, value, theme, surface, density = "comfortable", className } = props;
|
|
5745
6438
|
const { style } = useJsonViewTokens(theme, surface);
|
|
5746
6439
|
const cls = "squisq-json-view" + (density === "compact" ? " squisq-json-view--compact" : "") + (className ? ` ${className}` : "");
|
|
5747
|
-
return /* @__PURE__ */
|
|
6440
|
+
return /* @__PURE__ */ jsx32("div", { className: cls, style, children: /* @__PURE__ */ jsx32(
|
|
5748
6441
|
RenderNode,
|
|
5749
6442
|
{
|
|
5750
6443
|
value,
|
|
@@ -5758,6 +6451,7 @@ function JsonView(props) {
|
|
|
5758
6451
|
}
|
|
5759
6452
|
export {
|
|
5760
6453
|
BlockRenderer,
|
|
6454
|
+
CanvasSection,
|
|
5761
6455
|
CaptionOverlay,
|
|
5762
6456
|
DocControlsBottom,
|
|
5763
6457
|
DocControlsOverlay,
|
|
@@ -5775,7 +6469,12 @@ export {
|
|
|
5775
6469
|
MarkdownRenderer,
|
|
5776
6470
|
MediaClipLayer,
|
|
5777
6471
|
MediaContext,
|
|
6472
|
+
MermaidDiagram,
|
|
6473
|
+
MermaidLayer,
|
|
6474
|
+
PageSectionView,
|
|
6475
|
+
PageViewContext,
|
|
5778
6476
|
PathLayer,
|
|
6477
|
+
ResourcePolicyContext,
|
|
5779
6478
|
ShapeLayer,
|
|
5780
6479
|
SocialCaptionOverlay,
|
|
5781
6480
|
TableLayer,
|
|
@@ -5791,6 +6490,8 @@ export {
|
|
|
5791
6490
|
useMediaProvider,
|
|
5792
6491
|
useMediaSchedule,
|
|
5793
6492
|
useMediaUrl,
|
|
6493
|
+
useModalDialog,
|
|
6494
|
+
usePageView,
|
|
6495
|
+
useResourcePolicy,
|
|
5794
6496
|
useViewportOrientation
|
|
5795
6497
|
};
|
|
5796
|
-
//# sourceMappingURL=index.js.map
|