@bendyline/squisq-editor-react 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +75 -33
- package/dist/index.js +1717 -935
- package/dist/index.js.map +1 -1
- package/dist/styles/index.css +145 -5
- package/package.json +4 -4
- package/src/DocumentSettingsDialog.tsx +32 -18
- package/src/EditorShell.tsx +1 -1
- package/src/PreviewControls.tsx +57 -24
- package/src/RecorderEntry.tsx +2 -0
- package/src/Toolbar.tsx +164 -19
- package/src/__tests__/codeContextSectionView.test.tsx +8 -6
- package/src/__tests__/documentSettingsDialog.test.tsx +22 -0
- package/src/__tests__/mediaAttachmentFlow.test.ts +2 -2
- package/src/__tests__/previewControls.test.tsx +164 -0
- package/src/__tests__/recorderTheme.test.tsx +42 -0
- package/src/__tests__/selectionConversions.test.ts +80 -0
- package/src/__tests__/tiptapBridge.test.ts +48 -7
- package/src/__tests__/tiptapImageRoundTrip.test.ts +1 -1
- package/src/__tests__/toolbarSelectionConversion.test.tsx +164 -0
- package/src/asciiDiagram/AsciiDiagramWidget.tsx +34 -4
- package/src/asciiDiagram/__tests__/asciiDiagramCommands.test.ts +58 -1
- package/src/asciiDiagram/asciiDiagramCommands.ts +36 -10
- package/src/asciiDiagram/asciiDiagramData.ts +33 -0
- package/src/asciiDiagram/asciiDiagramOps.ts +19 -0
- package/src/codeContext/types.ts +1 -1
- package/src/customTemplates/__tests__/useMemoryLayerAdapter.test.ts +13 -0
- package/src/customTemplates/useMemoryLayerAdapter.ts +7 -1
- package/src/diagram/DiagramCanvas.tsx +16 -2
- package/src/frontmatterSettings.ts +23 -0
- package/src/index.ts +7 -1
- package/src/recorder/RecorderButton.tsx +9 -1
- package/src/recorder/RecorderModal.tsx +84 -41
- package/src/recorder/RecorderPanel.tsx +9 -1
- package/src/scene/__tests__/sceneIsolation.test.tsx +27 -1
- package/src/scene/adapters/DrawingAdapter.ts +6 -1
- package/src/scene/adapters/LayoutAdapter.ts +3 -0
- package/src/scene/commands/SceneCommand.ts +13 -2
- package/src/scene/tools/SelectTool.ts +5 -5
- package/src/selectionConversions.ts +155 -0
- package/src/styles/ascii-timeline.css +101 -4
- package/src/styles/editor.css +30 -0
- package/src/styles/tree-view.css +51 -1
- package/src/timeline/TimelineEditorWidget.tsx +200 -41
- package/src/timeline/__tests__/TimelineEditorWidget.test.tsx +61 -2
- package/src/timeline/__tests__/timelineCommands.test.ts +32 -0
- package/src/timeline/__tests__/timelineOps.test.ts +55 -0
- package/src/timeline/timelineCommands.ts +54 -0
- package/src/timeline/timelineOps.ts +107 -3
- package/src/tiptapBridge.ts +23 -5
- package/src/treeview/TreeOutlineWidget.tsx +153 -3
- package/src/treeview/__tests__/TreeOutlineWidget.test.tsx +156 -0
- package/src/treeview/__tests__/treeOps.test.ts +52 -0
- package/src/treeview/__tests__/treeViewCommands.test.ts +16 -0
- package/src/treeview/treeOps.ts +59 -0
- package/src/treeview/treeViewCommands.ts +5 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/EditorShell.tsx
|
|
2
|
-
import { useEffect as useEffect61, useRef as
|
|
2
|
+
import { useEffect as useEffect61, useRef as useRef61, useState as useState70, useCallback as useCallback56, useMemo as useMemo47 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/EditorContext.tsx
|
|
5
5
|
import {
|
|
@@ -204,7 +204,14 @@ function markdownToTiptap(markdown) {
|
|
|
204
204
|
}
|
|
205
205
|
if (line.startsWith("> ")) {
|
|
206
206
|
flushList();
|
|
207
|
-
|
|
207
|
+
const quoteLines = [line.slice(2)];
|
|
208
|
+
while (i + 1 < lines.length && lines[i + 1].startsWith("> ")) {
|
|
209
|
+
i++;
|
|
210
|
+
quoteLines.push(lines[i].slice(2));
|
|
211
|
+
}
|
|
212
|
+
pushBlock(
|
|
213
|
+
`<blockquote>${quoteLines.map((quoteLine) => `<p>${inlineToHtml(quoteLine)}</p>`).join("")}</blockquote>`
|
|
214
|
+
);
|
|
208
215
|
continue;
|
|
209
216
|
}
|
|
210
217
|
const taskMatch = line.match(/^[-*+]\s+\[([xX ])\]\s*(.*)$/);
|
|
@@ -329,10 +336,15 @@ function tiptapToMarkdown(html) {
|
|
|
329
336
|
}
|
|
330
337
|
const bqMatch = remaining.match(/^<blockquote>(.*?)<\/blockquote>/s);
|
|
331
338
|
if (bqMatch) {
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
339
|
+
const paragraphs = bqMatch[1].split(/<\/p>\s*<p[^>]*>/i).map((paragraph) => paragraph.replace(/^<p[^>]*>/i, "").replace(/<\/p>\s*$/i, ""));
|
|
340
|
+
for (const paragraph of paragraphs) {
|
|
341
|
+
for (const quoteLine of htmlToInline(paragraph).split("\n")) {
|
|
342
|
+
lines.push("> " + quoteLine);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const next = remaining.slice(bqMatch[0].length);
|
|
346
|
+
if (!/^\s*<blockquote>/.test(next)) lines.push("");
|
|
347
|
+
remaining = next;
|
|
336
348
|
continue;
|
|
337
349
|
}
|
|
338
350
|
if (remaining.startsWith("<hr>") || remaining.startsWith("<hr/>") || remaining.startsWith("<hr />")) {
|
|
@@ -346,11 +358,11 @@ function tiptapToMarkdown(html) {
|
|
|
346
358
|
/^<div[^>]*class="[^"]*tableWrapper[^"]*"[^>]*><table[^>]*>(.*?)<\/table>\s*<\/div>/s
|
|
347
359
|
) || remaining.match(/^<table[^>]*>(.*?)<\/table>/s);
|
|
348
360
|
if (tableMatch) {
|
|
349
|
-
const
|
|
361
|
+
const tableContent2 = tableMatch[1];
|
|
350
362
|
const rows = [];
|
|
351
363
|
const rowRegex = /<tr[^>]*>(.*?)<\/tr>/gs;
|
|
352
364
|
let rowExec;
|
|
353
|
-
while ((rowExec = rowRegex.exec(
|
|
365
|
+
while ((rowExec = rowRegex.exec(tableContent2)) !== null) {
|
|
354
366
|
const rowHtml = rowExec[1];
|
|
355
367
|
const cells = [];
|
|
356
368
|
const cellRegex = /<(th|td)([^>]*)>(.*?)<\/\1>/gs;
|
|
@@ -2357,9 +2369,27 @@ var overlayStyle = {
|
|
|
2357
2369
|
justifyContent: "center",
|
|
2358
2370
|
zIndex: 1e4
|
|
2359
2371
|
};
|
|
2372
|
+
function recorderThemeStyle(colorScheme) {
|
|
2373
|
+
const dark = colorScheme === "dark";
|
|
2374
|
+
return {
|
|
2375
|
+
colorScheme,
|
|
2376
|
+
"--squisq-recorder-surface": `var(--squisq-bg, ${dark ? "#1f2937" : "#fffdf7"})`,
|
|
2377
|
+
"--squisq-recorder-input": `var(--squisq-input-bg, ${dark ? "#374151" : "#fff"})`,
|
|
2378
|
+
"--squisq-recorder-border": `var(--squisq-border, ${dark ? "#4b5563" : "#c9b98a"})`,
|
|
2379
|
+
"--squisq-recorder-text": `var(--squisq-text, ${dark ? "#e5e7eb" : "#4a3c1f"})`,
|
|
2380
|
+
"--squisq-recorder-muted": `var(--squisq-text-muted, ${dark ? "#9ca3af" : "#5a4a2a"})`,
|
|
2381
|
+
"--squisq-recorder-accent": "var(--squisq-accent, #8b6914)",
|
|
2382
|
+
"--squisq-recorder-accent-text": "#fff",
|
|
2383
|
+
"--squisq-recorder-danger": dark ? "#dc4c4c" : "#b33a3a",
|
|
2384
|
+
"--squisq-recorder-danger-border": dark ? "#ef6a6a" : "#902929",
|
|
2385
|
+
"--squisq-recorder-error-bg": dark ? "#3f151b" : "#fceeee",
|
|
2386
|
+
"--squisq-recorder-error-border": dark ? "#7f1d1d" : "#d88a8a",
|
|
2387
|
+
"--squisq-recorder-error-text": dark ? "#fecdd3" : "#8c2a2a"
|
|
2388
|
+
};
|
|
2389
|
+
}
|
|
2360
2390
|
var modalStyle = {
|
|
2361
|
-
background: "
|
|
2362
|
-
border: "1px solid
|
|
2391
|
+
background: "var(--squisq-recorder-surface)",
|
|
2392
|
+
border: "1px solid var(--squisq-recorder-border)",
|
|
2363
2393
|
borderRadius: 0,
|
|
2364
2394
|
padding: "24px 28px",
|
|
2365
2395
|
width: "min(560px, calc(100vw - 48px))",
|
|
@@ -2367,30 +2397,30 @@ var modalStyle = {
|
|
|
2367
2397
|
overflowY: "auto",
|
|
2368
2398
|
boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
|
|
2369
2399
|
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
2370
|
-
color: "
|
|
2400
|
+
color: "var(--squisq-recorder-text)"
|
|
2371
2401
|
};
|
|
2372
2402
|
var titleStyle = {
|
|
2373
2403
|
margin: "0 0 16px 0",
|
|
2374
2404
|
fontSize: 18,
|
|
2375
2405
|
fontWeight: 600,
|
|
2376
|
-
color: "
|
|
2406
|
+
color: "var(--squisq-recorder-text)"
|
|
2377
2407
|
};
|
|
2378
2408
|
var labelStyle = {
|
|
2379
2409
|
display: "block",
|
|
2380
2410
|
fontSize: 13,
|
|
2381
2411
|
fontWeight: 500,
|
|
2382
2412
|
marginBottom: 4,
|
|
2383
|
-
color: "
|
|
2413
|
+
color: "var(--squisq-recorder-text)"
|
|
2384
2414
|
};
|
|
2385
2415
|
var inputStyle = {
|
|
2386
2416
|
width: "100%",
|
|
2387
2417
|
padding: "6px 8px",
|
|
2388
2418
|
fontSize: 13,
|
|
2389
2419
|
fontFamily: "inherit",
|
|
2390
|
-
border: "1px solid
|
|
2420
|
+
border: "1px solid var(--squisq-recorder-border)",
|
|
2391
2421
|
borderRadius: 0,
|
|
2392
|
-
background: "
|
|
2393
|
-
color: "
|
|
2422
|
+
background: "var(--squisq-recorder-input)",
|
|
2423
|
+
color: "var(--squisq-recorder-text)",
|
|
2394
2424
|
marginBottom: 12,
|
|
2395
2425
|
boxSizing: "border-box"
|
|
2396
2426
|
};
|
|
@@ -2405,9 +2435,9 @@ var btnPrimary = {
|
|
|
2405
2435
|
fontFamily: "inherit",
|
|
2406
2436
|
fontWeight: 500,
|
|
2407
2437
|
cursor: "pointer",
|
|
2408
|
-
background: "
|
|
2409
|
-
color: "
|
|
2410
|
-
border: "1px solid
|
|
2438
|
+
background: "var(--squisq-recorder-accent)",
|
|
2439
|
+
color: "var(--squisq-recorder-accent-text)",
|
|
2440
|
+
border: "1px solid var(--squisq-recorder-accent)",
|
|
2411
2441
|
borderRadius: 0
|
|
2412
2442
|
};
|
|
2413
2443
|
var btnSecondary = {
|
|
@@ -2416,15 +2446,15 @@ var btnSecondary = {
|
|
|
2416
2446
|
fontFamily: "inherit",
|
|
2417
2447
|
fontWeight: 500,
|
|
2418
2448
|
cursor: "pointer",
|
|
2419
|
-
background: "
|
|
2420
|
-
color: "
|
|
2421
|
-
border: "1px solid
|
|
2449
|
+
background: "var(--squisq-recorder-input)",
|
|
2450
|
+
color: "var(--squisq-recorder-text)",
|
|
2451
|
+
border: "1px solid var(--squisq-recorder-border)",
|
|
2422
2452
|
borderRadius: 0
|
|
2423
2453
|
};
|
|
2424
2454
|
var btnDanger = {
|
|
2425
2455
|
...btnPrimary,
|
|
2426
|
-
background: "
|
|
2427
|
-
borderColor: "
|
|
2456
|
+
background: "var(--squisq-recorder-danger)",
|
|
2457
|
+
borderColor: "var(--squisq-recorder-danger-border)"
|
|
2428
2458
|
};
|
|
2429
2459
|
var toggleRowStyle = {
|
|
2430
2460
|
display: "flex",
|
|
@@ -2437,16 +2467,16 @@ var toggleBase = {
|
|
|
2437
2467
|
fontFamily: "inherit",
|
|
2438
2468
|
cursor: "pointer",
|
|
2439
2469
|
background: "transparent",
|
|
2440
|
-
color: "
|
|
2441
|
-
border: "1px solid
|
|
2470
|
+
color: "var(--squisq-recorder-text)",
|
|
2471
|
+
border: "1px solid var(--squisq-recorder-border)",
|
|
2442
2472
|
borderRadius: 999
|
|
2443
2473
|
};
|
|
2444
2474
|
var toggleActive = {
|
|
2445
2475
|
...toggleBase,
|
|
2446
|
-
color: "
|
|
2476
|
+
color: "var(--squisq-recorder-accent-text)",
|
|
2447
2477
|
fontWeight: 600,
|
|
2448
|
-
background: "
|
|
2449
|
-
borderColor: "
|
|
2478
|
+
background: "var(--squisq-recorder-accent)",
|
|
2479
|
+
borderColor: "var(--squisq-recorder-accent)"
|
|
2450
2480
|
};
|
|
2451
2481
|
var previewBoxStyle = {
|
|
2452
2482
|
width: "100%",
|
|
@@ -2464,20 +2494,20 @@ var previewBoxStyle = {
|
|
|
2464
2494
|
var audioMeterStyle = {
|
|
2465
2495
|
width: "100%",
|
|
2466
2496
|
height: 56,
|
|
2467
|
-
background: "
|
|
2468
|
-
border: "1px solid
|
|
2497
|
+
background: "var(--squisq-recorder-input)",
|
|
2498
|
+
border: "1px solid var(--squisq-recorder-border)",
|
|
2469
2499
|
marginBottom: 12,
|
|
2470
2500
|
display: "flex",
|
|
2471
2501
|
alignItems: "center",
|
|
2472
2502
|
justifyContent: "center",
|
|
2473
|
-
color: "
|
|
2503
|
+
color: "var(--squisq-recorder-muted)",
|
|
2474
2504
|
fontSize: 13,
|
|
2475
2505
|
fontVariantNumeric: "tabular-nums"
|
|
2476
2506
|
};
|
|
2477
2507
|
var errorStyle = {
|
|
2478
|
-
background: "
|
|
2479
|
-
border: "1px solid
|
|
2480
|
-
color: "
|
|
2508
|
+
background: "var(--squisq-recorder-error-bg)",
|
|
2509
|
+
border: "1px solid var(--squisq-recorder-error-border)",
|
|
2510
|
+
color: "var(--squisq-recorder-error-text)",
|
|
2481
2511
|
padding: "8px 10px",
|
|
2482
2512
|
fontSize: 13,
|
|
2483
2513
|
marginBottom: 12
|
|
@@ -2488,6 +2518,18 @@ var buttonRowStyle = {
|
|
|
2488
2518
|
justifyContent: "flex-end",
|
|
2489
2519
|
marginTop: 8
|
|
2490
2520
|
};
|
|
2521
|
+
var summaryStyle = {
|
|
2522
|
+
margin: "0 0 12px 0",
|
|
2523
|
+
fontSize: 12,
|
|
2524
|
+
color: "var(--squisq-recorder-muted)"
|
|
2525
|
+
};
|
|
2526
|
+
var recordingStatusStyle = {
|
|
2527
|
+
fontSize: 13,
|
|
2528
|
+
fontVariantNumeric: "tabular-nums",
|
|
2529
|
+
marginBottom: 12,
|
|
2530
|
+
color: "var(--squisq-recorder-accent)",
|
|
2531
|
+
fontWeight: 600
|
|
2532
|
+
};
|
|
2491
2533
|
function formatDurationMs(ms) {
|
|
2492
2534
|
const totalSec = Math.floor(ms / 1e3);
|
|
2493
2535
|
const m = Math.floor(totalSec / 60);
|
|
@@ -2529,6 +2571,7 @@ function RecorderModal({
|
|
|
2529
2571
|
mediaProvider,
|
|
2530
2572
|
container = null,
|
|
2531
2573
|
initialMode = "mic",
|
|
2574
|
+
colorScheme = "light",
|
|
2532
2575
|
onClose,
|
|
2533
2576
|
onSave
|
|
2534
2577
|
}) {
|
|
@@ -2663,144 +2706,144 @@ function RecorderModal({
|
|
|
2663
2706
|
setVideo((v) => v === key ? "none" : key);
|
|
2664
2707
|
}
|
|
2665
2708
|
};
|
|
2666
|
-
return /* @__PURE__ */ jsx4(
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
}
|
|
2729
|
-
)
|
|
2730
|
-
] }),
|
|
2731
|
-
video === "screen" && /* @__PURE__ */ jsxs2(
|
|
2732
|
-
"label",
|
|
2733
|
-
{
|
|
2734
|
-
style: {
|
|
2735
|
-
display: "flex",
|
|
2736
|
-
alignItems: "center",
|
|
2737
|
-
gap: 6,
|
|
2738
|
-
marginBottom: 12,
|
|
2739
|
-
fontSize: 13
|
|
2740
|
-
},
|
|
2741
|
-
children: [
|
|
2709
|
+
return /* @__PURE__ */ jsx4(
|
|
2710
|
+
"div",
|
|
2711
|
+
{
|
|
2712
|
+
className: "squisq-editor-shell squisq-recorder-overlay",
|
|
2713
|
+
"data-theme": colorScheme,
|
|
2714
|
+
style: { ...overlayStyle, ...recorderThemeStyle(colorScheme) },
|
|
2715
|
+
role: "dialog",
|
|
2716
|
+
"aria-modal": "true",
|
|
2717
|
+
"aria-label": "Record media",
|
|
2718
|
+
children: /* @__PURE__ */ jsxs2("div", { style: modalStyle, onClick: (e2) => e2.stopPropagation(), children: [
|
|
2719
|
+
/* @__PURE__ */ jsx4("h2", { style: titleStyle, children: "Record media" }),
|
|
2720
|
+
/* @__PURE__ */ jsx4("div", { style: toggleRowStyle, role: "group", "aria-label": "Capture sources", children: TOGGLES.map((t) => {
|
|
2721
|
+
const active = toggleActiveFor(t.key);
|
|
2722
|
+
return /* @__PURE__ */ jsx4(
|
|
2723
|
+
"button",
|
|
2724
|
+
{
|
|
2725
|
+
type: "button",
|
|
2726
|
+
"aria-pressed": active,
|
|
2727
|
+
style: active ? toggleActive : toggleBase,
|
|
2728
|
+
onClick: () => onToggle(t.key),
|
|
2729
|
+
disabled: togglesLocked,
|
|
2730
|
+
children: t.label
|
|
2731
|
+
},
|
|
2732
|
+
t.key
|
|
2733
|
+
);
|
|
2734
|
+
}) }),
|
|
2735
|
+
/* @__PURE__ */ jsx4("p", { style: summaryStyle, children: captureSummary(micOn, video) }),
|
|
2736
|
+
recorder.error && /* @__PURE__ */ jsx4("div", { style: errorStyle, children: recorder.error.message }),
|
|
2737
|
+
saveError && /* @__PURE__ */ jsx4("div", { style: errorStyle, children: saveError }),
|
|
2738
|
+
!showPreview && /* @__PURE__ */ jsx4("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx4("span", { children: "Click Start Preview to start a recording." }) }),
|
|
2739
|
+
showPreview && recorder.state !== "stopped" && !isAudioOnly && /* @__PURE__ */ jsx4("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx4(
|
|
2740
|
+
"video",
|
|
2741
|
+
{
|
|
2742
|
+
ref: previewRef,
|
|
2743
|
+
autoPlay: true,
|
|
2744
|
+
muted: true,
|
|
2745
|
+
playsInline: true,
|
|
2746
|
+
style: { width: "100%", height: "100%", objectFit: "contain" }
|
|
2747
|
+
}
|
|
2748
|
+
) }),
|
|
2749
|
+
showPreview && recorder.state !== "stopped" && isAudioOnly && /* @__PURE__ */ jsx4("div", { style: audioMeterStyle, children: recorder.state === "recording" ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
2750
|
+
"\u25CF Recording ",
|
|
2751
|
+
formatDurationMs(recorder.durationMs)
|
|
2752
|
+
] }) : /* @__PURE__ */ jsx4(Fragment, { children: "Microphone ready" }) }),
|
|
2753
|
+
recorder.state === "stopped" && playbackUrl && !isAudioOnly && /* @__PURE__ */ jsx4("div", { style: previewBoxStyle, children: /* @__PURE__ */ jsx4(
|
|
2754
|
+
"video",
|
|
2755
|
+
{
|
|
2756
|
+
src: playbackUrl,
|
|
2757
|
+
controls: true,
|
|
2758
|
+
playsInline: true,
|
|
2759
|
+
style: { width: "100%", height: "100%", objectFit: "contain" }
|
|
2760
|
+
}
|
|
2761
|
+
) }),
|
|
2762
|
+
recorder.state === "stopped" && playbackUrl && isAudioOnly && /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 12 }, children: [
|
|
2763
|
+
/* @__PURE__ */ jsxs2("div", { style: { ...audioMeterStyle, marginBottom: 8 }, children: [
|
|
2764
|
+
"\u2713 Recorded ",
|
|
2765
|
+
formatDurationMs(recorder.durationMs)
|
|
2766
|
+
] }),
|
|
2767
|
+
/* @__PURE__ */ jsx4("audio", { src: playbackUrl, controls: true, style: { width: "100%" } })
|
|
2768
|
+
] }),
|
|
2769
|
+
source === "mic" && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
2770
|
+
/* @__PURE__ */ jsx4("label", { style: labelStyle, htmlFor: "recorder-source-text", children: "Script (used to auto-match this narration to a block)" }),
|
|
2742
2771
|
/* @__PURE__ */ jsx4(
|
|
2743
|
-
"
|
|
2772
|
+
"textarea",
|
|
2744
2773
|
{
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2774
|
+
id: "recorder-source-text",
|
|
2775
|
+
style: textareaStyle,
|
|
2776
|
+
placeholder: "Type the text you're going to read aloud.",
|
|
2777
|
+
value: sourceText,
|
|
2778
|
+
onChange: (e2) => setSourceText(e2.target.value),
|
|
2779
|
+
disabled: recorder.state === "recording"
|
|
2749
2780
|
}
|
|
2750
|
-
)
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
},
|
|
2778
|
-
|
|
2781
|
+
)
|
|
2782
|
+
] }),
|
|
2783
|
+
video === "screen" && /* @__PURE__ */ jsxs2(
|
|
2784
|
+
"label",
|
|
2785
|
+
{
|
|
2786
|
+
style: {
|
|
2787
|
+
display: "flex",
|
|
2788
|
+
alignItems: "center",
|
|
2789
|
+
gap: 6,
|
|
2790
|
+
marginBottom: 12,
|
|
2791
|
+
fontSize: 13
|
|
2792
|
+
},
|
|
2793
|
+
children: [
|
|
2794
|
+
/* @__PURE__ */ jsx4(
|
|
2795
|
+
"input",
|
|
2796
|
+
{
|
|
2797
|
+
type: "checkbox",
|
|
2798
|
+
style: { accentColor: "var(--squisq-recorder-accent)" },
|
|
2799
|
+
checked: includeSystemAudio,
|
|
2800
|
+
onChange: (e2) => setIncludeSystemAudio(e2.target.checked),
|
|
2801
|
+
disabled: recorder.state === "recording" || recorder.state === "requesting"
|
|
2802
|
+
}
|
|
2803
|
+
),
|
|
2804
|
+
"Include system audio (Chrome only)"
|
|
2805
|
+
]
|
|
2806
|
+
}
|
|
2807
|
+
),
|
|
2808
|
+
/* @__PURE__ */ jsx4("label", { style: labelStyle, htmlFor: "recorder-basename", children: "Filename (optional)" }),
|
|
2809
|
+
/* @__PURE__ */ jsx4(
|
|
2810
|
+
"input",
|
|
2811
|
+
{
|
|
2812
|
+
id: "recorder-basename",
|
|
2813
|
+
type: "text",
|
|
2814
|
+
style: inputStyle,
|
|
2815
|
+
placeholder: source === "mic" ? "narration" : "recording",
|
|
2816
|
+
value: basename,
|
|
2817
|
+
onChange: (e2) => setBasename(e2.target.value),
|
|
2818
|
+
disabled: recorder.state === "recording"
|
|
2819
|
+
}
|
|
2820
|
+
),
|
|
2821
|
+
recorder.state === "recording" && !isAudioOnly && /* @__PURE__ */ jsxs2("div", { style: recordingStatusStyle, children: [
|
|
2779
2822
|
"\u25CF Recording ",
|
|
2780
2823
|
formatDurationMs(recorder.durationMs)
|
|
2781
|
-
]
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2824
|
+
] }),
|
|
2825
|
+
/* @__PURE__ */ jsxs2("div", { style: buttonRowStyle, children: [
|
|
2826
|
+
/* @__PURE__ */ jsx4("button", { type: "button", style: btnSecondary, onClick: handleClose, disabled: isBusy, children: "Close" }),
|
|
2827
|
+
(recorder.state === "idle" || recorder.state === "error" || recorder.state === "requesting") && /* @__PURE__ */ jsx4(
|
|
2828
|
+
"button",
|
|
2829
|
+
{
|
|
2830
|
+
type: "button",
|
|
2831
|
+
style: btnPrimary,
|
|
2832
|
+
onClick: handleRequest,
|
|
2833
|
+
disabled: isBusy || !canCapture,
|
|
2834
|
+
children: recorder.state === "requesting" ? "Requesting\u2026" : "Start preview"
|
|
2835
|
+
}
|
|
2836
|
+
),
|
|
2837
|
+
canRecord && /* @__PURE__ */ jsx4("button", { type: "button", style: btnPrimary, onClick: handleStart, disabled: isBusy, children: "Record" }),
|
|
2838
|
+
canStop && /* @__PURE__ */ jsx4("button", { type: "button", style: btnDanger, onClick: handleStop, disabled: isBusy, children: "Stop" }),
|
|
2839
|
+
canSave && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
2840
|
+
/* @__PURE__ */ jsx4("button", { type: "button", style: btnSecondary, onClick: handleDiscard, disabled: isBusy, children: "Discard & re-record" }),
|
|
2841
|
+
/* @__PURE__ */ jsx4("button", { type: "button", style: btnPrimary, onClick: handleSave, disabled: isBusy, children: isSaving ? "Saving\u2026" : "Save to document" })
|
|
2842
|
+
] })
|
|
2843
|
+
] })
|
|
2801
2844
|
] })
|
|
2802
|
-
|
|
2803
|
-
|
|
2845
|
+
}
|
|
2846
|
+
);
|
|
2804
2847
|
}
|
|
2805
2848
|
|
|
2806
2849
|
// src/recorder/RecorderPanel.tsx
|
|
@@ -2809,6 +2852,7 @@ function RecorderPanel({
|
|
|
2809
2852
|
mediaProvider,
|
|
2810
2853
|
container = null,
|
|
2811
2854
|
initialMode = "mic",
|
|
2855
|
+
colorScheme = "light",
|
|
2812
2856
|
onSave,
|
|
2813
2857
|
tooltip = "Record media",
|
|
2814
2858
|
className
|
|
@@ -2835,6 +2879,7 @@ function RecorderPanel({
|
|
|
2835
2879
|
mediaProvider,
|
|
2836
2880
|
container,
|
|
2837
2881
|
initialMode,
|
|
2882
|
+
colorScheme,
|
|
2838
2883
|
onClose: handleClose,
|
|
2839
2884
|
onSave: (result) => {
|
|
2840
2885
|
onSave?.(result);
|
|
@@ -2892,7 +2937,8 @@ function RecorderEntry() {
|
|
|
2892
2937
|
insertAtCursor,
|
|
2893
2938
|
bumpMediaRevision,
|
|
2894
2939
|
markdownSource,
|
|
2895
|
-
setMarkdownSource
|
|
2940
|
+
setMarkdownSource,
|
|
2941
|
+
colorScheme
|
|
2896
2942
|
} = useEditorContext();
|
|
2897
2943
|
const handleSave = useCallback7(
|
|
2898
2944
|
(result) => {
|
|
@@ -2953,6 +2999,7 @@ ${videoTag}` : videoTag);
|
|
|
2953
2999
|
{
|
|
2954
3000
|
mediaProvider,
|
|
2955
3001
|
container: workspaceContainer,
|
|
3002
|
+
colorScheme,
|
|
2956
3003
|
onSave: handleSave,
|
|
2957
3004
|
className: "squisq-toolbar-button"
|
|
2958
3005
|
}
|
|
@@ -5812,13 +5859,25 @@ function resolvePersistedTransformStyleId(value) {
|
|
|
5812
5859
|
return VALID_TRANSFORM_IDS.has(normalized) ? normalized : null;
|
|
5813
5860
|
}
|
|
5814
5861
|
|
|
5815
|
-
// src/
|
|
5816
|
-
|
|
5817
|
-
var FM = {
|
|
5862
|
+
// src/frontmatterSettings.ts
|
|
5863
|
+
var FRONTMATTER_SETTING_KEYS = {
|
|
5818
5864
|
theme: { canonical: "squisq-theme", legacy: ["themeId", "theme"] },
|
|
5819
5865
|
transform: { canonical: "squisq-transform", legacy: "transform-style" },
|
|
5820
|
-
captions: { canonical: "squisq-captions", legacy: "caption-style" }
|
|
5866
|
+
captions: { canonical: "squisq-captions", legacy: "caption-style" },
|
|
5867
|
+
coverSlide: { canonical: "squisq-cover-slide", legacy: "cover-slide" }
|
|
5821
5868
|
};
|
|
5869
|
+
var FRONTMATTER_SETTING_DEFAULTS = {
|
|
5870
|
+
theme: "standard",
|
|
5871
|
+
transform: "",
|
|
5872
|
+
captions: "standard",
|
|
5873
|
+
coverSlide: true
|
|
5874
|
+
};
|
|
5875
|
+
function omitFrontmatterDefault(value, defaultValue) {
|
|
5876
|
+
return value === defaultValue ? null : value;
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
// src/DocumentSettingsDialog.tsx
|
|
5880
|
+
import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
5822
5881
|
function readFm(fm, canonical, legacy) {
|
|
5823
5882
|
if (!fm) return "";
|
|
5824
5883
|
const v = Object.prototype.hasOwnProperty.call(fm, canonical) ? fm[canonical] : fm[legacy];
|
|
@@ -5840,9 +5899,17 @@ function DocumentSettingsDialog({
|
|
|
5840
5899
|
const currentTitle = typeof frontmatter?.title === "string" ? frontmatter.title : "";
|
|
5841
5900
|
const currentTheme = readFrontmatterThemeId(frontmatter) ?? "";
|
|
5842
5901
|
const currentTransform = resolvePersistedTransformStyleId(
|
|
5843
|
-
readFm(
|
|
5902
|
+
readFm(
|
|
5903
|
+
frontmatter,
|
|
5904
|
+
FRONTMATTER_SETTING_KEYS.transform.canonical,
|
|
5905
|
+
FRONTMATTER_SETTING_KEYS.transform.legacy
|
|
5906
|
+
)
|
|
5844
5907
|
) ?? "";
|
|
5845
|
-
const currentCaptions = readFm(
|
|
5908
|
+
const currentCaptions = readFm(
|
|
5909
|
+
frontmatter,
|
|
5910
|
+
FRONTMATTER_SETTING_KEYS.captions.canonical,
|
|
5911
|
+
FRONTMATTER_SETTING_KEYS.captions.legacy
|
|
5912
|
+
);
|
|
5846
5913
|
const [title, setTitle] = useState14(currentTitle);
|
|
5847
5914
|
const [theme, setTheme] = useState14(currentTheme);
|
|
5848
5915
|
const [transform, setTransform] = useState14(currentTransform);
|
|
@@ -5880,19 +5947,28 @@ function DocumentSettingsDialog({
|
|
|
5880
5947
|
const nextTitle = !trimmedTitle || titleMatchesInferred ? null : trimmedTitle;
|
|
5881
5948
|
const updates = {
|
|
5882
5949
|
title: nextTitle,
|
|
5883
|
-
[
|
|
5950
|
+
[FRONTMATTER_SETTING_KEYS.theme.canonical]: omitFrontmatterDefault(
|
|
5951
|
+
theme || FRONTMATTER_SETTING_DEFAULTS.theme,
|
|
5952
|
+
FRONTMATTER_SETTING_DEFAULTS.theme
|
|
5953
|
+
),
|
|
5884
5954
|
// Saving settings canonicalizes all older theme spellings.
|
|
5885
|
-
[
|
|
5886
|
-
[
|
|
5887
|
-
[
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5955
|
+
[FRONTMATTER_SETTING_KEYS.theme.legacy[0]]: null,
|
|
5956
|
+
[FRONTMATTER_SETTING_KEYS.theme.legacy[1]]: null,
|
|
5957
|
+
[FRONTMATTER_SETTING_KEYS.transform.canonical]: omitFrontmatterDefault(
|
|
5958
|
+
transform,
|
|
5959
|
+
FRONTMATTER_SETTING_DEFAULTS.transform
|
|
5960
|
+
),
|
|
5961
|
+
[FRONTMATTER_SETTING_KEYS.transform.legacy]: null,
|
|
5962
|
+
[FRONTMATTER_SETTING_KEYS.captions.canonical]: omitFrontmatterDefault(
|
|
5963
|
+
captions || FRONTMATTER_SETTING_DEFAULTS.captions,
|
|
5964
|
+
FRONTMATTER_SETTING_DEFAULTS.captions
|
|
5965
|
+
),
|
|
5966
|
+
[FRONTMATTER_SETTING_KEYS.captions.legacy]: null
|
|
5891
5967
|
};
|
|
5892
5968
|
const nextSource = setFrontmatterValues(markdownSource, updates);
|
|
5893
5969
|
onSave(nextSource);
|
|
5894
5970
|
},
|
|
5895
|
-
[title, theme, transform, captions, inferredTitle, markdownSource,
|
|
5971
|
+
[title, theme, transform, captions, inferredTitle, markdownSource, onSave]
|
|
5896
5972
|
);
|
|
5897
5973
|
return /* @__PURE__ */ jsx16("div", { className: "squisq-doc-settings-overlay", onMouseDown: handleBackdrop, children: /* @__PURE__ */ jsxs11("form", { className: "squisq-doc-settings-dialog", onSubmit: handleSave, children: [
|
|
5898
5974
|
/* @__PURE__ */ jsxs11("div", { className: "squisq-doc-settings-header", children: [
|
|
@@ -7906,8 +7982,7 @@ var SelectTool = {
|
|
|
7906
7982
|
}
|
|
7907
7983
|
} else if (dragState.kind === "resize") {
|
|
7908
7984
|
const { x, y, width, height } = dragState.currentBounds;
|
|
7909
|
-
ctx.dispatch({ kind: "
|
|
7910
|
-
ctx.dispatch({ kind: "resizeLayer", id: dragState.layerId, width, height });
|
|
7985
|
+
ctx.dispatch({ kind: "resizeLayer", id: dragState.layerId, x, y, width, height });
|
|
7911
7986
|
}
|
|
7912
7987
|
e2.currentTarget.releasePointerCapture?.(e2.pointerId);
|
|
7913
7988
|
setDragState(ctx.interaction, null);
|
|
@@ -11078,6 +11153,9 @@ function useLayoutAdapter(editor, headingPos, options = {}) {
|
|
|
11078
11153
|
moveLayoutLayer(editor, headingPos, cmd.id, cmd.x, cmd.y);
|
|
11079
11154
|
return;
|
|
11080
11155
|
case "resizeLayer":
|
|
11156
|
+
if (cmd.x !== void 0 && cmd.y !== void 0) {
|
|
11157
|
+
moveLayoutLayer(editor, headingPos, cmd.id, cmd.x, cmd.y);
|
|
11158
|
+
}
|
|
11081
11159
|
resizeLayoutLayer(editor, headingPos, cmd.id, cmd.width, cmd.height);
|
|
11082
11160
|
return;
|
|
11083
11161
|
case "addLayer": {
|
|
@@ -12271,7 +12349,12 @@ function useDrawingAdapter(editor, headingPos, options = {}) {
|
|
|
12271
12349
|
case "resizeLayer": {
|
|
12272
12350
|
if (!isPrimaryShapeLayer(cmd.id)) return;
|
|
12273
12351
|
const id = shapeIdFromLayerId(cmd.id);
|
|
12274
|
-
if (id)
|
|
12352
|
+
if (id) {
|
|
12353
|
+
if (cmd.x !== void 0 && cmd.y !== void 0) {
|
|
12354
|
+
moveShape(editor, headingPos, id, cmd.x, cmd.y);
|
|
12355
|
+
}
|
|
12356
|
+
resizeShape(editor, headingPos, id, cmd.width, cmd.height);
|
|
12357
|
+
}
|
|
12275
12358
|
return;
|
|
12276
12359
|
}
|
|
12277
12360
|
case "addLayer": {
|
|
@@ -13493,7 +13576,13 @@ function applyCommand(layers, cmd) {
|
|
|
13493
13576
|
return layers.map(
|
|
13494
13577
|
(l) => l.id === cmd.id ? {
|
|
13495
13578
|
...l,
|
|
13496
|
-
position: {
|
|
13579
|
+
position: {
|
|
13580
|
+
...l.position,
|
|
13581
|
+
...cmd.x !== void 0 ? { x: cmd.x } : {},
|
|
13582
|
+
...cmd.y !== void 0 ? { y: cmd.y } : {},
|
|
13583
|
+
width: cmd.width,
|
|
13584
|
+
height: cmd.height
|
|
13585
|
+
}
|
|
13497
13586
|
} : l
|
|
13498
13587
|
);
|
|
13499
13588
|
case "addLayer":
|
|
@@ -15957,12 +16046,6 @@ function resolveFrontmatterBoolean(value) {
|
|
|
15957
16046
|
if (v === "false" || v === "no" || v === "off" || v === "hide" || v === "hidden") return false;
|
|
15958
16047
|
return null;
|
|
15959
16048
|
}
|
|
15960
|
-
var FM_KEYS = {
|
|
15961
|
-
theme: { canonical: "squisq-theme", legacy: ["themeId", "theme"] },
|
|
15962
|
-
transform: { canonical: "squisq-transform", legacy: "transform-style" },
|
|
15963
|
-
captions: { canonical: "squisq-captions", legacy: "caption-style" },
|
|
15964
|
-
coverSlide: { canonical: "squisq-cover-slide", legacy: "cover-slide" }
|
|
15965
|
-
};
|
|
15966
16049
|
function readFrontmatterKey(fm, canonical, legacy) {
|
|
15967
16050
|
if (!fm) return void 0;
|
|
15968
16051
|
return Object.prototype.hasOwnProperty.call(fm, canonical) ? fm[canonical] : fm[legacy];
|
|
@@ -16009,7 +16092,7 @@ function PreviewSettingsProvider({
|
|
|
16009
16092
|
);
|
|
16010
16093
|
const [selectedThemeId, setSelectedThemeId] = useState32(null);
|
|
16011
16094
|
useEffect26(() => setSelectedThemeId(null), [fmTheme]);
|
|
16012
|
-
const resolvedThemeId = selectedThemeId ?? fmTheme ??
|
|
16095
|
+
const resolvedThemeId = selectedThemeId ?? fmTheme ?? FRONTMATTER_SETTING_DEFAULTS.theme;
|
|
16013
16096
|
const resolvedTheme = useMemo21(
|
|
16014
16097
|
() => customThemes.find((theme) => theme.id === resolvedThemeId) ?? resolveThemeForDoc(doc, resolvedThemeId),
|
|
16015
16098
|
[customThemes, doc, resolvedThemeId]
|
|
@@ -16028,9 +16111,12 @@ function PreviewSettingsProvider({
|
|
|
16028
16111
|
const selectedCustom = customThemes.find((theme) => theme.id === id);
|
|
16029
16112
|
const alreadyDocScoped = docThemes.some((theme) => theme.id === id);
|
|
16030
16113
|
const updates = {
|
|
16031
|
-
[
|
|
16032
|
-
|
|
16033
|
-
|
|
16114
|
+
[FRONTMATTER_SETTING_KEYS.theme.canonical]: omitFrontmatterDefault(
|
|
16115
|
+
id,
|
|
16116
|
+
FRONTMATTER_SETTING_DEFAULTS.theme
|
|
16117
|
+
),
|
|
16118
|
+
[FRONTMATTER_SETTING_KEYS.theme.legacy[0]]: null,
|
|
16119
|
+
[FRONTMATTER_SETTING_KEYS.theme.legacy[1]]: null
|
|
16034
16120
|
};
|
|
16035
16121
|
if (selectedCustom && !alreadyDocScoped) {
|
|
16036
16122
|
updates[FRONTMATTER_CUSTOM_THEMES_KEY2] = writeCustomThemesToFrontmatter2([...docThemes, selectedCustom]) ?? null;
|
|
@@ -16057,7 +16143,7 @@ function PreviewSettingsProvider({
|
|
|
16057
16143
|
const nextThemes = idx >= 0 ? docThemes2.map((t, i) => i === idx ? theme : t) : [...docThemes2, theme];
|
|
16058
16144
|
const updates = {
|
|
16059
16145
|
[FRONTMATTER_CUSTOM_THEMES_KEY2]: writeCustomThemesToFrontmatter2(nextThemes) ?? null,
|
|
16060
|
-
[
|
|
16146
|
+
[FRONTMATTER_SETTING_KEYS.theme.canonical]: theme.id
|
|
16061
16147
|
};
|
|
16062
16148
|
if (extras?.templates && extras.templates.length > 0) {
|
|
16063
16149
|
const existing = doc?.customTemplates ?? [];
|
|
@@ -16083,7 +16169,11 @@ function PreviewSettingsProvider({
|
|
|
16083
16169
|
);
|
|
16084
16170
|
const fmTransform = useMemo21(
|
|
16085
16171
|
() => resolvePersistedTransformStyleId(
|
|
16086
|
-
readFrontmatterKey(
|
|
16172
|
+
readFrontmatterKey(
|
|
16173
|
+
frontmatter,
|
|
16174
|
+
FRONTMATTER_SETTING_KEYS.transform.canonical,
|
|
16175
|
+
FRONTMATTER_SETTING_KEYS.transform.legacy
|
|
16176
|
+
)
|
|
16087
16177
|
),
|
|
16088
16178
|
[frontmatter]
|
|
16089
16179
|
);
|
|
@@ -16094,42 +16184,71 @@ function PreviewSettingsProvider({
|
|
|
16094
16184
|
(id) => {
|
|
16095
16185
|
setSelectedTransformStyle(id);
|
|
16096
16186
|
if (id !== null) {
|
|
16097
|
-
persistFrontmatter({
|
|
16187
|
+
persistFrontmatter({
|
|
16188
|
+
[FRONTMATTER_SETTING_KEYS.transform.canonical]: omitFrontmatterDefault(
|
|
16189
|
+
id,
|
|
16190
|
+
FRONTMATTER_SETTING_DEFAULTS.transform
|
|
16191
|
+
),
|
|
16192
|
+
[FRONTMATTER_SETTING_KEYS.transform.legacy]: null
|
|
16193
|
+
});
|
|
16098
16194
|
}
|
|
16099
16195
|
},
|
|
16100
16196
|
[persistFrontmatter]
|
|
16101
16197
|
);
|
|
16102
16198
|
const fmCaptionMode = useMemo21(
|
|
16103
16199
|
() => resolveFrontmatterCaptionMode(
|
|
16104
|
-
readFrontmatterKey(
|
|
16200
|
+
readFrontmatterKey(
|
|
16201
|
+
frontmatter,
|
|
16202
|
+
FRONTMATTER_SETTING_KEYS.captions.canonical,
|
|
16203
|
+
FRONTMATTER_SETTING_KEYS.captions.legacy
|
|
16204
|
+
)
|
|
16105
16205
|
),
|
|
16106
16206
|
[frontmatter]
|
|
16107
16207
|
);
|
|
16108
16208
|
const [selectedCaptionMode, setSelectedCaptionMode] = useState32(null);
|
|
16109
16209
|
useEffect26(() => setSelectedCaptionMode(null), [fmCaptionMode]);
|
|
16110
|
-
const activeCaptionMode = selectedCaptionMode ?? fmCaptionMode ??
|
|
16210
|
+
const activeCaptionMode = selectedCaptionMode ?? fmCaptionMode ?? FRONTMATTER_SETTING_DEFAULTS.captions;
|
|
16111
16211
|
const activeCaptionsEnabled = activeCaptionMode !== "off";
|
|
16112
16212
|
const activeCaptionStyle = activeCaptionMode === "social" ? "social" : "standard";
|
|
16113
16213
|
const handleSetCaptionMode = useCallback29(
|
|
16114
16214
|
(mode) => {
|
|
16115
16215
|
setSelectedCaptionMode(mode);
|
|
16116
|
-
persistFrontmatter({
|
|
16216
|
+
persistFrontmatter({
|
|
16217
|
+
[FRONTMATTER_SETTING_KEYS.captions.canonical]: omitFrontmatterDefault(
|
|
16218
|
+
mode,
|
|
16219
|
+
FRONTMATTER_SETTING_DEFAULTS.captions
|
|
16220
|
+
),
|
|
16221
|
+
[FRONTMATTER_SETTING_KEYS.captions.legacy]: null
|
|
16222
|
+
});
|
|
16117
16223
|
},
|
|
16118
16224
|
[persistFrontmatter]
|
|
16119
16225
|
);
|
|
16120
16226
|
const fmCoverSlide = useMemo21(
|
|
16121
16227
|
() => resolveFrontmatterBoolean(
|
|
16122
|
-
readFrontmatterKey(
|
|
16228
|
+
readFrontmatterKey(
|
|
16229
|
+
frontmatter,
|
|
16230
|
+
FRONTMATTER_SETTING_KEYS.coverSlide.canonical,
|
|
16231
|
+
FRONTMATTER_SETTING_KEYS.coverSlide.legacy
|
|
16232
|
+
)
|
|
16123
16233
|
),
|
|
16124
16234
|
[frontmatter]
|
|
16125
16235
|
);
|
|
16126
16236
|
const [selectedCoverSlide, setSelectedCoverSlide] = useState32(null);
|
|
16127
16237
|
useEffect26(() => setSelectedCoverSlide(null), [fmCoverSlide]);
|
|
16128
|
-
const activeCoverSlide = selectedCoverSlide ?? fmCoverSlide ??
|
|
16238
|
+
const activeCoverSlide = selectedCoverSlide ?? fmCoverSlide ?? FRONTMATTER_SETTING_DEFAULTS.coverSlide;
|
|
16129
16239
|
const handleSetCoverSlideEnabled = useCallback29(
|
|
16130
16240
|
(enabled) => {
|
|
16131
16241
|
setSelectedCoverSlide(enabled);
|
|
16132
|
-
persistFrontmatter({
|
|
16242
|
+
persistFrontmatter({
|
|
16243
|
+
// The cover is enabled by default, so only persist the non-default
|
|
16244
|
+
// state. Pass the boolean through so YAML writes `false`, not
|
|
16245
|
+
// the string `"false"`.
|
|
16246
|
+
[FRONTMATTER_SETTING_KEYS.coverSlide.canonical]: omitFrontmatterDefault(
|
|
16247
|
+
enabled,
|
|
16248
|
+
FRONTMATTER_SETTING_DEFAULTS.coverSlide
|
|
16249
|
+
),
|
|
16250
|
+
[FRONTMATTER_SETTING_KEYS.coverSlide.legacy]: null
|
|
16251
|
+
});
|
|
16133
16252
|
},
|
|
16134
16253
|
[persistFrontmatter]
|
|
16135
16254
|
);
|
|
@@ -16770,6 +16889,106 @@ function filterVisibleMediaEntries(entries) {
|
|
|
16770
16889
|
return entries.filter(isVisibleMediaEntry);
|
|
16771
16890
|
}
|
|
16772
16891
|
|
|
16892
|
+
// src/selectionConversions.ts
|
|
16893
|
+
function selectionLines(text) {
|
|
16894
|
+
return text.replace(/\r\n?/g, "\n").split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
16895
|
+
}
|
|
16896
|
+
function splitPipeLine(line) {
|
|
16897
|
+
let value = line.trim();
|
|
16898
|
+
if (value.startsWith("|")) value = value.slice(1);
|
|
16899
|
+
if (value.endsWith("|") && !value.endsWith("\\|")) value = value.slice(0, -1);
|
|
16900
|
+
const cells = [];
|
|
16901
|
+
let cell = "";
|
|
16902
|
+
for (let i = 0; i < value.length; i++) {
|
|
16903
|
+
const char = value[i];
|
|
16904
|
+
if (char === "\\" && value[i + 1] === "|") {
|
|
16905
|
+
cell += "|";
|
|
16906
|
+
i++;
|
|
16907
|
+
} else if (char === "|") {
|
|
16908
|
+
cells.push(cell.trim());
|
|
16909
|
+
cell = "";
|
|
16910
|
+
} else {
|
|
16911
|
+
cell += char;
|
|
16912
|
+
}
|
|
16913
|
+
}
|
|
16914
|
+
cells.push(cell.trim());
|
|
16915
|
+
return cells;
|
|
16916
|
+
}
|
|
16917
|
+
function splitCommaLine(line) {
|
|
16918
|
+
const cells = [];
|
|
16919
|
+
let cell = "";
|
|
16920
|
+
let quoted = false;
|
|
16921
|
+
for (let i = 0; i < line.length; i++) {
|
|
16922
|
+
const char = line[i];
|
|
16923
|
+
if (char === '"' && !quoted && cell.trim().length === 0) {
|
|
16924
|
+
quoted = true;
|
|
16925
|
+
cell = "";
|
|
16926
|
+
} else if (char === '"' && quoted) {
|
|
16927
|
+
if (line[i + 1] === '"') {
|
|
16928
|
+
cell += '"';
|
|
16929
|
+
i++;
|
|
16930
|
+
} else {
|
|
16931
|
+
quoted = false;
|
|
16932
|
+
}
|
|
16933
|
+
} else if (char === "," && !quoted) {
|
|
16934
|
+
cells.push(cell.trim());
|
|
16935
|
+
cell = "";
|
|
16936
|
+
} else {
|
|
16937
|
+
cell += char;
|
|
16938
|
+
}
|
|
16939
|
+
}
|
|
16940
|
+
if (quoted) return line.split(",").map((value) => value.trim());
|
|
16941
|
+
cells.push(cell.trim());
|
|
16942
|
+
return cells;
|
|
16943
|
+
}
|
|
16944
|
+
function splitLine(line, delimiter) {
|
|
16945
|
+
switch (delimiter) {
|
|
16946
|
+
case "pipe":
|
|
16947
|
+
return splitPipeLine(line);
|
|
16948
|
+
case "comma":
|
|
16949
|
+
return splitCommaLine(line);
|
|
16950
|
+
case "tab":
|
|
16951
|
+
return line.trim().split(" ").map((cell) => cell.trim());
|
|
16952
|
+
case "multispace":
|
|
16953
|
+
return line.trim().split(/[ \u00a0]{2,}/).map((cell) => cell.trim());
|
|
16954
|
+
}
|
|
16955
|
+
}
|
|
16956
|
+
function selectionToTable(text) {
|
|
16957
|
+
const lines = selectionLines(text);
|
|
16958
|
+
const candidates = ["pipe", "comma", "tab", "multispace"];
|
|
16959
|
+
if (lines.length >= 2) {
|
|
16960
|
+
for (const delimiter of candidates) {
|
|
16961
|
+
const rows = lines.map((line) => splitLine(line, delimiter));
|
|
16962
|
+
const columnCount = rows[0]?.length ?? 0;
|
|
16963
|
+
if (columnCount >= 2 && rows.every((row) => row.length === columnCount)) {
|
|
16964
|
+
return { delimiter, rows };
|
|
16965
|
+
}
|
|
16966
|
+
}
|
|
16967
|
+
}
|
|
16968
|
+
return { delimiter: null, rows: lines.map((line) => [line]) };
|
|
16969
|
+
}
|
|
16970
|
+
function escapeTableCell(cell) {
|
|
16971
|
+
return cell.replace(/\|/g, "\\|");
|
|
16972
|
+
}
|
|
16973
|
+
function selectionToTableMarkdown(text) {
|
|
16974
|
+
const { rows } = selectionToTable(text);
|
|
16975
|
+
if (rows.length === 0) return "";
|
|
16976
|
+
const formatRow = (row) => `| ${row.map(escapeTableCell).join(" | ")} |`;
|
|
16977
|
+
const separator = rows[0].map(() => "---");
|
|
16978
|
+
return [formatRow(rows[0]), formatRow(separator), ...rows.slice(1).map(formatRow)].join("\n");
|
|
16979
|
+
}
|
|
16980
|
+
function selectionToTaskItems(text) {
|
|
16981
|
+
return selectionLines(text).map((line) => {
|
|
16982
|
+
const task = /^[-*+]\s+\[([ xX])\]\s*(.*)$/.exec(line);
|
|
16983
|
+
if (task) return { checked: task[1].toLowerCase() === "x", text: task[2].trim() };
|
|
16984
|
+
const withoutListMarker = line.replace(/^(?:[-*+]\s+|\d+[.)]\s+)/, "");
|
|
16985
|
+
return { checked: false, text: withoutListMarker.trim() };
|
|
16986
|
+
});
|
|
16987
|
+
}
|
|
16988
|
+
function selectionToTaskListMarkdown(text) {
|
|
16989
|
+
return selectionToTaskItems(text).map((item) => `- [${item.checked ? "x" : " "}] ${item.text}`).join("\n");
|
|
16990
|
+
}
|
|
16991
|
+
|
|
16773
16992
|
// src/Toolbar.tsx
|
|
16774
16993
|
import { Fragment as Fragment14, jsx as jsx39, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
16775
16994
|
var VIEWS = [
|
|
@@ -16947,6 +17166,7 @@ var BUTTONS = [
|
|
|
16947
17166
|
];
|
|
16948
17167
|
var FIRST_MEDIA_INDEX = BUTTONS.findIndex((b) => b.group === "media");
|
|
16949
17168
|
var MEDIA_BUTTONS = BUTTONS.filter((b) => b.group === "media");
|
|
17169
|
+
var CONVERT_BUTTONS = MEDIA_BUTTONS.filter((b) => b.id === "table" || b.id === "tasklist");
|
|
16950
17170
|
var INSERT_MENU_WIDTH = 200;
|
|
16951
17171
|
var TASK_LIST_ITEMS = ["Task 1", "Task 2", "Task 3"];
|
|
16952
17172
|
var TASK_LIST_MARKDOWN = TASK_LIST_ITEMS.map((item) => `- [ ] ${item}`).join("\n");
|
|
@@ -16960,18 +17180,31 @@ function fileCountLabel(count) {
|
|
|
16960
17180
|
function fileCountBadge(count) {
|
|
16961
17181
|
return count > 99 ? "99+" : String(count);
|
|
16962
17182
|
}
|
|
16963
|
-
function
|
|
17183
|
+
function paragraphContent(text) {
|
|
17184
|
+
return text ? { type: "paragraph", content: [{ type: "text", text }] } : { type: "paragraph" };
|
|
17185
|
+
}
|
|
17186
|
+
function tableContent(rows) {
|
|
17187
|
+
return {
|
|
17188
|
+
type: "table",
|
|
17189
|
+
content: rows.map((row, rowIndex) => ({
|
|
17190
|
+
type: "tableRow",
|
|
17191
|
+
content: row.map((cell) => ({
|
|
17192
|
+
type: rowIndex === 0 ? "tableHeader" : "tableCell",
|
|
17193
|
+
content: [paragraphContent(cell)]
|
|
17194
|
+
}))
|
|
17195
|
+
}))
|
|
17196
|
+
};
|
|
17197
|
+
}
|
|
17198
|
+
function taskListContent(items = TASK_LIST_ITEMS.map((text) => ({
|
|
17199
|
+
checked: false,
|
|
17200
|
+
text
|
|
17201
|
+
}))) {
|
|
16964
17202
|
return {
|
|
16965
17203
|
type: "taskList",
|
|
16966
|
-
content:
|
|
17204
|
+
content: items.map((item) => ({
|
|
16967
17205
|
type: "taskItem",
|
|
16968
|
-
attrs: { checked:
|
|
16969
|
-
content: [
|
|
16970
|
-
{
|
|
16971
|
-
type: "paragraph",
|
|
16972
|
-
content: [{ type: "text", text: item }]
|
|
16973
|
-
}
|
|
16974
|
-
]
|
|
17206
|
+
attrs: { checked: item.checked },
|
|
17207
|
+
content: [paragraphContent(item.text)]
|
|
16975
17208
|
}))
|
|
16976
17209
|
};
|
|
16977
17210
|
}
|
|
@@ -16980,6 +17213,13 @@ function insertTaskList(editor) {
|
|
|
16980
17213
|
const content = supportsTaskList ? taskListContent() : TASK_LIST_MARKDOWN;
|
|
16981
17214
|
editor.chain().focus().insertContent(content).run();
|
|
16982
17215
|
}
|
|
17216
|
+
function blockConversionRange(editor) {
|
|
17217
|
+
const { from, to, $from, $to } = editor.state.selection;
|
|
17218
|
+
return {
|
|
17219
|
+
from: $from.depth === 1 && $from.parent.isTextblock && $from.parentOffset === 0 ? $from.before(1) : from,
|
|
17220
|
+
to: $to.depth === 1 && $to.parent.isTextblock && $to.parentOffset === $to.parent.content.size ? $to.after(1) : to
|
|
17221
|
+
};
|
|
17222
|
+
}
|
|
16983
17223
|
function isTiptapActive(editor, id) {
|
|
16984
17224
|
if (!editor) return false;
|
|
16985
17225
|
switch (id) {
|
|
@@ -17024,14 +17264,16 @@ var LAYOUT_STARTER_MARKDOWN = `
|
|
|
17024
17264
|
Layout
|
|
17025
17265
|
`;
|
|
17026
17266
|
var DIAGRAM_STARTER_ART = [
|
|
17027
|
-
"
|
|
17028
|
-
"
|
|
17029
|
-
"\
|
|
17030
|
-
"
|
|
17031
|
-
"
|
|
17032
|
-
"\
|
|
17033
|
-
"\
|
|
17034
|
-
"\
|
|
17267
|
+
"",
|
|
17268
|
+
"",
|
|
17269
|
+
" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
|
|
17270
|
+
" \u2502 Start \u2502",
|
|
17271
|
+
" \u2514\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2518",
|
|
17272
|
+
" \u2502",
|
|
17273
|
+
" \u25BC",
|
|
17274
|
+
" \u250C\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2510",
|
|
17275
|
+
" \u2502 Next \u2502",
|
|
17276
|
+
" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"
|
|
17035
17277
|
].join("\n");
|
|
17036
17278
|
var DIAGRAM_STARTER_MARKDOWN = "\n```diagram\n" + DIAGRAM_STARTER_ART + "\n```\n";
|
|
17037
17279
|
function insertAsciiDiagramBlock(editor) {
|
|
@@ -17677,6 +17919,43 @@ ${TASK_LIST_MARKDOWN}
|
|
|
17677
17919
|
},
|
|
17678
17920
|
[monacoEditor, markdownSource, setMarkdownSource]
|
|
17679
17921
|
);
|
|
17922
|
+
const readSelectedText = useCallback30(() => {
|
|
17923
|
+
if (activeSceneText) return "";
|
|
17924
|
+
if (activeView === "wysiwyg" && tiptapEditor) {
|
|
17925
|
+
const { from, to, empty } = tiptapEditor.state.selection;
|
|
17926
|
+
return empty ? "" : tiptapEditor.state.doc.textBetween(from, to, "\n");
|
|
17927
|
+
}
|
|
17928
|
+
if (activeView === "raw" && monacoEditor) {
|
|
17929
|
+
const selection = monacoEditor.getSelection();
|
|
17930
|
+
const model = monacoEditor.getModel();
|
|
17931
|
+
return selection && model ? model.getValueInRange(selection) : "";
|
|
17932
|
+
}
|
|
17933
|
+
return "";
|
|
17934
|
+
}, [activeSceneText, activeView, monacoEditor, tiptapEditor]);
|
|
17935
|
+
const handleConvertSelection = useCallback30(
|
|
17936
|
+
(target) => {
|
|
17937
|
+
const selectedText = readSelectedText();
|
|
17938
|
+
if (!selectedText.trim()) return;
|
|
17939
|
+
if (activeView === "wysiwyg" && tiptapEditor) {
|
|
17940
|
+
const content = target === "table" ? tableContent(selectionToTable(selectedText).rows) : taskListContent(selectionToTaskItems(selectedText));
|
|
17941
|
+
tiptapEditor.chain().focus().insertContentAt(blockConversionRange(tiptapEditor), content).run();
|
|
17942
|
+
return;
|
|
17943
|
+
}
|
|
17944
|
+
if (activeView === "raw" && monacoEditor) {
|
|
17945
|
+
const selection = monacoEditor.getSelection();
|
|
17946
|
+
if (!selection) return;
|
|
17947
|
+
const converted = target === "table" ? selectionToTableMarkdown(selectedText) : selectionToTaskListMarkdown(selectedText);
|
|
17948
|
+
if (!converted) return;
|
|
17949
|
+
const leadingNewline = /^(?:\r?\n)/.test(selectedText) ? "\n" : "";
|
|
17950
|
+
const trailingNewline = /(?:\r?\n)$/.test(selectedText) ? "\n" : "";
|
|
17951
|
+
monacoEditor.executeEdits("toolbar-convert-selection", [
|
|
17952
|
+
{ range: selection, text: leadingNewline + converted + trailingNewline }
|
|
17953
|
+
]);
|
|
17954
|
+
monacoEditor.focus();
|
|
17955
|
+
}
|
|
17956
|
+
},
|
|
17957
|
+
[activeView, monacoEditor, readSelectedText, tiptapEditor]
|
|
17958
|
+
);
|
|
17680
17959
|
const handleImageFile = useCallback30(
|
|
17681
17960
|
async (file) => {
|
|
17682
17961
|
if (!mediaProvider) return;
|
|
@@ -17866,6 +18145,7 @@ ${TASK_LIST_MARKDOWN}
|
|
|
17866
18145
|
if (sceneTextLevel === "block") return BLOCK_SCENE_BUTTONS.has(id);
|
|
17867
18146
|
return INLINE_SCENE_BUTTONS.has(id);
|
|
17868
18147
|
};
|
|
18148
|
+
const showConvertActions = insertMenuAnchor !== null && readSelectedText().trim().length > 0 && CONVERT_BUTTONS.some((button) => buttonAllowed(button.id));
|
|
17869
18149
|
const maxHeadingLevelInDoc = useMemo22(() => {
|
|
17870
18150
|
if (!markdownSource) return 0;
|
|
17871
18151
|
let max = 0;
|
|
@@ -18682,7 +18962,37 @@ ${TASK_LIST_MARKDOWN}
|
|
|
18682
18962
|
style: { position: "fixed", top: insertMenuAnchor.top, left: insertMenuAnchor.left },
|
|
18683
18963
|
role: "menu",
|
|
18684
18964
|
children: [
|
|
18685
|
-
/* @__PURE__ */
|
|
18965
|
+
showConvertActions && /* @__PURE__ */ jsxs29(Fragment14, { children: [
|
|
18966
|
+
/* @__PURE__ */ jsx39("div", { className: "squisq-insert-menu-header", children: "Convert" }),
|
|
18967
|
+
CONVERT_BUTTONS.map((btn) => {
|
|
18968
|
+
const label = btn.id === "table" ? "Table" : "Task List";
|
|
18969
|
+
return /* @__PURE__ */ jsxs29(
|
|
18970
|
+
"button",
|
|
18971
|
+
{
|
|
18972
|
+
className: "squisq-toolbar-overflow-item",
|
|
18973
|
+
disabled: !buttonAllowed(btn.id),
|
|
18974
|
+
onClick: () => {
|
|
18975
|
+
handleConvertSelection(btn.id);
|
|
18976
|
+
closeInsertMenu();
|
|
18977
|
+
},
|
|
18978
|
+
role: "menuitem",
|
|
18979
|
+
"aria-label": `Convert selection to ${label}`,
|
|
18980
|
+
children: [
|
|
18981
|
+
/* @__PURE__ */ jsx39("span", { className: "squisq-toolbar-overflow-icon", children: buttonIcon(btn) }),
|
|
18982
|
+
/* @__PURE__ */ jsx39("span", { children: label })
|
|
18983
|
+
]
|
|
18984
|
+
},
|
|
18985
|
+
`convert-${btn.id}`
|
|
18986
|
+
);
|
|
18987
|
+
})
|
|
18988
|
+
] }),
|
|
18989
|
+
/* @__PURE__ */ jsx39(
|
|
18990
|
+
"div",
|
|
18991
|
+
{
|
|
18992
|
+
className: `squisq-insert-menu-header${showConvertActions ? " squisq-insert-menu-header--separated" : ""}`,
|
|
18993
|
+
children: "Insert"
|
|
18994
|
+
}
|
|
18995
|
+
),
|
|
18686
18996
|
MEDIA_BUTTONS.filter((b) => isButtonVisible(b.id)).map((btn) => {
|
|
18687
18997
|
const disabled = btn.id === "image" && !mediaProvider || !buttonAllowed(btn.id);
|
|
18688
18998
|
const stripped = btn.title.replace(/^Insert\s+/i, "");
|
|
@@ -19310,7 +19620,7 @@ function RawEditor({
|
|
|
19310
19620
|
}
|
|
19311
19621
|
|
|
19312
19622
|
// src/WysiwygEditor.tsx
|
|
19313
|
-
import { useCallback as useCallback36, useEffect as useEffect36, useMemo as useMemo30, useRef as
|
|
19623
|
+
import { useCallback as useCallback36, useEffect as useEffect36, useMemo as useMemo30, useRef as useRef35, useState as useState44 } from "react";
|
|
19314
19624
|
import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
|
|
19315
19625
|
import { Selection as Selection2 } from "@tiptap/pm/state";
|
|
19316
19626
|
import StarterKit2 from "@tiptap/starter-kit";
|
|
@@ -19648,7 +19958,14 @@ function DiagramCanvas({
|
|
|
19648
19958
|
if (!cmd.id.startsWith("node-card-")) return;
|
|
19649
19959
|
const nodeId = nodeIdFromCardLayerId(cmd.id);
|
|
19650
19960
|
if (!nodeId) return;
|
|
19651
|
-
onCommand({
|
|
19961
|
+
onCommand({
|
|
19962
|
+
kind: "resizeNode",
|
|
19963
|
+
nodeId,
|
|
19964
|
+
width: cmd.width,
|
|
19965
|
+
height: cmd.height,
|
|
19966
|
+
...cmd.x !== void 0 ? { x: cmd.x } : {},
|
|
19967
|
+
...cmd.y !== void 0 ? { y: cmd.y } : {}
|
|
19968
|
+
});
|
|
19652
19969
|
return;
|
|
19653
19970
|
}
|
|
19654
19971
|
case "setLayerAttr":
|
|
@@ -19734,6 +20051,168 @@ import {
|
|
|
19734
20051
|
ASCII_CHAR_H,
|
|
19735
20052
|
ASCII_CHAR_W
|
|
19736
20053
|
} from "@bendyline/squisq/doc";
|
|
20054
|
+
|
|
20055
|
+
// src/asciiDiagram/asciiDiagramOps.ts
|
|
20056
|
+
function sanitizeAsciiLabel(label) {
|
|
20057
|
+
return label.replace(/[─-╿▲▼◀-◄▶-►←-↓`]/gu, " ").replace(/\s+/g, " ").trim();
|
|
20058
|
+
}
|
|
20059
|
+
function descendantsOf(diagram, nodeId) {
|
|
20060
|
+
const out = /* @__PURE__ */ new Set();
|
|
20061
|
+
let grew = true;
|
|
20062
|
+
while (grew) {
|
|
20063
|
+
grew = false;
|
|
20064
|
+
for (const n of diagram.nodes) {
|
|
20065
|
+
if (out.has(n.id)) continue;
|
|
20066
|
+
if (n.containerId === nodeId || n.containerId && out.has(n.containerId)) {
|
|
20067
|
+
out.add(n.id);
|
|
20068
|
+
grew = true;
|
|
20069
|
+
}
|
|
20070
|
+
}
|
|
20071
|
+
}
|
|
20072
|
+
return out;
|
|
20073
|
+
}
|
|
20074
|
+
function translateDiagramOp(diagram, dCol, dRow) {
|
|
20075
|
+
const colDelta = Math.round(dCol);
|
|
20076
|
+
const rowDelta = Math.round(dRow);
|
|
20077
|
+
if (colDelta === 0 && rowDelta === 0) return diagram;
|
|
20078
|
+
return {
|
|
20079
|
+
...diagram,
|
|
20080
|
+
nodes: diagram.nodes.map((node) => ({
|
|
20081
|
+
...node,
|
|
20082
|
+
col: Math.max(0, node.col + colDelta),
|
|
20083
|
+
row: Math.max(0, node.row + rowDelta)
|
|
20084
|
+
}))
|
|
20085
|
+
};
|
|
20086
|
+
}
|
|
20087
|
+
function moveNodeOp(diagram, nodeId, col, row) {
|
|
20088
|
+
const node = diagram.nodes.find((n) => n.id === nodeId);
|
|
20089
|
+
if (!node) return diagram;
|
|
20090
|
+
const dCol = Math.max(0, col) - node.col;
|
|
20091
|
+
const dRow = Math.max(0, row) - node.row;
|
|
20092
|
+
if (dCol === 0 && dRow === 0) return diagram;
|
|
20093
|
+
const moving = descendantsOf(diagram, nodeId);
|
|
20094
|
+
moving.add(nodeId);
|
|
20095
|
+
return {
|
|
20096
|
+
...diagram,
|
|
20097
|
+
nodes: diagram.nodes.map(
|
|
20098
|
+
(n) => moving.has(n.id) ? { ...n, col: Math.max(0, n.col + dCol), row: Math.max(0, n.row + dRow) } : n
|
|
20099
|
+
)
|
|
20100
|
+
};
|
|
20101
|
+
}
|
|
20102
|
+
function resizeNodeOp(diagram, nodeId, wCols, hRows) {
|
|
20103
|
+
const node = diagram.nodes.find((n) => n.id === nodeId);
|
|
20104
|
+
if (!node) return diagram;
|
|
20105
|
+
const labelLines = node.label.length > 0 ? node.label.split("\n") : [];
|
|
20106
|
+
const minW = Math.max(3, labelLines.reduce((w, l) => Math.max(w, l.length), 0) + 4);
|
|
20107
|
+
const minH = Math.max(3, labelLines.length + 2);
|
|
20108
|
+
return {
|
|
20109
|
+
...diagram,
|
|
20110
|
+
nodes: diagram.nodes.map(
|
|
20111
|
+
(n) => n.id === nodeId ? {
|
|
20112
|
+
...n,
|
|
20113
|
+
wCols: Math.max(minW, Math.round(wCols)),
|
|
20114
|
+
hRows: Math.max(minH, Math.round(hRows))
|
|
20115
|
+
} : n
|
|
20116
|
+
)
|
|
20117
|
+
};
|
|
20118
|
+
}
|
|
20119
|
+
function addEdgeOp(diagram, source, target, label) {
|
|
20120
|
+
const ids = new Set(diagram.nodes.map((n) => n.id));
|
|
20121
|
+
if (!ids.has(source) || !ids.has(target) || source === target) return diagram;
|
|
20122
|
+
const exists = diagram.edges.some(
|
|
20123
|
+
(e2) => e2.source === source && e2.target === target && (e2.label ?? "") === (label ?? "")
|
|
20124
|
+
);
|
|
20125
|
+
if (exists) return diagram;
|
|
20126
|
+
const edge = {
|
|
20127
|
+
source,
|
|
20128
|
+
target,
|
|
20129
|
+
...label ? { label } : {},
|
|
20130
|
+
directed: true
|
|
20131
|
+
};
|
|
20132
|
+
return { ...diagram, edges: [...diagram.edges, edge] };
|
|
20133
|
+
}
|
|
20134
|
+
function removeEdgeOp(diagram, source, target, label) {
|
|
20135
|
+
let removed = false;
|
|
20136
|
+
const edges = diagram.edges.filter((e2) => {
|
|
20137
|
+
if (removed) return true;
|
|
20138
|
+
const match = e2.source === source && e2.target === target && (label !== void 0 ? (e2.label ?? "") === label : true);
|
|
20139
|
+
if (match) {
|
|
20140
|
+
removed = true;
|
|
20141
|
+
return false;
|
|
20142
|
+
}
|
|
20143
|
+
return true;
|
|
20144
|
+
});
|
|
20145
|
+
return removed ? { ...diagram, edges } : diagram;
|
|
20146
|
+
}
|
|
20147
|
+
function renameNodeOp(diagram, nodeId, label) {
|
|
20148
|
+
const node = diagram.nodes.find((n) => n.id === nodeId);
|
|
20149
|
+
if (!node) return diagram;
|
|
20150
|
+
const clean = sanitizeAsciiLabel(label);
|
|
20151
|
+
if (clean.length === 0 || clean === node.label) return diagram;
|
|
20152
|
+
return {
|
|
20153
|
+
...diagram,
|
|
20154
|
+
nodes: diagram.nodes.map((n) => n.id === nodeId ? { ...n, label: clean } : n)
|
|
20155
|
+
};
|
|
20156
|
+
}
|
|
20157
|
+
function addNodeOp(diagram, opts) {
|
|
20158
|
+
let label = opts.label;
|
|
20159
|
+
if (!label) {
|
|
20160
|
+
let i = 1;
|
|
20161
|
+
const labels = new Set(diagram.nodes.map((n) => n.label.split("\n")[0]));
|
|
20162
|
+
while (labels.has(`Node ${i}`)) i++;
|
|
20163
|
+
label = `Node ${i}`;
|
|
20164
|
+
}
|
|
20165
|
+
const clean = sanitizeAsciiLabel(label) || "Node";
|
|
20166
|
+
const col = Math.max(0, Math.round(opts.col));
|
|
20167
|
+
const row = Math.max(0, Math.round(opts.row));
|
|
20168
|
+
const wCols = clean.length + 4;
|
|
20169
|
+
const hRows = 3;
|
|
20170
|
+
const centerCol = col + wCols / 2;
|
|
20171
|
+
const centerRow = row + hRows / 2;
|
|
20172
|
+
const containers = diagram.nodes.filter((n) => diagram.nodes.some((m) => m.containerId === n.id)).filter(
|
|
20173
|
+
(n) => centerCol > n.col && centerCol < n.col + n.wCols && centerRow > n.row && centerRow < n.row + n.hRows
|
|
20174
|
+
).sort((a, b) => a.wCols * a.hRows - b.wCols * b.hRows);
|
|
20175
|
+
const node = {
|
|
20176
|
+
id: `__new-${diagram.nodes.length}`,
|
|
20177
|
+
// provisional; the re-parse derives the real slug id
|
|
20178
|
+
label: clean,
|
|
20179
|
+
col,
|
|
20180
|
+
row,
|
|
20181
|
+
wCols,
|
|
20182
|
+
hRows,
|
|
20183
|
+
...containers[0] ? { containerId: containers[0].id } : {}
|
|
20184
|
+
};
|
|
20185
|
+
return { diagram: { ...diagram, nodes: [...diagram.nodes, node] }, label: clean };
|
|
20186
|
+
}
|
|
20187
|
+
function removeNodeOp(diagram, nodeId) {
|
|
20188
|
+
const node = diagram.nodes.find((n) => n.id === nodeId);
|
|
20189
|
+
if (!node) return diagram;
|
|
20190
|
+
const nodes = diagram.nodes.filter((n) => n.id !== nodeId).map((n) => {
|
|
20191
|
+
if (n.containerId !== nodeId) return n;
|
|
20192
|
+
const promoted = { ...n };
|
|
20193
|
+
if (node.containerId) promoted.containerId = node.containerId;
|
|
20194
|
+
else delete promoted.containerId;
|
|
20195
|
+
return promoted;
|
|
20196
|
+
});
|
|
20197
|
+
const edges = diagram.edges.filter((e2) => e2.source !== nodeId && e2.target !== nodeId);
|
|
20198
|
+
return { ...diagram, nodes, edges };
|
|
20199
|
+
}
|
|
20200
|
+
|
|
20201
|
+
// src/asciiDiagram/asciiDiagramData.ts
|
|
20202
|
+
var ASCII_DIAGRAM_GUTTER_COLS = 8;
|
|
20203
|
+
var ASCII_DIAGRAM_GUTTER_ROWS = 2;
|
|
20204
|
+
function initialAsciiDiagramCanvasOffset(diagram) {
|
|
20205
|
+
if (diagram.nodes.length === 0) return { col: 0, row: 0 };
|
|
20206
|
+
const minCol = Math.min(...diagram.nodes.map((node) => node.col));
|
|
20207
|
+
const minRow = Math.min(...diagram.nodes.map((node) => node.row));
|
|
20208
|
+
return {
|
|
20209
|
+
col: Math.max(0, ASCII_DIAGRAM_GUTTER_COLS - minCol),
|
|
20210
|
+
row: Math.max(0, ASCII_DIAGRAM_GUTTER_ROWS - minRow)
|
|
20211
|
+
};
|
|
20212
|
+
}
|
|
20213
|
+
function offsetAsciiDiagram(diagram, offset) {
|
|
20214
|
+
return translateDiagramOp(diagram, offset.col, offset.row);
|
|
20215
|
+
}
|
|
19737
20216
|
function asciiDiagramToCanvas(diagram) {
|
|
19738
20217
|
const containerIds = new Set(
|
|
19739
20218
|
diagram.nodes.map((n) => n.containerId).filter((id) => id !== void 0)
|
|
@@ -19941,139 +20420,6 @@ var RepairableDiagramExtension = Extension.create({
|
|
|
19941
20420
|
}
|
|
19942
20421
|
});
|
|
19943
20422
|
|
|
19944
|
-
// src/asciiDiagram/asciiDiagramOps.ts
|
|
19945
|
-
function sanitizeAsciiLabel(label) {
|
|
19946
|
-
return label.replace(/[─-╿▲▼◀-◄▶-►←-↓`]/gu, " ").replace(/\s+/g, " ").trim();
|
|
19947
|
-
}
|
|
19948
|
-
function descendantsOf(diagram, nodeId) {
|
|
19949
|
-
const out = /* @__PURE__ */ new Set();
|
|
19950
|
-
let grew = true;
|
|
19951
|
-
while (grew) {
|
|
19952
|
-
grew = false;
|
|
19953
|
-
for (const n of diagram.nodes) {
|
|
19954
|
-
if (out.has(n.id)) continue;
|
|
19955
|
-
if (n.containerId === nodeId || n.containerId && out.has(n.containerId)) {
|
|
19956
|
-
out.add(n.id);
|
|
19957
|
-
grew = true;
|
|
19958
|
-
}
|
|
19959
|
-
}
|
|
19960
|
-
}
|
|
19961
|
-
return out;
|
|
19962
|
-
}
|
|
19963
|
-
function moveNodeOp(diagram, nodeId, col, row) {
|
|
19964
|
-
const node = diagram.nodes.find((n) => n.id === nodeId);
|
|
19965
|
-
if (!node) return diagram;
|
|
19966
|
-
const dCol = Math.max(0, col) - node.col;
|
|
19967
|
-
const dRow = Math.max(0, row) - node.row;
|
|
19968
|
-
if (dCol === 0 && dRow === 0) return diagram;
|
|
19969
|
-
const moving = descendantsOf(diagram, nodeId);
|
|
19970
|
-
moving.add(nodeId);
|
|
19971
|
-
return {
|
|
19972
|
-
...diagram,
|
|
19973
|
-
nodes: diagram.nodes.map(
|
|
19974
|
-
(n) => moving.has(n.id) ? { ...n, col: Math.max(0, n.col + dCol), row: Math.max(0, n.row + dRow) } : n
|
|
19975
|
-
)
|
|
19976
|
-
};
|
|
19977
|
-
}
|
|
19978
|
-
function resizeNodeOp(diagram, nodeId, wCols, hRows) {
|
|
19979
|
-
const node = diagram.nodes.find((n) => n.id === nodeId);
|
|
19980
|
-
if (!node) return diagram;
|
|
19981
|
-
const labelLines = node.label.length > 0 ? node.label.split("\n") : [];
|
|
19982
|
-
const minW = Math.max(3, labelLines.reduce((w, l) => Math.max(w, l.length), 0) + 4);
|
|
19983
|
-
const minH = Math.max(3, labelLines.length + 2);
|
|
19984
|
-
return {
|
|
19985
|
-
...diagram,
|
|
19986
|
-
nodes: diagram.nodes.map(
|
|
19987
|
-
(n) => n.id === nodeId ? {
|
|
19988
|
-
...n,
|
|
19989
|
-
wCols: Math.max(minW, Math.round(wCols)),
|
|
19990
|
-
hRows: Math.max(minH, Math.round(hRows))
|
|
19991
|
-
} : n
|
|
19992
|
-
)
|
|
19993
|
-
};
|
|
19994
|
-
}
|
|
19995
|
-
function addEdgeOp(diagram, source, target, label) {
|
|
19996
|
-
const ids = new Set(diagram.nodes.map((n) => n.id));
|
|
19997
|
-
if (!ids.has(source) || !ids.has(target) || source === target) return diagram;
|
|
19998
|
-
const exists = diagram.edges.some(
|
|
19999
|
-
(e2) => e2.source === source && e2.target === target && (e2.label ?? "") === (label ?? "")
|
|
20000
|
-
);
|
|
20001
|
-
if (exists) return diagram;
|
|
20002
|
-
const edge = {
|
|
20003
|
-
source,
|
|
20004
|
-
target,
|
|
20005
|
-
...label ? { label } : {},
|
|
20006
|
-
directed: true
|
|
20007
|
-
};
|
|
20008
|
-
return { ...diagram, edges: [...diagram.edges, edge] };
|
|
20009
|
-
}
|
|
20010
|
-
function removeEdgeOp(diagram, source, target, label) {
|
|
20011
|
-
let removed = false;
|
|
20012
|
-
const edges = diagram.edges.filter((e2) => {
|
|
20013
|
-
if (removed) return true;
|
|
20014
|
-
const match = e2.source === source && e2.target === target && (label !== void 0 ? (e2.label ?? "") === label : true);
|
|
20015
|
-
if (match) {
|
|
20016
|
-
removed = true;
|
|
20017
|
-
return false;
|
|
20018
|
-
}
|
|
20019
|
-
return true;
|
|
20020
|
-
});
|
|
20021
|
-
return removed ? { ...diagram, edges } : diagram;
|
|
20022
|
-
}
|
|
20023
|
-
function renameNodeOp(diagram, nodeId, label) {
|
|
20024
|
-
const node = diagram.nodes.find((n) => n.id === nodeId);
|
|
20025
|
-
if (!node) return diagram;
|
|
20026
|
-
const clean = sanitizeAsciiLabel(label);
|
|
20027
|
-
if (clean.length === 0 || clean === node.label) return diagram;
|
|
20028
|
-
return {
|
|
20029
|
-
...diagram,
|
|
20030
|
-
nodes: diagram.nodes.map((n) => n.id === nodeId ? { ...n, label: clean } : n)
|
|
20031
|
-
};
|
|
20032
|
-
}
|
|
20033
|
-
function addNodeOp(diagram, opts) {
|
|
20034
|
-
let label = opts.label;
|
|
20035
|
-
if (!label) {
|
|
20036
|
-
let i = 1;
|
|
20037
|
-
const labels = new Set(diagram.nodes.map((n) => n.label.split("\n")[0]));
|
|
20038
|
-
while (labels.has(`Node ${i}`)) i++;
|
|
20039
|
-
label = `Node ${i}`;
|
|
20040
|
-
}
|
|
20041
|
-
const clean = sanitizeAsciiLabel(label) || "Node";
|
|
20042
|
-
const col = Math.max(0, Math.round(opts.col));
|
|
20043
|
-
const row = Math.max(0, Math.round(opts.row));
|
|
20044
|
-
const wCols = clean.length + 4;
|
|
20045
|
-
const hRows = 3;
|
|
20046
|
-
const centerCol = col + wCols / 2;
|
|
20047
|
-
const centerRow = row + hRows / 2;
|
|
20048
|
-
const containers = diagram.nodes.filter((n) => diagram.nodes.some((m) => m.containerId === n.id)).filter(
|
|
20049
|
-
(n) => centerCol > n.col && centerCol < n.col + n.wCols && centerRow > n.row && centerRow < n.row + n.hRows
|
|
20050
|
-
).sort((a, b) => a.wCols * a.hRows - b.wCols * b.hRows);
|
|
20051
|
-
const node = {
|
|
20052
|
-
id: `__new-${diagram.nodes.length}`,
|
|
20053
|
-
// provisional; the re-parse derives the real slug id
|
|
20054
|
-
label: clean,
|
|
20055
|
-
col,
|
|
20056
|
-
row,
|
|
20057
|
-
wCols,
|
|
20058
|
-
hRows,
|
|
20059
|
-
...containers[0] ? { containerId: containers[0].id } : {}
|
|
20060
|
-
};
|
|
20061
|
-
return { diagram: { ...diagram, nodes: [...diagram.nodes, node] }, label: clean };
|
|
20062
|
-
}
|
|
20063
|
-
function removeNodeOp(diagram, nodeId) {
|
|
20064
|
-
const node = diagram.nodes.find((n) => n.id === nodeId);
|
|
20065
|
-
if (!node) return diagram;
|
|
20066
|
-
const nodes = diagram.nodes.filter((n) => n.id !== nodeId).map((n) => {
|
|
20067
|
-
if (n.containerId !== nodeId) return n;
|
|
20068
|
-
const promoted = { ...n };
|
|
20069
|
-
if (node.containerId) promoted.containerId = node.containerId;
|
|
20070
|
-
else delete promoted.containerId;
|
|
20071
|
-
return promoted;
|
|
20072
|
-
});
|
|
20073
|
-
const edges = diagram.edges.filter((e2) => e2.source !== nodeId && e2.target !== nodeId);
|
|
20074
|
-
return { ...diagram, nodes, edges };
|
|
20075
|
-
}
|
|
20076
|
-
|
|
20077
20423
|
// src/asciiDiagram/asciiDiagramCommands.ts
|
|
20078
20424
|
function replaceAsciiFenceText(editor, pos, nextText, ensureLanguage) {
|
|
20079
20425
|
return editor.chain().command(({ tr, state }) => {
|
|
@@ -20101,43 +20447,52 @@ function applyRepairCommand(editor, blockId) {
|
|
|
20101
20447
|
if (!repaired) return false;
|
|
20102
20448
|
return replaceAsciiFenceText(editor, pos, repaired.art, "diagram");
|
|
20103
20449
|
}
|
|
20104
|
-
function applyOp(editor, blockId, op) {
|
|
20450
|
+
function applyOp(editor, blockId, op, diagramOffset) {
|
|
20105
20451
|
const pos = findAsciiDiagramBlockPos(editor, blockId);
|
|
20106
20452
|
if (pos === null) return false;
|
|
20107
20453
|
const node = editor.state.doc.nodeAt(pos);
|
|
20108
20454
|
if (!node || node.type.name !== "codeBlock") return false;
|
|
20109
|
-
const
|
|
20110
|
-
if (!
|
|
20455
|
+
const sourceDiagram = parseAsciiDiagramForNode(node);
|
|
20456
|
+
if (!sourceDiagram) return false;
|
|
20457
|
+
const diagram = diagramOffset ? translateDiagramOp(sourceDiagram, diagramOffset.col, diagramOffset.row) : sourceDiagram;
|
|
20111
20458
|
const next = op(diagram);
|
|
20112
|
-
if (next === diagram) return false;
|
|
20459
|
+
if (next === diagram && diagram === sourceDiagram) return false;
|
|
20113
20460
|
const rendered = renderAsciiDiagram(next);
|
|
20114
20461
|
const verification = parseAsciiDiagram(rendered);
|
|
20115
20462
|
if (verification.nodes.length !== next.nodes.length) return false;
|
|
20116
20463
|
return replaceAsciiFenceText(editor, pos, rendered, "diagram");
|
|
20117
20464
|
}
|
|
20118
|
-
function applyAsciiDiagramCommand(editor, blockId, cmd) {
|
|
20465
|
+
function applyAsciiDiagramCommand(editor, blockId, cmd, options = {}) {
|
|
20466
|
+
const apply = (op) => applyOp(editor, blockId, op, options.diagramOffset);
|
|
20119
20467
|
switch (cmd.kind) {
|
|
20120
20468
|
case "moveNode": {
|
|
20121
20469
|
const { col, row } = canvasToAsciiCell(cmd.x, cmd.y);
|
|
20122
|
-
return
|
|
20470
|
+
return apply((d) => moveNodeOp(d, cmd.nodeId, col, row));
|
|
20123
20471
|
}
|
|
20124
20472
|
case "resizeNode": {
|
|
20125
20473
|
const wCols = Math.max(3, Math.round(cmd.width / ASCII_CHAR_W2));
|
|
20126
20474
|
const hRows = Math.max(3, Math.round(cmd.height / ASCII_CHAR_H2));
|
|
20127
|
-
return
|
|
20475
|
+
return apply((d) => {
|
|
20476
|
+
let next = d;
|
|
20477
|
+
if (cmd.x !== void 0 && cmd.y !== void 0) {
|
|
20478
|
+
const { col, row } = canvasToAsciiCell(cmd.x, cmd.y);
|
|
20479
|
+
next = moveNodeOp(next, cmd.nodeId, col, row);
|
|
20480
|
+
}
|
|
20481
|
+
return resizeNodeOp(next, cmd.nodeId, wCols, hRows);
|
|
20482
|
+
});
|
|
20128
20483
|
}
|
|
20129
20484
|
case "addConnection":
|
|
20130
|
-
return
|
|
20485
|
+
return apply((d) => addEdgeOp(d, cmd.source, cmd.target, cmd.type));
|
|
20131
20486
|
case "removeConnection":
|
|
20132
|
-
return
|
|
20487
|
+
return apply((d) => removeEdgeOp(d, cmd.source, cmd.target, cmd.type));
|
|
20133
20488
|
case "renameNode":
|
|
20134
|
-
return
|
|
20489
|
+
return apply((d) => renameNodeOp(d, cmd.nodeId, cmd.newLabel));
|
|
20135
20490
|
case "addNode": {
|
|
20136
20491
|
const { col, row } = canvasToAsciiCell(cmd.x, cmd.y);
|
|
20137
|
-
return
|
|
20492
|
+
return apply((d) => addNodeOp(d, { col, row }).diagram);
|
|
20138
20493
|
}
|
|
20139
20494
|
case "removeNode":
|
|
20140
|
-
return
|
|
20495
|
+
return apply((d) => removeNodeOp(d, cmd.nodeId));
|
|
20141
20496
|
}
|
|
20142
20497
|
const _exhaustive = cmd;
|
|
20143
20498
|
void _exhaustive;
|
|
@@ -20158,11 +20513,28 @@ function AsciiDiagramWidget({
|
|
|
20158
20513
|
const [maximized, setMaximized] = useState36(false);
|
|
20159
20514
|
const [height, setHeight] = useState36(null);
|
|
20160
20515
|
const [dragHeight, setDragHeight] = useState36(null);
|
|
20516
|
+
const canvasOffsetRef = useRef29(null);
|
|
20517
|
+
const [canvasOffsetVersion, setCanvasOffsetVersion] = useState36(0);
|
|
20161
20518
|
const inlineRef = useRef29(null);
|
|
20162
20519
|
const effectiveHeight = dragHeight ?? height;
|
|
20520
|
+
if (view && canvasOffsetRef.current === null) {
|
|
20521
|
+
canvasOffsetRef.current = initialAsciiDiagramCanvasOffset(view.diagram);
|
|
20522
|
+
}
|
|
20523
|
+
const canvasView = useMemo26(() => {
|
|
20524
|
+
if (!view) return null;
|
|
20525
|
+
const offset = canvasOffsetRef.current ?? { col: 0, row: 0 };
|
|
20526
|
+
return asciiDiagramToCanvas(offsetAsciiDiagram(view.diagram, offset));
|
|
20527
|
+
}, [view, canvasOffsetVersion]);
|
|
20163
20528
|
const dispatch = useCallback33(
|
|
20164
20529
|
(cmd) => {
|
|
20165
|
-
|
|
20530
|
+
const offset = canvasOffsetRef.current ?? { col: 0, row: 0 };
|
|
20531
|
+
const applied = applyAsciiDiagramCommand(editor, blockId, cmd, {
|
|
20532
|
+
diagramOffset: offset
|
|
20533
|
+
});
|
|
20534
|
+
if (applied && (offset.col !== 0 || offset.row !== 0)) {
|
|
20535
|
+
canvasOffsetRef.current = { col: 0, row: 0 };
|
|
20536
|
+
setCanvasOffsetVersion((version) => version + 1);
|
|
20537
|
+
}
|
|
20166
20538
|
},
|
|
20167
20539
|
[editor, blockId]
|
|
20168
20540
|
);
|
|
@@ -20253,8 +20625,8 @@ function AsciiDiagramWidget({
|
|
|
20253
20625
|
DiagramCanvas,
|
|
20254
20626
|
{
|
|
20255
20627
|
textChannel,
|
|
20256
|
-
nodes: view.nodes,
|
|
20257
|
-
edges: view.edges,
|
|
20628
|
+
nodes: canvasView?.nodes ?? view.nodes,
|
|
20629
|
+
edges: canvasView?.edges ?? view.edges,
|
|
20258
20630
|
onCommand: dispatch,
|
|
20259
20631
|
showMaximize: true,
|
|
20260
20632
|
maximized,
|
|
@@ -20530,7 +20902,7 @@ import {
|
|
|
20530
20902
|
} from "@bendyline/squisq/doc";
|
|
20531
20903
|
|
|
20532
20904
|
// src/treeview/TreeOutlineWidget.tsx
|
|
20533
|
-
import { useCallback as useCallback34, useState as useState38 } from "react";
|
|
20905
|
+
import { useCallback as useCallback34, useRef as useRef30, useState as useState38 } from "react";
|
|
20534
20906
|
|
|
20535
20907
|
// src/treeview/treeViewData.ts
|
|
20536
20908
|
import { useEffect as useEffect30, useMemo as useMemo27, useState as useState37 } from "react";
|
|
@@ -20584,6 +20956,9 @@ function locate(roots, id, parent = null) {
|
|
|
20584
20956
|
function markDir(n) {
|
|
20585
20957
|
n.isDir = n.label.endsWith("/") || n.children.length > 0;
|
|
20586
20958
|
}
|
|
20959
|
+
function containsNode(node, id) {
|
|
20960
|
+
return node.id === id || node.children.some((child) => containsNode(child, id));
|
|
20961
|
+
}
|
|
20587
20962
|
function renameItemOp(tree, id, label) {
|
|
20588
20963
|
const clean = sanitizeTreeLabel(label);
|
|
20589
20964
|
const next = cloneTree(tree);
|
|
@@ -20642,6 +21017,34 @@ function outdentItemOp(tree, id) {
|
|
|
20642
21017
|
markDir(loc.parent);
|
|
20643
21018
|
return next;
|
|
20644
21019
|
}
|
|
21020
|
+
function moveItemOp(tree, id, targetId, position) {
|
|
21021
|
+
const currentSource = locate(tree.roots, id);
|
|
21022
|
+
const currentTarget = locate(tree.roots, targetId);
|
|
21023
|
+
if (!currentSource || !currentTarget || id === targetId || containsNode(currentSource.node, targetId)) {
|
|
21024
|
+
return tree;
|
|
21025
|
+
}
|
|
21026
|
+
if (currentSource.siblings === currentTarget.siblings) {
|
|
21027
|
+
if (position === "before" && currentSource.index === currentTarget.index - 1) return tree;
|
|
21028
|
+
if (position === "after" && currentSource.index === currentTarget.index + 1) return tree;
|
|
21029
|
+
}
|
|
21030
|
+
if (position === "child" && currentSource.parent?.id === currentTarget.node.id && currentSource.index === currentSource.siblings.length - 1) {
|
|
21031
|
+
return tree;
|
|
21032
|
+
}
|
|
21033
|
+
const next = cloneTree(tree);
|
|
21034
|
+
const source = locate(next.roots, id);
|
|
21035
|
+
if (!source) return tree;
|
|
21036
|
+
const [moved] = source.siblings.splice(source.index, 1);
|
|
21037
|
+
if (source.parent) markDir(source.parent);
|
|
21038
|
+
const target = locate(next.roots, targetId);
|
|
21039
|
+
if (!target) return tree;
|
|
21040
|
+
if (position === "child") {
|
|
21041
|
+
target.node.children.push(moved);
|
|
21042
|
+
markDir(target.node);
|
|
21043
|
+
} else {
|
|
21044
|
+
target.siblings.splice(target.index + (position === "after" ? 1 : 0), 0, moved);
|
|
21045
|
+
}
|
|
21046
|
+
return next;
|
|
21047
|
+
}
|
|
20645
21048
|
function moveItemUpOp(tree, id) {
|
|
20646
21049
|
const next = cloneTree(tree);
|
|
20647
21050
|
const loc = locate(next.roots, id);
|
|
@@ -20713,6 +21116,8 @@ function applyTreeCommand(editor, blockId, cmd) {
|
|
|
20713
21116
|
return applyOp2(editor, blockId, (t) => indentItemOp(t, cmd.id));
|
|
20714
21117
|
case "outdentItem":
|
|
20715
21118
|
return applyOp2(editor, blockId, (t) => outdentItemOp(t, cmd.id));
|
|
21119
|
+
case "moveItem":
|
|
21120
|
+
return applyOp2(editor, blockId, (t) => moveItemOp(t, cmd.id, cmd.targetId, cmd.position));
|
|
20716
21121
|
case "moveItemUp":
|
|
20717
21122
|
return applyOp2(editor, blockId, (t) => moveItemUpOp(t, cmd.id));
|
|
20718
21123
|
case "moveItemDown":
|
|
@@ -20729,9 +21134,36 @@ function applyTreeCommand(editor, blockId, cmd) {
|
|
|
20729
21134
|
|
|
20730
21135
|
// src/treeview/TreeOutlineWidget.tsx
|
|
20731
21136
|
import { jsx as jsx44, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
21137
|
+
var TREE_DRAG_MIME = "application/x-squisq-tree-node";
|
|
21138
|
+
function findNode(nodes, id) {
|
|
21139
|
+
for (const node of nodes) {
|
|
21140
|
+
if (node.id === id) return node;
|
|
21141
|
+
const child = findNode(node.children, id);
|
|
21142
|
+
if (child) return child;
|
|
21143
|
+
}
|
|
21144
|
+
return null;
|
|
21145
|
+
}
|
|
21146
|
+
function nodeContains(node, id) {
|
|
21147
|
+
return node.id === id || node.children.some((child) => nodeContains(child, id));
|
|
21148
|
+
}
|
|
21149
|
+
function canDropNode(nodes, sourceId, targetId) {
|
|
21150
|
+
const source = findNode(nodes, sourceId);
|
|
21151
|
+
return source != null && !nodeContains(source, targetId);
|
|
21152
|
+
}
|
|
21153
|
+
function dropPositionForPointer(event) {
|
|
21154
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
21155
|
+
if (rect.height <= 0) return "child";
|
|
21156
|
+
const ratio = (event.clientY - rect.top) / rect.height;
|
|
21157
|
+
if (ratio < 0.3) return "before";
|
|
21158
|
+
if (ratio > 0.7) return "after";
|
|
21159
|
+
return "child";
|
|
21160
|
+
}
|
|
20732
21161
|
function TreeOutlineWidget({ editor, blockId }) {
|
|
20733
21162
|
const view = useTreeViewData(editor, blockId);
|
|
20734
21163
|
const [collapsed, setCollapsed] = useState38(() => /* @__PURE__ */ new Set());
|
|
21164
|
+
const activeDragRef = useRef30(null);
|
|
21165
|
+
const [draggedId, setDraggedId] = useState38(null);
|
|
21166
|
+
const [dropTarget, setDropTarget] = useState38(null);
|
|
20735
21167
|
const dispatch = useCallback34(
|
|
20736
21168
|
(cmd) => applyTreeCommand(editor, blockId, cmd),
|
|
20737
21169
|
[editor, blockId]
|
|
@@ -20744,6 +21176,61 @@ function TreeOutlineWidget({ editor, blockId }) {
|
|
|
20744
21176
|
return next;
|
|
20745
21177
|
});
|
|
20746
21178
|
}, []);
|
|
21179
|
+
const clearDragState = useCallback34(() => {
|
|
21180
|
+
activeDragRef.current = null;
|
|
21181
|
+
setDraggedId(null);
|
|
21182
|
+
setDropTarget(null);
|
|
21183
|
+
}, []);
|
|
21184
|
+
const handleDragStart = useCallback34((event, id) => {
|
|
21185
|
+
event.stopPropagation();
|
|
21186
|
+
activeDragRef.current = id;
|
|
21187
|
+
setDraggedId(id);
|
|
21188
|
+
setDropTarget(null);
|
|
21189
|
+
event.dataTransfer.effectAllowed = "move";
|
|
21190
|
+
event.dataTransfer.setData(TREE_DRAG_MIME, id);
|
|
21191
|
+
event.dataTransfer.setData("text/plain", id);
|
|
21192
|
+
}, []);
|
|
21193
|
+
const handleDragOver = useCallback34(
|
|
21194
|
+
(event, targetId) => {
|
|
21195
|
+
const sourceId = activeDragRef.current;
|
|
21196
|
+
if (!sourceId) return;
|
|
21197
|
+
event.preventDefault();
|
|
21198
|
+
event.stopPropagation();
|
|
21199
|
+
if (!view || !canDropNode(view.tree.roots, sourceId, targetId)) {
|
|
21200
|
+
event.dataTransfer.dropEffect = "none";
|
|
21201
|
+
setDropTarget(null);
|
|
21202
|
+
return;
|
|
21203
|
+
}
|
|
21204
|
+
const position = dropPositionForPointer(event);
|
|
21205
|
+
event.dataTransfer.dropEffect = "move";
|
|
21206
|
+
setDropTarget(
|
|
21207
|
+
(current) => current?.id === targetId && current.position === position ? current : { id: targetId, position }
|
|
21208
|
+
);
|
|
21209
|
+
},
|
|
21210
|
+
[view]
|
|
21211
|
+
);
|
|
21212
|
+
const handleDrop = useCallback34(
|
|
21213
|
+
(event, targetId) => {
|
|
21214
|
+
const sourceId = activeDragRef.current;
|
|
21215
|
+
if (!sourceId) return;
|
|
21216
|
+
event.preventDefault();
|
|
21217
|
+
event.stopPropagation();
|
|
21218
|
+
const position = dropPositionForPointer(event);
|
|
21219
|
+
const canMove = view && canDropNode(view.tree.roots, sourceId, targetId);
|
|
21220
|
+
clearDragState();
|
|
21221
|
+
if (!canMove) return;
|
|
21222
|
+
const moved = dispatch({ kind: "moveItem", id: sourceId, targetId, position });
|
|
21223
|
+
if (moved && position === "child") {
|
|
21224
|
+
setCollapsed((current) => {
|
|
21225
|
+
if (!current.has(targetId)) return current;
|
|
21226
|
+
const next = new Set(current);
|
|
21227
|
+
next.delete(targetId);
|
|
21228
|
+
return next;
|
|
21229
|
+
});
|
|
21230
|
+
}
|
|
21231
|
+
},
|
|
21232
|
+
[clearDragState, dispatch, view]
|
|
21233
|
+
);
|
|
20747
21234
|
if (!view) return null;
|
|
20748
21235
|
const roots = view.tree.roots;
|
|
20749
21236
|
const firstRootId = roots[0]?.id;
|
|
@@ -20789,8 +21276,14 @@ function TreeOutlineWidget({ editor, blockId }) {
|
|
|
20789
21276
|
node,
|
|
20790
21277
|
depth: 0,
|
|
20791
21278
|
collapsed,
|
|
21279
|
+
draggedId,
|
|
21280
|
+
dropTarget,
|
|
20792
21281
|
toggleCollapse,
|
|
20793
|
-
dispatch
|
|
21282
|
+
dispatch,
|
|
21283
|
+
onDragStart: handleDragStart,
|
|
21284
|
+
onDragOver: handleDragOver,
|
|
21285
|
+
onDrop: handleDrop,
|
|
21286
|
+
onDragEnd: clearDragState
|
|
20794
21287
|
},
|
|
20795
21288
|
node.id
|
|
20796
21289
|
)) }),
|
|
@@ -20805,129 +21298,167 @@ function TreeRowView({
|
|
|
20805
21298
|
node,
|
|
20806
21299
|
depth,
|
|
20807
21300
|
collapsed,
|
|
21301
|
+
draggedId,
|
|
21302
|
+
dropTarget,
|
|
20808
21303
|
toggleCollapse,
|
|
20809
|
-
dispatch
|
|
21304
|
+
dispatch,
|
|
21305
|
+
onDragStart,
|
|
21306
|
+
onDragOver,
|
|
21307
|
+
onDrop,
|
|
21308
|
+
onDragEnd
|
|
20810
21309
|
}) {
|
|
20811
21310
|
const hasChildren = node.children.length > 0;
|
|
20812
21311
|
const isCollapsed = collapsed.has(node.id);
|
|
20813
21312
|
const isDir = node.isDir || hasChildren;
|
|
20814
21313
|
const [draft, setDraft] = useState38(node.label);
|
|
21314
|
+
const dropPosition = dropTarget?.id === node.id ? dropTarget.position : null;
|
|
21315
|
+
const itemClassName = [
|
|
21316
|
+
"squisq-tree-item",
|
|
21317
|
+
draggedId === node.id ? "squisq-tree-item--dragging" : "",
|
|
21318
|
+
dropPosition ? `squisq-tree-item--drop-${dropPosition}` : ""
|
|
21319
|
+
].filter(Boolean).join(" ");
|
|
20815
21320
|
const commit = () => {
|
|
20816
21321
|
if (draft !== node.label && draft.trim().length > 0) {
|
|
20817
21322
|
dispatch({ kind: "renameItem", id: node.id, label: draft });
|
|
20818
21323
|
}
|
|
20819
21324
|
};
|
|
20820
|
-
return /* @__PURE__ */ jsxs33("li", { role: "treeitem", style: { paddingLeft: `${depth * 18}px` }, children: [
|
|
20821
|
-
/* @__PURE__ */ jsxs33(
|
|
20822
|
-
|
|
20823
|
-
|
|
20824
|
-
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
|
|
20831
|
-
|
|
20832
|
-
|
|
20833
|
-
|
|
20834
|
-
|
|
20835
|
-
|
|
20836
|
-
className: "squisq-tree-icon",
|
|
20837
|
-
title: isDir ? "Make a file" : "Make a folder",
|
|
20838
|
-
onClick: () => dispatch({ kind: "toggleDir", id: node.id }),
|
|
20839
|
-
children: /* @__PURE__ */ jsx44(Icon, { icon: `fa-solid ${isDir ? "fa-folder" : "fa-file"}` })
|
|
20840
|
-
}
|
|
20841
|
-
),
|
|
20842
|
-
/* @__PURE__ */ jsx44(
|
|
20843
|
-
"input",
|
|
20844
|
-
{
|
|
20845
|
-
className: "squisq-tree-label",
|
|
20846
|
-
value: draft,
|
|
20847
|
-
onChange: (e2) => setDraft(e2.target.value),
|
|
20848
|
-
onBlur: commit,
|
|
20849
|
-
onKeyDown: (e2) => {
|
|
20850
|
-
if (e2.key === "Enter") {
|
|
20851
|
-
e2.preventDefault();
|
|
20852
|
-
commit();
|
|
20853
|
-
dispatch({ kind: "addItem", targetId: node.id, position: "siblingAfter" });
|
|
20854
|
-
} else if (e2.key === "Backspace" && draft.length === 0) {
|
|
20855
|
-
e2.preventDefault();
|
|
20856
|
-
dispatch({ kind: "removeItem", id: node.id });
|
|
20857
|
-
} else if (e2.key === "Tab") {
|
|
20858
|
-
e2.preventDefault();
|
|
20859
|
-
commit();
|
|
20860
|
-
dispatch({ kind: e2.shiftKey ? "outdentItem" : "indentItem", id: node.id });
|
|
21325
|
+
return /* @__PURE__ */ jsxs33("li", { className: itemClassName, role: "treeitem", style: { paddingLeft: `${depth * 18}px` }, children: [
|
|
21326
|
+
/* @__PURE__ */ jsxs33(
|
|
21327
|
+
"div",
|
|
21328
|
+
{
|
|
21329
|
+
className: "squisq-tree-row",
|
|
21330
|
+
onDragOver: (event) => onDragOver(event, node.id),
|
|
21331
|
+
onDrop: (event) => onDrop(event, node.id),
|
|
21332
|
+
children: [
|
|
21333
|
+
hasChildren ? /* @__PURE__ */ jsx44(
|
|
21334
|
+
"button",
|
|
21335
|
+
{
|
|
21336
|
+
type: "button",
|
|
21337
|
+
className: "squisq-tree-chevron",
|
|
21338
|
+
"aria-label": isCollapsed ? "Expand" : "Collapse",
|
|
21339
|
+
onClick: () => toggleCollapse(node.id),
|
|
21340
|
+
children: /* @__PURE__ */ jsx44(Icon, { icon: `fa-solid ${isCollapsed ? "fa-chevron-right" : "fa-chevron-down"}` })
|
|
20861
21341
|
}
|
|
20862
|
-
}
|
|
20863
|
-
|
|
20864
|
-
|
|
20865
|
-
|
|
20866
|
-
|
|
20867
|
-
|
|
20868
|
-
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
20872
|
-
|
|
20873
|
-
|
|
20874
|
-
|
|
20875
|
-
|
|
20876
|
-
|
|
20877
|
-
|
|
20878
|
-
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
|
|
20882
|
-
|
|
20883
|
-
|
|
20884
|
-
|
|
20885
|
-
|
|
20886
|
-
|
|
20887
|
-
|
|
20888
|
-
|
|
20889
|
-
|
|
20890
|
-
|
|
20891
|
-
|
|
20892
|
-
|
|
20893
|
-
|
|
20894
|
-
|
|
20895
|
-
|
|
20896
|
-
|
|
20897
|
-
|
|
20898
|
-
|
|
20899
|
-
|
|
20900
|
-
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
|
|
20904
|
-
|
|
20905
|
-
|
|
20906
|
-
|
|
20907
|
-
|
|
20908
|
-
|
|
20909
|
-
|
|
20910
|
-
|
|
20911
|
-
|
|
20912
|
-
|
|
20913
|
-
|
|
20914
|
-
|
|
20915
|
-
|
|
20916
|
-
|
|
20917
|
-
|
|
20918
|
-
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
|
|
21342
|
+
) : /* @__PURE__ */ jsx44("span", { className: "squisq-tree-chevron squisq-tree-chevron--empty" }),
|
|
21343
|
+
/* @__PURE__ */ jsx44(
|
|
21344
|
+
"button",
|
|
21345
|
+
{
|
|
21346
|
+
type: "button",
|
|
21347
|
+
className: "squisq-tree-icon",
|
|
21348
|
+
title: isDir ? "Make a file" : "Make a folder",
|
|
21349
|
+
onClick: () => dispatch({ kind: "toggleDir", id: node.id }),
|
|
21350
|
+
children: /* @__PURE__ */ jsx44(Icon, { icon: `fa-solid ${isDir ? "fa-folder" : "fa-file"}` })
|
|
21351
|
+
}
|
|
21352
|
+
),
|
|
21353
|
+
/* @__PURE__ */ jsx44(
|
|
21354
|
+
"input",
|
|
21355
|
+
{
|
|
21356
|
+
className: "squisq-tree-label",
|
|
21357
|
+
value: draft,
|
|
21358
|
+
onChange: (e2) => setDraft(e2.target.value),
|
|
21359
|
+
onBlur: commit,
|
|
21360
|
+
onKeyDown: (e2) => {
|
|
21361
|
+
if (e2.key === "Enter") {
|
|
21362
|
+
e2.preventDefault();
|
|
21363
|
+
commit();
|
|
21364
|
+
dispatch({ kind: "addItem", targetId: node.id, position: "siblingAfter" });
|
|
21365
|
+
} else if (e2.key === "Backspace" && draft.length === 0) {
|
|
21366
|
+
e2.preventDefault();
|
|
21367
|
+
dispatch({ kind: "removeItem", id: node.id });
|
|
21368
|
+
} else if (e2.key === "Tab") {
|
|
21369
|
+
e2.preventDefault();
|
|
21370
|
+
commit();
|
|
21371
|
+
dispatch({ kind: e2.shiftKey ? "outdentItem" : "indentItem", id: node.id });
|
|
21372
|
+
}
|
|
21373
|
+
}
|
|
21374
|
+
}
|
|
21375
|
+
),
|
|
21376
|
+
/* @__PURE__ */ jsxs33("span", { className: "squisq-tree-controls", children: [
|
|
21377
|
+
/* @__PURE__ */ jsx44(
|
|
21378
|
+
"span",
|
|
21379
|
+
{
|
|
21380
|
+
className: "squisq-tree-drag-handle",
|
|
21381
|
+
draggable: true,
|
|
21382
|
+
"aria-hidden": "true",
|
|
21383
|
+
title: `Drag ${node.label} to move`,
|
|
21384
|
+
onDragStart: (event) => onDragStart(event, node.id),
|
|
21385
|
+
onDragEnd,
|
|
21386
|
+
children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-grip-vertical" })
|
|
21387
|
+
}
|
|
21388
|
+
),
|
|
21389
|
+
/* @__PURE__ */ jsx44(
|
|
21390
|
+
"button",
|
|
21391
|
+
{
|
|
21392
|
+
type: "button",
|
|
21393
|
+
title: "Add child",
|
|
21394
|
+
onClick: () => dispatch({ kind: "addItem", targetId: node.id, position: "child" }),
|
|
21395
|
+
children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-plus" })
|
|
21396
|
+
}
|
|
21397
|
+
),
|
|
21398
|
+
/* @__PURE__ */ jsx44(
|
|
21399
|
+
"button",
|
|
21400
|
+
{
|
|
21401
|
+
type: "button",
|
|
21402
|
+
title: "Outdent",
|
|
21403
|
+
onClick: () => dispatch({ kind: "outdentItem", id: node.id }),
|
|
21404
|
+
children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-outdent" })
|
|
21405
|
+
}
|
|
21406
|
+
),
|
|
21407
|
+
/* @__PURE__ */ jsx44(
|
|
21408
|
+
"button",
|
|
21409
|
+
{
|
|
21410
|
+
type: "button",
|
|
21411
|
+
title: "Indent",
|
|
21412
|
+
onClick: () => dispatch({ kind: "indentItem", id: node.id }),
|
|
21413
|
+
children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-indent" })
|
|
21414
|
+
}
|
|
21415
|
+
),
|
|
21416
|
+
/* @__PURE__ */ jsx44(
|
|
21417
|
+
"button",
|
|
21418
|
+
{
|
|
21419
|
+
type: "button",
|
|
21420
|
+
title: "Move up",
|
|
21421
|
+
onClick: () => dispatch({ kind: "moveItemUp", id: node.id }),
|
|
21422
|
+
children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-arrow-up" })
|
|
21423
|
+
}
|
|
21424
|
+
),
|
|
21425
|
+
/* @__PURE__ */ jsx44(
|
|
21426
|
+
"button",
|
|
21427
|
+
{
|
|
21428
|
+
type: "button",
|
|
21429
|
+
title: "Move down",
|
|
21430
|
+
onClick: () => dispatch({ kind: "moveItemDown", id: node.id }),
|
|
21431
|
+
children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-arrow-down" })
|
|
21432
|
+
}
|
|
21433
|
+
),
|
|
21434
|
+
/* @__PURE__ */ jsx44(
|
|
21435
|
+
"button",
|
|
21436
|
+
{
|
|
21437
|
+
type: "button",
|
|
21438
|
+
title: "Delete",
|
|
21439
|
+
className: "squisq-tree-delete",
|
|
21440
|
+
onClick: () => dispatch({ kind: "removeItem", id: node.id }),
|
|
21441
|
+
children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-trash" })
|
|
21442
|
+
}
|
|
21443
|
+
)
|
|
21444
|
+
] })
|
|
21445
|
+
]
|
|
21446
|
+
}
|
|
21447
|
+
),
|
|
20923
21448
|
hasChildren && !isCollapsed ? /* @__PURE__ */ jsx44("ul", { className: "squisq-tree-rows", role: "group", children: node.children.map((child) => /* @__PURE__ */ jsx44(
|
|
20924
21449
|
TreeRowView,
|
|
20925
21450
|
{
|
|
20926
21451
|
node: child,
|
|
20927
21452
|
depth: depth + 1,
|
|
20928
21453
|
collapsed,
|
|
21454
|
+
draggedId,
|
|
21455
|
+
dropTarget,
|
|
20929
21456
|
toggleCollapse,
|
|
20930
|
-
dispatch
|
|
21457
|
+
dispatch,
|
|
21458
|
+
onDragStart,
|
|
21459
|
+
onDragOver,
|
|
21460
|
+
onDrop,
|
|
21461
|
+
onDragEnd
|
|
20931
21462
|
},
|
|
20932
21463
|
child.id
|
|
20933
21464
|
)) }) : null
|
|
@@ -21118,16 +21649,16 @@ import {
|
|
|
21118
21649
|
} from "@bendyline/squisq/doc";
|
|
21119
21650
|
|
|
21120
21651
|
// src/timeline/TimelineEditorWidget.tsx
|
|
21121
|
-
import { useCallback as useCallback35, useEffect as useEffect32, useMemo as useMemo29, useRef as
|
|
21652
|
+
import { useCallback as useCallback35, useEffect as useEffect32, useMemo as useMemo29, useRef as useRef32, useState as useState40 } from "react";
|
|
21122
21653
|
import {
|
|
21123
21654
|
asciiTimelineToTemplateData
|
|
21124
21655
|
} from "@bendyline/squisq/doc";
|
|
21125
21656
|
|
|
21126
21657
|
// src/timeline/timelineData.ts
|
|
21127
|
-
import { useEffect as useEffect31, useMemo as useMemo28, useRef as
|
|
21658
|
+
import { useEffect as useEffect31, useMemo as useMemo28, useRef as useRef31, useState as useState39 } from "react";
|
|
21128
21659
|
function useTimelineData(editor, blockId) {
|
|
21129
21660
|
const [version, setVersion] = useState39(0);
|
|
21130
|
-
const dataCache =
|
|
21661
|
+
const dataCache = useRef31(null);
|
|
21131
21662
|
useEffect31(() => {
|
|
21132
21663
|
const onEditorChange = () => setVersion((value) => value + 1);
|
|
21133
21664
|
editor.on("transaction", onEditorChange);
|
|
@@ -21176,12 +21707,79 @@ function sanitizeTimelineText(value) {
|
|
|
21176
21707
|
}
|
|
21177
21708
|
function nextTimelineEventId(timeline, base = "event") {
|
|
21178
21709
|
const used = new Set(timeline.tracks.flatMap((track) => track.events.map((event) => event.id)));
|
|
21179
|
-
const safeBase = safeId(base
|
|
21710
|
+
const safeBase = safeId(base, "event");
|
|
21711
|
+
if (!used.has(safeBase)) return safeBase;
|
|
21712
|
+
let suffix = 2;
|
|
21713
|
+
while (used.has(`${safeBase}-${suffix}`)) suffix++;
|
|
21714
|
+
return `${safeBase}-${suffix}`;
|
|
21715
|
+
}
|
|
21716
|
+
function nextTimelineTrackId(timeline, base = "track") {
|
|
21717
|
+
const used = new Set(timeline.tracks.map((track) => track.id));
|
|
21718
|
+
const safeBase = safeId(base, "track");
|
|
21180
21719
|
if (!used.has(safeBase)) return safeBase;
|
|
21181
21720
|
let suffix = 2;
|
|
21182
21721
|
while (used.has(`${safeBase}-${suffix}`)) suffix++;
|
|
21183
21722
|
return `${safeBase}-${suffix}`;
|
|
21184
21723
|
}
|
|
21724
|
+
function addTimelineTrackOp(timeline, options = {}) {
|
|
21725
|
+
const label = sanitizeTimelineText(options.label ?? "") || "New line";
|
|
21726
|
+
const eventLabel = sanitizeTimelineText(options.eventLabel ?? "") || "New event";
|
|
21727
|
+
const trackId = nextTimelineTrackId(timeline, options.id ?? label);
|
|
21728
|
+
const eventId = nextTimelineEventId(timeline, options.eventId ?? eventLabel);
|
|
21729
|
+
const { start, span } = globalBounds(timeline);
|
|
21730
|
+
const column = Math.round((start + span / 2) * COORDINATE_PRECISION) / COORDINATE_PRECISION;
|
|
21731
|
+
const row = timeline.tracks.reduce((maximum, track2) => Math.max(maximum, track2.row), -1) + 1;
|
|
21732
|
+
const track = {
|
|
21733
|
+
id: trackId,
|
|
21734
|
+
label,
|
|
21735
|
+
row,
|
|
21736
|
+
startColumn: start,
|
|
21737
|
+
endColumn: start + span,
|
|
21738
|
+
events: [
|
|
21739
|
+
{
|
|
21740
|
+
id: eventId,
|
|
21741
|
+
label: eventLabel,
|
|
21742
|
+
column,
|
|
21743
|
+
side: "above",
|
|
21744
|
+
marker: "filled"
|
|
21745
|
+
}
|
|
21746
|
+
]
|
|
21747
|
+
};
|
|
21748
|
+
return {
|
|
21749
|
+
timeline: {
|
|
21750
|
+
...timeline,
|
|
21751
|
+
tracks: [...timeline.tracks, track],
|
|
21752
|
+
width: Math.max(timeline.width, Math.ceil(track.endColumn) + 1),
|
|
21753
|
+
height: Math.max(timeline.height, row + 1)
|
|
21754
|
+
},
|
|
21755
|
+
trackId,
|
|
21756
|
+
eventId
|
|
21757
|
+
};
|
|
21758
|
+
}
|
|
21759
|
+
function updateTimelineTrackOp(timeline, trackId, label) {
|
|
21760
|
+
const track = timeline.tracks.find((candidate) => candidate.id === trackId);
|
|
21761
|
+
const nextLabel = sanitizeTimelineText(label);
|
|
21762
|
+
if (!track || !nextLabel || track.label === nextLabel) return timeline;
|
|
21763
|
+
return {
|
|
21764
|
+
...timeline,
|
|
21765
|
+
tracks: timeline.tracks.map(
|
|
21766
|
+
(candidate) => candidate.id === trackId ? { ...candidate, label: nextLabel } : candidate
|
|
21767
|
+
)
|
|
21768
|
+
};
|
|
21769
|
+
}
|
|
21770
|
+
function removeTimelineTrackOp(timeline, trackId) {
|
|
21771
|
+
if (timeline.tracks.length <= 1) return timeline;
|
|
21772
|
+
const track = timeline.tracks.find((candidate) => candidate.id === trackId);
|
|
21773
|
+
if (!track) return timeline;
|
|
21774
|
+
const removedEventIds = new Set(track.events.map((event) => event.id));
|
|
21775
|
+
return {
|
|
21776
|
+
...timeline,
|
|
21777
|
+
tracks: timeline.tracks.filter((candidate) => candidate.id !== trackId),
|
|
21778
|
+
links: timeline.links.filter(
|
|
21779
|
+
(link) => !removedEventIds.has(link.source) && !removedEventIds.has(link.target)
|
|
21780
|
+
)
|
|
21781
|
+
};
|
|
21782
|
+
}
|
|
21185
21783
|
function addTimelineEventOp(timeline, trackId, position, options = {}) {
|
|
21186
21784
|
if (!Number.isFinite(position)) return null;
|
|
21187
21785
|
const track = timeline.tracks.find((candidate) => candidate.id === trackId);
|
|
@@ -21284,8 +21882,8 @@ function columnAtPosition(timeline, position) {
|
|
|
21284
21882
|
const clamped = Math.max(0, Math.min(1, position));
|
|
21285
21883
|
return Math.round((start + clamped * span) * COORDINATE_PRECISION) / COORDINATE_PRECISION;
|
|
21286
21884
|
}
|
|
21287
|
-
function safeId(value) {
|
|
21288
|
-
return sanitizeTimelineText(value).toLowerCase().replace(/[^a-z0-9_.~-]+/g, "-").replace(/^-+|-+$/g, "") ||
|
|
21885
|
+
function safeId(value, fallback) {
|
|
21886
|
+
return sanitizeTimelineText(value).toLowerCase().replace(/[^a-z0-9_.~-]+/g, "-").replace(/^-+|-+$/g, "") || fallback;
|
|
21289
21887
|
}
|
|
21290
21888
|
function findEvent(timeline, eventId) {
|
|
21291
21889
|
for (const track of timeline.tracks) {
|
|
@@ -21308,6 +21906,9 @@ function countEvents(timeline) {
|
|
|
21308
21906
|
function hasEvent(timeline, eventId) {
|
|
21309
21907
|
return timeline.tracks.some((track) => track.events.some((event) => event.id === eventId));
|
|
21310
21908
|
}
|
|
21909
|
+
function hasTrack(timeline, trackId) {
|
|
21910
|
+
return timeline.tracks.some((track) => track.id === trackId);
|
|
21911
|
+
}
|
|
21311
21912
|
function semanticSignature(timeline) {
|
|
21312
21913
|
return JSON.stringify({
|
|
21313
21914
|
style: timeline.style,
|
|
@@ -21460,6 +22061,8 @@ function verifyRenderedTimeline(next, rendered) {
|
|
|
21460
22061
|
const ids = new Set(
|
|
21461
22062
|
verification.tracks.flatMap((track) => track.events.map((event) => event.id))
|
|
21462
22063
|
);
|
|
22064
|
+
const trackIds = new Set(verification.tracks.map((track) => track.id));
|
|
22065
|
+
if (trackIds.size !== verification.tracks.length) return null;
|
|
21463
22066
|
if (ids.size !== countEvents(verification)) return null;
|
|
21464
22067
|
if (verification.links.some((link) => !ids.has(link.source) || !ids.has(link.target))) {
|
|
21465
22068
|
return null;
|
|
@@ -21478,18 +22081,50 @@ function applyOp3(editor, blockId, op, verifyResult) {
|
|
|
21478
22081
|
if (!result || result.timeline === timeline) return NOT_APPLIED;
|
|
21479
22082
|
const rendered = renderAsciiTimeline(result.timeline);
|
|
21480
22083
|
const verification = verifyRenderedTimeline(result.timeline, rendered);
|
|
21481
|
-
if (!verification || result.eventId && !hasEvent(verification, result.eventId) || verifyResult && !verifyResult(verification)) {
|
|
22084
|
+
if (!verification || result.eventId && !hasEvent(verification, result.eventId) || result.trackId && !hasTrack(verification, result.trackId) || verifyResult && !verifyResult(verification)) {
|
|
21482
22085
|
return NOT_APPLIED;
|
|
21483
22086
|
}
|
|
21484
22087
|
if (!replaceAsciiFenceText(editor, pos, rendered, "timeline")) return NOT_APPLIED;
|
|
21485
22088
|
return {
|
|
21486
22089
|
applied: true,
|
|
21487
|
-
...result.eventId ? { eventId: result.eventId } : {}
|
|
22090
|
+
...result.eventId ? { eventId: result.eventId } : {},
|
|
22091
|
+
...result.trackId ? { trackId: result.trackId } : {}
|
|
21488
22092
|
};
|
|
21489
22093
|
}
|
|
21490
22094
|
function applyTimelineCommand(editor, blockId, command2) {
|
|
21491
22095
|
if (!editor.isEditable) return READ_ONLY;
|
|
21492
22096
|
switch (command2.kind) {
|
|
22097
|
+
case "addTrack":
|
|
22098
|
+
return applyOp3(
|
|
22099
|
+
editor,
|
|
22100
|
+
blockId,
|
|
22101
|
+
(timeline) => addTimelineTrackOp(timeline, {
|
|
22102
|
+
id: command2.id,
|
|
22103
|
+
label: command2.label,
|
|
22104
|
+
eventId: command2.eventId,
|
|
22105
|
+
eventLabel: command2.eventLabel
|
|
22106
|
+
})
|
|
22107
|
+
);
|
|
22108
|
+
case "updateTrack": {
|
|
22109
|
+
const expectedLabel = sanitizeTimelineText(command2.label);
|
|
22110
|
+
return applyOp3(
|
|
22111
|
+
editor,
|
|
22112
|
+
blockId,
|
|
22113
|
+
(timeline) => ({
|
|
22114
|
+
timeline: updateTimelineTrackOp(timeline, command2.trackId, command2.label)
|
|
22115
|
+
}),
|
|
22116
|
+
(timeline) => timeline.tracks.some(
|
|
22117
|
+
(track) => track.id === command2.trackId && track.label === expectedLabel
|
|
22118
|
+
)
|
|
22119
|
+
);
|
|
22120
|
+
}
|
|
22121
|
+
case "removeTrack":
|
|
22122
|
+
return applyOp3(
|
|
22123
|
+
editor,
|
|
22124
|
+
blockId,
|
|
22125
|
+
(timeline) => ({ timeline: removeTimelineTrackOp(timeline, command2.trackId) }),
|
|
22126
|
+
(timeline) => !hasTrack(timeline, command2.trackId)
|
|
22127
|
+
);
|
|
21493
22128
|
case "addEvent":
|
|
21494
22129
|
return applyOp3(editor, blockId, (timeline) => {
|
|
21495
22130
|
const result = addTimelineEventOp(timeline, command2.trackId, command2.position, {
|
|
@@ -21526,14 +22161,16 @@ function applyTimelineCommand(editor, blockId, command2) {
|
|
|
21526
22161
|
}
|
|
21527
22162
|
|
|
21528
22163
|
// src/timeline/TimelineEditorWidget.tsx
|
|
21529
|
-
import { jsx as jsx45, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
22164
|
+
import { Fragment as Fragment15, jsx as jsx45, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
21530
22165
|
var clamp01 = (value) => Math.max(0, Math.min(1, value));
|
|
21531
22166
|
function TimelineEditorWidget({ editor, blockId }) {
|
|
21532
22167
|
const view = useTimelineData(editor, blockId);
|
|
21533
22168
|
const [selectedEventId, setSelectedEventId] = useState40(null);
|
|
22169
|
+
const [addingTrackId, setAddingTrackId] = useState40(null);
|
|
22170
|
+
const [editingTrackId, setEditingTrackId] = useState40(null);
|
|
21534
22171
|
const [hover, setHover] = useState40(null);
|
|
21535
22172
|
const [dragged, setDragged] = useState40(null);
|
|
21536
|
-
const suppressClickEventId =
|
|
22173
|
+
const suppressClickEventId = useRef32(null);
|
|
21537
22174
|
const [announcement, setAnnouncement] = useState40("");
|
|
21538
22175
|
const dispatch = useCallback35(
|
|
21539
22176
|
(command2) => {
|
|
@@ -21556,6 +22193,8 @@ function TimelineEditorWidget({ editor, blockId }) {
|
|
|
21556
22193
|
// editor's cursor/selection hot path.
|
|
21557
22194
|
[sourceText, sourceTimeline]
|
|
21558
22195
|
);
|
|
22196
|
+
const editorEditable = editor.isEditable;
|
|
22197
|
+
const editable = editorEditable && sourceSafe;
|
|
21559
22198
|
const positioned = useMemo29(() => {
|
|
21560
22199
|
if (!view) return [];
|
|
21561
22200
|
const normalized = asciiTimelineToTemplateData(view.timeline).tracks;
|
|
@@ -21603,22 +22242,58 @@ function TimelineEditorWidget({ editor, blockId }) {
|
|
|
21603
22242
|
if (selectedEventId && ids.has(selectedEventId)) return;
|
|
21604
22243
|
setSelectedEventId(view.timeline.tracks[0]?.events[0]?.id ?? null);
|
|
21605
22244
|
}, [selectedEventId, view]);
|
|
22245
|
+
useEffect32(() => {
|
|
22246
|
+
if (editable) return;
|
|
22247
|
+
setAddingTrackId(null);
|
|
22248
|
+
setEditingTrackId(null);
|
|
22249
|
+
setHover(null);
|
|
22250
|
+
}, [editable]);
|
|
22251
|
+
useEffect32(() => {
|
|
22252
|
+
if (!editingTrackId || view?.timeline.tracks.some((track) => track.id === editingTrackId)) {
|
|
22253
|
+
return;
|
|
22254
|
+
}
|
|
22255
|
+
setEditingTrackId(null);
|
|
22256
|
+
}, [editingTrackId, view]);
|
|
21606
22257
|
if (!view) return null;
|
|
21607
|
-
const editorEditable = editor.isEditable;
|
|
21608
|
-
const editable = editorEditable && sourceSafe;
|
|
21609
22258
|
const totalEvents = positioned.length;
|
|
22259
|
+
const addTrack = () => {
|
|
22260
|
+
if (!editable) return;
|
|
22261
|
+
const result = dispatch({ kind: "addTrack" });
|
|
22262
|
+
if (result.applied && result.eventId && result.trackId) {
|
|
22263
|
+
setAddingTrackId(null);
|
|
22264
|
+
setEditingTrackId(result.trackId);
|
|
22265
|
+
setHover(null);
|
|
22266
|
+
setSelectedEventId(result.eventId);
|
|
22267
|
+
setAnnouncement("New timeline line added. Edit its name on the line.");
|
|
22268
|
+
}
|
|
22269
|
+
};
|
|
21610
22270
|
const addEvent = (trackId, position) => {
|
|
21611
22271
|
if (!editable) return;
|
|
21612
22272
|
const result = dispatch({ kind: "addEvent", trackId, position: clamp01(position) });
|
|
21613
22273
|
if (result.applied && result.eventId) {
|
|
22274
|
+
setAddingTrackId(null);
|
|
22275
|
+
setEditingTrackId(null);
|
|
22276
|
+
setHover(null);
|
|
21614
22277
|
setSelectedEventId(result.eventId);
|
|
21615
22278
|
setAnnouncement("New timeline point added. Edit its text below.");
|
|
21616
22279
|
}
|
|
21617
22280
|
};
|
|
21618
22281
|
const selectEvent = (event, trackLabel) => {
|
|
22282
|
+
setAddingTrackId(null);
|
|
22283
|
+
setHover(null);
|
|
21619
22284
|
setSelectedEventId(event.id);
|
|
21620
22285
|
setAnnouncement(`${event.label || "Timeline point"} selected on ${trackLabel}.`);
|
|
21621
22286
|
};
|
|
22287
|
+
const deleteTrack = (trackId, trackLabel) => {
|
|
22288
|
+
const remaining = positioned.filter((point) => point.trackId !== trackId);
|
|
22289
|
+
const result = dispatch({ kind: "removeTrack", trackId });
|
|
22290
|
+
if (!result.applied) return;
|
|
22291
|
+
setAddingTrackId(null);
|
|
22292
|
+
setEditingTrackId((current) => current === trackId ? null : current);
|
|
22293
|
+
setHover(null);
|
|
22294
|
+
if (selected?.trackId === trackId) setSelectedEventId(remaining[0]?.event.id ?? null);
|
|
22295
|
+
setAnnouncement(`${trackLabel} line deleted.`);
|
|
22296
|
+
};
|
|
21622
22297
|
const dragPosition = (event) => {
|
|
21623
22298
|
const rail = event.currentTarget.closest(".squisq-ascii-timeline-rail");
|
|
21624
22299
|
if (!rail) return null;
|
|
@@ -21626,291 +22301,408 @@ function TimelineEditorWidget({ editor, blockId }) {
|
|
|
21626
22301
|
if (rect.width <= 0 || !Number.isFinite(event.clientX)) return null;
|
|
21627
22302
|
return clamp01((event.clientX - rect.left) / rect.width);
|
|
21628
22303
|
};
|
|
21629
|
-
return /* @__PURE__ */ jsxs34(
|
|
21630
|
-
|
|
21631
|
-
|
|
21632
|
-
|
|
21633
|
-
|
|
21634
|
-
|
|
21635
|
-
|
|
21636
|
-
|
|
21637
|
-
|
|
21638
|
-
|
|
21639
|
-
|
|
21640
|
-
|
|
21641
|
-
|
|
21642
|
-
|
|
21643
|
-
|
|
21644
|
-
"
|
|
21645
|
-
|
|
21646
|
-
|
|
21647
|
-
|
|
21648
|
-
|
|
21649
|
-
"
|
|
21650
|
-
|
|
21651
|
-
|
|
21652
|
-
|
|
21653
|
-
|
|
21654
|
-
|
|
21655
|
-
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
21661
|
-
|
|
21662
|
-
{
|
|
21663
|
-
d: `M ${sx} ${sy} C ${sx} ${controlY}, ${tx} ${controlY}, ${tx} ${ty}`,
|
|
21664
|
-
vectorEffect: "non-scaling-stroke"
|
|
21665
|
-
},
|
|
21666
|
-
`${link.source}-${link.target}-${index}`
|
|
21667
|
-
);
|
|
21668
|
-
})
|
|
21669
|
-
}
|
|
21670
|
-
) : null,
|
|
21671
|
-
view.timeline.tracks.map((track, trackIndex) => {
|
|
21672
|
-
const trackLabel = track.label || `Track ${trackIndex + 1}`;
|
|
21673
|
-
const events = track.events.map((event) => ({ event, position: positionById.get(event.id) ?? 0 })).sort((a, b) => a.position - b.position);
|
|
21674
|
-
const eventPositions = events.map((entry) => entry.position);
|
|
21675
|
-
const gaps = insertionGaps(eventPositions);
|
|
21676
|
-
return /* @__PURE__ */ jsxs34("div", { className: "squisq-ascii-timeline-track", children: [
|
|
21677
|
-
/* @__PURE__ */ jsxs34("div", { className: "squisq-ascii-timeline-track-label", title: trackLabel, children: [
|
|
21678
|
-
/* @__PURE__ */ jsx45("span", { children: trackLabel }),
|
|
21679
|
-
editable ? /* @__PURE__ */ jsx45(
|
|
21680
|
-
"button",
|
|
21681
|
-
{
|
|
21682
|
-
type: "button",
|
|
21683
|
-
className: "squisq-ascii-timeline-track-add",
|
|
21684
|
-
"aria-label": `Add point to ${trackLabel} timeline`,
|
|
21685
|
-
title: "Add point in the largest available gap",
|
|
21686
|
-
onClick: () => addEvent(track.id, largestGap(eventPositions)),
|
|
21687
|
-
children: "+"
|
|
21688
|
-
}
|
|
21689
|
-
) : null
|
|
21690
|
-
] }),
|
|
21691
|
-
/* @__PURE__ */ jsxs34(
|
|
21692
|
-
"div",
|
|
22304
|
+
return /* @__PURE__ */ jsxs34(
|
|
22305
|
+
"section",
|
|
22306
|
+
{
|
|
22307
|
+
className: "squisq-ascii-timeline-editor",
|
|
22308
|
+
"aria-label": "Timeline editor",
|
|
22309
|
+
onKeyDown: (event) => {
|
|
22310
|
+
if (event.key !== "Escape" || !addingTrackId) return;
|
|
22311
|
+
setAddingTrackId(null);
|
|
22312
|
+
setHover(null);
|
|
22313
|
+
setAnnouncement("Add point cancelled.");
|
|
22314
|
+
},
|
|
22315
|
+
children: [
|
|
22316
|
+
/* @__PURE__ */ jsxs34("header", { className: "squisq-ascii-timeline-header", children: [
|
|
22317
|
+
/* @__PURE__ */ jsxs34("span", { children: [
|
|
22318
|
+
/* @__PURE__ */ jsx45(Icon, { icon: "fa-solid fa-timeline" }),
|
|
22319
|
+
" Timeline"
|
|
22320
|
+
] }),
|
|
22321
|
+
/* @__PURE__ */ jsxs34("div", { className: "squisq-ascii-timeline-header-actions", children: [
|
|
22322
|
+
editable && addingTrackId ? /* @__PURE__ */ jsx45("small", { children: "Click the line to add a point \xB7 Esc to cancel" }) : editable ? /* @__PURE__ */ jsx45("small", { children: "Use + to add a point \xB7 Drag dots to move" }) : editorEditable ? /* @__PURE__ */ jsx45("small", { children: "Source repair needed" }) : /* @__PURE__ */ jsx45("small", { children: "Read only" }),
|
|
22323
|
+
editable ? /* @__PURE__ */ jsxs34("button", { type: "button", onClick: addTrack, children: [
|
|
22324
|
+
/* @__PURE__ */ jsx45(Icon, { icon: "fa-solid fa-plus" }),
|
|
22325
|
+
" Add line"
|
|
22326
|
+
] }) : null
|
|
22327
|
+
] })
|
|
22328
|
+
] }),
|
|
22329
|
+
/* @__PURE__ */ jsxs34(
|
|
22330
|
+
"div",
|
|
22331
|
+
{
|
|
22332
|
+
className: "squisq-ascii-timeline-canvas",
|
|
22333
|
+
style: { "--squisq-ascii-timeline-track-count": view.timeline.tracks.length },
|
|
22334
|
+
children: [
|
|
22335
|
+
view.timeline.links.length > 0 ? /* @__PURE__ */ jsx45(
|
|
22336
|
+
"svg",
|
|
21693
22337
|
{
|
|
21694
|
-
className:
|
|
21695
|
-
|
|
21696
|
-
"
|
|
21697
|
-
|
|
21698
|
-
|
|
21699
|
-
|
|
21700
|
-
const
|
|
21701
|
-
if (
|
|
21702
|
-
|
|
21703
|
-
|
|
21704
|
-
|
|
21705
|
-
|
|
21706
|
-
|
|
21707
|
-
|
|
21708
|
-
|
|
21709
|
-
|
|
21710
|
-
const rect = event.currentTarget.getBoundingClientRect();
|
|
21711
|
-
if (rect.width <= 0) return;
|
|
21712
|
-
addEvent(track.id, (event.clientX - rect.left) / rect.width);
|
|
21713
|
-
},
|
|
21714
|
-
children: [
|
|
21715
|
-
/* @__PURE__ */ jsx45("span", { className: "squisq-ascii-timeline-rail-line", "aria-hidden": "true" }),
|
|
21716
|
-
hover?.trackId === track.id && !dragged ? /* @__PURE__ */ jsx45(
|
|
21717
|
-
"span",
|
|
22338
|
+
className: "squisq-ascii-timeline-branches",
|
|
22339
|
+
viewBox: `0 0 100 ${Math.max(1, view.timeline.tracks.length) * 96}`,
|
|
22340
|
+
preserveAspectRatio: "none",
|
|
22341
|
+
"aria-hidden": "true",
|
|
22342
|
+
children: view.timeline.links.map((link, index) => {
|
|
22343
|
+
const source = displayedPositioned.find((point) => point.event.id === link.source);
|
|
22344
|
+
const target = displayedPositioned.find((point) => point.event.id === link.target);
|
|
22345
|
+
if (!source || !target) return null;
|
|
22346
|
+
const sx = source.position * 100;
|
|
22347
|
+
const tx = target.position * 100;
|
|
22348
|
+
const sy = source.trackIndex * 96 + 48;
|
|
22349
|
+
const ty = target.trackIndex * 96 + 48;
|
|
22350
|
+
const sameTrack = source.trackIndex === target.trackIndex;
|
|
22351
|
+
const controlY = sameTrack ? sy + (source.event.side === "below" ? -30 : 30) : (sy + ty) / 2;
|
|
22352
|
+
return /* @__PURE__ */ jsx45(
|
|
22353
|
+
"path",
|
|
21718
22354
|
{
|
|
21719
|
-
|
|
21720
|
-
|
|
21721
|
-
|
|
21722
|
-
|
|
21723
|
-
|
|
21724
|
-
|
|
21725
|
-
|
|
21726
|
-
|
|
22355
|
+
d: `M ${sx} ${sy} C ${sx} ${controlY}, ${tx} ${controlY}, ${tx} ${ty}`,
|
|
22356
|
+
vectorEffect: "non-scaling-stroke"
|
|
22357
|
+
},
|
|
22358
|
+
`${link.source}-${link.target}-${index}`
|
|
22359
|
+
);
|
|
22360
|
+
})
|
|
22361
|
+
}
|
|
22362
|
+
) : null,
|
|
22363
|
+
view.timeline.tracks.map((track, trackIndex) => {
|
|
22364
|
+
const trackLabel = track.label || `Track ${trackIndex + 1}`;
|
|
22365
|
+
const events = track.events.map((event) => ({ event, position: positionById.get(event.id) ?? 0 })).sort((a, b) => a.position - b.position);
|
|
22366
|
+
const eventPositions = events.map((entry) => entry.position);
|
|
22367
|
+
const gaps = insertionGaps(eventPositions);
|
|
22368
|
+
const addingPoint = editable && addingTrackId === track.id;
|
|
22369
|
+
return /* @__PURE__ */ jsxs34("div", { className: "squisq-ascii-timeline-track", children: [
|
|
22370
|
+
/* @__PURE__ */ jsxs34("div", { className: "squisq-ascii-timeline-track-label", children: [
|
|
22371
|
+
/* @__PURE__ */ jsx45(
|
|
22372
|
+
InlineTrackLabel,
|
|
21727
22373
|
{
|
|
21728
|
-
|
|
21729
|
-
|
|
21730
|
-
|
|
21731
|
-
|
|
21732
|
-
|
|
21733
|
-
|
|
21734
|
-
|
|
21735
|
-
|
|
22374
|
+
trackId: track.id,
|
|
22375
|
+
label: trackLabel,
|
|
22376
|
+
editing: editable && editingTrackId === track.id,
|
|
22377
|
+
editable,
|
|
22378
|
+
dispatch,
|
|
22379
|
+
onStart: () => {
|
|
22380
|
+
setAddingTrackId(null);
|
|
22381
|
+
setHover(null);
|
|
22382
|
+
setEditingTrackId(track.id);
|
|
21736
22383
|
},
|
|
21737
|
-
|
|
21738
|
-
}
|
|
21739
|
-
|
|
21740
|
-
|
|
21741
|
-
|
|
21742
|
-
|
|
21743
|
-
const draggedPoint = dragged?.eventId === event.id;
|
|
21744
|
-
const side = event.side ?? (eventIndex % 2 === 0 ? "above" : "below");
|
|
21745
|
-
const descriptionSide = event.descriptionSide ?? side;
|
|
21746
|
-
return /* @__PURE__ */ jsxs34(
|
|
21747
|
-
"span",
|
|
22384
|
+
onFinish: () => setEditingTrackId(null)
|
|
22385
|
+
}
|
|
22386
|
+
),
|
|
22387
|
+
editable ? /* @__PURE__ */ jsxs34(Fragment15, { children: [
|
|
22388
|
+
/* @__PURE__ */ jsx45(
|
|
22389
|
+
"button",
|
|
21748
22390
|
{
|
|
21749
|
-
|
|
21750
|
-
|
|
21751
|
-
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
|
|
21755
|
-
|
|
21756
|
-
|
|
21757
|
-
|
|
21758
|
-
|
|
21759
|
-
|
|
21760
|
-
|
|
21761
|
-
|
|
21762
|
-
|
|
21763
|
-
|
|
21764
|
-
|
|
21765
|
-
|
|
21766
|
-
|
|
21767
|
-
|
|
21768
|
-
|
|
21769
|
-
|
|
21770
|
-
|
|
21771
|
-
|
|
21772
|
-
|
|
21773
|
-
|
|
21774
|
-
|
|
21775
|
-
|
|
21776
|
-
|
|
21777
|
-
|
|
21778
|
-
|
|
21779
|
-
|
|
21780
|
-
|
|
21781
|
-
|
|
21782
|
-
|
|
21783
|
-
|
|
21784
|
-
|
|
21785
|
-
|
|
21786
|
-
|
|
21787
|
-
|
|
21788
|
-
|
|
21789
|
-
|
|
21790
|
-
|
|
21791
|
-
|
|
21792
|
-
|
|
21793
|
-
|
|
21794
|
-
|
|
21795
|
-
|
|
21796
|
-
|
|
21797
|
-
|
|
21798
|
-
|
|
21799
|
-
|
|
21800
|
-
|
|
21801
|
-
|
|
21802
|
-
|
|
21803
|
-
|
|
21804
|
-
|
|
21805
|
-
|
|
21806
|
-
|
|
21807
|
-
|
|
21808
|
-
|
|
21809
|
-
|
|
21810
|
-
|
|
21811
|
-
|
|
21812
|
-
|
|
21813
|
-
|
|
21814
|
-
|
|
21815
|
-
|
|
21816
|
-
|
|
21817
|
-
|
|
21818
|
-
|
|
21819
|
-
|
|
21820
|
-
|
|
21821
|
-
|
|
21822
|
-
|
|
21823
|
-
|
|
21824
|
-
|
|
21825
|
-
|
|
21826
|
-
|
|
21827
|
-
|
|
21828
|
-
|
|
21829
|
-
|
|
21830
|
-
|
|
21831
|
-
|
|
21832
|
-
|
|
21833
|
-
|
|
21834
|
-
|
|
21835
|
-
|
|
21836
|
-
|
|
21837
|
-
|
|
21838
|
-
|
|
21839
|
-
|
|
21840
|
-
|
|
21841
|
-
|
|
21842
|
-
|
|
21843
|
-
|
|
21844
|
-
|
|
21845
|
-
}
|
|
21846
|
-
},
|
|
21847
|
-
onPointerCancel: (pointerEvent) => {
|
|
21848
|
-
if (!draggedPoint || dragged.pointerId !== pointerEvent.pointerId) {
|
|
21849
|
-
return;
|
|
22391
|
+
type: "button",
|
|
22392
|
+
className: "squisq-ascii-timeline-track-delete",
|
|
22393
|
+
"aria-label": `Delete line: ${trackLabel}`,
|
|
22394
|
+
disabled: view.timeline.tracks.length <= 1,
|
|
22395
|
+
title: view.timeline.tracks.length > 1 ? `Delete ${trackLabel} line and all of its points` : "A timeline needs one line",
|
|
22396
|
+
onClick: () => deleteTrack(track.id, trackLabel),
|
|
22397
|
+
children: /* @__PURE__ */ jsx45(Icon, { icon: "fa-solid fa-trash" })
|
|
22398
|
+
}
|
|
22399
|
+
),
|
|
22400
|
+
/* @__PURE__ */ jsx45(
|
|
22401
|
+
"button",
|
|
22402
|
+
{
|
|
22403
|
+
type: "button",
|
|
22404
|
+
className: "squisq-ascii-timeline-track-add",
|
|
22405
|
+
"aria-label": `Add point to ${trackLabel} timeline`,
|
|
22406
|
+
"aria-pressed": addingPoint,
|
|
22407
|
+
title: addingPoint ? "Cancel adding a point" : "Choose where to add a point",
|
|
22408
|
+
onClick: () => {
|
|
22409
|
+
setEditingTrackId(null);
|
|
22410
|
+
setAddingTrackId((current) => current === track.id ? null : track.id);
|
|
22411
|
+
setHover(null);
|
|
22412
|
+
setAnnouncement(
|
|
22413
|
+
addingPoint ? "Add point cancelled." : `Choose where to add a point on ${trackLabel}.`
|
|
22414
|
+
);
|
|
22415
|
+
},
|
|
22416
|
+
children: "+"
|
|
22417
|
+
}
|
|
22418
|
+
)
|
|
22419
|
+
] }) : null
|
|
22420
|
+
] }),
|
|
22421
|
+
/* @__PURE__ */ jsxs34(
|
|
22422
|
+
"div",
|
|
22423
|
+
{
|
|
22424
|
+
className: `squisq-ascii-timeline-rail${addingPoint ? " squisq-ascii-timeline-rail--adding" : ""}${editable ? "" : " squisq-ascii-timeline-rail--readonly"}`,
|
|
22425
|
+
role: "group",
|
|
22426
|
+
"aria-label": `${trackLabel} timeline rail`,
|
|
22427
|
+
title: addingPoint ? "Click anywhere on the line to add a point" : void 0,
|
|
22428
|
+
onPointerMove: (event) => {
|
|
22429
|
+
if (!addingPoint || dragged) return;
|
|
22430
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
22431
|
+
if (rect.width <= 0) return;
|
|
22432
|
+
setHover({
|
|
22433
|
+
trackId: track.id,
|
|
22434
|
+
position: clamp01((event.clientX - rect.left) / rect.width)
|
|
22435
|
+
});
|
|
22436
|
+
},
|
|
22437
|
+
onPointerLeave: () => setHover((current) => current?.trackId === track.id ? null : current),
|
|
22438
|
+
onClick: (event) => {
|
|
22439
|
+
if (!addingPoint || event.target !== event.currentTarget) return;
|
|
22440
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
22441
|
+
if (rect.width <= 0) return;
|
|
22442
|
+
addEvent(track.id, (event.clientX - rect.left) / rect.width);
|
|
22443
|
+
},
|
|
22444
|
+
children: [
|
|
22445
|
+
/* @__PURE__ */ jsx45("span", { className: "squisq-ascii-timeline-rail-line", "aria-hidden": "true" }),
|
|
22446
|
+
addingPoint && hover?.trackId === track.id && !dragged ? /* @__PURE__ */ jsx45(
|
|
22447
|
+
"span",
|
|
22448
|
+
{
|
|
22449
|
+
className: "squisq-ascii-timeline-add-ghost",
|
|
22450
|
+
style: { left: `${hover.position * 100}%` },
|
|
22451
|
+
"aria-hidden": "true",
|
|
22452
|
+
children: "+"
|
|
22453
|
+
}
|
|
22454
|
+
) : null,
|
|
22455
|
+
addingPoint && !dragged ? gaps.map((position) => /* @__PURE__ */ jsx45(
|
|
22456
|
+
"button",
|
|
22457
|
+
{
|
|
22458
|
+
type: "button",
|
|
22459
|
+
className: "squisq-ascii-timeline-gap-add",
|
|
22460
|
+
style: { left: `${position * 100}%` },
|
|
22461
|
+
"aria-label": `Add point to ${trackLabel} at ${Math.round(position * 100)} percent`,
|
|
22462
|
+
title: "Add point",
|
|
22463
|
+
onClick: (event) => {
|
|
22464
|
+
event.stopPropagation();
|
|
22465
|
+
addEvent(track.id, position);
|
|
22466
|
+
},
|
|
22467
|
+
children: "+"
|
|
22468
|
+
},
|
|
22469
|
+
position
|
|
22470
|
+
)) : null,
|
|
22471
|
+
events.map(({ event, position }, eventIndex) => {
|
|
22472
|
+
const selectedPoint = selectedEventId === event.id;
|
|
22473
|
+
const draggedPoint = dragged?.eventId === event.id;
|
|
22474
|
+
const side = event.side ?? (eventIndex % 2 === 0 ? "above" : "below");
|
|
22475
|
+
const descriptionSide = event.descriptionSide ?? side;
|
|
22476
|
+
return /* @__PURE__ */ jsxs34(
|
|
22477
|
+
"span",
|
|
22478
|
+
{
|
|
22479
|
+
className: `squisq-ascii-timeline-point squisq-ascii-timeline-point--${side}${draggedPoint ? " squisq-ascii-timeline-point--dragging" : ""}`,
|
|
22480
|
+
style: { left: `${position * 100}%` },
|
|
22481
|
+
children: [
|
|
22482
|
+
event.callout !== false ? /* @__PURE__ */ jsx45(
|
|
22483
|
+
"span",
|
|
22484
|
+
{
|
|
22485
|
+
className: `squisq-ascii-timeline-callout squisq-ascii-timeline-callout--${side}`,
|
|
22486
|
+
children: event.label
|
|
21850
22487
|
}
|
|
21851
|
-
|
|
21852
|
-
|
|
21853
|
-
|
|
21854
|
-
|
|
22488
|
+
) : null,
|
|
22489
|
+
event.callout !== false && event.description ? /* @__PURE__ */ jsx45(
|
|
22490
|
+
"span",
|
|
22491
|
+
{
|
|
22492
|
+
className: `squisq-ascii-timeline-description squisq-ascii-timeline-description--${descriptionSide}`,
|
|
22493
|
+
children: event.description
|
|
21855
22494
|
}
|
|
21856
|
-
|
|
21857
|
-
|
|
21858
|
-
|
|
21859
|
-
|
|
21860
|
-
|
|
21861
|
-
|
|
21862
|
-
|
|
21863
|
-
|
|
22495
|
+
) : null,
|
|
22496
|
+
/* @__PURE__ */ jsx45(
|
|
22497
|
+
"button",
|
|
22498
|
+
{
|
|
22499
|
+
type: "button",
|
|
22500
|
+
className: `squisq-ascii-timeline-marker squisq-ascii-timeline-marker--${event.marker ?? "filled"}${selectedPoint ? " squisq-ascii-timeline-marker--selected" : ""}`,
|
|
22501
|
+
"aria-label": `Edit ${event.label || "timeline point"}, ${trackLabel}, ${Math.round(position * 100)} percent`,
|
|
22502
|
+
"aria-pressed": selectedPoint,
|
|
22503
|
+
"aria-roledescription": editable ? "draggable timeline point" : void 0,
|
|
22504
|
+
title: editable ? `${event.description || event.label || "Timeline point"} \xB7 Drag to move` : event.description || event.label,
|
|
22505
|
+
draggable: false,
|
|
22506
|
+
onPointerDown: (pointerEvent) => {
|
|
22507
|
+
if (!editable || pointerEvent.button !== void 0 && pointerEvent.button !== 0) {
|
|
22508
|
+
return;
|
|
22509
|
+
}
|
|
22510
|
+
const point = dragPosition(pointerEvent);
|
|
22511
|
+
if (point === null) return;
|
|
22512
|
+
pointerEvent.stopPropagation();
|
|
22513
|
+
setAddingTrackId(null);
|
|
22514
|
+
setHover(null);
|
|
22515
|
+
setSelectedEventId(event.id);
|
|
22516
|
+
suppressClickEventId.current = null;
|
|
22517
|
+
setDragged({
|
|
22518
|
+
eventId: event.id,
|
|
22519
|
+
pointerId: pointerEvent.pointerId,
|
|
22520
|
+
startClientX: pointerEvent.clientX,
|
|
22521
|
+
startPosition: position,
|
|
22522
|
+
position,
|
|
22523
|
+
moved: false
|
|
22524
|
+
});
|
|
22525
|
+
pointerEvent.currentTarget.setPointerCapture?.(pointerEvent.pointerId);
|
|
22526
|
+
},
|
|
22527
|
+
onPointerMove: (pointerEvent) => {
|
|
22528
|
+
if (!editable || !draggedPoint || dragged.pointerId !== pointerEvent.pointerId) {
|
|
22529
|
+
return;
|
|
22530
|
+
}
|
|
22531
|
+
const point = dragPosition(pointerEvent);
|
|
22532
|
+
if (point === null) return;
|
|
22533
|
+
pointerEvent.preventDefault();
|
|
22534
|
+
pointerEvent.stopPropagation();
|
|
22535
|
+
setDragged(
|
|
22536
|
+
(current) => current?.eventId === event.id && current.pointerId === pointerEvent.pointerId ? {
|
|
22537
|
+
...current,
|
|
22538
|
+
position: point,
|
|
22539
|
+
moved: current.moved || Math.abs(pointerEvent.clientX - current.startClientX) >= 3
|
|
22540
|
+
} : current
|
|
22541
|
+
);
|
|
22542
|
+
},
|
|
22543
|
+
onPointerUp: (pointerEvent) => {
|
|
22544
|
+
if (!draggedPoint || dragged.pointerId !== pointerEvent.pointerId) {
|
|
22545
|
+
return;
|
|
22546
|
+
}
|
|
22547
|
+
const point = dragPosition(pointerEvent);
|
|
22548
|
+
const finalPosition = point ?? dragged.position;
|
|
22549
|
+
const moved = dragged.moved || Math.abs(pointerEvent.clientX - dragged.startClientX) >= 3;
|
|
22550
|
+
pointerEvent.preventDefault();
|
|
22551
|
+
pointerEvent.stopPropagation();
|
|
22552
|
+
if (pointerEvent.currentTarget.hasPointerCapture?.(pointerEvent.pointerId)) {
|
|
22553
|
+
pointerEvent.currentTarget.releasePointerCapture(
|
|
22554
|
+
pointerEvent.pointerId
|
|
22555
|
+
);
|
|
22556
|
+
}
|
|
22557
|
+
setDragged(null);
|
|
22558
|
+
if (!moved) {
|
|
22559
|
+
selectEvent(event, trackLabel);
|
|
22560
|
+
return;
|
|
22561
|
+
}
|
|
22562
|
+
suppressClickEventId.current = event.id;
|
|
22563
|
+
const result = dispatch({
|
|
22564
|
+
kind: "updateEvent",
|
|
22565
|
+
eventId: event.id,
|
|
22566
|
+
patch: { position: finalPosition }
|
|
22567
|
+
});
|
|
22568
|
+
if (result.applied) {
|
|
22569
|
+
setAnnouncement(
|
|
22570
|
+
`${event.label || "Timeline point"} moved to ${Math.round(finalPosition * 100)} percent.`
|
|
22571
|
+
);
|
|
22572
|
+
} else if (!result.reason && Math.abs(finalPosition - dragged.startPosition) > 1e-3) {
|
|
22573
|
+
setAnnouncement(
|
|
22574
|
+
"Timeline points cannot overlap. Move the point elsewhere."
|
|
22575
|
+
);
|
|
22576
|
+
}
|
|
22577
|
+
},
|
|
22578
|
+
onPointerCancel: (pointerEvent) => {
|
|
22579
|
+
if (!draggedPoint || dragged.pointerId !== pointerEvent.pointerId) {
|
|
22580
|
+
return;
|
|
22581
|
+
}
|
|
22582
|
+
if (pointerEvent.currentTarget.hasPointerCapture?.(pointerEvent.pointerId)) {
|
|
22583
|
+
pointerEvent.currentTarget.releasePointerCapture(
|
|
22584
|
+
pointerEvent.pointerId
|
|
22585
|
+
);
|
|
22586
|
+
}
|
|
22587
|
+
setDragged(null);
|
|
22588
|
+
setAnnouncement("Timeline point move cancelled.");
|
|
22589
|
+
},
|
|
22590
|
+
onClick: (clickEvent) => {
|
|
22591
|
+
clickEvent.stopPropagation();
|
|
22592
|
+
if (suppressClickEventId.current === event.id) {
|
|
22593
|
+
suppressClickEventId.current = null;
|
|
22594
|
+
return;
|
|
22595
|
+
}
|
|
22596
|
+
selectEvent(event, trackLabel);
|
|
22597
|
+
},
|
|
22598
|
+
onKeyDown: (keyEvent) => navigateTrackPoints(keyEvent)
|
|
21864
22599
|
}
|
|
21865
|
-
|
|
21866
|
-
|
|
21867
|
-
|
|
21868
|
-
|
|
21869
|
-
|
|
21870
|
-
|
|
21871
|
-
}
|
|
21872
|
-
|
|
21873
|
-
|
|
21874
|
-
|
|
21875
|
-
|
|
21876
|
-
|
|
21877
|
-
|
|
21878
|
-
)
|
|
21879
|
-
] }, track.id);
|
|
21880
|
-
})
|
|
21881
|
-
]
|
|
21882
|
-
}
|
|
21883
|
-
),
|
|
21884
|
-
view.timeline.links.length > 0 ? /* @__PURE__ */ jsx45("ul", { className: "squisq-ascii-timeline-link-list", "aria-label": "Timeline branches", children: view.timeline.links.map((link, index) => /* @__PURE__ */ jsxs34("li", { children: [
|
|
21885
|
-
link.source,
|
|
21886
|
-
" \u2192 ",
|
|
21887
|
-
link.target,
|
|
21888
|
-
link.label ? `: ${link.label}` : ""
|
|
21889
|
-
] }, `${link.source}-${link.target}-${index}`)) }) : null,
|
|
21890
|
-
selected ? /* @__PURE__ */ jsx45(
|
|
21891
|
-
EventInspector,
|
|
21892
|
-
{
|
|
21893
|
-
selected,
|
|
21894
|
-
editable,
|
|
21895
|
-
canDelete: totalEvents > 1,
|
|
21896
|
-
dispatch,
|
|
21897
|
-
onDelete: () => {
|
|
21898
|
-
const remaining = positioned.filter((point) => point.event.id !== selected.event.id);
|
|
21899
|
-
const result = dispatch({ kind: "removeEvent", eventId: selected.event.id });
|
|
21900
|
-
if (result.applied) {
|
|
21901
|
-
setSelectedEventId(remaining[0]?.event.id ?? null);
|
|
21902
|
-
setAnnouncement(`${selected.event.label} deleted.`);
|
|
22600
|
+
)
|
|
22601
|
+
]
|
|
22602
|
+
},
|
|
22603
|
+
event.id
|
|
22604
|
+
);
|
|
22605
|
+
}),
|
|
22606
|
+
track.endLabel ? /* @__PURE__ */ jsx45("span", { className: "squisq-ascii-timeline-end-label", children: track.endLabel }) : null
|
|
22607
|
+
]
|
|
22608
|
+
}
|
|
22609
|
+
)
|
|
22610
|
+
] }, track.id);
|
|
22611
|
+
})
|
|
22612
|
+
]
|
|
21903
22613
|
}
|
|
22614
|
+
),
|
|
22615
|
+
view.timeline.links.length > 0 ? /* @__PURE__ */ jsx45("ul", { className: "squisq-ascii-timeline-link-list", "aria-label": "Timeline branches", children: view.timeline.links.map((link, index) => /* @__PURE__ */ jsxs34("li", { children: [
|
|
22616
|
+
link.source,
|
|
22617
|
+
" \u2192 ",
|
|
22618
|
+
link.target,
|
|
22619
|
+
link.label ? `: ${link.label}` : ""
|
|
22620
|
+
] }, `${link.source}-${link.target}-${index}`)) }) : null,
|
|
22621
|
+
selected ? /* @__PURE__ */ jsx45(
|
|
22622
|
+
EventInspector,
|
|
22623
|
+
{
|
|
22624
|
+
selected,
|
|
22625
|
+
editable,
|
|
22626
|
+
canDelete: totalEvents > 1,
|
|
22627
|
+
dispatch,
|
|
22628
|
+
onDelete: () => {
|
|
22629
|
+
const remaining = positioned.filter((point) => point.event.id !== selected.event.id);
|
|
22630
|
+
const result = dispatch({ kind: "removeEvent", eventId: selected.event.id });
|
|
22631
|
+
if (result.applied) {
|
|
22632
|
+
setSelectedEventId(remaining[0]?.event.id ?? null);
|
|
22633
|
+
setAnnouncement(`${selected.event.label} deleted.`);
|
|
22634
|
+
}
|
|
22635
|
+
}
|
|
22636
|
+
},
|
|
22637
|
+
`${selected.trackId}:${selected.event.id}`
|
|
22638
|
+
) : null,
|
|
22639
|
+
!sourceSafe ? /* @__PURE__ */ jsxs34("div", { className: "squisq-ascii-timeline-warnings", role: "status", children: [
|
|
22640
|
+
"Visual editing paused: this timeline contains source the canvas cannot safely rewrite. Repair it in Source view first.",
|
|
22641
|
+
view.warnings.length > 0 ? ` (${view.warnings.length} parser note${view.warnings.length === 1 ? "" : "s"})` : ""
|
|
22642
|
+
] }) : null,
|
|
22643
|
+
/* @__PURE__ */ jsx45("div", { className: "squisq-sr-only", "aria-live": "polite", children: announcement })
|
|
22644
|
+
]
|
|
22645
|
+
}
|
|
22646
|
+
);
|
|
22647
|
+
}
|
|
22648
|
+
function InlineTrackLabel({
|
|
22649
|
+
trackId,
|
|
22650
|
+
label,
|
|
22651
|
+
editing,
|
|
22652
|
+
editable,
|
|
22653
|
+
dispatch,
|
|
22654
|
+
onStart,
|
|
22655
|
+
onFinish
|
|
22656
|
+
}) {
|
|
22657
|
+
const [draft, setDraft] = useState40(label);
|
|
22658
|
+
useEffect32(() => {
|
|
22659
|
+
if (editing) setDraft(label);
|
|
22660
|
+
}, [editing, label]);
|
|
22661
|
+
const commitLabel = () => {
|
|
22662
|
+
const next = draft.trim();
|
|
22663
|
+
if (!next) {
|
|
22664
|
+
setDraft(label);
|
|
22665
|
+
} else if (next !== label) {
|
|
22666
|
+
const result = dispatch({ kind: "updateTrack", trackId, label: next });
|
|
22667
|
+
if (!result.applied) setDraft(label);
|
|
22668
|
+
}
|
|
22669
|
+
onFinish();
|
|
22670
|
+
};
|
|
22671
|
+
if (!editable) return /* @__PURE__ */ jsx45("span", { className: "squisq-ascii-timeline-track-name", children: label });
|
|
22672
|
+
return editing ? /* @__PURE__ */ jsx45(
|
|
22673
|
+
"input",
|
|
22674
|
+
{
|
|
22675
|
+
className: "squisq-ascii-timeline-track-name-input",
|
|
22676
|
+
"aria-label": `Rename line: ${label}`,
|
|
22677
|
+
value: draft,
|
|
22678
|
+
autoFocus: true,
|
|
22679
|
+
onFocus: (event) => event.currentTarget.select(),
|
|
22680
|
+
onChange: (event) => setDraft(event.target.value),
|
|
22681
|
+
onBlur: commitLabel,
|
|
22682
|
+
onKeyDown: (event) => {
|
|
22683
|
+
if (event.key === "Enter") {
|
|
22684
|
+
event.preventDefault();
|
|
22685
|
+
event.stopPropagation();
|
|
22686
|
+
commitLabel();
|
|
22687
|
+
} else if (event.key === "Escape") {
|
|
22688
|
+
event.preventDefault();
|
|
22689
|
+
event.stopPropagation();
|
|
22690
|
+
setDraft(label);
|
|
22691
|
+
onFinish();
|
|
21904
22692
|
}
|
|
21905
|
-
}
|
|
21906
|
-
|
|
21907
|
-
|
|
21908
|
-
|
|
21909
|
-
|
|
21910
|
-
|
|
21911
|
-
|
|
21912
|
-
|
|
21913
|
-
|
|
22693
|
+
}
|
|
22694
|
+
}
|
|
22695
|
+
) : /* @__PURE__ */ jsx45(
|
|
22696
|
+
"button",
|
|
22697
|
+
{
|
|
22698
|
+
type: "button",
|
|
22699
|
+
className: "squisq-ascii-timeline-track-name",
|
|
22700
|
+
"aria-label": `Rename line: ${label}`,
|
|
22701
|
+
title: `Rename ${label}`,
|
|
22702
|
+
onClick: onStart,
|
|
22703
|
+
children: label
|
|
22704
|
+
}
|
|
22705
|
+
);
|
|
21914
22706
|
}
|
|
21915
22707
|
function EventInspector({
|
|
21916
22708
|
selected,
|
|
@@ -22075,19 +22867,6 @@ function insertionGaps(positions) {
|
|
|
22075
22867
|
}
|
|
22076
22868
|
return gaps;
|
|
22077
22869
|
}
|
|
22078
|
-
function largestGap(positions) {
|
|
22079
|
-
if (positions.length === 0) return 0.5;
|
|
22080
|
-
const sorted = [0, ...positions.map(clamp01).sort((a, b) => a - b), 1];
|
|
22081
|
-
let start = sorted[0];
|
|
22082
|
-
let end = sorted[1];
|
|
22083
|
-
for (let index = 1; index < sorted.length - 1; index++) {
|
|
22084
|
-
if (sorted[index + 1] - sorted[index] > end - start) {
|
|
22085
|
-
start = sorted[index];
|
|
22086
|
-
end = sorted[index + 1];
|
|
22087
|
-
}
|
|
22088
|
-
}
|
|
22089
|
-
return (start + end) / 2;
|
|
22090
|
-
}
|
|
22091
22870
|
function navigateTrackPoints(event) {
|
|
22092
22871
|
if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
|
|
22093
22872
|
const rail = event.currentTarget.closest(".squisq-ascii-timeline-rail");
|
|
@@ -22475,7 +23254,7 @@ var InlineIcon = Node2.create({
|
|
|
22475
23254
|
});
|
|
22476
23255
|
|
|
22477
23256
|
// src/ImageNodeView.tsx
|
|
22478
|
-
import { useEffect as useEffect33, useRef as
|
|
23257
|
+
import { useEffect as useEffect33, useRef as useRef33, useState as useState41 } from "react";
|
|
22479
23258
|
import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
|
|
22480
23259
|
import Image2 from "@tiptap/extension-image";
|
|
22481
23260
|
|
|
@@ -22491,13 +23270,13 @@ function normalizeMalformedAssetUrl(src) {
|
|
|
22491
23270
|
}
|
|
22492
23271
|
|
|
22493
23272
|
// src/ImageNodeView.tsx
|
|
22494
|
-
import { Fragment as
|
|
23273
|
+
import { Fragment as Fragment16, jsx as jsx46, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
22495
23274
|
function ImageComponent({ node, selected, editor, updateAttributes: updateAttributes2 }) {
|
|
22496
23275
|
const { src, alt, title, width } = node.attrs;
|
|
22497
23276
|
const { mediaProvider, imageDisplayMode, openImageEdit, mediaRevision } = useEditorContext();
|
|
22498
23277
|
const [resolvedSrc, setResolvedSrc] = useState41(src);
|
|
22499
23278
|
const [hovered, setHovered] = useState41(false);
|
|
22500
|
-
const imgRef =
|
|
23279
|
+
const imgRef = useRef33(null);
|
|
22501
23280
|
const [previewWidth, setPreviewWidth] = useState41(null);
|
|
22502
23281
|
const isThumbnail = imageDisplayMode === "thumbnail";
|
|
22503
23282
|
const isEditable = editor?.isEditable ?? true;
|
|
@@ -22619,7 +23398,7 @@ function ImageComponent({ node, selected, editor, updateAttributes: updateAttrib
|
|
|
22619
23398
|
]
|
|
22620
23399
|
}
|
|
22621
23400
|
),
|
|
22622
|
-
showResize && /* @__PURE__ */ jsxs35(
|
|
23401
|
+
showResize && /* @__PURE__ */ jsxs35(Fragment16, { children: [
|
|
22623
23402
|
/* @__PURE__ */ jsx46(
|
|
22624
23403
|
"span",
|
|
22625
23404
|
{
|
|
@@ -22827,7 +23606,7 @@ var TiptapAudio = Node2.create({
|
|
|
22827
23606
|
});
|
|
22828
23607
|
|
|
22829
23608
|
// src/BlockPropertiesPopover.tsx
|
|
22830
|
-
import { useEffect as useEffect35, useId as useId8, useRef as
|
|
23609
|
+
import { useEffect as useEffect35, useId as useId8, useRef as useRef34, useState as useState43 } from "react";
|
|
22831
23610
|
import { createPortal as createPortal10 } from "react-dom";
|
|
22832
23611
|
import { jsx as jsx49, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
22833
23612
|
var TRANSITION_FLYOUT = ".squisq-transition-flyout";
|
|
@@ -22841,7 +23620,7 @@ function BlockPropertiesPopover({
|
|
|
22841
23620
|
accentColor,
|
|
22842
23621
|
onClose
|
|
22843
23622
|
}) {
|
|
22844
|
-
const panelRef =
|
|
23623
|
+
const panelRef = useRef34(null);
|
|
22845
23624
|
const panelId = `squisq-block-props-portal-${useId8().replace(/:/g, "")}`;
|
|
22846
23625
|
const [inner, setInner] = useState43(blockAttrs);
|
|
22847
23626
|
const [templateInner, setTemplateInner] = useState43(templateParams);
|
|
@@ -23383,19 +24162,19 @@ function WysiwygEditor({
|
|
|
23383
24162
|
},
|
|
23384
24163
|
[docTemplates, onDocTemplatesChange]
|
|
23385
24164
|
);
|
|
23386
|
-
const mentionProviderRef =
|
|
24165
|
+
const mentionProviderRef = useRef35(mentionProvider);
|
|
23387
24166
|
useEffect36(() => {
|
|
23388
24167
|
mentionProviderRef.current = mentionProvider;
|
|
23389
24168
|
}, [mentionProvider]);
|
|
23390
24169
|
const resolvedPlaceholder = useMemo30(() => placeholder ?? pickEmptyPrompt(), [placeholder]);
|
|
23391
|
-
const isExternalUpdate =
|
|
23392
|
-
const lastSourceRef =
|
|
23393
|
-
const mediaProviderRef =
|
|
24170
|
+
const isExternalUpdate = useRef35(false);
|
|
24171
|
+
const lastSourceRef = useRef35(editorSource);
|
|
24172
|
+
const mediaProviderRef = useRef35(mediaProvider);
|
|
23394
24173
|
useEffect36(() => {
|
|
23395
24174
|
mediaProviderRef.current = mediaProvider;
|
|
23396
24175
|
}, [mediaProvider]);
|
|
23397
|
-
const frontmatterRef =
|
|
23398
|
-
const submitOnEnterRef =
|
|
24176
|
+
const frontmatterRef = useRef35(stripFrontmatter(editorSource).frontmatter);
|
|
24177
|
+
const submitOnEnterRef = useRef35(submitOnEnter);
|
|
23399
24178
|
useEffect36(() => {
|
|
23400
24179
|
submitOnEnterRef.current = submitOnEnter;
|
|
23401
24180
|
}, [submitOnEnter]);
|
|
@@ -23584,7 +24363,7 @@ function WysiwygEditor({
|
|
|
23584
24363
|
useEffect36(() => {
|
|
23585
24364
|
if (editor) editor.setEditable(!readOnly);
|
|
23586
24365
|
}, [editor, readOnly]);
|
|
23587
|
-
const containerRef =
|
|
24366
|
+
const containerRef = useRef35(null);
|
|
23588
24367
|
const [badgeMenu, setBadgeMenu] = useState44(null);
|
|
23589
24368
|
const [propsMenu, setPropsMenu] = useState44(null);
|
|
23590
24369
|
const closeBadgeMenu = useCallback36(() => {
|
|
@@ -23861,7 +24640,7 @@ function moveSelectionToDropPoint(view, event) {
|
|
|
23861
24640
|
}
|
|
23862
24641
|
|
|
23863
24642
|
// src/InlinePreviewGutter.tsx
|
|
23864
|
-
import { useLayoutEffect as useLayoutEffect4, useMemo as useMemo32, useRef as
|
|
24643
|
+
import { useLayoutEffect as useLayoutEffect4, useMemo as useMemo32, useRef as useRef36, useState as useState46 } from "react";
|
|
23865
24644
|
import { VIEWPORT_PRESETS as VIEWPORT_PRESETS4 } from "@bendyline/squisq/schemas";
|
|
23866
24645
|
import {
|
|
23867
24646
|
flattenBlocks as flattenBlocks4,
|
|
@@ -24071,7 +24850,7 @@ function sameEntries(a, b) {
|
|
|
24071
24850
|
}
|
|
24072
24851
|
|
|
24073
24852
|
// src/InlinePreviewGutter.tsx
|
|
24074
|
-
import { Fragment as
|
|
24853
|
+
import { Fragment as Fragment17, jsx as jsx51, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
24075
24854
|
function isAnnotated(block) {
|
|
24076
24855
|
const annotation = block.sourceHeading?.templateAnnotation;
|
|
24077
24856
|
if (!annotation) return false;
|
|
@@ -24237,7 +25016,7 @@ function InlinePreviewGutter({
|
|
|
24237
25016
|
mediaProvider = null
|
|
24238
25017
|
}) {
|
|
24239
25018
|
const { doc } = useEditorContext();
|
|
24240
|
-
const gutterRef =
|
|
25019
|
+
const gutterRef = useRef36(null);
|
|
24241
25020
|
const { entries: headingEntries, scrollToBlock } = useHeadingLayout(gutterRef);
|
|
24242
25021
|
const previewSettings = usePreviewSettingsOptional();
|
|
24243
25022
|
const activeTheme = previewSettings?.activeTheme ?? DEFAULT_THEME3;
|
|
@@ -24457,7 +25236,7 @@ function InlinePreviewGutter({
|
|
|
24457
25236
|
children: [
|
|
24458
25237
|
/* @__PURE__ */ jsxs38("div", { className: "squisq-inline-preview-card-label", children: [
|
|
24459
25238
|
/* @__PURE__ */ jsx51("span", { className: "squisq-inline-preview-card-template", children: templateLabel(item.template) }),
|
|
24460
|
-
item.headingText && /* @__PURE__ */ jsxs38(
|
|
25239
|
+
item.headingText && /* @__PURE__ */ jsxs38(Fragment17, { children: [
|
|
24461
25240
|
/* @__PURE__ */ jsx51("span", { className: "squisq-inline-preview-card-sep", children: "\u2014" }),
|
|
24462
25241
|
/* @__PURE__ */ jsx51("span", { className: "squisq-inline-preview-card-title", children: item.headingText })
|
|
24463
25242
|
] })
|
|
@@ -24870,7 +25649,7 @@ import {
|
|
|
24870
25649
|
useCallback as useCallback38,
|
|
24871
25650
|
useEffect as useEffect38,
|
|
24872
25651
|
useMemo as useMemo34,
|
|
24873
|
-
useRef as
|
|
25652
|
+
useRef as useRef37,
|
|
24874
25653
|
useState as useState47
|
|
24875
25654
|
} from "react";
|
|
24876
25655
|
import { flattenBlocks as flattenBlocks6, hasTemplate as hasTemplate3 } from "@bendyline/squisq/doc";
|
|
@@ -24986,10 +25765,10 @@ function OutlinePanel({ width, className, readOnly = false }) {
|
|
|
24986
25765
|
goToBlockByLine,
|
|
24987
25766
|
activeBlockStartLine
|
|
24988
25767
|
} = useEditorContext();
|
|
24989
|
-
const paneRef =
|
|
25768
|
+
const paneRef = useRef37(null);
|
|
24990
25769
|
const { scrollToBlock } = useHeadingLayout(paneRef);
|
|
24991
25770
|
const cursorActiveId = useActiveOutlineBlockId();
|
|
24992
|
-
const activeDragRef =
|
|
25771
|
+
const activeDragRef = useRef37(null);
|
|
24993
25772
|
const [draggedBlockId, setDraggedBlockId] = useState47(null);
|
|
24994
25773
|
const [dropTarget, setDropTarget] = useState47(null);
|
|
24995
25774
|
const blockModeActiveId = useMemo34(() => {
|
|
@@ -25335,7 +26114,7 @@ function bumpHeadingLevelInSource(source, line, delta) {
|
|
|
25335
26114
|
}
|
|
25336
26115
|
|
|
25337
26116
|
// src/codeContext/CodeContextZones.tsx
|
|
25338
|
-
import { useCallback as useCallback40, useEffect as useEffect40, useMemo as useMemo36, useRef as
|
|
26117
|
+
import { useCallback as useCallback40, useEffect as useEffect40, useMemo as useMemo36, useRef as useRef39, useState as useState48 } from "react";
|
|
25339
26118
|
import { createPortal as createPortal11 } from "react-dom";
|
|
25340
26119
|
|
|
25341
26120
|
// src/codeContext/diffContextSections.ts
|
|
@@ -25459,7 +26238,7 @@ function setOrdinal(zone, ordinal) {
|
|
|
25459
26238
|
// src/codeContext/CodeContextSectionView.tsx
|
|
25460
26239
|
import { parseMarkdown as parseMarkdown7 } from "@bendyline/squisq/markdown";
|
|
25461
26240
|
import { MarkdownRenderer } from "@bendyline/squisq-react";
|
|
25462
|
-
import { useCallback as useCallback39, useEffect as useEffect39, useMemo as useMemo35, useRef as
|
|
26241
|
+
import { useCallback as useCallback39, useEffect as useEffect39, useMemo as useMemo35, useRef as useRef38 } from "react";
|
|
25463
26242
|
import { jsx as jsx55, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
25464
26243
|
function CodeContextSectionView({
|
|
25465
26244
|
section,
|
|
@@ -25470,7 +26249,7 @@ function CodeContextSectionView({
|
|
|
25470
26249
|
onRevealLine,
|
|
25471
26250
|
onMeasure
|
|
25472
26251
|
}) {
|
|
25473
|
-
const rootRef =
|
|
26252
|
+
const rootRef = useRef38(null);
|
|
25474
26253
|
const stripNodes = useMemo35(
|
|
25475
26254
|
() => parseMarkdown7(section.summaryMarkdown).children,
|
|
25476
26255
|
[section.summaryMarkdown]
|
|
@@ -25548,13 +26327,13 @@ function CodeContextSectionView({
|
|
|
25548
26327
|
}
|
|
25549
26328
|
|
|
25550
26329
|
// src/codeContext/CodeContextZones.tsx
|
|
25551
|
-
import { Fragment as
|
|
26330
|
+
import { Fragment as Fragment18, jsx as jsx56 } from "react/jsx-runtime";
|
|
25552
26331
|
function CodeContextZones({ options }) {
|
|
25553
26332
|
const { monacoEditor } = useEditorContext();
|
|
25554
26333
|
const [manager, setManager] = useState48(null);
|
|
25555
26334
|
const [, setZonesVersion] = useState48(0);
|
|
25556
26335
|
const [expandedById, setExpandedById] = useState48({});
|
|
25557
|
-
const seenIds =
|
|
26336
|
+
const seenIds = useRef39(/* @__PURE__ */ new Set());
|
|
25558
26337
|
useEffect40(() => {
|
|
25559
26338
|
if (!monacoEditor) return;
|
|
25560
26339
|
const mgr = new CodeContextZoneManager(monacoEditor);
|
|
@@ -25617,7 +26396,7 @@ function CodeContextZones({ options }) {
|
|
|
25617
26396
|
[monacoEditor]
|
|
25618
26397
|
);
|
|
25619
26398
|
if (!manager) return null;
|
|
25620
|
-
return /* @__PURE__ */ jsx56(
|
|
26399
|
+
return /* @__PURE__ */ jsx56(Fragment18, { children: resolved.map(({ section }) => {
|
|
25621
26400
|
const domNode = manager.getDomNode(section.id);
|
|
25622
26401
|
if (!domNode) return null;
|
|
25623
26402
|
return createPortal11(
|
|
@@ -25749,7 +26528,7 @@ function BlockCardView({
|
|
|
25749
26528
|
}
|
|
25750
26529
|
|
|
25751
26530
|
// src/TimelineTrack.tsx
|
|
25752
|
-
import { useCallback as useCallback42, useEffect as useEffect42, useMemo as useMemo37, useRef as
|
|
26531
|
+
import { useCallback as useCallback42, useEffect as useEffect42, useMemo as useMemo37, useRef as useRef41, useState as useState50 } from "react";
|
|
25753
26532
|
import {
|
|
25754
26533
|
resolveMediaSchedule,
|
|
25755
26534
|
getDocPlaybackDuration,
|
|
@@ -25943,7 +26722,7 @@ function collectEmbeddedMedia(block) {
|
|
|
25943
26722
|
}
|
|
25944
26723
|
|
|
25945
26724
|
// src/useTimelineClock.ts
|
|
25946
|
-
import { useCallback as useCallback41, useEffect as useEffect41, useRef as
|
|
26725
|
+
import { useCallback as useCallback41, useEffect as useEffect41, useRef as useRef40, useState as useState49 } from "react";
|
|
25947
26726
|
function advanceTime(prev, dt, total) {
|
|
25948
26727
|
if (total <= 0) return 0;
|
|
25949
26728
|
return Math.min(total, Math.max(0, prev + dt));
|
|
@@ -25951,8 +26730,8 @@ function advanceTime(prev, dt, total) {
|
|
|
25951
26730
|
function useTimelineClock(total) {
|
|
25952
26731
|
const [currentTime, setCurrentTime] = useState49(0);
|
|
25953
26732
|
const [isPlaying, setIsPlaying] = useState49(false);
|
|
25954
|
-
const rafRef =
|
|
25955
|
-
const lastRef =
|
|
26733
|
+
const rafRef = useRef40(null);
|
|
26734
|
+
const lastRef = useRef40(0);
|
|
25956
26735
|
useEffect41(() => {
|
|
25957
26736
|
setCurrentTime((t) => Math.min(t, Math.max(0, total)));
|
|
25958
26737
|
}, [total]);
|
|
@@ -26014,7 +26793,7 @@ function TimelineTrack({ height = 160 }) {
|
|
|
26014
26793
|
} = useEditorContext();
|
|
26015
26794
|
const [drag, setDrag] = useState50(null);
|
|
26016
26795
|
const [pxPerSecond, setPxPerSecond] = useState50(DEFAULT_PX_PER_SECOND);
|
|
26017
|
-
const scrollRef =
|
|
26796
|
+
const scrollRef = useRef41(null);
|
|
26018
26797
|
const blocks = useMemo37(() => doc ? flattenBlocks7(doc.blocks) : [], [doc]);
|
|
26019
26798
|
const previewSettings = usePreviewSettingsOptional();
|
|
26020
26799
|
const previewTheme = previewSettings?.activeTheme ?? DEFAULT_THEME5;
|
|
@@ -26074,7 +26853,7 @@ function TimelineTrack({ height = 160 }) {
|
|
|
26074
26853
|
},
|
|
26075
26854
|
[blocks]
|
|
26076
26855
|
);
|
|
26077
|
-
const followedBlockRef =
|
|
26856
|
+
const followedBlockRef = useRef41(null);
|
|
26078
26857
|
useEffect42(() => {
|
|
26079
26858
|
if (!isPlaying) {
|
|
26080
26859
|
followedBlockRef.current = null;
|
|
@@ -26144,9 +26923,9 @@ function TimelineTrack({ height = 160 }) {
|
|
|
26144
26923
|
ro.observe(el);
|
|
26145
26924
|
return () => ro.disconnect();
|
|
26146
26925
|
}, []);
|
|
26147
|
-
const scaleRef =
|
|
26926
|
+
const scaleRef = useRef41(pxPerSecond);
|
|
26148
26927
|
scaleRef.current = pxPerSecond;
|
|
26149
|
-
const dragRef =
|
|
26928
|
+
const dragRef = useRef41(null);
|
|
26150
26929
|
dragRef.current = drag;
|
|
26151
26930
|
const isDragging = drag != null && !drag.committed;
|
|
26152
26931
|
useEffect42(() => {
|
|
@@ -26482,7 +27261,7 @@ function clipName(src) {
|
|
|
26482
27261
|
}
|
|
26483
27262
|
|
|
26484
27263
|
// src/PreviewPanel.tsx
|
|
26485
|
-
import { useState as useState58, useEffect as useEffect51, useMemo as useMemo44, useCallback as useCallback49, useRef as
|
|
27264
|
+
import { useState as useState58, useEffect as useEffect51, useMemo as useMemo44, useCallback as useCallback49, useRef as useRef51 } from "react";
|
|
26486
27265
|
import { createPortal as createPortal14 } from "react-dom";
|
|
26487
27266
|
import { DocPlayer, LinearDocView, useMediaProvider } from "@bendyline/squisq-react";
|
|
26488
27267
|
import { applyTransform } from "@bendyline/squisq/transform";
|
|
@@ -26618,7 +27397,7 @@ function buildDocumentPreviewMarkdown(doc) {
|
|
|
26618
27397
|
}
|
|
26619
27398
|
|
|
26620
27399
|
// src/PlainHtmlPreview.tsx
|
|
26621
|
-
import { useEffect as useEffect43, useMemo as useMemo38, useRef as
|
|
27400
|
+
import { useEffect as useEffect43, useMemo as useMemo38, useRef as useRef42, useState as useState51 } from "react";
|
|
26622
27401
|
import { parseMarkdown as parseMarkdown8 } from "@bendyline/squisq/markdown";
|
|
26623
27402
|
|
|
26624
27403
|
// src/utils/collectInlineFontAwesomeCss.ts
|
|
@@ -26684,7 +27463,7 @@ function PlainHtmlPreview({
|
|
|
26684
27463
|
style,
|
|
26685
27464
|
globalKeyboardShortcuts = false
|
|
26686
27465
|
}) {
|
|
26687
|
-
const iframeRef =
|
|
27466
|
+
const iframeRef = useRef42(null);
|
|
26688
27467
|
const mdDoc = useMemo38(() => parseMarkdown8(markdown), [markdown]);
|
|
26689
27468
|
const [resolvedImages, setResolvedImages] = useState51(null);
|
|
26690
27469
|
useEffect43(() => {
|
|
@@ -26813,12 +27592,12 @@ function collectImageRefs(doc) {
|
|
|
26813
27592
|
}
|
|
26814
27593
|
|
|
26815
27594
|
// src/teleprompter/TeleprompterView.tsx
|
|
26816
|
-
import { useCallback as useCallback47, useEffect as useEffect49, useMemo as useMemo42, useRef as
|
|
27595
|
+
import { useCallback as useCallback47, useEffect as useEffect49, useMemo as useMemo42, useRef as useRef49, useState as useState56 } from "react";
|
|
26817
27596
|
import { createPortal as createPortal12 } from "react-dom";
|
|
26818
27597
|
import { wordIndexAtTime } from "@bendyline/squisq/narration";
|
|
26819
27598
|
|
|
26820
27599
|
// src/teleprompter/useTeleprompter.ts
|
|
26821
|
-
import { useCallback as useCallback44, useEffect as useEffect45, useMemo as useMemo39, useRef as
|
|
27600
|
+
import { useCallback as useCallback44, useEffect as useEffect45, useMemo as useMemo39, useRef as useRef44, useState as useState53 } from "react";
|
|
26822
27601
|
import {
|
|
26823
27602
|
buildNarrationScript,
|
|
26824
27603
|
createNarrationSession,
|
|
@@ -26828,7 +27607,7 @@ import {
|
|
|
26828
27607
|
} from "@bendyline/squisq/narration";
|
|
26829
27608
|
|
|
26830
27609
|
// src/teleprompter/useMicAnalysis.ts
|
|
26831
|
-
import { useCallback as useCallback43, useEffect as useEffect44, useRef as
|
|
27610
|
+
import { useCallback as useCallback43, useEffect as useEffect44, useRef as useRef43, useState as useState52 } from "react";
|
|
26832
27611
|
|
|
26833
27612
|
// src/teleprompter/pcmWorklet.ts
|
|
26834
27613
|
var PCM_WORKLET_NAME = "squisq-pcm-tap";
|
|
@@ -26881,9 +27660,9 @@ function useMicAnalysis() {
|
|
|
26881
27660
|
const [devices, setDevices] = useState52([]);
|
|
26882
27661
|
const [stream, setStream] = useState52(null);
|
|
26883
27662
|
const [sampleRate, setSampleRate] = useState52(null);
|
|
26884
|
-
const graphRef =
|
|
26885
|
-
const listenersRef =
|
|
26886
|
-
const generationRef =
|
|
27663
|
+
const graphRef = useRef43(null);
|
|
27664
|
+
const listenersRef = useRef43(/* @__PURE__ */ new Set());
|
|
27665
|
+
const generationRef = useRef43(0);
|
|
26887
27666
|
const refreshDevices = useCallback43(async () => {
|
|
26888
27667
|
try {
|
|
26889
27668
|
const all = await navigator.mediaDevices.enumerateDevices();
|
|
@@ -27075,17 +27854,17 @@ function useTeleprompter(opts) {
|
|
|
27075
27854
|
const [countdownRemaining, setCountdownRemaining] = useState53(null);
|
|
27076
27855
|
const [view, setView] = useState53({ wordPos: 0, micLevel: 0, voiceActive: false });
|
|
27077
27856
|
const mic = useMicAnalysis();
|
|
27078
|
-
const scriptRef =
|
|
27079
|
-
const prefsRef =
|
|
27080
|
-
const transportRef =
|
|
27081
|
-
const wordPosRef =
|
|
27082
|
-
const sessionRef =
|
|
27083
|
-
const sessionKeyRef =
|
|
27084
|
-
const lastPublishRef =
|
|
27085
|
-
const tickSubsRef =
|
|
27086
|
-
const levelRef =
|
|
27087
|
-
const voiceRef =
|
|
27088
|
-
const countdownTimerRef =
|
|
27857
|
+
const scriptRef = useRef44(script);
|
|
27858
|
+
const prefsRef = useRef44(prefs);
|
|
27859
|
+
const transportRef = useRef44(transport);
|
|
27860
|
+
const wordPosRef = useRef44(0);
|
|
27861
|
+
const sessionRef = useRef44(null);
|
|
27862
|
+
const sessionKeyRef = useRef44("");
|
|
27863
|
+
const lastPublishRef = useRef44(0);
|
|
27864
|
+
const tickSubsRef = useRef44(/* @__PURE__ */ new Set());
|
|
27865
|
+
const levelRef = useRef44(0);
|
|
27866
|
+
const voiceRef = useRef44(false);
|
|
27867
|
+
const countdownTimerRef = useRef44(null);
|
|
27089
27868
|
scriptRef.current = script;
|
|
27090
27869
|
prefsRef.current = prefs;
|
|
27091
27870
|
transportRef.current = transport;
|
|
@@ -27347,7 +28126,7 @@ function useTeleprompter(opts) {
|
|
|
27347
28126
|
}
|
|
27348
28127
|
|
|
27349
28128
|
// src/teleprompter/useFloatingWindow.ts
|
|
27350
|
-
import { useCallback as useCallback45, useEffect as useEffect46, useMemo as useMemo40, useRef as
|
|
28129
|
+
import { useCallback as useCallback45, useEffect as useEffect46, useMemo as useMemo40, useRef as useRef45, useState as useState54 } from "react";
|
|
27351
28130
|
|
|
27352
28131
|
// src/teleprompter/floatingWindow.ts
|
|
27353
28132
|
function detectFloatTiers() {
|
|
@@ -27590,7 +28369,7 @@ html,body{margin:0;height:100%;}#squisq-float-root{height:100%;display:flex;}`;
|
|
|
27590
28369
|
var FLOAT_WIDTH = 380;
|
|
27591
28370
|
var FLOAT_HEIGHT = 540;
|
|
27592
28371
|
function useFloatingWindow(styleCss) {
|
|
27593
|
-
const managerRef =
|
|
28372
|
+
const managerRef = useRef45(null);
|
|
27594
28373
|
if (managerRef.current === null) {
|
|
27595
28374
|
managerRef.current = createFloatingWindowManager({ styleCss });
|
|
27596
28375
|
}
|
|
@@ -27630,7 +28409,7 @@ function useFloatingWindow(styleCss) {
|
|
|
27630
28409
|
}
|
|
27631
28410
|
|
|
27632
28411
|
// src/teleprompter/TeleprompterSurface.tsx
|
|
27633
|
-
import { memo as memo2, useEffect as useEffect47, useMemo as useMemo41, useRef as
|
|
28412
|
+
import { memo as memo2, useEffect as useEffect47, useMemo as useMemo41, useRef as useRef46 } from "react";
|
|
27634
28413
|
|
|
27635
28414
|
// src/teleprompter/scrollModel.ts
|
|
27636
28415
|
var EYE_LINE_FRACTION = 0.35;
|
|
@@ -27961,7 +28740,7 @@ var TELEPROMPTER_CSS = `
|
|
|
27961
28740
|
`;
|
|
27962
28741
|
|
|
27963
28742
|
// src/teleprompter/TeleprompterSurface.tsx
|
|
27964
|
-
import { Fragment as
|
|
28743
|
+
import { Fragment as Fragment19, jsx as jsx60, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
27965
28744
|
function groupScript(script) {
|
|
27966
28745
|
return script.blocks.map((range) => {
|
|
27967
28746
|
const paragraphs = [];
|
|
@@ -27986,7 +28765,7 @@ var ScriptColumn = memo2(function ScriptColumn2({
|
|
|
27986
28765
|
compact
|
|
27987
28766
|
}) {
|
|
27988
28767
|
const groups = useMemo41(() => groupScript(script), [script]);
|
|
27989
|
-
return /* @__PURE__ */ jsx60(
|
|
28768
|
+
return /* @__PURE__ */ jsx60(Fragment19, { children: groups.map((group) => /* @__PURE__ */ jsxs43("section", { "data-block-id": group.blockId, children: [
|
|
27990
28769
|
!compact && group.heading ? /* @__PURE__ */ jsx60("span", { className: "squisq-teleprompter-block-marker", children: group.heading }) : null,
|
|
27991
28770
|
group.paragraphs.map((paragraph) => /* @__PURE__ */ jsx60("p", { className: "squisq-teleprompter-para", children: paragraph.tokenIndexes.map((idx) => /* @__PURE__ */ jsxs43("span", { className: "squisq-teleprompter-word", "data-token-idx": idx, children: [
|
|
27992
28771
|
script.tokens[idx].text,
|
|
@@ -28006,9 +28785,9 @@ function TeleprompterSurface({
|
|
|
28006
28785
|
compact = false,
|
|
28007
28786
|
onSeekToken
|
|
28008
28787
|
}) {
|
|
28009
|
-
const surfaceRef =
|
|
28010
|
-
const columnRef =
|
|
28011
|
-
const wordPosRef =
|
|
28788
|
+
const surfaceRef = useRef46(null);
|
|
28789
|
+
const columnRef = useRef46(null);
|
|
28790
|
+
const wordPosRef = useRef46(wordPos);
|
|
28012
28791
|
wordPosRef.current = wordPos;
|
|
28013
28792
|
const vars = useMemo41(() => prompterVarsFromTheme(theme), [theme]);
|
|
28014
28793
|
useEffect47(() => {
|
|
@@ -28105,7 +28884,7 @@ function TeleprompterSurface({
|
|
|
28105
28884
|
children: /* @__PURE__ */ jsx60(ScriptColumn, { script, compact })
|
|
28106
28885
|
}
|
|
28107
28886
|
) }),
|
|
28108
|
-
lineGuide ? /* @__PURE__ */ jsxs43(
|
|
28887
|
+
lineGuide ? /* @__PURE__ */ jsxs43(Fragment19, { children: [
|
|
28109
28888
|
/* @__PURE__ */ jsx60(
|
|
28110
28889
|
"div",
|
|
28111
28890
|
{
|
|
@@ -28129,7 +28908,7 @@ function TeleprompterSurface({
|
|
|
28129
28908
|
}
|
|
28130
28909
|
|
|
28131
28910
|
// src/teleprompter/TeleprompterControls.tsx
|
|
28132
|
-
import { Fragment as
|
|
28911
|
+
import { Fragment as Fragment20, jsx as jsx61, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
28133
28912
|
var TIER_LABELS = {
|
|
28134
28913
|
"document-pip": "Floating window (always on top)",
|
|
28135
28914
|
"video-pip": "Picture-in-picture (read-only)",
|
|
@@ -28213,7 +28992,7 @@ function TeleprompterControls({ controller, float, recordSlot }) {
|
|
|
28213
28992
|
children: "\u{1F399} Voice pace"
|
|
28214
28993
|
}
|
|
28215
28994
|
),
|
|
28216
|
-
prefs.voiceTracking ? /* @__PURE__ */ jsxs44(
|
|
28995
|
+
prefs.voiceTracking ? /* @__PURE__ */ jsxs44(Fragment20, { children: [
|
|
28217
28996
|
/* @__PURE__ */ jsx61("label", { htmlFor: "squisq-prompter-sensitivity", title: "Voice detection sensitivity", children: "Sens." }),
|
|
28218
28997
|
/* @__PURE__ */ jsx61(
|
|
28219
28998
|
"input",
|
|
@@ -28299,7 +29078,7 @@ function TeleprompterControls({ controller, float, recordSlot }) {
|
|
|
28299
29078
|
)
|
|
28300
29079
|
] }),
|
|
28301
29080
|
recordSlot,
|
|
28302
|
-
float.supportedTiers.length > 0 ? /* @__PURE__ */ jsx61("span", { className: "squisq-teleprompter-group", style: { marginLeft: "auto" }, children: float.isOpen ? /* @__PURE__ */ jsx61("button", { type: "button", onClick: float.close, children: "\u21E4 Bring back" }) : /* @__PURE__ */ jsxs44(
|
|
29081
|
+
float.supportedTiers.length > 0 ? /* @__PURE__ */ jsx61("span", { className: "squisq-teleprompter-group", style: { marginLeft: "auto" }, children: float.isOpen ? /* @__PURE__ */ jsx61("button", { type: "button", onClick: float.close, children: "\u21E4 Bring back" }) : /* @__PURE__ */ jsxs44(Fragment20, { children: [
|
|
28303
29082
|
float.supportedTiers.length > 1 ? /* @__PURE__ */ jsx61(
|
|
28304
29083
|
"select",
|
|
28305
29084
|
{
|
|
@@ -28332,10 +29111,10 @@ function TeleprompterControls({ controller, float, recordSlot }) {
|
|
|
28332
29111
|
}
|
|
28333
29112
|
|
|
28334
29113
|
// src/teleprompter/TeleprompterSelfView.tsx
|
|
28335
|
-
import { useRef as
|
|
29114
|
+
import { useRef as useRef47 } from "react";
|
|
28336
29115
|
import { jsx as jsx62 } from "react/jsx-runtime";
|
|
28337
29116
|
function TeleprompterSelfView({ stream }) {
|
|
28338
|
-
const videoRef =
|
|
29117
|
+
const videoRef = useRef47(null);
|
|
28339
29118
|
useStreamPreview(videoRef, stream);
|
|
28340
29119
|
if (!stream) return null;
|
|
28341
29120
|
return /* @__PURE__ */ jsx62(
|
|
@@ -28465,7 +29244,7 @@ function drawPrompterFrame(canvas, frame) {
|
|
|
28465
29244
|
}
|
|
28466
29245
|
|
|
28467
29246
|
// src/teleprompter/recording/useNarrationRecorder.ts
|
|
28468
|
-
import { useCallback as useCallback46, useEffect as useEffect48, useRef as
|
|
29247
|
+
import { useCallback as useCallback46, useEffect as useEffect48, useRef as useRef48, useState as useState55 } from "react";
|
|
28469
29248
|
import {
|
|
28470
29249
|
alignNarration
|
|
28471
29250
|
} from "@bendyline/squisq/narration";
|
|
@@ -28499,8 +29278,8 @@ function useNarrationRecorder(options) {
|
|
|
28499
29278
|
const [withCamera, setWithCamera] = useState55(false);
|
|
28500
29279
|
const [cameraStream, setCameraStream] = useState55(null);
|
|
28501
29280
|
const [take, setTake] = useState55(null);
|
|
28502
|
-
const captureRef =
|
|
28503
|
-
const optionsRef =
|
|
29281
|
+
const captureRef = useRef48(null);
|
|
29282
|
+
const optionsRef = useRef48(options);
|
|
28504
29283
|
optionsRef.current = options;
|
|
28505
29284
|
const teardownCapture = useCallback46(() => {
|
|
28506
29285
|
const capture = captureRef.current;
|
|
@@ -28802,13 +29581,13 @@ async function executeNarrationSave(plan, take, deps) {
|
|
|
28802
29581
|
}
|
|
28803
29582
|
|
|
28804
29583
|
// src/teleprompter/TeleprompterView.tsx
|
|
28805
|
-
import { Fragment as
|
|
29584
|
+
import { Fragment as Fragment21, jsx as jsx63, jsxs as jsxs45 } from "react/jsx-runtime";
|
|
28806
29585
|
function TeleprompterView(props) {
|
|
28807
29586
|
const { doc, theme, presentationTarget = null, recording = null } = props;
|
|
28808
29587
|
const controller = useTeleprompter({ doc });
|
|
28809
29588
|
const float = useFloatingWindow(TELEPROMPTER_CSS);
|
|
28810
|
-
const rootRef =
|
|
28811
|
-
const controllerRef =
|
|
29589
|
+
const rootRef = useRef49(null);
|
|
29590
|
+
const controllerRef = useRef49(controller);
|
|
28812
29591
|
controllerRef.current = controller;
|
|
28813
29592
|
const [saveNotice, setSaveNotice] = useState56(null);
|
|
28814
29593
|
const recorder = useNarrationRecorder({
|
|
@@ -28819,7 +29598,7 @@ function TeleprompterView(props) {
|
|
|
28819
29598
|
onRecordingStart: () => controllerRef.current.play(),
|
|
28820
29599
|
onRecordingStop: () => controllerRef.current.pause()
|
|
28821
29600
|
});
|
|
28822
|
-
const recorderRef =
|
|
29601
|
+
const recorderRef = useRef49(recorder);
|
|
28823
29602
|
recorderRef.current = recorder;
|
|
28824
29603
|
useEffect49(() => {
|
|
28825
29604
|
const ownerDoc = rootRef.current?.ownerDocument;
|
|
@@ -28828,7 +29607,7 @@ function TeleprompterView(props) {
|
|
|
28828
29607
|
useEffect49(() => {
|
|
28829
29608
|
if (presentationTarget) ensureTeleprompterStyles(presentationTarget.ownerDocument);
|
|
28830
29609
|
}, [presentationTarget]);
|
|
28831
|
-
const canvasFrameRef =
|
|
29610
|
+
const canvasFrameRef = useRef49(null);
|
|
28832
29611
|
canvasFrameRef.current = controller.script ? {
|
|
28833
29612
|
script: controller.script,
|
|
28834
29613
|
fontSizePx: controller.prefs.fontSizePx,
|
|
@@ -28927,7 +29706,7 @@ function TeleprompterView(props) {
|
|
|
28927
29706
|
]
|
|
28928
29707
|
);
|
|
28929
29708
|
if (!controller.script) {
|
|
28930
|
-
return /* @__PURE__ */ jsxs45(
|
|
29709
|
+
return /* @__PURE__ */ jsxs45(Fragment21, { children: [
|
|
28931
29710
|
/* @__PURE__ */ jsx63("div", { ref: rootRef, className: "squisq-teleprompter-root", "data-testid": "teleprompter-view", children: /* @__PURE__ */ jsx63("div", { className: "squisq-teleprompter-float-note", children: /* @__PURE__ */ jsx63("p", { children: "Nothing to narrate yet \u2014 add some content to the document." }) }) }),
|
|
28932
29711
|
presentationTarget ? createPortal12(
|
|
28933
29712
|
/* @__PURE__ */ jsx63("div", { className: "squisq-presentation-teleprompter", "aria-label": "Audience presentation", children: /* @__PURE__ */ jsx63("div", { className: "squisq-teleprompter-float-note", children: /* @__PURE__ */ jsx63("p", { children: "Nothing to narrate yet \u2014 add some content to the document." }) }) }),
|
|
@@ -28938,7 +29717,7 @@ function TeleprompterView(props) {
|
|
|
28938
29717
|
const script = controller.script;
|
|
28939
29718
|
const portalOpen = float.portalTarget !== null;
|
|
28940
29719
|
const busyRecording = recorder.state === "recording" || recorder.state === "starting";
|
|
28941
|
-
const recordSlot = recording ? /* @__PURE__ */ jsx63("span", { className: "squisq-teleprompter-group", "data-testid": "teleprompter-record", children: recorder.state === "idle" || recorder.state === "error" ? /* @__PURE__ */ jsxs45(
|
|
29720
|
+
const recordSlot = recording ? /* @__PURE__ */ jsx63("span", { className: "squisq-teleprompter-group", "data-testid": "teleprompter-record", children: recorder.state === "idle" || recorder.state === "error" ? /* @__PURE__ */ jsxs45(Fragment21, { children: [
|
|
28942
29721
|
/* @__PURE__ */ jsx63(
|
|
28943
29722
|
"button",
|
|
28944
29723
|
{
|
|
@@ -29038,7 +29817,7 @@ import {
|
|
|
29038
29817
|
useId as useId9,
|
|
29039
29818
|
useLayoutEffect as useLayoutEffect5,
|
|
29040
29819
|
useMemo as useMemo43,
|
|
29041
|
-
useRef as
|
|
29820
|
+
useRef as useRef50,
|
|
29042
29821
|
useState as useState57
|
|
29043
29822
|
} from "react";
|
|
29044
29823
|
import { createPortal as createPortal13 } from "react-dom";
|
|
@@ -29105,12 +29884,12 @@ function PresentationModeProvider({ rootRef, children }) {
|
|
|
29105
29884
|
const [activeTarget, setActiveTarget] = useState57(null);
|
|
29106
29885
|
const [popupRoot, setPopupRoot] = useState57(null);
|
|
29107
29886
|
const [error, setError] = useState57(null);
|
|
29108
|
-
const activeTargetRef =
|
|
29887
|
+
const activeTargetRef = useRef50(activeTarget);
|
|
29109
29888
|
activeTargetRef.current = activeTarget;
|
|
29110
|
-
const previousActiveTargetRef =
|
|
29111
|
-
const returnFocusRef =
|
|
29112
|
-
const popupRef =
|
|
29113
|
-
const popupCleanupRef =
|
|
29889
|
+
const previousActiveTargetRef = useRef50(null);
|
|
29890
|
+
const returnFocusRef = useRef50(null);
|
|
29891
|
+
const popupRef = useRef50(null);
|
|
29892
|
+
const popupCleanupRef = useRef50(null);
|
|
29114
29893
|
const fullscreenSupported = typeof document !== "undefined" && typeof document.documentElement.requestFullscreen === "function";
|
|
29115
29894
|
const releasePopup = useCallback48((closeWindow) => {
|
|
29116
29895
|
const popup = popupRef.current;
|
|
@@ -29349,8 +30128,8 @@ var MENU_GAP = 4;
|
|
|
29349
30128
|
function PresentationModeControl() {
|
|
29350
30129
|
const { selectedTarget, activeTarget, fullscreenSupported, selectTarget, start, stop } = usePresentationMode();
|
|
29351
30130
|
const { colorScheme } = useEditorContext();
|
|
29352
|
-
const triggerRef =
|
|
29353
|
-
const menuRef =
|
|
30131
|
+
const triggerRef = useRef50(null);
|
|
30132
|
+
const menuRef = useRef50(null);
|
|
29354
30133
|
const [open, setOpen] = useState57(false);
|
|
29355
30134
|
const [anchor, setAnchor] = useState57(null);
|
|
29356
30135
|
const selected = PRESENTATION_OPTIONS.find((option) => option.target === selectedTarget) ?? PRESENTATION_OPTIONS[0];
|
|
@@ -29515,7 +30294,7 @@ function PresentationModeControl() {
|
|
|
29515
30294
|
}
|
|
29516
30295
|
|
|
29517
30296
|
// src/PreviewPanel.tsx
|
|
29518
|
-
import { Fragment as
|
|
30297
|
+
import { Fragment as Fragment22, jsx as jsx65, jsxs as jsxs47 } from "react/jsx-runtime";
|
|
29519
30298
|
function PreviewPanel({ basePath = "/", className, workspaceContainer }) {
|
|
29520
30299
|
const {
|
|
29521
30300
|
doc,
|
|
@@ -29539,8 +30318,8 @@ function PreviewPanel({ basePath = "/", className, workspaceContainer }) {
|
|
|
29539
30318
|
activeCaptionsEnabled,
|
|
29540
30319
|
activeCoverSlide
|
|
29541
30320
|
} = usePreviewSettings();
|
|
29542
|
-
const mainSurfaceRef =
|
|
29543
|
-
const popupSurfaceRef =
|
|
30321
|
+
const mainSurfaceRef = useRef51(null);
|
|
30322
|
+
const popupSurfaceRef = useRef51(null);
|
|
29544
30323
|
const [playbackState, setPlaybackState] = useState58(null);
|
|
29545
30324
|
const handlePlaybackStateChange = useCallback49((next) => {
|
|
29546
30325
|
setPlaybackState(next);
|
|
@@ -29759,7 +30538,7 @@ function PreviewPanel({ basePath = "/", className, workspaceContainer }) {
|
|
|
29759
30538
|
overflow: "hidden",
|
|
29760
30539
|
minHeight: 0
|
|
29761
30540
|
};
|
|
29762
|
-
return /* @__PURE__ */ jsxs47(
|
|
30541
|
+
return /* @__PURE__ */ jsxs47(Fragment22, { children: [
|
|
29763
30542
|
/* @__PURE__ */ jsx65(
|
|
29764
30543
|
"div",
|
|
29765
30544
|
{
|
|
@@ -29800,14 +30579,14 @@ function PreviewPanel({ basePath = "/", className, workspaceContainer }) {
|
|
|
29800
30579
|
}
|
|
29801
30580
|
|
|
29802
30581
|
// src/ImageViewer.tsx
|
|
29803
|
-
import { useCallback as useCallback50, useEffect as useEffect52, useRef as
|
|
29804
|
-
import { Fragment as
|
|
30582
|
+
import { useCallback as useCallback50, useEffect as useEffect52, useRef as useRef52, useState as useState59 } from "react";
|
|
30583
|
+
import { Fragment as Fragment23, jsx as jsx66, jsxs as jsxs48 } from "react/jsx-runtime";
|
|
29805
30584
|
var MIN_ZOOM = 0.1;
|
|
29806
30585
|
var MAX_ZOOM = 16;
|
|
29807
30586
|
var ZOOM_STEP = 1.25;
|
|
29808
30587
|
function ImageViewer({ src, alt = "", className, theme = "light" }) {
|
|
29809
|
-
const imgRef =
|
|
29810
|
-
const stageRef =
|
|
30588
|
+
const imgRef = useRef52(null);
|
|
30589
|
+
const stageRef = useRef52(null);
|
|
29811
30590
|
const [naturalSize, setNaturalSize] = useState59(null);
|
|
29812
30591
|
const [fitZoom, setFitZoom] = useState59(1);
|
|
29813
30592
|
const [state, setState2] = useState59({ mode: "fit" });
|
|
@@ -29859,7 +30638,7 @@ function ImageViewer({ src, alt = "", className, theme = "light" }) {
|
|
|
29859
30638
|
}, [setZoom]);
|
|
29860
30639
|
const onZoomIn = useCallback50(() => setZoom(effectiveZoom * ZOOM_STEP), [effectiveZoom, setZoom]);
|
|
29861
30640
|
const onZoomOut = useCallback50(() => setZoom(effectiveZoom / ZOOM_STEP), [effectiveZoom, setZoom]);
|
|
29862
|
-
const dragRef =
|
|
30641
|
+
const dragRef = useRef52(
|
|
29863
30642
|
null
|
|
29864
30643
|
);
|
|
29865
30644
|
const onMouseDown = useCallback50(
|
|
@@ -29966,7 +30745,7 @@ function ImageViewer({ src, alt = "", className, theme = "light" }) {
|
|
|
29966
30745
|
]
|
|
29967
30746
|
}
|
|
29968
30747
|
),
|
|
29969
|
-
/* @__PURE__ */ jsx66("div", { className: "squisq-image-viewer-status", children: naturalSize ? /* @__PURE__ */ jsxs48(
|
|
30748
|
+
/* @__PURE__ */ jsx66("div", { className: "squisq-image-viewer-status", children: naturalSize ? /* @__PURE__ */ jsxs48(Fragment23, { children: [
|
|
29970
30749
|
/* @__PURE__ */ jsxs48("span", { children: [
|
|
29971
30750
|
naturalSize.w,
|
|
29972
30751
|
" \xD7 ",
|
|
@@ -29981,11 +30760,11 @@ function ImageViewer({ src, alt = "", className, theme = "light" }) {
|
|
|
29981
30760
|
}
|
|
29982
30761
|
|
|
29983
30762
|
// src/ImageEditor.tsx
|
|
29984
|
-
import { useCallback as useCallback54, useRef as
|
|
30763
|
+
import { useCallback as useCallback54, useRef as useRef58, useState as useState66 } from "react";
|
|
29985
30764
|
import { exportImageEditDoc as exportImageEditDoc2 } from "@bendyline/squisq/imageEdit";
|
|
29986
30765
|
|
|
29987
30766
|
// src/imageEditor/CanvasSurface.tsx
|
|
29988
|
-
import { useCallback as useCallback51, useEffect as useEffect54, useId as useId10, useLayoutEffect as useLayoutEffect6, useRef as
|
|
30767
|
+
import { useCallback as useCallback51, useEffect as useEffect54, useId as useId10, useLayoutEffect as useLayoutEffect6, useRef as useRef53, useState as useState61 } from "react";
|
|
29989
30768
|
import { PathLayer as PathLayer2 } from "@bendyline/squisq-react";
|
|
29990
30769
|
|
|
29991
30770
|
// src/imageEditor/layers/EditorImageLayer.tsx
|
|
@@ -30180,16 +30959,16 @@ function CanvasSurface({
|
|
|
30180
30959
|
requestEditLayerId
|
|
30181
30960
|
}) {
|
|
30182
30961
|
const checkerId = `squisq-image-editor-checker-${useId10().replace(/:/g, "")}`;
|
|
30183
|
-
const svgRef =
|
|
30184
|
-
const dragRef =
|
|
30962
|
+
const svgRef = useRef53(null);
|
|
30963
|
+
const dragRef = useRef53(null);
|
|
30185
30964
|
const [, forceRender] = useState61(0);
|
|
30186
30965
|
const [cropDrag, setCropDrag] = useState61(null);
|
|
30187
30966
|
const [shapeLineDrag, setShapeLineDrag] = useState61(null);
|
|
30188
|
-
const internalWrapRef =
|
|
30189
|
-
const pendingScrollRef =
|
|
30967
|
+
const internalWrapRef = useRef53(null);
|
|
30968
|
+
const pendingScrollRef = useRef53(null);
|
|
30190
30969
|
const [zoomDrag, setZoomDrag] = useState61(null);
|
|
30191
30970
|
const [editingLayerId, setEditingLayerId] = useState61(null);
|
|
30192
|
-
const editTextareaRef =
|
|
30971
|
+
const editTextareaRef = useRef53(null);
|
|
30193
30972
|
const toCanvas = useCallback51(
|
|
30194
30973
|
(clientX, clientY) => {
|
|
30195
30974
|
const svg = svgRef.current;
|
|
@@ -30698,7 +31477,7 @@ function measureTextLayerBox(layer, fallback) {
|
|
|
30698
31477
|
}
|
|
30699
31478
|
|
|
30700
31479
|
// src/imageEditor/ImageVersionHistoryDropdown.tsx
|
|
30701
|
-
import { useCallback as useCallback52, useEffect as useEffect55, useRef as
|
|
31480
|
+
import { useCallback as useCallback52, useEffect as useEffect55, useRef as useRef54, useState as useState62 } from "react";
|
|
30702
31481
|
import {
|
|
30703
31482
|
exportImageEditDoc
|
|
30704
31483
|
} from "@bendyline/squisq/imageEdit";
|
|
@@ -30715,9 +31494,9 @@ function ImageVersionHistoryDropdown({
|
|
|
30715
31494
|
const [loading, setLoading] = useState62(false);
|
|
30716
31495
|
const [busyTimestamp, setBusyTimestamp] = useState62(null);
|
|
30717
31496
|
const [meta, setMeta2] = useState62({});
|
|
30718
|
-
const popoverRef =
|
|
30719
|
-
const triggerRef =
|
|
30720
|
-
const urlsRef =
|
|
31497
|
+
const popoverRef = useRef54(null);
|
|
31498
|
+
const triggerRef = useRef54(null);
|
|
31499
|
+
const urlsRef = useRef54(/* @__PURE__ */ new Set());
|
|
30721
31500
|
useEffect55(() => {
|
|
30722
31501
|
if (!open) return;
|
|
30723
31502
|
let cancelled = false;
|
|
@@ -30966,7 +31745,7 @@ function emptyDoc() {
|
|
|
30966
31745
|
}
|
|
30967
31746
|
|
|
30968
31747
|
// src/imageEditor/LayersPanel.tsx
|
|
30969
|
-
import { useEffect as useEffect56, useRef as
|
|
31748
|
+
import { useEffect as useEffect56, useRef as useRef55, useState as useState63 } from "react";
|
|
30970
31749
|
|
|
30971
31750
|
// src/imageEditor/icons.tsx
|
|
30972
31751
|
import { jsx as jsx73 } from "react/jsx-runtime";
|
|
@@ -31022,7 +31801,7 @@ var ADD_LAYER_OPTIONS = [
|
|
|
31022
31801
|
];
|
|
31023
31802
|
function LayersPanel({ doc, selectedLayerId, dispatch, onAddLayer }) {
|
|
31024
31803
|
const [addMenuOpen, setAddMenuOpen] = useState63(false);
|
|
31025
|
-
const addMenuRef =
|
|
31804
|
+
const addMenuRef = useRef55(null);
|
|
31026
31805
|
useEffect56(() => {
|
|
31027
31806
|
if (!addMenuOpen) return;
|
|
31028
31807
|
const closeOnOutsideClick = (event) => {
|
|
@@ -31194,7 +31973,7 @@ function defaultLayerName(layer) {
|
|
|
31194
31973
|
}
|
|
31195
31974
|
|
|
31196
31975
|
// src/imageEditor/PropertiesPanel.tsx
|
|
31197
|
-
import { Fragment as
|
|
31976
|
+
import { Fragment as Fragment24, jsx as jsx75, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
31198
31977
|
function PropertiesPanel({ doc, selectedLayerId, dispatch }) {
|
|
31199
31978
|
const selected = selectedLayerId ? doc.layers.find((l) => l.id === selectedLayerId) ?? null : null;
|
|
31200
31979
|
return /* @__PURE__ */ jsxs53("div", { className: "squisq-image-editor-properties", "data-testid": "image-editor-properties", children: [
|
|
@@ -31246,7 +32025,7 @@ function LayerSection({
|
|
|
31246
32025
|
dispatch
|
|
31247
32026
|
}) {
|
|
31248
32027
|
const update = (patch) => dispatch({ type: "update-layer", layerId: layer.id, patch });
|
|
31249
|
-
return /* @__PURE__ */ jsxs53(
|
|
32028
|
+
return /* @__PURE__ */ jsxs53(Fragment24, { children: [
|
|
31250
32029
|
/* @__PURE__ */ jsxs53("fieldset", { className: "squisq-image-editor-fieldset", children: [
|
|
31251
32030
|
/* @__PURE__ */ jsx75("legend", { children: "Layer" }),
|
|
31252
32031
|
/* @__PURE__ */ jsx75(TextField, { label: "Name", value: layer.name ?? "", onChange: (name) => update({ name }) }),
|
|
@@ -31590,7 +32369,7 @@ function normalizeColor2(v) {
|
|
|
31590
32369
|
}
|
|
31591
32370
|
|
|
31592
32371
|
// src/imageEditor/Toolbar.tsx
|
|
31593
|
-
import { useRef as
|
|
32372
|
+
import { useRef as useRef56, useState as useState64, useEffect as useEffect57 } from "react";
|
|
31594
32373
|
import { jsx as jsx76, jsxs as jsxs54 } from "react/jsx-runtime";
|
|
31595
32374
|
function RedlineArrowIcon() {
|
|
31596
32375
|
return /* @__PURE__ */ jsxs54("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: [
|
|
@@ -31697,7 +32476,7 @@ function Toolbar2({
|
|
|
31697
32476
|
onZoomFit,
|
|
31698
32477
|
onZoom1to1
|
|
31699
32478
|
}) {
|
|
31700
|
-
const internalImageInputRef =
|
|
32479
|
+
const internalImageInputRef = useRef56(null);
|
|
31701
32480
|
const fileInputRef = imageInputRef ?? internalImageInputRef;
|
|
31702
32481
|
const [shapePaletteOpen, setShapePaletteOpen] = useState64(false);
|
|
31703
32482
|
const onFilePicked = async (file) => {
|
|
@@ -31900,8 +32679,8 @@ function Toolbar2({
|
|
|
31900
32679
|
}
|
|
31901
32680
|
function ExportDropdown({ onExport }) {
|
|
31902
32681
|
const [open, setOpen] = useState64(false);
|
|
31903
|
-
const wrapRef =
|
|
31904
|
-
const triggerRef =
|
|
32682
|
+
const wrapRef = useRef56(null);
|
|
32683
|
+
const triggerRef = useRef56(null);
|
|
31905
32684
|
useEffect57(() => {
|
|
31906
32685
|
if (!open) return;
|
|
31907
32686
|
function onDocClick(e2) {
|
|
@@ -31977,7 +32756,7 @@ function probeDims(file) {
|
|
|
31977
32756
|
}
|
|
31978
32757
|
|
|
31979
32758
|
// src/imageEditor/useImageEditor.ts
|
|
31980
|
-
import { useCallback as useCallback53, useEffect as useEffect58, useMemo as useMemo45, useReducer as useReducer2, useRef as
|
|
32759
|
+
import { useCallback as useCallback53, useEffect as useEffect58, useMemo as useMemo45, useReducer as useReducer2, useRef as useRef57, useState as useState65 } from "react";
|
|
31981
32760
|
import {
|
|
31982
32761
|
IMAGE_EDIT_ASSETS_PREFIX,
|
|
31983
32762
|
IMAGE_EDIT_STATE_FILENAME,
|
|
@@ -32093,7 +32872,7 @@ function useImageEditor(options) {
|
|
|
32093
32872
|
);
|
|
32094
32873
|
const [ready, setReady] = useState65(false);
|
|
32095
32874
|
const [error, setError] = useState65(null);
|
|
32096
|
-
const seededOnLoadRef =
|
|
32875
|
+
const seededOnLoadRef = useRef57(false);
|
|
32097
32876
|
useEffect58(() => {
|
|
32098
32877
|
let cancelled = false;
|
|
32099
32878
|
setReady(false);
|
|
@@ -32124,11 +32903,11 @@ function useImageEditor(options) {
|
|
|
32124
32903
|
cancelled = true;
|
|
32125
32904
|
};
|
|
32126
32905
|
}, [container, stateFilename, initialSrc]);
|
|
32127
|
-
const persistTimerRef =
|
|
32128
|
-
const docRef =
|
|
32129
|
-
const dirtyRef =
|
|
32130
|
-
const revisionRef =
|
|
32131
|
-
const previousDocRef =
|
|
32906
|
+
const persistTimerRef = useRef57(null);
|
|
32907
|
+
const docRef = useRef57(null);
|
|
32908
|
+
const dirtyRef = useRef57(false);
|
|
32909
|
+
const revisionRef = useRef57(0);
|
|
32910
|
+
const previousDocRef = useRef57(null);
|
|
32132
32911
|
const nextDoc = state?.doc ?? null;
|
|
32133
32912
|
if (nextDoc !== previousDocRef.current) {
|
|
32134
32913
|
previousDocRef.current = nextDoc;
|
|
@@ -32136,8 +32915,8 @@ function useImageEditor(options) {
|
|
|
32136
32915
|
}
|
|
32137
32916
|
docRef.current = nextDoc;
|
|
32138
32917
|
dirtyRef.current = state?.dirty ?? false;
|
|
32139
|
-
const writeQueueRef =
|
|
32140
|
-
const persistTargetRef =
|
|
32918
|
+
const writeQueueRef = useRef57(Promise.resolve());
|
|
32919
|
+
const persistTargetRef = useRef57({ container, stateFilename });
|
|
32141
32920
|
persistTargetRef.current = { container, stateFilename };
|
|
32142
32921
|
const enqueueWrite = useCallback53(
|
|
32143
32922
|
(doc, revision, markClean) => {
|
|
@@ -32224,8 +33003,8 @@ function useImageEditor(options) {
|
|
|
32224
33003
|
}, versioningAutoSaveIdleMs);
|
|
32225
33004
|
return () => clearTimeout(timer);
|
|
32226
33005
|
}, [versioning, versioningAutoSaveIdleMs, state?.doc]);
|
|
32227
|
-
const urlCacheRef =
|
|
32228
|
-
const urlCacheGenerationRef =
|
|
33006
|
+
const urlCacheRef = useRef57(/* @__PURE__ */ new Map());
|
|
33007
|
+
const urlCacheGenerationRef = useRef57(0);
|
|
32229
33008
|
const resolveAssetUrl = useCallback53(
|
|
32230
33009
|
async (path) => {
|
|
32231
33010
|
const cache2 = urlCacheRef.current;
|
|
@@ -32627,8 +33406,8 @@ function ImageEditor(props) {
|
|
|
32627
33406
|
versioningAutoSaveIdleMs
|
|
32628
33407
|
});
|
|
32629
33408
|
const [historyRefreshKey, setHistoryRefreshKey] = useState66(0);
|
|
32630
|
-
const surfaceRef =
|
|
32631
|
-
const imageInputRef =
|
|
33409
|
+
const surfaceRef = useRef58(null);
|
|
33410
|
+
const imageInputRef = useRef58(null);
|
|
32632
33411
|
const [zoom, setZoom] = useState66(1);
|
|
32633
33412
|
const handleZoomIn = useCallback54(() => {
|
|
32634
33413
|
setZoom((z) => ZOOM_STEPS.find((s) => s > z + 1e-3) ?? 16);
|
|
@@ -32879,7 +33658,7 @@ function ImageEditor(props) {
|
|
|
32879
33658
|
}
|
|
32880
33659
|
|
|
32881
33660
|
// src/MediaBin.tsx
|
|
32882
|
-
import { useState as useState67, useEffect as useEffect59, useRef as
|
|
33661
|
+
import { useState as useState67, useEffect as useEffect59, useRef as useRef59, useCallback as useCallback55 } from "react";
|
|
32883
33662
|
import { jsx as jsx78, jsxs as jsxs56 } from "react/jsx-runtime";
|
|
32884
33663
|
function formatSize(bytes) {
|
|
32885
33664
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -32933,9 +33712,9 @@ function MediaBin({
|
|
|
32933
33712
|
const [loading, setLoading] = useState67(false);
|
|
32934
33713
|
const [isDropActive, setIsDropActive] = useState67(false);
|
|
32935
33714
|
const [contextMenu, setContextMenu] = useState67(null);
|
|
32936
|
-
const fileInputRef =
|
|
32937
|
-
const contextMenuRef =
|
|
32938
|
-
const dropDepthRef =
|
|
33715
|
+
const fileInputRef = useRef59(null);
|
|
33716
|
+
const contextMenuRef = useRef59(null);
|
|
33717
|
+
const dropDepthRef = useRef59(0);
|
|
32939
33718
|
const updateEntries = useCallback55(
|
|
32940
33719
|
async (provider) => {
|
|
32941
33720
|
const list = sortMediaEntries(filterVisibleMediaEntries(await provider.listMedia()));
|
|
@@ -33265,7 +34044,7 @@ ${formatSize(entry.size)}`,
|
|
|
33265
34044
|
|
|
33266
34045
|
// src/DropZoneOverlay.tsx
|
|
33267
34046
|
import { useState as useState68 } from "react";
|
|
33268
|
-
import { Fragment as
|
|
34047
|
+
import { Fragment as Fragment25, jsx as jsx79, jsxs as jsxs57 } from "react/jsx-runtime";
|
|
33269
34048
|
function DropZoneOverlay({
|
|
33270
34049
|
dragContentType,
|
|
33271
34050
|
zoneProps,
|
|
@@ -33286,7 +34065,7 @@ function DropZoneOverlay({
|
|
|
33286
34065
|
variant: "media"
|
|
33287
34066
|
}
|
|
33288
34067
|
),
|
|
33289
|
-
showText && /* @__PURE__ */ jsxs57(
|
|
34068
|
+
showText && /* @__PURE__ */ jsxs57(Fragment25, { children: [
|
|
33290
34069
|
/* @__PURE__ */ jsx79(
|
|
33291
34070
|
DropZone,
|
|
33292
34071
|
{
|
|
@@ -33365,7 +34144,7 @@ function DropZone({
|
|
|
33365
34144
|
}
|
|
33366
34145
|
|
|
33367
34146
|
// src/Tooltip.tsx
|
|
33368
|
-
import { useEffect as useEffect60, useLayoutEffect as useLayoutEffect7, useRef as
|
|
34147
|
+
import { useEffect as useEffect60, useLayoutEffect as useLayoutEffect7, useRef as useRef60, useState as useState69 } from "react";
|
|
33369
34148
|
import { createPortal as createPortal15 } from "react-dom";
|
|
33370
34149
|
|
|
33371
34150
|
// src/tooltipPlacement.ts
|
|
@@ -33382,10 +34161,10 @@ import { jsx as jsx80 } from "react/jsx-runtime";
|
|
|
33382
34161
|
var SHOW_DELAY_MS = 180;
|
|
33383
34162
|
function TooltipLayer() {
|
|
33384
34163
|
const [state, setState2] = useState69(null);
|
|
33385
|
-
const tooltipRef =
|
|
33386
|
-
const timerRef =
|
|
33387
|
-
const currentTargetRef =
|
|
33388
|
-
const visibleRef =
|
|
34164
|
+
const tooltipRef = useRef60(null);
|
|
34165
|
+
const timerRef = useRef60(null);
|
|
34166
|
+
const currentTargetRef = useRef60(null);
|
|
34167
|
+
const visibleRef = useRef60(false);
|
|
33389
34168
|
useLayoutEffect7(() => {
|
|
33390
34169
|
if (!state) return;
|
|
33391
34170
|
const node = tooltipRef.current;
|
|
@@ -33717,7 +34496,7 @@ import {
|
|
|
33717
34496
|
createMediaProviderFromContainer
|
|
33718
34497
|
} from "@bendyline/squisq/storage";
|
|
33719
34498
|
import { MediaContext as MediaContext6 } from "@bendyline/squisq-react";
|
|
33720
|
-
import { Fragment as
|
|
34499
|
+
import { Fragment as Fragment26, jsx as jsx81, jsxs as jsxs58 } from "react/jsx-runtime";
|
|
33721
34500
|
function EditorShell({
|
|
33722
34501
|
initialMarkdown = "",
|
|
33723
34502
|
initialView = "wysiwyg",
|
|
@@ -33879,7 +34658,7 @@ function EditorShellInner({
|
|
|
33879
34658
|
outlineWidth,
|
|
33880
34659
|
themeOverride
|
|
33881
34660
|
}) {
|
|
33882
|
-
const shellRef =
|
|
34661
|
+
const shellRef = useRef61(null);
|
|
33883
34662
|
const {
|
|
33884
34663
|
activeView,
|
|
33885
34664
|
markdownSource,
|
|
@@ -33920,7 +34699,7 @@ function EditorShellInner({
|
|
|
33920
34699
|
() => collectMediaReferencesFromMarkdown(markdownSource),
|
|
33921
34700
|
[markdownSource]
|
|
33922
34701
|
);
|
|
33923
|
-
const imageEditFallbackContainerRef =
|
|
34702
|
+
const imageEditFallbackContainerRef = useRef61(null);
|
|
33924
34703
|
if (imageEditFallbackContainerRef.current === null) {
|
|
33925
34704
|
imageEditFallbackContainerRef.current = new MemoryContentContainer();
|
|
33926
34705
|
}
|
|
@@ -34093,7 +34872,7 @@ ${snippet}` : snippet);
|
|
|
34093
34872
|
fileCount: mediaCount,
|
|
34094
34873
|
onToggleFiles: !isCodeMode && filesToggleEnabled ? handleToggleFiles : void 0,
|
|
34095
34874
|
slotLeft: toolbarSlotLeft,
|
|
34096
|
-
slotAfterTabs: !isCodeMode && isPreview && /* @__PURE__ */ jsxs58(
|
|
34875
|
+
slotAfterTabs: !isCodeMode && isPreview && /* @__PURE__ */ jsxs58(Fragment26, { children: [
|
|
34097
34876
|
/* @__PURE__ */ jsx81(PreviewToolbarControls, {}),
|
|
34098
34877
|
/* @__PURE__ */ jsx81(PresentationModeControl, {})
|
|
34099
34878
|
] }),
|
|
@@ -34279,9 +35058,9 @@ function ImageEditModal({
|
|
|
34279
35058
|
versioningAutoSaveIdleMs,
|
|
34280
35059
|
shellTheme
|
|
34281
35060
|
}) {
|
|
34282
|
-
const modalRef =
|
|
34283
|
-
const surfaceRef =
|
|
34284
|
-
const onCloseRef =
|
|
35061
|
+
const modalRef = useRef61(null);
|
|
35062
|
+
const surfaceRef = useRef61(null);
|
|
35063
|
+
const onCloseRef = useRef61(onClose);
|
|
34285
35064
|
onCloseRef.current = onClose;
|
|
34286
35065
|
const extension = relativePath.split(/[?#]/, 1)[0]?.split(".").pop()?.toLowerCase();
|
|
34287
35066
|
const saveFormat = extension === "png" ? "png" : extension === "jpg" || extension === "jpeg" ? "jpeg" : extension === "webp" ? "webp" : null;
|
|
@@ -34533,7 +35312,7 @@ function ViewSwitcher({ className }) {
|
|
|
34533
35312
|
}
|
|
34534
35313
|
|
|
34535
35314
|
// src/ThemeCustomizerPanel.tsx
|
|
34536
|
-
import { useCallback as useCallback57, useEffect as useEffect62, useMemo as useMemo48, useRef as
|
|
35315
|
+
import { useCallback as useCallback57, useEffect as useEffect62, useMemo as useMemo48, useRef as useRef62, useState as useState71 } from "react";
|
|
34537
35316
|
import { deriveScale, isHex as isHex2, serializeTheme } from "@bendyline/squisq/schemas";
|
|
34538
35317
|
import { jsx as jsx84, jsxs as jsxs61 } from "react/jsx-runtime";
|
|
34539
35318
|
function ThemeCustomizerPanel({
|
|
@@ -34545,8 +35324,8 @@ function ThemeCustomizerPanel({
|
|
|
34545
35324
|
}) {
|
|
34546
35325
|
const [open, setOpen] = useState71(false);
|
|
34547
35326
|
const [draft, setDraft] = useState71(() => themeToDraft(value));
|
|
34548
|
-
const containerRef =
|
|
34549
|
-
const externalIdRef =
|
|
35327
|
+
const containerRef = useRef62(null);
|
|
35328
|
+
const externalIdRef = useRef62(value?.id ?? null);
|
|
34550
35329
|
useEffect62(() => {
|
|
34551
35330
|
const incomingId = value?.id ?? null;
|
|
34552
35331
|
if (incomingId !== externalIdRef.current) {
|
|
@@ -34871,14 +35650,14 @@ import {
|
|
|
34871
35650
|
import { useId as useId12 } from "react";
|
|
34872
35651
|
|
|
34873
35652
|
// src/jsonEditor/editors.tsx
|
|
34874
|
-
import { useEffect as useEffect65, useId as useId11, useRef as
|
|
35653
|
+
import { useEffect as useEffect65, useId as useId11, useRef as useRef64, useState as useState72 } from "react";
|
|
34875
35654
|
import {
|
|
34876
35655
|
appendPointer,
|
|
34877
35656
|
arrayItemKind
|
|
34878
35657
|
} from "@bendyline/squisq/jsonForm";
|
|
34879
35658
|
|
|
34880
35659
|
// src/jsonEditor/EmbeddedRichTextField.tsx
|
|
34881
|
-
import { useEffect as useEffect64, useRef as
|
|
35660
|
+
import { useEffect as useEffect64, useRef as useRef63 } from "react";
|
|
34882
35661
|
import { useEditor as useEditor4, EditorContent as EditorContent3 } from "@tiptap/react";
|
|
34883
35662
|
import StarterKit3 from "@tiptap/starter-kit";
|
|
34884
35663
|
import Table2 from "@tiptap/extension-table";
|
|
@@ -34891,8 +35670,8 @@ import Placeholder2 from "@tiptap/extension-placeholder";
|
|
|
34891
35670
|
import { jsx as jsx86 } from "react/jsx-runtime";
|
|
34892
35671
|
function EmbeddedRichTextField(props) {
|
|
34893
35672
|
const { value, onChange, readOnly = false, placeholder, className } = props;
|
|
34894
|
-
const isExternalUpdate =
|
|
34895
|
-
const lastValueRef =
|
|
35673
|
+
const isExternalUpdate = useRef63(false);
|
|
35674
|
+
const lastValueRef = useRef63(value);
|
|
34896
35675
|
const editor = useEditor4({
|
|
34897
35676
|
editable: !readOnly,
|
|
34898
35677
|
extensions: [
|
|
@@ -34977,7 +35756,7 @@ function TextEditor(props) {
|
|
|
34977
35756
|
function MultilineEditor(props) {
|
|
34978
35757
|
const { value, schema, pointer, disabled } = props;
|
|
34979
35758
|
const { setAtPath } = useJsonEditor();
|
|
34980
|
-
const ref =
|
|
35759
|
+
const ref = useRef64(null);
|
|
34981
35760
|
useEffect65(() => {
|
|
34982
35761
|
const ta = ref.current;
|
|
34983
35762
|
if (!ta) return;
|
|
@@ -35676,11 +36455,12 @@ function JsonEditor(props) {
|
|
|
35676
36455
|
// src/recorder/RecorderButton.tsx
|
|
35677
36456
|
import { useCallback as useCallback58, useState as useState73 } from "react";
|
|
35678
36457
|
import { createPortal as createPortal16 } from "react-dom";
|
|
35679
|
-
import { Fragment as
|
|
36458
|
+
import { Fragment as Fragment27, jsx as jsx90, jsxs as jsxs64 } from "react/jsx-runtime";
|
|
35680
36459
|
function RecorderButton({
|
|
35681
36460
|
mediaProvider,
|
|
35682
36461
|
container = null,
|
|
35683
36462
|
initialMode = "mic",
|
|
36463
|
+
colorScheme = "light",
|
|
35684
36464
|
onSave,
|
|
35685
36465
|
label = "Record",
|
|
35686
36466
|
style,
|
|
@@ -35695,7 +36475,7 @@ function RecorderButton({
|
|
|
35695
36475
|
},
|
|
35696
36476
|
[onSave]
|
|
35697
36477
|
);
|
|
35698
|
-
return /* @__PURE__ */ jsxs64(
|
|
36478
|
+
return /* @__PURE__ */ jsxs64(Fragment27, { children: [
|
|
35699
36479
|
/* @__PURE__ */ jsx90("button", { type: "button", onClick: handleOpen, style, disabled, children: label }),
|
|
35700
36480
|
open && typeof document !== "undefined" && createPortal16(
|
|
35701
36481
|
/* @__PURE__ */ jsx90(
|
|
@@ -35704,6 +36484,7 @@ function RecorderButton({
|
|
|
35704
36484
|
mediaProvider,
|
|
35705
36485
|
container,
|
|
35706
36486
|
initialMode,
|
|
36487
|
+
colorScheme,
|
|
35707
36488
|
onClose: handleClose,
|
|
35708
36489
|
onSave: handleSave
|
|
35709
36490
|
}
|
|
@@ -35878,6 +36659,7 @@ export {
|
|
|
35878
36659
|
toggleAsciiSource,
|
|
35879
36660
|
toggleDirOp,
|
|
35880
36661
|
transitionLabel,
|
|
36662
|
+
translateDiagramOp,
|
|
35881
36663
|
updateTimelineEventOp,
|
|
35882
36664
|
useAsciiDiagramData,
|
|
35883
36665
|
useBlockNavigator,
|