@bendyline/squisq-react 1.4.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -23
- package/dist/index.d.ts +81 -21
- package/dist/index.js +461 -178
- package/dist/index.js.map +1 -1
- package/dist/squisq-player.css +1 -1
- package/dist/squisq-player.css.map +1 -1
- package/dist/squisq-player.global.js +49 -17
- package/dist/squisq-player.global.js.map +1 -1
- package/dist/standalone-source.js +1 -1
- package/dist/styles/index.css +2263 -0
- package/package.json +8 -5
- package/src/DocControlsSlideshow.tsx +13 -2
- package/src/DocPlayer.tsx +255 -58
- package/src/DocPlayerWithSidebar.tsx +21 -9
- package/src/LinearDocView.tsx +59 -10
- package/src/MarkdownRenderer.tsx +52 -35
- package/src/__tests__/DocPlayer.test.tsx +85 -6
- package/src/__tests__/DocPlayerStylesSentinel.test.tsx +41 -0
- package/src/__tests__/LinearDocView.test.tsx +53 -1
- package/src/__tests__/MarkdownRenderer.test.tsx +18 -0
- package/src/__tests__/useJsonViewTokens.test.ts +41 -0
- package/src/__tests__/useSlideSwipe.test.ts +81 -0
- package/src/hooks/{AudioProvider.ts → AudioController.ts} +3 -3
- package/src/hooks/index.ts +7 -2
- package/src/hooks/useAudioSync.ts +5 -4
- package/src/hooks/useSlideSwipe.ts +265 -0
- package/src/index.ts +1 -1
- package/src/jsonView/useJsonViewTokens.ts +6 -31
- package/src/standalone-entry.tsx +1 -1
- package/src/styles/doc-animations.css +46 -0
- package/src/types.ts +6 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/DocPlayer.tsx
|
|
2
|
-
import { Fragment as Fragment3, useRef as
|
|
2
|
+
import { Fragment as Fragment3, useRef as useRef7, useState as useState7, useEffect as useEffect8, useCallback as useCallback6, useMemo as useMemo9 } from "react";
|
|
3
3
|
import {
|
|
4
4
|
isTemplateBlock as isTemplateBlock2,
|
|
5
5
|
getCaptionAtTime as getCaptionAtTime2,
|
|
@@ -2245,16 +2245,164 @@ function useViewportOrientation() {
|
|
|
2245
2245
|
};
|
|
2246
2246
|
}
|
|
2247
2247
|
|
|
2248
|
+
// src/hooks/useSlideSwipe.ts
|
|
2249
|
+
import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5, useState as useState5 } from "react";
|
|
2250
|
+
var DISTANCE_RATIO = 0.3;
|
|
2251
|
+
var FLICK_VELOCITY = 0.5;
|
|
2252
|
+
var MIN_FLICK_DISTANCE = 12;
|
|
2253
|
+
var RUBBER_BAND = 0.35;
|
|
2254
|
+
var DEFAULT_SETTLE_MS = 260;
|
|
2255
|
+
function decideSwipe({
|
|
2256
|
+
dx,
|
|
2257
|
+
width,
|
|
2258
|
+
elapsedMs,
|
|
2259
|
+
canNext,
|
|
2260
|
+
canPrev
|
|
2261
|
+
}) {
|
|
2262
|
+
const distanceThreshold = width > 0 ? width * DISTANCE_RATIO : Infinity;
|
|
2263
|
+
const velocity = elapsedMs > 0 ? Math.abs(dx) / elapsedMs : 0;
|
|
2264
|
+
const passesDistance = Math.abs(dx) >= distanceThreshold;
|
|
2265
|
+
const passesFlick = velocity >= FLICK_VELOCITY && Math.abs(dx) >= MIN_FLICK_DISTANCE;
|
|
2266
|
+
if (!passesDistance && !passesFlick) return "snap";
|
|
2267
|
+
if (dx < 0) return canNext ? "next" : "snap";
|
|
2268
|
+
if (dx > 0) return canPrev ? "prev" : "snap";
|
|
2269
|
+
return "snap";
|
|
2270
|
+
}
|
|
2271
|
+
function useSlideSwipe(opts) {
|
|
2272
|
+
const settleMs = opts.settleMs ?? DEFAULT_SETTLE_MS;
|
|
2273
|
+
const [offsetPx, setOffsetPx] = useState5(0);
|
|
2274
|
+
const [phase, setPhase] = useState5("idle");
|
|
2275
|
+
const optsRef = useRef5(opts);
|
|
2276
|
+
optsRef.current = opts;
|
|
2277
|
+
const dragRef = useRef5(null);
|
|
2278
|
+
const phaseRef = useRef5("idle");
|
|
2279
|
+
phaseRef.current = phase;
|
|
2280
|
+
const settleTimer = useRef5(null);
|
|
2281
|
+
const settleRaf = useRef5(null);
|
|
2282
|
+
const clearPending = useCallback4(() => {
|
|
2283
|
+
if (settleTimer.current != null) {
|
|
2284
|
+
clearTimeout(settleTimer.current);
|
|
2285
|
+
settleTimer.current = null;
|
|
2286
|
+
}
|
|
2287
|
+
if (settleRaf.current != null) {
|
|
2288
|
+
cancelAnimationFrame(settleRaf.current);
|
|
2289
|
+
settleRaf.current = null;
|
|
2290
|
+
}
|
|
2291
|
+
}, []);
|
|
2292
|
+
const onPointerDown = useCallback4((e) => {
|
|
2293
|
+
const o = optsRef.current;
|
|
2294
|
+
if (!o.enabled) return;
|
|
2295
|
+
if (phaseRef.current === "settling") return;
|
|
2296
|
+
if (e.pointerType === "mouse" && e.button !== 0) return;
|
|
2297
|
+
const target = e.target;
|
|
2298
|
+
if (target.closest?.("button, a, input, textarea, select, [data-no-swipe]")) return;
|
|
2299
|
+
dragRef.current = {
|
|
2300
|
+
pointerId: e.pointerId,
|
|
2301
|
+
startX: e.clientX,
|
|
2302
|
+
startTime: performance.now(),
|
|
2303
|
+
target
|
|
2304
|
+
};
|
|
2305
|
+
try {
|
|
2306
|
+
target.setPointerCapture?.(e.pointerId);
|
|
2307
|
+
} catch {
|
|
2308
|
+
}
|
|
2309
|
+
setPhase("dragging");
|
|
2310
|
+
setOffsetPx(0);
|
|
2311
|
+
}, []);
|
|
2312
|
+
useEffect7(() => {
|
|
2313
|
+
function currentWidth() {
|
|
2314
|
+
return optsRef.current.containerRef.current?.getBoundingClientRect().width ?? 0;
|
|
2315
|
+
}
|
|
2316
|
+
function endDrag(drag) {
|
|
2317
|
+
dragRef.current = null;
|
|
2318
|
+
try {
|
|
2319
|
+
drag.target.releasePointerCapture?.(drag.pointerId);
|
|
2320
|
+
} catch {
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
function settleTo(target, onArrive) {
|
|
2324
|
+
setPhase("settling");
|
|
2325
|
+
settleRaf.current = requestAnimationFrame(() => {
|
|
2326
|
+
settleRaf.current = null;
|
|
2327
|
+
setOffsetPx(target);
|
|
2328
|
+
settleTimer.current = setTimeout(() => {
|
|
2329
|
+
settleTimer.current = null;
|
|
2330
|
+
onArrive?.();
|
|
2331
|
+
setOffsetPx(0);
|
|
2332
|
+
setPhase("idle");
|
|
2333
|
+
}, settleMs);
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
function onMove(e) {
|
|
2337
|
+
const drag = dragRef.current;
|
|
2338
|
+
if (!drag || e.pointerId !== drag.pointerId) return;
|
|
2339
|
+
const o = optsRef.current;
|
|
2340
|
+
const raw = e.clientX - drag.startX;
|
|
2341
|
+
const blocked = raw > 0 && !o.canGoPrev || raw < 0 && !o.canGoNext;
|
|
2342
|
+
setOffsetPx(blocked ? raw * RUBBER_BAND : raw);
|
|
2343
|
+
}
|
|
2344
|
+
function onUp(e) {
|
|
2345
|
+
const drag = dragRef.current;
|
|
2346
|
+
if (!drag || e.pointerId !== drag.pointerId) return;
|
|
2347
|
+
endDrag(drag);
|
|
2348
|
+
const o = optsRef.current;
|
|
2349
|
+
const rawDx = e.clientX - drag.startX;
|
|
2350
|
+
const width = currentWidth();
|
|
2351
|
+
const elapsedMs = performance.now() - drag.startTime;
|
|
2352
|
+
const decision = decideSwipe({
|
|
2353
|
+
dx: rawDx,
|
|
2354
|
+
width,
|
|
2355
|
+
elapsedMs,
|
|
2356
|
+
canNext: o.canGoNext,
|
|
2357
|
+
canPrev: o.canGoPrev
|
|
2358
|
+
});
|
|
2359
|
+
if (decision === "snap") {
|
|
2360
|
+
settleTo(0);
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
const distance = Math.max(width, Math.abs(rawDx));
|
|
2364
|
+
const target = decision === "next" ? -distance : distance;
|
|
2365
|
+
settleTo(target, decision === "next" ? o.onNext : o.onPrev);
|
|
2366
|
+
}
|
|
2367
|
+
function onCancel(e) {
|
|
2368
|
+
const drag = dragRef.current;
|
|
2369
|
+
if (!drag || e.pointerId !== drag.pointerId) return;
|
|
2370
|
+
endDrag(drag);
|
|
2371
|
+
settleTo(0);
|
|
2372
|
+
}
|
|
2373
|
+
window.addEventListener("pointermove", onMove);
|
|
2374
|
+
window.addEventListener("pointerup", onUp);
|
|
2375
|
+
window.addEventListener("pointercancel", onCancel);
|
|
2376
|
+
return () => {
|
|
2377
|
+
window.removeEventListener("pointermove", onMove);
|
|
2378
|
+
window.removeEventListener("pointerup", onUp);
|
|
2379
|
+
window.removeEventListener("pointercancel", onCancel);
|
|
2380
|
+
};
|
|
2381
|
+
}, [settleMs]);
|
|
2382
|
+
useEffect7(() => {
|
|
2383
|
+
if (!opts.enabled) {
|
|
2384
|
+
dragRef.current = null;
|
|
2385
|
+
clearPending();
|
|
2386
|
+
setPhase("idle");
|
|
2387
|
+
setOffsetPx(0);
|
|
2388
|
+
}
|
|
2389
|
+
}, [opts.enabled, clearPending]);
|
|
2390
|
+
useEffect7(() => clearPending, [clearPending]);
|
|
2391
|
+
return { offsetPx, phase, onPointerDown };
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2248
2394
|
// src/DocPlayer.tsx
|
|
2249
2395
|
import {
|
|
2250
2396
|
expandCoverBlock,
|
|
2251
2397
|
createTemplateContext,
|
|
2398
|
+
markdownToDoc as markdownToDoc2,
|
|
2252
2399
|
DEFAULT_THEME as DEFAULT_THEME3,
|
|
2253
2400
|
VIEWPORT_PRESETS as VIEWPORT_PRESETS4
|
|
2254
2401
|
} from "@bendyline/squisq/doc";
|
|
2402
|
+
import { parseMarkdown as parseMarkdown2 } from "@bendyline/squisq/markdown";
|
|
2255
2403
|
|
|
2256
2404
|
// src/DocProgressBar.tsx
|
|
2257
|
-
import { useRef as
|
|
2405
|
+
import { useRef as useRef6, useState as useState6, useCallback as useCallback5 } from "react";
|
|
2258
2406
|
|
|
2259
2407
|
// src/types.ts
|
|
2260
2408
|
function formatTime(seconds) {
|
|
@@ -2272,10 +2420,10 @@ function DocProgressBar({
|
|
|
2272
2420
|
expandedBlocks,
|
|
2273
2421
|
getBlockTitle
|
|
2274
2422
|
}) {
|
|
2275
|
-
const progressBarRef =
|
|
2276
|
-
const [hoverPosition, setHoverPosition] =
|
|
2423
|
+
const progressBarRef = useRef6(null);
|
|
2424
|
+
const [hoverPosition, setHoverPosition] = useState6(null);
|
|
2277
2425
|
const playProgress = state.totalDuration > 0 ? Math.max(0, Math.min(1, state.currentTime / state.totalDuration)) : 0;
|
|
2278
|
-
const handleProgressHover =
|
|
2426
|
+
const handleProgressHover = useCallback5((e) => {
|
|
2279
2427
|
const bar = progressBarRef.current;
|
|
2280
2428
|
if (!bar) return;
|
|
2281
2429
|
const rect = bar.getBoundingClientRect();
|
|
@@ -2283,10 +2431,10 @@ function DocProgressBar({
|
|
|
2283
2431
|
const progress = Math.max(0, Math.min(1, x / rect.width));
|
|
2284
2432
|
setHoverPosition(progress);
|
|
2285
2433
|
}, []);
|
|
2286
|
-
const handleProgressLeave =
|
|
2434
|
+
const handleProgressLeave = useCallback5(() => {
|
|
2287
2435
|
setHoverPosition(null);
|
|
2288
2436
|
}, []);
|
|
2289
|
-
const getBlockAtTimeLocal =
|
|
2437
|
+
const getBlockAtTimeLocal = useCallback5(
|
|
2290
2438
|
(time) => {
|
|
2291
2439
|
for (let i = expandedBlocks.length - 1; i >= 0; i--) {
|
|
2292
2440
|
const blk = expandedBlocks[i];
|
|
@@ -2564,9 +2712,16 @@ function DocControlsOverlay({
|
|
|
2564
2712
|
// src/DocControlsSlideshow.tsx
|
|
2565
2713
|
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2566
2714
|
function DocControlsSlideshow({ state, slideNav }) {
|
|
2567
|
-
const {
|
|
2715
|
+
const {
|
|
2716
|
+
currentBlockIndex,
|
|
2717
|
+
currentSlideLabel,
|
|
2718
|
+
currentSlideNumber,
|
|
2719
|
+
totalBlocks,
|
|
2720
|
+
totalSlideNumber
|
|
2721
|
+
} = state;
|
|
2568
2722
|
const isFirst = currentBlockIndex <= 0;
|
|
2569
2723
|
const isLast = currentBlockIndex >= totalBlocks - 1;
|
|
2724
|
+
const counterText = totalBlocks > 0 ? currentSlideLabel ?? `${currentSlideNumber ?? currentBlockIndex + 1} / ${totalSlideNumber ?? totalBlocks}` : "\u2014";
|
|
2570
2725
|
return /* @__PURE__ */ jsxs10(
|
|
2571
2726
|
"div",
|
|
2572
2727
|
{
|
|
@@ -2634,7 +2789,7 @@ function DocControlsSlideshow({ state, slideNav }) {
|
|
|
2634
2789
|
padding: "0 4px",
|
|
2635
2790
|
letterSpacing: "0.02em"
|
|
2636
2791
|
},
|
|
2637
|
-
children:
|
|
2792
|
+
children: counterText
|
|
2638
2793
|
}
|
|
2639
2794
|
),
|
|
2640
2795
|
/* @__PURE__ */ jsx15(
|
|
@@ -2681,8 +2836,14 @@ import {
|
|
|
2681
2836
|
resolveFontFamily as resolveFontFamily2
|
|
2682
2837
|
} from "@bendyline/squisq/schemas";
|
|
2683
2838
|
import { VIEWPORT_PRESETS as VIEWPORT_PRESETS3 } from "@bendyline/squisq/schemas";
|
|
2684
|
-
import {
|
|
2685
|
-
|
|
2839
|
+
import {
|
|
2840
|
+
getLayers,
|
|
2841
|
+
hasTemplate,
|
|
2842
|
+
markdownToDoc,
|
|
2843
|
+
DEFAULT_THEME as DEFAULT_THEME2,
|
|
2844
|
+
deriveTemplateInputs
|
|
2845
|
+
} from "@bendyline/squisq/doc";
|
|
2846
|
+
import { extractPlainText, parseMarkdown } from "@bendyline/squisq/markdown";
|
|
2686
2847
|
|
|
2687
2848
|
// src/MarkdownRenderer.tsx
|
|
2688
2849
|
import { Fragment as Fragment2 } from "react";
|
|
@@ -2737,7 +2898,8 @@ function InlineAudioPlayer({
|
|
|
2737
2898
|
|
|
2738
2899
|
// src/MarkdownRenderer.tsx
|
|
2739
2900
|
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2740
|
-
|
|
2901
|
+
var DEFAULT_CTX = { htmlPolicy: "sanitize" };
|
|
2902
|
+
function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
2741
2903
|
return nodes.map((node, i) => {
|
|
2742
2904
|
const key = `${keyPrefix}i${i}`;
|
|
2743
2905
|
switch (node.type) {
|
|
@@ -2752,17 +2914,17 @@ function renderInline(nodes, keyPrefix = "", htmlPolicy = "sanitize") {
|
|
|
2752
2914
|
] }, j)) }, key);
|
|
2753
2915
|
}
|
|
2754
2916
|
case "emphasis":
|
|
2755
|
-
return /* @__PURE__ */ jsx18("em", { className: "squisq-md-em", children: renderInline(node.children, key,
|
|
2917
|
+
return /* @__PURE__ */ jsx18("em", { className: "squisq-md-em", children: renderInline(node.children, key, ctx) }, key);
|
|
2756
2918
|
case "strong":
|
|
2757
|
-
return /* @__PURE__ */ jsx18("strong", { className: "squisq-md-strong", children: renderInline(node.children, key,
|
|
2919
|
+
return /* @__PURE__ */ jsx18("strong", { className: "squisq-md-strong", children: renderInline(node.children, key, ctx) }, key);
|
|
2758
2920
|
case "delete":
|
|
2759
|
-
return /* @__PURE__ */ jsx18("del", { className: "squisq-md-del", children: renderInline(node.children, key,
|
|
2921
|
+
return /* @__PURE__ */ jsx18("del", { className: "squisq-md-del", children: renderInline(node.children, key, ctx) }, key);
|
|
2760
2922
|
case "inlineCode":
|
|
2761
2923
|
return /* @__PURE__ */ jsx18("code", { className: "squisq-md-inline-code", children: node.value }, key);
|
|
2762
2924
|
case "link": {
|
|
2763
|
-
const href = sanitizeUrl(node.url, "link");
|
|
2925
|
+
const href = sanitizeUrl(node.url, "link", { extraLinkSchemes: ctx.linkSchemes });
|
|
2764
2926
|
if (!href) {
|
|
2765
|
-
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-link squisq-md-link--blocked", children: renderInline(node.children, key,
|
|
2927
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-link squisq-md-link--blocked", children: renderInline(node.children, key, ctx) }, key);
|
|
2766
2928
|
}
|
|
2767
2929
|
return /* @__PURE__ */ jsx18(
|
|
2768
2930
|
"a",
|
|
@@ -2772,7 +2934,7 @@ function renderInline(nodes, keyPrefix = "", htmlPolicy = "sanitize") {
|
|
|
2772
2934
|
title: node.title ?? void 0,
|
|
2773
2935
|
target: "_blank",
|
|
2774
2936
|
rel: "noopener noreferrer",
|
|
2775
|
-
children: renderInline(node.children, key,
|
|
2937
|
+
children: renderInline(node.children, key, ctx)
|
|
2776
2938
|
},
|
|
2777
2939
|
key
|
|
2778
2940
|
);
|
|
@@ -2784,8 +2946,8 @@ function renderInline(nodes, keyPrefix = "", htmlPolicy = "sanitize") {
|
|
|
2784
2946
|
case "inlineMath":
|
|
2785
2947
|
return /* @__PURE__ */ jsx18("code", { className: "squisq-md-inline-math", children: node.value }, key);
|
|
2786
2948
|
case "htmlInline":
|
|
2787
|
-
if (htmlPolicy === "strip") return null;
|
|
2788
|
-
if (htmlPolicy === "trusted" && !containsMediaTag(node.htmlChildren) && !containsDangerousTag(node.htmlChildren) && !hasDangerousRawHtml(node.rawHtml)) {
|
|
2949
|
+
if (ctx.htmlPolicy === "strip") return null;
|
|
2950
|
+
if (ctx.htmlPolicy === "trusted" && !containsMediaTag(node.htmlChildren) && !containsDangerousTag(node.htmlChildren) && !hasDangerousRawHtml(node.rawHtml)) {
|
|
2789
2951
|
return /* @__PURE__ */ jsx18(
|
|
2790
2952
|
"span",
|
|
2791
2953
|
{
|
|
@@ -2795,7 +2957,7 @@ function renderInline(nodes, keyPrefix = "", htmlPolicy = "sanitize") {
|
|
|
2795
2957
|
key
|
|
2796
2958
|
);
|
|
2797
2959
|
}
|
|
2798
|
-
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-html-inline", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, htmlPolicy), `${key}h`) }, key);
|
|
2960
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-html-inline", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`) }, key);
|
|
2799
2961
|
case "footnoteReference":
|
|
2800
2962
|
return /* @__PURE__ */ jsx18("sup", { className: "squisq-md-footnote-ref", children: /* @__PURE__ */ jsxs11("a", { href: `#fn-${node.identifier}`, children: [
|
|
2801
2963
|
"[",
|
|
@@ -2803,7 +2965,7 @@ function renderInline(nodes, keyPrefix = "", htmlPolicy = "sanitize") {
|
|
|
2803
2965
|
"]"
|
|
2804
2966
|
] }) }, key);
|
|
2805
2967
|
case "linkReference":
|
|
2806
|
-
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-link-ref", children: renderInline(node.children, key,
|
|
2968
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-link-ref", children: renderInline(node.children, key, ctx) }, key);
|
|
2807
2969
|
case "imageReference":
|
|
2808
2970
|
return /* @__PURE__ */ jsxs11("span", { className: "squisq-md-image-ref", children: [
|
|
2809
2971
|
"[",
|
|
@@ -2811,7 +2973,7 @@ function renderInline(nodes, keyPrefix = "", htmlPolicy = "sanitize") {
|
|
|
2811
2973
|
"]"
|
|
2812
2974
|
] }, key);
|
|
2813
2975
|
case "textDirective":
|
|
2814
|
-
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-text-directive", "data-directive": node.name, children: renderInline(node.children, key,
|
|
2976
|
+
return /* @__PURE__ */ jsx18("span", { className: "squisq-md-text-directive", "data-directive": node.name, children: renderInline(node.children, key, ctx) }, key);
|
|
2815
2977
|
case "mention":
|
|
2816
2978
|
return /* @__PURE__ */ jsxs11(
|
|
2817
2979
|
"span",
|
|
@@ -2833,30 +2995,30 @@ function renderInline(nodes, keyPrefix = "", htmlPolicy = "sanitize") {
|
|
|
2833
2995
|
}
|
|
2834
2996
|
});
|
|
2835
2997
|
}
|
|
2836
|
-
function renderBlock(node, key,
|
|
2998
|
+
function renderBlock(node, key, ctx = DEFAULT_CTX) {
|
|
2837
2999
|
switch (node.type) {
|
|
2838
3000
|
case "paragraph":
|
|
2839
|
-
return /* @__PURE__ */ jsx18("p", { className: "squisq-md-p", children: renderInline(node.children, key,
|
|
3001
|
+
return /* @__PURE__ */ jsx18("p", { className: "squisq-md-p", children: renderInline(node.children, key, ctx) }, key);
|
|
2840
3002
|
case "heading": {
|
|
2841
3003
|
const Tag = `h${node.depth}`;
|
|
2842
|
-
return /* @__PURE__ */ jsx18(Tag, { className: `squisq-md-heading squisq-md-h${node.depth}`, children: renderInline(node.children, key,
|
|
3004
|
+
return /* @__PURE__ */ jsx18(Tag, { className: `squisq-md-heading squisq-md-h${node.depth}`, children: renderInline(node.children, key, ctx) }, key);
|
|
2843
3005
|
}
|
|
2844
3006
|
case "blockquote":
|
|
2845
|
-
return /* @__PURE__ */ jsx18("blockquote", { className: "squisq-md-blockquote", children: renderBlocks(node.children, key,
|
|
3007
|
+
return /* @__PURE__ */ jsx18("blockquote", { className: "squisq-md-blockquote", children: renderBlocks(node.children, key, ctx) }, key);
|
|
2846
3008
|
case "list":
|
|
2847
3009
|
if (node.ordered) {
|
|
2848
|
-
return /* @__PURE__ */ jsx18("ol", { className: "squisq-md-list squisq-md-ol", start: node.start ?? void 0, children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`,
|
|
3010
|
+
return /* @__PURE__ */ jsx18("ol", { className: "squisq-md-list squisq-md-ol", start: node.start ?? void 0, children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, ctx)) }, key);
|
|
2849
3011
|
}
|
|
2850
|
-
return /* @__PURE__ */ jsx18("ul", { className: "squisq-md-list squisq-md-ul", children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`,
|
|
3012
|
+
return /* @__PURE__ */ jsx18("ul", { className: "squisq-md-list squisq-md-ul", children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, ctx)) }, key);
|
|
2851
3013
|
case "code":
|
|
2852
3014
|
return /* @__PURE__ */ jsx18("pre", { className: "squisq-md-code-block", children: /* @__PURE__ */ jsx18("code", { className: node.lang ? `language-${node.lang}` : void 0, children: node.value }) }, key);
|
|
2853
3015
|
case "thematicBreak":
|
|
2854
3016
|
return /* @__PURE__ */ jsx18("hr", { className: "squisq-md-hr" }, key);
|
|
2855
3017
|
case "table":
|
|
2856
|
-
return renderTable(node.children, node.align, key,
|
|
3018
|
+
return renderTable(node.children, node.align, key, ctx);
|
|
2857
3019
|
case "htmlBlock":
|
|
2858
|
-
if (htmlPolicy === "strip") return null;
|
|
2859
|
-
if (htmlPolicy === "trusted" && !containsMediaTag(node.htmlChildren) && !containsDangerousTag(node.htmlChildren) && !hasDangerousRawHtml(node.rawHtml)) {
|
|
3020
|
+
if (ctx.htmlPolicy === "strip") return null;
|
|
3021
|
+
if (ctx.htmlPolicy === "trusted" && !containsMediaTag(node.htmlChildren) && !containsDangerousTag(node.htmlChildren) && !hasDangerousRawHtml(node.rawHtml)) {
|
|
2860
3022
|
return /* @__PURE__ */ jsx18(
|
|
2861
3023
|
"div",
|
|
2862
3024
|
{
|
|
@@ -2866,7 +3028,7 @@ function renderBlock(node, key, htmlPolicy = "sanitize") {
|
|
|
2866
3028
|
key
|
|
2867
3029
|
);
|
|
2868
3030
|
}
|
|
2869
|
-
return /* @__PURE__ */ jsx18("div", { className: "squisq-md-html-block", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, htmlPolicy), `${key}h`) }, key);
|
|
3031
|
+
return /* @__PURE__ */ jsx18("div", { className: "squisq-md-html-block", children: renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`) }, key);
|
|
2870
3032
|
case "math":
|
|
2871
3033
|
return /* @__PURE__ */ jsx18("pre", { className: "squisq-md-math-block", children: /* @__PURE__ */ jsx18("code", { children: node.value }) }, key);
|
|
2872
3034
|
case "definition":
|
|
@@ -2874,7 +3036,7 @@ function renderBlock(node, key, htmlPolicy = "sanitize") {
|
|
|
2874
3036
|
case "footnoteDefinition":
|
|
2875
3037
|
return /* @__PURE__ */ jsxs11("div", { className: "squisq-md-footnote-def", id: `fn-${node.identifier}`, children: [
|
|
2876
3038
|
/* @__PURE__ */ jsx18("sup", { children: node.label ?? node.identifier }),
|
|
2877
|
-
renderBlocks(node.children, key,
|
|
3039
|
+
renderBlocks(node.children, key, ctx)
|
|
2878
3040
|
] }, key);
|
|
2879
3041
|
case "containerDirective":
|
|
2880
3042
|
return /* @__PURE__ */ jsxs11(
|
|
@@ -2884,7 +3046,7 @@ function renderBlock(node, key, htmlPolicy = "sanitize") {
|
|
|
2884
3046
|
"data-directive": node.name,
|
|
2885
3047
|
children: [
|
|
2886
3048
|
node.label && /* @__PURE__ */ jsx18("div", { className: "squisq-md-directive-label", children: node.label }),
|
|
2887
|
-
renderBlocks(node.children, key,
|
|
3049
|
+
renderBlocks(node.children, key, ctx)
|
|
2888
3050
|
]
|
|
2889
3051
|
},
|
|
2890
3052
|
key
|
|
@@ -2895,29 +3057,29 @@ function renderBlock(node, key, htmlPolicy = "sanitize") {
|
|
|
2895
3057
|
{
|
|
2896
3058
|
className: `squisq-md-directive squisq-md-directive-${node.name}`,
|
|
2897
3059
|
"data-directive": node.name,
|
|
2898
|
-
children: renderInline(node.children, key,
|
|
3060
|
+
children: renderInline(node.children, key, ctx)
|
|
2899
3061
|
},
|
|
2900
3062
|
key
|
|
2901
3063
|
);
|
|
2902
3064
|
case "definitionList":
|
|
2903
3065
|
return /* @__PURE__ */ jsx18("dl", { className: "squisq-md-dl", children: node.children.map((child, i) => {
|
|
2904
3066
|
if (child.type === "definitionTerm") {
|
|
2905
|
-
return /* @__PURE__ */ jsx18("dt", { className: "squisq-md-dt", children: renderInline(child.children, `${key}dt${i}`,
|
|
3067
|
+
return /* @__PURE__ */ jsx18("dt", { className: "squisq-md-dt", children: renderInline(child.children, `${key}dt${i}`, ctx) }, `${key}dt${i}`);
|
|
2906
3068
|
}
|
|
2907
|
-
return /* @__PURE__ */ jsx18("dd", { className: "squisq-md-dd", children: renderBlocks(child.children, `${key}dd${i}`,
|
|
3069
|
+
return /* @__PURE__ */ jsx18("dd", { className: "squisq-md-dd", children: renderBlocks(child.children, `${key}dd${i}`, ctx) }, `${key}dd${i}`);
|
|
2908
3070
|
}) }, key);
|
|
2909
3071
|
default:
|
|
2910
3072
|
return null;
|
|
2911
3073
|
}
|
|
2912
3074
|
}
|
|
2913
|
-
function renderListItem(item, key,
|
|
3075
|
+
function renderListItem(item, key, ctx = DEFAULT_CTX) {
|
|
2914
3076
|
const isTask = item.checked !== null && item.checked !== void 0;
|
|
2915
3077
|
return /* @__PURE__ */ jsxs11("li", { className: `squisq-md-li${isTask ? " squisq-md-task" : ""}`, children: [
|
|
2916
3078
|
isTask && /* @__PURE__ */ jsx18("input", { type: "checkbox", checked: !!item.checked, readOnly: true, className: "squisq-md-checkbox" }),
|
|
2917
|
-
renderBlocks(item.children, key,
|
|
3079
|
+
renderBlocks(item.children, key, ctx)
|
|
2918
3080
|
] }, key);
|
|
2919
3081
|
}
|
|
2920
|
-
function renderTable(rows, align, key,
|
|
3082
|
+
function renderTable(rows, align, key, ctx = DEFAULT_CTX) {
|
|
2921
3083
|
const [headerRow, ...bodyRows] = rows;
|
|
2922
3084
|
return /* @__PURE__ */ jsxs11("table", { className: "squisq-md-table", children: [
|
|
2923
3085
|
headerRow && /* @__PURE__ */ jsx18("thead", { children: /* @__PURE__ */ jsx18("tr", { children: headerRow.children.map((cell, ci) => /* @__PURE__ */ jsx18(
|
|
@@ -2925,7 +3087,7 @@ function renderTable(rows, align, key, htmlPolicy = "sanitize") {
|
|
|
2925
3087
|
{
|
|
2926
3088
|
className: "squisq-md-th",
|
|
2927
3089
|
style: align?.[ci] ? { textAlign: align[ci] } : void 0,
|
|
2928
|
-
children: renderInline(cell.children, `${key}th${ci}`,
|
|
3090
|
+
children: renderInline(cell.children, `${key}th${ci}`, ctx)
|
|
2929
3091
|
},
|
|
2930
3092
|
`${key}th${ci}`
|
|
2931
3093
|
)) }) }),
|
|
@@ -2934,14 +3096,14 @@ function renderTable(rows, align, key, htmlPolicy = "sanitize") {
|
|
|
2934
3096
|
{
|
|
2935
3097
|
className: "squisq-md-td",
|
|
2936
3098
|
style: align?.[ci] ? { textAlign: align[ci] } : void 0,
|
|
2937
|
-
children: renderInline(cell.children, `${key}td${ri}-${ci}`,
|
|
3099
|
+
children: renderInline(cell.children, `${key}td${ri}-${ci}`, ctx)
|
|
2938
3100
|
},
|
|
2939
3101
|
`${key}td${ri}-${ci}`
|
|
2940
3102
|
)) }, `${key}tr${ri}`)) })
|
|
2941
3103
|
] }, key);
|
|
2942
3104
|
}
|
|
2943
|
-
function renderBlocks(nodes, keyPrefix = "",
|
|
2944
|
-
return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}`,
|
|
3105
|
+
function renderBlocks(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
|
|
3106
|
+
return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}`, ctx));
|
|
2945
3107
|
}
|
|
2946
3108
|
function MdImage({ src, alt, title }) {
|
|
2947
3109
|
const safeSrc = sanitizeUrl(src, "media");
|
|
@@ -3070,18 +3232,29 @@ function renderHtmlNodes(nodes, keyPrefix) {
|
|
|
3070
3232
|
function MarkdownRenderer({
|
|
3071
3233
|
nodes,
|
|
3072
3234
|
className,
|
|
3073
|
-
htmlPolicy = "sanitize"
|
|
3235
|
+
htmlPolicy = "sanitize",
|
|
3236
|
+
linkSchemes
|
|
3074
3237
|
}) {
|
|
3075
3238
|
if (!nodes || nodes.length === 0) return null;
|
|
3076
|
-
return /* @__PURE__ */ jsx18("div", { className: `squisq-md ${className || ""}`, children: renderBlocks(nodes, "", htmlPolicy) });
|
|
3239
|
+
return /* @__PURE__ */ jsx18("div", { className: `squisq-md ${className || ""}`, children: renderBlocks(nodes, "", { htmlPolicy, linkSchemes }) });
|
|
3077
3240
|
}
|
|
3078
3241
|
|
|
3079
3242
|
// src/LinearDocView.tsx
|
|
3080
3243
|
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3244
|
+
var warnedUnknownTemplates = /* @__PURE__ */ new Set();
|
|
3081
3245
|
function isAnnotatedBlock(block) {
|
|
3082
3246
|
const annotation = block.sourceHeading?.templateAnnotation;
|
|
3083
|
-
if (!annotation) return false;
|
|
3084
|
-
|
|
3247
|
+
if (!annotation?.template) return false;
|
|
3248
|
+
if (!hasTemplate(annotation.template)) {
|
|
3249
|
+
if (!warnedUnknownTemplates.has(annotation.template)) {
|
|
3250
|
+
warnedUnknownTemplates.add(annotation.template);
|
|
3251
|
+
console.warn(
|
|
3252
|
+
`[squisq] Unknown template "${annotation.template}" \u2014 rendering the block as plain markdown.`
|
|
3253
|
+
);
|
|
3254
|
+
}
|
|
3255
|
+
return false;
|
|
3256
|
+
}
|
|
3257
|
+
return true;
|
|
3085
3258
|
}
|
|
3086
3259
|
function countAll(blocks) {
|
|
3087
3260
|
let count = 0;
|
|
@@ -3173,6 +3346,7 @@ function BlockSection({ block, basePath, viewport, renderContext, blockIndex })
|
|
|
3173
3346
|
}
|
|
3174
3347
|
function LinearDocView({
|
|
3175
3348
|
doc,
|
|
3349
|
+
markdown,
|
|
3176
3350
|
basePath = "/",
|
|
3177
3351
|
viewport,
|
|
3178
3352
|
className,
|
|
@@ -3182,7 +3356,15 @@ function LinearDocView({
|
|
|
3182
3356
|
imageDisplayMode = "inline"
|
|
3183
3357
|
}) {
|
|
3184
3358
|
const activeViewport = viewport ?? VIEWPORT_PRESETS3.landscape;
|
|
3185
|
-
const
|
|
3359
|
+
const markdownDoc = useMemo8(
|
|
3360
|
+
() => !doc && markdown !== void 0 ? markdownToDoc(parseMarkdown(markdown)) : void 0,
|
|
3361
|
+
[doc, markdown]
|
|
3362
|
+
);
|
|
3363
|
+
const resolvedDoc = doc ?? markdownDoc;
|
|
3364
|
+
const totalBlocks = useMemo8(
|
|
3365
|
+
() => resolvedDoc ? countAll(resolvedDoc.blocks) : 0,
|
|
3366
|
+
[resolvedDoc]
|
|
3367
|
+
);
|
|
3186
3368
|
const autoSurface = useAutoSurface(surface === "auto");
|
|
3187
3369
|
const resolvedSurface = surface === "auto" ? autoSurface : surface;
|
|
3188
3370
|
const renderContext = useMemo8(() => {
|
|
@@ -3198,6 +3380,9 @@ function LinearDocView({
|
|
|
3198
3380
|
};
|
|
3199
3381
|
}, [activeViewport, totalBlocks, theme, resolvedSurface]);
|
|
3200
3382
|
const activeTheme = renderContext.theme;
|
|
3383
|
+
if (!resolvedDoc) {
|
|
3384
|
+
return /* @__PURE__ */ jsx19("div", { className: `squisq-linear squisq-linear--empty ${className || ""}` });
|
|
3385
|
+
}
|
|
3201
3386
|
const bgColor = activeTheme.colors.background;
|
|
3202
3387
|
const textColor = activeTheme.colors.text;
|
|
3203
3388
|
const mutedColor = activeTheme.colors.textMuted;
|
|
@@ -3358,7 +3543,7 @@ function LinearDocView({
|
|
|
3358
3543
|
background: color-mix(in srgb, var(--squisq-linear-primary) 8%, transparent);
|
|
3359
3544
|
}
|
|
3360
3545
|
` }),
|
|
3361
|
-
|
|
3546
|
+
resolvedDoc.blocks.map((block, i) => /* @__PURE__ */ jsx19(
|
|
3362
3547
|
BlockSection,
|
|
3363
3548
|
{
|
|
3364
3549
|
block,
|
|
@@ -3395,9 +3580,9 @@ var SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
|
3395
3580
|
"by",
|
|
3396
3581
|
"is"
|
|
3397
3582
|
]);
|
|
3398
|
-
function buildSegmentTitleMap(
|
|
3583
|
+
function buildSegmentTitleMap(doc) {
|
|
3399
3584
|
const map = /* @__PURE__ */ new Map();
|
|
3400
|
-
for (const block of
|
|
3585
|
+
for (const block of doc.blocks) {
|
|
3401
3586
|
if (isTemplateBlock2(block) && block.template === "sectionHeader" && "title" in block) {
|
|
3402
3587
|
const segIdx = block.audioSegment;
|
|
3403
3588
|
if (!map.has(segIdx)) {
|
|
@@ -3405,9 +3590,9 @@ function buildSegmentTitleMap(script) {
|
|
|
3405
3590
|
}
|
|
3406
3591
|
}
|
|
3407
3592
|
}
|
|
3408
|
-
for (let i = 0; i <
|
|
3593
|
+
for (let i = 0; i < doc.audio.segments.length; i++) {
|
|
3409
3594
|
if (!map.has(i)) {
|
|
3410
|
-
const name =
|
|
3595
|
+
const name = doc.audio.segments[i].name;
|
|
3411
3596
|
if (name === "intro" || name.includes("intro")) {
|
|
3412
3597
|
map.set(i, "Introduction");
|
|
3413
3598
|
} else if (name === "flight-context" || name.includes("flight-context")) {
|
|
@@ -3423,14 +3608,34 @@ function buildSegmentTitleMap(script) {
|
|
|
3423
3608
|
}
|
|
3424
3609
|
return map;
|
|
3425
3610
|
}
|
|
3426
|
-
function
|
|
3427
|
-
|
|
3428
|
-
|
|
3611
|
+
function isDevEnvironment() {
|
|
3612
|
+
try {
|
|
3613
|
+
return typeof process !== "undefined" && process.env.NODE_ENV !== "production";
|
|
3614
|
+
} catch {
|
|
3615
|
+
return false;
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
var warnedMissingStyles = false;
|
|
3619
|
+
function DocPlayer(props) {
|
|
3620
|
+
const { doc, markdown } = props;
|
|
3621
|
+
const markdownDoc = useMemo9(
|
|
3622
|
+
() => !doc && markdown !== void 0 ? markdownToDoc2(parseMarkdown2(markdown)) : void 0,
|
|
3623
|
+
[doc, markdown]
|
|
3624
|
+
);
|
|
3625
|
+
const resolvedDoc = doc ?? markdownDoc;
|
|
3626
|
+
if (!resolvedDoc) {
|
|
3627
|
+
return /* @__PURE__ */ jsx20("div", { className: "doc-player doc-player--empty" });
|
|
3628
|
+
}
|
|
3629
|
+
return /* @__PURE__ */ jsx20(DocPlayerContent, { ...props, doc: resolvedDoc });
|
|
3630
|
+
}
|
|
3631
|
+
function DocPlayerContent({
|
|
3632
|
+
doc,
|
|
3633
|
+
basePath = ".",
|
|
3429
3634
|
renderMode = false,
|
|
3430
3635
|
autoPlay = false,
|
|
3431
3636
|
onEnded,
|
|
3432
3637
|
onTimeUpdate,
|
|
3433
|
-
|
|
3638
|
+
audioController: externalAudioController,
|
|
3434
3639
|
showControls = true,
|
|
3435
3640
|
showScrubber = false,
|
|
3436
3641
|
muted = false,
|
|
@@ -3443,16 +3648,18 @@ function DocPlayer({
|
|
|
3443
3648
|
onBlockMarkers,
|
|
3444
3649
|
forceViewport,
|
|
3445
3650
|
displayMode = "video",
|
|
3651
|
+
showCoverSlide = true,
|
|
3446
3652
|
theme,
|
|
3447
3653
|
surface,
|
|
3448
|
-
captionStyle = "standard"
|
|
3654
|
+
captionStyle = "standard",
|
|
3655
|
+
enableSwipe = true
|
|
3449
3656
|
}) {
|
|
3450
3657
|
const isSlideshowMode = displayMode === "slideshow";
|
|
3451
3658
|
const isLinearMode = displayMode === "linear";
|
|
3452
|
-
const audioRef =
|
|
3453
|
-
const containerRef =
|
|
3454
|
-
const [tapFeedback, setTapFeedback] =
|
|
3455
|
-
const tapFeedbackTimer =
|
|
3659
|
+
const audioRef = useRef7(null);
|
|
3660
|
+
const containerRef = useRef7(null);
|
|
3661
|
+
const [tapFeedback, setTapFeedback] = useState7(null);
|
|
3662
|
+
const tapFeedbackTimer = useRef7();
|
|
3456
3663
|
const { viewport, orientation } = useViewportOrientation();
|
|
3457
3664
|
const activeViewport = forceViewport || (renderMode ? VIEWPORT_PRESETS4.landscape : viewport);
|
|
3458
3665
|
const isDebugMode = useMemo9(() => {
|
|
@@ -3460,8 +3667,20 @@ function DocPlayer({
|
|
|
3460
3667
|
const params = new URLSearchParams(window.location.search);
|
|
3461
3668
|
return params.get("debug") === "true";
|
|
3462
3669
|
}, []);
|
|
3463
|
-
const internalAudio = useAudioSync(audioRef,
|
|
3464
|
-
const audio =
|
|
3670
|
+
const internalAudio = useAudioSync(audioRef, doc.audio, basePath);
|
|
3671
|
+
const audio = externalAudioController || internalAudio;
|
|
3672
|
+
useEffect8(() => {
|
|
3673
|
+
if (warnedMissingStyles || !isDevEnvironment()) return;
|
|
3674
|
+
const el = containerRef.current;
|
|
3675
|
+
if (!el || typeof getComputedStyle !== "function") return;
|
|
3676
|
+
const value = getComputedStyle(el).getPropertyValue("--squisq-styles-loaded");
|
|
3677
|
+
if (!value.trim()) {
|
|
3678
|
+
warnedMissingStyles = true;
|
|
3679
|
+
console.warn(
|
|
3680
|
+
'[squisq] @bendyline/squisq-react/styles is not loaded \u2014 import "@bendyline/squisq-react/styles"'
|
|
3681
|
+
);
|
|
3682
|
+
}
|
|
3683
|
+
}, []);
|
|
3465
3684
|
const {
|
|
3466
3685
|
currentTime,
|
|
3467
3686
|
isPlaying,
|
|
@@ -3478,13 +3697,13 @@ function DocPlayer({
|
|
|
3478
3697
|
skipToSegment: _skipToSegment,
|
|
3479
3698
|
restart
|
|
3480
3699
|
} = audio;
|
|
3481
|
-
const mediaSchedule = useMemo9(() => resolveMediaSchedule(
|
|
3482
|
-
const currentTimeRef =
|
|
3700
|
+
const mediaSchedule = useMemo9(() => resolveMediaSchedule(doc), [doc]);
|
|
3701
|
+
const currentTimeRef = useRef7(currentTime);
|
|
3483
3702
|
currentTimeRef.current = currentTime;
|
|
3484
|
-
const totalDurationRef =
|
|
3703
|
+
const totalDurationRef = useRef7(totalDuration);
|
|
3485
3704
|
totalDurationRef.current = totalDuration;
|
|
3486
|
-
const expandedBlocksLenRef =
|
|
3487
|
-
const handleContainerClick =
|
|
3705
|
+
const expandedBlocksLenRef = useRef7(0);
|
|
3706
|
+
const handleContainerClick = useCallback6(
|
|
3488
3707
|
(e) => {
|
|
3489
3708
|
if (renderMode || isSlideshowMode || isLinearMode) return;
|
|
3490
3709
|
const target = e.target;
|
|
@@ -3518,9 +3737,10 @@ function DocPlayer({
|
|
|
3518
3737
|
nextBlock: _nextBlock,
|
|
3519
3738
|
prevBlock: _prevBlock,
|
|
3520
3739
|
blocks: expandedBlocks
|
|
3521
|
-
} = useDocPlayback(
|
|
3740
|
+
} = useDocPlayback(doc, currentTime, activeViewport, renderMode, effectiveTheme);
|
|
3522
3741
|
const coverBlock = useMemo9(() => {
|
|
3523
|
-
const startBlockConfig =
|
|
3742
|
+
const startBlockConfig = doc.startBlock;
|
|
3743
|
+
if (!showCoverSlide) return null;
|
|
3524
3744
|
if (!startBlockConfig) return null;
|
|
3525
3745
|
const context = createTemplateContext(effectiveTheme, 0, 1, activeViewport);
|
|
3526
3746
|
const layers = expandCoverBlock(startBlockConfig, context);
|
|
@@ -3533,40 +3753,59 @@ function DocPlayer({
|
|
|
3533
3753
|
audioSegment: -1,
|
|
3534
3754
|
layers
|
|
3535
3755
|
};
|
|
3536
|
-
}, [
|
|
3537
|
-
const
|
|
3538
|
-
const [
|
|
3539
|
-
const
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3756
|
+
}, [doc.startBlock, activeViewport, effectiveTheme, showCoverSlide]);
|
|
3757
|
+
const hasManagedCover = !!coverBlock;
|
|
3758
|
+
const [slideshowCoverVisible, setSlideshowCoverVisible] = useState7(false);
|
|
3759
|
+
const slideshowCoverInitKeyRef = useRef7("");
|
|
3760
|
+
useEffect8(() => {
|
|
3761
|
+
const initKey = `${isSlideshowMode}:${hasManagedCover}:${renderMode}`;
|
|
3762
|
+
if (slideshowCoverInitKeyRef.current === initKey) return;
|
|
3763
|
+
slideshowCoverInitKeyRef.current = initKey;
|
|
3764
|
+
if (isSlideshowMode && hasManagedCover && !renderMode) {
|
|
3765
|
+
setSlideshowCoverVisible(true);
|
|
3766
|
+
pause();
|
|
3767
|
+
} else {
|
|
3768
|
+
setSlideshowCoverVisible(false);
|
|
3769
|
+
}
|
|
3770
|
+
}, [isSlideshowMode, hasManagedCover, renderMode, pause]);
|
|
3771
|
+
const [coverForced, setCoverForced] = useState7(false);
|
|
3772
|
+
const [coverGraceActive, setCoverGraceActive] = useState7(false);
|
|
3773
|
+
const coverGraceTimer = useRef7();
|
|
3774
|
+
const coverWasShowing = useRef7(false);
|
|
3775
|
+
const hasPlayedOnce = useRef7(false);
|
|
3776
|
+
const atRest = !!(coverBlock && !isSlideshowMode && !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
3543
3777
|
if (atRest) coverWasShowing.current = true;
|
|
3544
|
-
|
|
3545
|
-
if (isPlaying && coverWasShowing.current && coverBlock && !renderMode) {
|
|
3778
|
+
useEffect8(() => {
|
|
3779
|
+
if (isPlaying && coverWasShowing.current && coverBlock && !renderMode && !isSlideshowMode) {
|
|
3546
3780
|
coverWasShowing.current = false;
|
|
3547
3781
|
hasPlayedOnce.current = true;
|
|
3548
3782
|
setCoverGraceActive(true);
|
|
3549
3783
|
coverGraceTimer.current = setTimeout(() => setCoverGraceActive(false), 3e3);
|
|
3550
3784
|
}
|
|
3551
|
-
}, [isPlaying, coverBlock, renderMode]);
|
|
3552
|
-
|
|
3553
|
-
const
|
|
3554
|
-
const
|
|
3555
|
-
|
|
3785
|
+
}, [isPlaying, coverBlock, renderMode, isSlideshowMode]);
|
|
3786
|
+
useEffect8(() => () => clearTimeout(coverGraceTimer.current), []);
|
|
3787
|
+
const showVideoCoverBlock = !isSlideshowMode && !isLinearMode && !!coverBlock && (coverForced || coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
|
3788
|
+
const showSlideshowCover = !!(isSlideshowMode && !isLinearMode && !renderMode && coverBlock && slideshowCoverVisible);
|
|
3789
|
+
const showCoverBlock = showVideoCoverBlock || showSlideshowCover;
|
|
3790
|
+
const slideshowHasCover = !!(isSlideshowMode && !renderMode && coverBlock);
|
|
3791
|
+
const slideshowSlideIndex = slideshowHasCover ? slideshowCoverVisible ? 0 : currentBlockIndex + 1 : currentBlockIndex;
|
|
3792
|
+
const slideshowTotalSlides = slideshowHasCover ? expandedBlocks.length + 1 : expandedBlocks.length;
|
|
3793
|
+
const hasAutoPlayed = useRef7(false);
|
|
3794
|
+
useEffect8(() => {
|
|
3556
3795
|
if (isAudioReady && autoPlay && !hasAutoPlayed.current) {
|
|
3557
3796
|
hasAutoPlayed.current = true;
|
|
3558
3797
|
play();
|
|
3559
3798
|
}
|
|
3560
3799
|
}, [isAudioReady, autoPlay, play]);
|
|
3561
|
-
|
|
3800
|
+
useEffect8(() => {
|
|
3562
3801
|
onTimeUpdate?.(currentTime);
|
|
3563
3802
|
}, [currentTime, onTimeUpdate]);
|
|
3564
|
-
|
|
3803
|
+
useEffect8(() => {
|
|
3565
3804
|
if (isEnded) {
|
|
3566
3805
|
onEnded?.();
|
|
3567
3806
|
}
|
|
3568
3807
|
}, [isEnded, onEnded]);
|
|
3569
|
-
|
|
3808
|
+
useEffect8(() => {
|
|
3570
3809
|
if ((renderMode || isDebugMode) && typeof window !== "undefined") {
|
|
3571
3810
|
const w = window;
|
|
3572
3811
|
w.seekTo = (time) => {
|
|
@@ -3645,7 +3884,7 @@ function DocPlayer({
|
|
|
3645
3884
|
});
|
|
3646
3885
|
};
|
|
3647
3886
|
w.getDuration = () => {
|
|
3648
|
-
const mediaDuration = getDocPlaybackDuration(
|
|
3887
|
+
const mediaDuration = getDocPlaybackDuration(doc);
|
|
3649
3888
|
if (totalDuration > 0) return Math.max(totalDuration, mediaDuration);
|
|
3650
3889
|
return mediaDuration;
|
|
3651
3890
|
};
|
|
@@ -3655,20 +3894,20 @@ function DocPlayer({
|
|
|
3655
3894
|
startTime: s.startTime,
|
|
3656
3895
|
duration: s.duration
|
|
3657
3896
|
}));
|
|
3658
|
-
w.getAudioSegments = () =>
|
|
3897
|
+
w.getAudioSegments = () => doc.audio.segments.map((seg) => ({
|
|
3659
3898
|
src: seg.src,
|
|
3660
3899
|
name: seg.name,
|
|
3661
3900
|
duration: seg.duration,
|
|
3662
3901
|
startTime: seg.startTime
|
|
3663
3902
|
}));
|
|
3664
|
-
w.getCaptions = () =>
|
|
3903
|
+
w.getCaptions = () => doc.captions?.phrases?.map((p) => ({
|
|
3665
3904
|
text: p.text,
|
|
3666
3905
|
startTime: p.startTime,
|
|
3667
3906
|
endTime: p.endTime
|
|
3668
3907
|
})) || [];
|
|
3669
3908
|
w.getChapters = () => {
|
|
3670
|
-
const titleMap = buildSegmentTitleMap(
|
|
3671
|
-
return
|
|
3909
|
+
const titleMap = buildSegmentTitleMap(doc);
|
|
3910
|
+
return doc.audio.segments.map((seg, i) => ({
|
|
3672
3911
|
title: titleMap.get(i) || seg.name,
|
|
3673
3912
|
startTime: seg.startTime,
|
|
3674
3913
|
duration: seg.duration
|
|
@@ -3700,48 +3939,54 @@ function DocPlayer({
|
|
|
3700
3939
|
};
|
|
3701
3940
|
}, [renderMode, isDebugMode, seekTo, totalDuration, expandedBlocks, coverBlock]);
|
|
3702
3941
|
const defaultMode = captionsEnabledProp === false ? "off" : captionStyle || "standard";
|
|
3703
|
-
const [captionMode, setCaptionMode] =
|
|
3942
|
+
const [captionMode, setCaptionMode] = useState7(defaultMode);
|
|
3943
|
+
useEffect8(() => {
|
|
3944
|
+
setCaptionMode(defaultMode);
|
|
3945
|
+
}, [defaultMode]);
|
|
3704
3946
|
const captionsEnabled = captionMode !== "off";
|
|
3705
3947
|
const activeCaptionStyle = captionMode === "social" ? "social" : "standard";
|
|
3706
|
-
const setCaptionsEnabled =
|
|
3948
|
+
const setCaptionsEnabled = useCallback6(
|
|
3707
3949
|
(enabled) => {
|
|
3708
3950
|
setCaptionMode(enabled ? captionStyle || "standard" : "off");
|
|
3709
3951
|
onCaptionsToggle?.(enabled);
|
|
3710
3952
|
},
|
|
3711
3953
|
[onCaptionsToggle, captionStyle]
|
|
3712
3954
|
);
|
|
3713
|
-
const cycleCaptionMode =
|
|
3955
|
+
const cycleCaptionMode = useCallback6(() => {
|
|
3714
3956
|
setCaptionMode((prev) => {
|
|
3715
3957
|
const next = prev === "off" ? "standard" : prev === "standard" ? "social" : "off";
|
|
3716
3958
|
onCaptionsToggle?.(next !== "off");
|
|
3717
3959
|
return next;
|
|
3718
3960
|
});
|
|
3719
3961
|
}, [onCaptionsToggle]);
|
|
3720
|
-
const hasCaptions =
|
|
3721
|
-
const segmentTitleMap = useMemo9(() => buildSegmentTitleMap(
|
|
3962
|
+
const hasCaptions = doc.captions && doc.captions.phrases.length > 0;
|
|
3963
|
+
const segmentTitleMap = useMemo9(() => buildSegmentTitleMap(doc), [doc]);
|
|
3722
3964
|
const playbackState = useMemo9(
|
|
3723
3965
|
() => ({
|
|
3724
3966
|
isPlaying,
|
|
3725
3967
|
currentTime,
|
|
3726
3968
|
totalDuration,
|
|
3727
|
-
currentBlockIndex,
|
|
3728
|
-
totalBlocks:
|
|
3969
|
+
currentBlockIndex: slideshowSlideIndex,
|
|
3970
|
+
totalBlocks: slideshowTotalSlides,
|
|
3729
3971
|
docProgress,
|
|
3730
3972
|
hasCaptions: !!hasCaptions,
|
|
3731
3973
|
captionsEnabled,
|
|
3732
3974
|
captionMode,
|
|
3733
3975
|
isFullscreen,
|
|
3734
3976
|
currentSegmentIndex: currentSegment,
|
|
3735
|
-
currentSegmentName: segmentTitleMap.get(currentSegment) ??
|
|
3736
|
-
currentBlock: currentBlock ?? null
|
|
3977
|
+
currentSegmentName: segmentTitleMap.get(currentSegment) ?? doc.audio.segments[currentSegment]?.name ?? null,
|
|
3978
|
+
currentBlock: showSlideshowCover ? coverBlock : currentBlock ?? null,
|
|
3979
|
+
currentSlideLabel: showSlideshowCover ? "Cover" : void 0,
|
|
3980
|
+
currentSlideNumber: slideshowHasCover && !showSlideshowCover ? currentBlockIndex + 1 : void 0,
|
|
3981
|
+
totalSlideNumber: slideshowHasCover ? expandedBlocks.length : void 0
|
|
3737
3982
|
}),
|
|
3738
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps --
|
|
3983
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- doc.audio.segments is stable within a given doc
|
|
3739
3984
|
[
|
|
3740
3985
|
isPlaying,
|
|
3741
3986
|
currentTime,
|
|
3742
3987
|
totalDuration,
|
|
3743
|
-
|
|
3744
|
-
|
|
3988
|
+
slideshowSlideIndex,
|
|
3989
|
+
slideshowTotalSlides,
|
|
3745
3990
|
docProgress,
|
|
3746
3991
|
hasCaptions,
|
|
3747
3992
|
captionsEnabled,
|
|
@@ -3749,7 +3994,12 @@ function DocPlayer({
|
|
|
3749
3994
|
isFullscreen,
|
|
3750
3995
|
currentSegment,
|
|
3751
3996
|
segmentTitleMap,
|
|
3752
|
-
currentBlock
|
|
3997
|
+
currentBlock,
|
|
3998
|
+
currentBlockIndex,
|
|
3999
|
+
showSlideshowCover,
|
|
4000
|
+
coverBlock,
|
|
4001
|
+
slideshowHasCover,
|
|
4002
|
+
expandedBlocks.length
|
|
3753
4003
|
]
|
|
3754
4004
|
);
|
|
3755
4005
|
const playbackActions = useMemo9(
|
|
@@ -3766,24 +4016,56 @@ function DocPlayer({
|
|
|
3766
4016
|
const slideNavActions = useMemo9(
|
|
3767
4017
|
() => ({
|
|
3768
4018
|
nextSlide: () => {
|
|
4019
|
+
if (slideshowHasCover && slideshowCoverVisible) {
|
|
4020
|
+
const target = expandedBlocks[0];
|
|
4021
|
+
if (target) {
|
|
4022
|
+
setSlideshowCoverVisible(false);
|
|
4023
|
+
seekTo(target.startTime);
|
|
4024
|
+
pause();
|
|
4025
|
+
}
|
|
4026
|
+
return;
|
|
4027
|
+
}
|
|
3769
4028
|
if (currentBlockIndex < expandedBlocks.length - 1) {
|
|
3770
4029
|
const target = expandedBlocks[currentBlockIndex + 1];
|
|
3771
4030
|
if (target) {
|
|
4031
|
+
setSlideshowCoverVisible(false);
|
|
3772
4032
|
seekTo(target.startTime);
|
|
3773
4033
|
pause();
|
|
3774
4034
|
}
|
|
3775
4035
|
}
|
|
3776
4036
|
},
|
|
3777
4037
|
prevSlide: () => {
|
|
4038
|
+
if (slideshowHasCover && !slideshowCoverVisible && currentBlockIndex <= 0) {
|
|
4039
|
+
setSlideshowCoverVisible(true);
|
|
4040
|
+
seekTo(0);
|
|
4041
|
+
pause();
|
|
4042
|
+
return;
|
|
4043
|
+
}
|
|
3778
4044
|
if (currentBlockIndex > 0) {
|
|
3779
4045
|
const target = expandedBlocks[currentBlockIndex - 1];
|
|
3780
4046
|
if (target) {
|
|
4047
|
+
setSlideshowCoverVisible(false);
|
|
3781
4048
|
seekTo(target.startTime);
|
|
3782
4049
|
pause();
|
|
3783
4050
|
}
|
|
3784
4051
|
}
|
|
3785
4052
|
},
|
|
3786
4053
|
goToSlide: (index) => {
|
|
4054
|
+
if (slideshowHasCover) {
|
|
4055
|
+
if (index === 0) {
|
|
4056
|
+
setSlideshowCoverVisible(true);
|
|
4057
|
+
seekTo(0);
|
|
4058
|
+
pause();
|
|
4059
|
+
return;
|
|
4060
|
+
}
|
|
4061
|
+
const target = expandedBlocks[index - 1];
|
|
4062
|
+
if (target) {
|
|
4063
|
+
setSlideshowCoverVisible(false);
|
|
4064
|
+
seekTo(target.startTime);
|
|
4065
|
+
pause();
|
|
4066
|
+
}
|
|
4067
|
+
return;
|
|
4068
|
+
}
|
|
3787
4069
|
if (index >= 0 && index < expandedBlocks.length) {
|
|
3788
4070
|
const target = expandedBlocks[index];
|
|
3789
4071
|
if (target) {
|
|
@@ -3793,15 +4075,24 @@ function DocPlayer({
|
|
|
3793
4075
|
}
|
|
3794
4076
|
}
|
|
3795
4077
|
}),
|
|
3796
|
-
[currentBlockIndex, expandedBlocks, seekTo, pause]
|
|
4078
|
+
[currentBlockIndex, expandedBlocks, seekTo, pause, slideshowHasCover, slideshowCoverVisible]
|
|
3797
4079
|
);
|
|
3798
|
-
|
|
4080
|
+
const swipeEnabled = isSlideshowMode && !isLinearMode && !renderMode && enableSwipe;
|
|
4081
|
+
const swipe = useSlideSwipe({
|
|
4082
|
+
enabled: swipeEnabled,
|
|
4083
|
+
containerRef,
|
|
4084
|
+
canGoNext: slideshowSlideIndex < slideshowTotalSlides - 1,
|
|
4085
|
+
canGoPrev: slideshowSlideIndex > 0,
|
|
4086
|
+
onNext: slideNavActions.nextSlide,
|
|
4087
|
+
onPrev: slideNavActions.prevSlide
|
|
4088
|
+
});
|
|
4089
|
+
useEffect8(() => {
|
|
3799
4090
|
onPlaybackStateChange?.(playbackState);
|
|
3800
4091
|
}, [playbackState, onPlaybackStateChange]);
|
|
3801
|
-
|
|
4092
|
+
useEffect8(() => {
|
|
3802
4093
|
onControlsReady?.({ play, pause, ...playbackActions });
|
|
3803
4094
|
}, [play, pause, playbackActions, onControlsReady]);
|
|
3804
|
-
const getBlockTitle =
|
|
4095
|
+
const getBlockTitle = useCallback6((block) => {
|
|
3805
4096
|
const docBlock = block;
|
|
3806
4097
|
if (isTemplateBlock2(docBlock)) {
|
|
3807
4098
|
const props = docBlock;
|
|
@@ -3840,13 +4131,13 @@ function DocPlayer({
|
|
|
3840
4131
|
};
|
|
3841
4132
|
});
|
|
3842
4133
|
}, [expandedBlocks, totalDuration, getBlockTitle]);
|
|
3843
|
-
|
|
4134
|
+
useEffect8(() => {
|
|
3844
4135
|
if (blockMarkers.length > 0) {
|
|
3845
4136
|
onBlockMarkers?.(blockMarkers);
|
|
3846
4137
|
}
|
|
3847
4138
|
}, [blockMarkers, onBlockMarkers]);
|
|
3848
|
-
expandedBlocksLenRef.current = expandedBlocks.length;
|
|
3849
|
-
const handleKeyDown =
|
|
4139
|
+
expandedBlocksLenRef.current = isSlideshowMode ? slideshowTotalSlides : expandedBlocks.length;
|
|
4140
|
+
const handleKeyDown = useCallback6(
|
|
3850
4141
|
(e) => {
|
|
3851
4142
|
const activeEl = document.activeElement;
|
|
3852
4143
|
if (activeEl && (activeEl.tagName === "INPUT" || activeEl.tagName === "TEXTAREA" || activeEl.tagName === "SELECT")) {
|
|
@@ -3892,7 +4183,7 @@ function DocPlayer({
|
|
|
3892
4183
|
},
|
|
3893
4184
|
[isSlideshowMode, isLinearMode, toggle, seekTo, slideNavActions]
|
|
3894
4185
|
);
|
|
3895
|
-
|
|
4186
|
+
useEffect8(() => {
|
|
3896
4187
|
if (renderMode) return;
|
|
3897
4188
|
window.addEventListener("keydown", handleKeyDown);
|
|
3898
4189
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
@@ -3912,7 +4203,7 @@ function DocPlayer({
|
|
|
3912
4203
|
children: /* @__PURE__ */ jsx20(
|
|
3913
4204
|
LinearDocView,
|
|
3914
4205
|
{
|
|
3915
|
-
doc
|
|
4206
|
+
doc,
|
|
3916
4207
|
basePath,
|
|
3917
4208
|
viewport: activeViewport,
|
|
3918
4209
|
theme,
|
|
@@ -3926,15 +4217,19 @@ function DocPlayer({
|
|
|
3926
4217
|
"div",
|
|
3927
4218
|
{
|
|
3928
4219
|
ref: containerRef,
|
|
3929
|
-
className: "doc-player"
|
|
4220
|
+
className: `doc-player${swipeEnabled ? " doc-player--swipe" : ""}${swipe.phase === "dragging" ? " doc-player--grabbing" : ""}`,
|
|
3930
4221
|
onClick: handleContainerClick,
|
|
4222
|
+
onPointerDown: swipe.onPointerDown,
|
|
3931
4223
|
style: {
|
|
3932
4224
|
position: "relative",
|
|
3933
4225
|
width: "100%",
|
|
3934
4226
|
aspectRatio: `${activeViewport.width} / ${activeViewport.height}`,
|
|
3935
4227
|
margin: "0 auto",
|
|
3936
4228
|
overflow: "hidden",
|
|
3937
|
-
cursor
|
|
4229
|
+
// Swipe uses the grab/grabbing cursor via CSS classes; let vertical page
|
|
4230
|
+
// scroll through on touch while we own horizontal drags.
|
|
4231
|
+
cursor: renderMode || swipeEnabled ? void 0 : "pointer",
|
|
4232
|
+
touchAction: swipeEnabled ? "pan-y" : void 0
|
|
3938
4233
|
},
|
|
3939
4234
|
children: [
|
|
3940
4235
|
/* @__PURE__ */ jsx20("audio", { ref: audioRef, preload: "auto", muted }),
|
|
@@ -3974,21 +4269,29 @@ function DocPlayer({
|
|
|
3974
4269
|
viewport: activeViewport
|
|
3975
4270
|
}
|
|
3976
4271
|
) }, previousBlock.id),
|
|
3977
|
-
!showCoverBlock && currentBlock && /* @__PURE__ */ jsx20(
|
|
3978
|
-
|
|
4272
|
+
!showCoverBlock && currentBlock && /* @__PURE__ */ jsx20(
|
|
4273
|
+
"div",
|
|
3979
4274
|
{
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
4275
|
+
className: `doc-player__block doc-player__block--active${swipe.phase !== "idle" ? ` doc-player__block--${swipe.phase}` : ""}`,
|
|
4276
|
+
style: swipe.phase !== "idle" ? { transform: `translateX(${swipe.offsetPx}px)` } : void 0,
|
|
4277
|
+
children: /* @__PURE__ */ jsx20(
|
|
4278
|
+
BlockRenderer,
|
|
4279
|
+
{
|
|
4280
|
+
block: currentBlock,
|
|
4281
|
+
blockTime,
|
|
4282
|
+
basePath,
|
|
4283
|
+
isEntering,
|
|
4284
|
+
viewport: activeViewport,
|
|
4285
|
+
isPlaying
|
|
4286
|
+
}
|
|
4287
|
+
)
|
|
4288
|
+
},
|
|
4289
|
+
currentBlock.id
|
|
4290
|
+
),
|
|
3988
4291
|
hasCaptions && (renderMode ? captionsEnabled : true) && /* @__PURE__ */ jsx20(
|
|
3989
4292
|
CaptionOverlay,
|
|
3990
4293
|
{
|
|
3991
|
-
captions:
|
|
4294
|
+
captions: doc.captions,
|
|
3992
4295
|
currentTime,
|
|
3993
4296
|
enabled: captionsEnabled && (renderMode || isPlaying || currentTime > 0),
|
|
3994
4297
|
fontSize: 16,
|
|
@@ -4049,9 +4352,8 @@ function DocPlayer({
|
|
|
4049
4352
|
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
4050
4353
|
"(progress: ",
|
|
4051
4354
|
(docProgress * 100).toFixed(1),
|
|
4052
|
-
"%, scriptDur:",
|
|
4053
|
-
|
|
4054
|
-
script.duration.toFixed(1),
|
|
4355
|
+
"%, scriptDur: ",
|
|
4356
|
+
doc.duration.toFixed(1),
|
|
4055
4357
|
")"
|
|
4056
4358
|
] })
|
|
4057
4359
|
] }),
|
|
@@ -4069,11 +4371,11 @@ function DocPlayer({
|
|
|
4069
4371
|
" ",
|
|
4070
4372
|
currentSegment,
|
|
4071
4373
|
"/",
|
|
4072
|
-
|
|
4374
|
+
doc.audio.segments.length - 1,
|
|
4073
4375
|
" ",
|
|
4074
4376
|
/* @__PURE__ */ jsxs13("span", { style: { color: "#666" }, children: [
|
|
4075
4377
|
"(",
|
|
4076
|
-
|
|
4378
|
+
doc.audio.segments[currentSegment]?.name || "none",
|
|
4077
4379
|
")"
|
|
4078
4380
|
] })
|
|
4079
4381
|
] }),
|
|
@@ -4095,13 +4397,13 @@ function DocPlayer({
|
|
|
4095
4397
|
showCoverBlock && /* @__PURE__ */ jsx20("span", { style: { color: "#60a5fa" }, children: " (cover)" })
|
|
4096
4398
|
] }),
|
|
4097
4399
|
hasCaptions && (() => {
|
|
4098
|
-
const debugPhrase = getCaptionAtTime2(
|
|
4400
|
+
const debugPhrase = getCaptionAtTime2(doc.captions, currentTime);
|
|
4099
4401
|
const debugEnabled = captionsEnabled && (isPlaying || currentTime > 0);
|
|
4100
4402
|
return /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
4101
4403
|
/* @__PURE__ */ jsxs13("div", { children: [
|
|
4102
4404
|
/* @__PURE__ */ jsx20("span", { style: { color: "#888" }, children: "captions:" }),
|
|
4103
4405
|
" ",
|
|
4104
|
-
|
|
4406
|
+
doc.captions?.phrases.length || 0,
|
|
4105
4407
|
" phrases",
|
|
4106
4408
|
" ",
|
|
4107
4409
|
/* @__PURE__ */ jsxs13("span", { style: { color: captionsEnabled ? "#4ade80" : "#666" }, children: [
|
|
@@ -4331,7 +4633,7 @@ function DocControlsSidebar({ state, actions }) {
|
|
|
4331
4633
|
}
|
|
4332
4634
|
|
|
4333
4635
|
// src/DocPlayerWithSidebar.tsx
|
|
4334
|
-
import { useRef as
|
|
4636
|
+
import { useRef as useRef8, useState as useState8, useCallback as useCallback7, useEffect as useEffect9 } from "react";
|
|
4335
4637
|
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4336
4638
|
var DEFAULT_STATE = {
|
|
4337
4639
|
isPlaying: false,
|
|
@@ -4348,24 +4650,25 @@ var DEFAULT_STATE = {
|
|
|
4348
4650
|
currentBlock: null
|
|
4349
4651
|
};
|
|
4350
4652
|
function DocPlayerWithSidebar({
|
|
4351
|
-
|
|
4653
|
+
doc,
|
|
4352
4654
|
basePath,
|
|
4353
4655
|
autoPlay = false,
|
|
4354
4656
|
onEnded,
|
|
4355
4657
|
onTimeUpdate,
|
|
4356
|
-
|
|
4658
|
+
audioController,
|
|
4357
4659
|
muted,
|
|
4358
4660
|
captionsEnabled,
|
|
4359
4661
|
isFullscreen,
|
|
4360
4662
|
onFullscreenToggle,
|
|
4361
4663
|
forceViewport,
|
|
4362
|
-
onPlayingChange
|
|
4664
|
+
onPlayingChange,
|
|
4665
|
+
theme
|
|
4363
4666
|
}) {
|
|
4364
|
-
const stateRef =
|
|
4365
|
-
const actionsRef =
|
|
4366
|
-
const wasPlayingRef =
|
|
4367
|
-
const [, setTick] =
|
|
4368
|
-
const handleStateChange =
|
|
4667
|
+
const stateRef = useRef8(DEFAULT_STATE);
|
|
4668
|
+
const actionsRef = useRef8(null);
|
|
4669
|
+
const wasPlayingRef = useRef8(false);
|
|
4670
|
+
const [, setTick] = useState8(0);
|
|
4671
|
+
const handleStateChange = useCallback7(
|
|
4369
4672
|
(state) => {
|
|
4370
4673
|
stateRef.current = state;
|
|
4371
4674
|
if (onPlayingChange && state.isPlaying !== wasPlayingRef.current) {
|
|
@@ -4375,7 +4678,7 @@ function DocPlayerWithSidebar({
|
|
|
4375
4678
|
},
|
|
4376
4679
|
[onPlayingChange]
|
|
4377
4680
|
);
|
|
4378
|
-
const handleControlsReady =
|
|
4681
|
+
const handleControlsReady = useCallback7(
|
|
4379
4682
|
(controls) => {
|
|
4380
4683
|
const isFirst = !actionsRef.current;
|
|
4381
4684
|
actionsRef.current = controls;
|
|
@@ -4383,7 +4686,7 @@ function DocPlayerWithSidebar({
|
|
|
4383
4686
|
},
|
|
4384
4687
|
[]
|
|
4385
4688
|
);
|
|
4386
|
-
|
|
4689
|
+
useEffect9(() => {
|
|
4387
4690
|
const interval = setInterval(() => {
|
|
4388
4691
|
setTick((t) => t + 1);
|
|
4389
4692
|
}, 250);
|
|
@@ -4393,12 +4696,13 @@ function DocPlayerWithSidebar({
|
|
|
4393
4696
|
/* @__PURE__ */ jsx23("div", { className: "doc-player-sidebar-layout__video", children: /* @__PURE__ */ jsx23(
|
|
4394
4697
|
DocPlayer,
|
|
4395
4698
|
{
|
|
4396
|
-
|
|
4699
|
+
doc,
|
|
4700
|
+
theme,
|
|
4397
4701
|
basePath,
|
|
4398
4702
|
autoPlay,
|
|
4399
4703
|
onEnded,
|
|
4400
4704
|
onTimeUpdate,
|
|
4401
|
-
|
|
4705
|
+
audioController,
|
|
4402
4706
|
muted,
|
|
4403
4707
|
captionsEnabled,
|
|
4404
4708
|
showControls: isFullscreen,
|
|
@@ -4416,36 +4720,15 @@ function DocPlayerWithSidebar({
|
|
|
4416
4720
|
|
|
4417
4721
|
// src/jsonView/useJsonViewTokens.ts
|
|
4418
4722
|
import { useMemo as useMemo10 } from "react";
|
|
4419
|
-
import {
|
|
4420
|
-
applySurface as applySurface3,
|
|
4421
|
-
resolveFontFamily as resolveFontFamily3
|
|
4422
|
-
} from "@bendyline/squisq/schemas";
|
|
4423
|
-
import { DEFAULT_THEME as DEFAULT_THEME4 } from "@bendyline/squisq/doc";
|
|
4723
|
+
import { buildJsonFormTokens, resolveJsonFormTheme } from "@bendyline/squisq/jsonForm";
|
|
4424
4724
|
function useJsonViewTokens(theme, surface) {
|
|
4425
4725
|
const auto = useAutoSurface(surface === "auto");
|
|
4426
4726
|
const effectiveSurface = surface === "auto" ? auto : surface ?? void 0;
|
|
4427
4727
|
return useMemo10(() => {
|
|
4428
|
-
const
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
const monoFont = resolveFontFamily3(
|
|
4433
|
-
finalTheme.typography.monoFont,
|
|
4434
|
-
"ui-monospace, Consolas, monospace"
|
|
4435
|
-
);
|
|
4436
|
-
const style = {
|
|
4437
|
-
["--squisq-json-bg"]: finalTheme.colors.background,
|
|
4438
|
-
["--squisq-json-text"]: finalTheme.colors.text,
|
|
4439
|
-
["--squisq-json-muted"]: finalTheme.colors.textMuted,
|
|
4440
|
-
["--squisq-json-primary"]: finalTheme.colors.primary,
|
|
4441
|
-
["--squisq-json-accent"]: finalTheme.colors.secondary,
|
|
4442
|
-
["--squisq-json-border"]: `color-mix(in srgb, ${finalTheme.colors.textMuted} 35%, transparent)`,
|
|
4443
|
-
["--squisq-json-title-font"]: titleFont,
|
|
4444
|
-
["--squisq-json-body-font"]: bodyFont,
|
|
4445
|
-
["--squisq-json-mono-font"]: monoFont,
|
|
4446
|
-
["--squisq-json-radius"]: `${finalTheme.style.borderRadius ?? 8}px`
|
|
4447
|
-
};
|
|
4448
|
-
return { style, theme: finalTheme };
|
|
4728
|
+
const style = buildJsonFormTokens(theme, effectiveSurface, {
|
|
4729
|
+
prefix: "--squisq-json"
|
|
4730
|
+
});
|
|
4731
|
+
return { style, theme: resolveJsonFormTheme(theme, effectiveSurface) };
|
|
4449
4732
|
}, [theme, effectiveSurface]);
|
|
4450
4733
|
}
|
|
4451
4734
|
|
|
@@ -4461,7 +4744,7 @@ import { Fragment as Fragment4, useMemo as useMemo11 } from "react";
|
|
|
4461
4744
|
import {
|
|
4462
4745
|
arrayItemKind
|
|
4463
4746
|
} from "@bendyline/squisq/jsonForm";
|
|
4464
|
-
import { parseMarkdown } from "@bendyline/squisq/markdown";
|
|
4747
|
+
import { parseMarkdown as parseMarkdown3 } from "@bendyline/squisq/markdown";
|
|
4465
4748
|
import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4466
4749
|
function TextViewer({ value }) {
|
|
4467
4750
|
if (value === void 0 || value === null || value === "") {
|
|
@@ -4479,7 +4762,7 @@ function RichTextViewer({ value }) {
|
|
|
4479
4762
|
const nodes = useMemo11(() => {
|
|
4480
4763
|
if (typeof value !== "string" || value === "") return null;
|
|
4481
4764
|
try {
|
|
4482
|
-
const doc =
|
|
4765
|
+
const doc = parseMarkdown3(value);
|
|
4483
4766
|
return doc.children;
|
|
4484
4767
|
} catch {
|
|
4485
4768
|
return null;
|