@bendyline/squisq 2.7.1 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WN27GHA3.js → chunk-KEAU5NEM.js} +1 -1
- package/dist/{chunk-FPAS63KT.js → chunk-PTK2SW2V.js} +1234 -10
- package/dist/{chunk-ENNNQIYV.js → chunk-V6NV4GDE.js} +2 -0
- package/dist/doc/index.d.ts +595 -1
- package/dist/doc/index.js +66 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +66 -4
- package/dist/narration/index.js +2 -2
- package/package.json +1 -1
|
@@ -2,29 +2,35 @@ import {
|
|
|
2
2
|
SHAPE_NAMES,
|
|
3
3
|
buildChartData,
|
|
4
4
|
buildNarrationScript,
|
|
5
|
+
buildRegistry,
|
|
5
6
|
coerceTemplateParams,
|
|
6
7
|
deriveTemplateInputs,
|
|
8
|
+
expandPersistentLayers,
|
|
7
9
|
extractBodyPlainText,
|
|
8
10
|
extractRichListItems,
|
|
9
11
|
flattenBlocks,
|
|
10
12
|
flattenRenderableBlocks,
|
|
11
13
|
getBlockBodyText,
|
|
12
14
|
getPinnedBlockMeta,
|
|
15
|
+
getThemeFont,
|
|
13
16
|
hasTemplate,
|
|
14
17
|
isDataFence,
|
|
15
18
|
isShapeName,
|
|
16
19
|
lintTemplateParams,
|
|
17
20
|
markdownToDoc,
|
|
21
|
+
materializeBlockLayersWithRuntime,
|
|
18
22
|
nearestName,
|
|
19
23
|
normalizeShapeKind,
|
|
20
24
|
parseDataFence,
|
|
21
25
|
parseNarrationTimingJson,
|
|
22
26
|
parseStandaloneAnnotation,
|
|
27
|
+
placeLayersInRect,
|
|
23
28
|
readCustomThemesFromFrontmatter,
|
|
29
|
+
resolvePersistentLayers,
|
|
24
30
|
templateRegistry,
|
|
25
31
|
writeCustomTemplatesToFrontmatter,
|
|
26
32
|
writeCustomThemesToFrontmatter
|
|
27
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-V6NV4GDE.js";
|
|
28
34
|
import {
|
|
29
35
|
iconMarker
|
|
30
36
|
} from "./chunk-7ZAAICW4.js";
|
|
@@ -53,11 +59,18 @@ import {
|
|
|
53
59
|
FRONTMATTER_CUSTOM_TEMPLATES_KEY,
|
|
54
60
|
FRONTMATTER_CUSTOM_THEMES_KEY,
|
|
55
61
|
hexHueDegrees,
|
|
62
|
+
oklchDarken,
|
|
63
|
+
oklchLighten,
|
|
64
|
+
relativeLuminance,
|
|
56
65
|
resolveFontFamily,
|
|
57
|
-
resolveTheme
|
|
66
|
+
resolveTheme,
|
|
67
|
+
withAlpha
|
|
58
68
|
} from "./chunk-SBAX4ZPO.js";
|
|
59
69
|
import {
|
|
60
70
|
VIEWPORT_PRESETS,
|
|
71
|
+
calculateFontScale,
|
|
72
|
+
createTemplateContext,
|
|
73
|
+
getViewportOrientation,
|
|
61
74
|
isTemplateBlock
|
|
62
75
|
} from "./chunk-BAOV476U.js";
|
|
63
76
|
import {
|
|
@@ -953,7 +966,7 @@ function buildPreviewDoc(doc, options) {
|
|
|
953
966
|
}
|
|
954
967
|
slides.push(slide);
|
|
955
968
|
}
|
|
956
|
-
const unusedImages = allImages.filter((img) => !usedImageSrcs.has(img.src));
|
|
969
|
+
const unusedImages = options?.interleaveImages ?? true ? allImages.filter((img) => !usedImageSrcs.has(img.src)) : [];
|
|
957
970
|
if (unusedImages.length > 0 && slides.length > 0) {
|
|
958
971
|
const interval = Math.max(2, Math.floor(slides.length / (unusedImages.length + 1)));
|
|
959
972
|
let insertOffset = 0;
|
|
@@ -2439,6 +2452,1186 @@ ${varLines}
|
|
|
2439
2452
|
${PAGE_BASE_CSS}`;
|
|
2440
2453
|
}
|
|
2441
2454
|
|
|
2455
|
+
// src/doc/dashboard/dashboardZoom.ts
|
|
2456
|
+
var DASHBOARD_ZOOM_LEVELS = [1, 1.5, 2];
|
|
2457
|
+
function normalizeDashboardZoom(value) {
|
|
2458
|
+
const raw = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim().replace(/%$/, "")) : Number.NaN;
|
|
2459
|
+
if (!Number.isFinite(raw)) return void 0;
|
|
2460
|
+
const multiplier = raw > 3 ? raw / 100 : raw;
|
|
2461
|
+
return DASHBOARD_ZOOM_LEVELS.find((level) => Math.abs(level - multiplier) < 1e-3);
|
|
2462
|
+
}
|
|
2463
|
+
var ZOOM_ELIGIBLE_TEMPLATES = /* @__PURE__ */ new Set([
|
|
2464
|
+
"content",
|
|
2465
|
+
"list",
|
|
2466
|
+
"quote",
|
|
2467
|
+
"definitionCard",
|
|
2468
|
+
"factCard",
|
|
2469
|
+
"dateEvent"
|
|
2470
|
+
]);
|
|
2471
|
+
var ZOOM_200_MAX_CHARS = 160;
|
|
2472
|
+
var ZOOM_150_MAX_CHARS = 360;
|
|
2473
|
+
function desiredCellZoom(candidate) {
|
|
2474
|
+
if (!candidate.template || !ZOOM_ELIGIBLE_TEMPLATES.has(candidate.template)) return 1;
|
|
2475
|
+
if (candidate.textLength < ZOOM_200_MAX_CHARS) return 2;
|
|
2476
|
+
if (candidate.textLength < ZOOM_150_MAX_CHARS) return 1.5;
|
|
2477
|
+
return 1;
|
|
2478
|
+
}
|
|
2479
|
+
function resolveDashboardZooms(candidates, mode) {
|
|
2480
|
+
const levels = candidates.map((candidate) => {
|
|
2481
|
+
if (candidate.explicit !== void 0) return candidate.explicit;
|
|
2482
|
+
return mode === "auto" ? desiredCellZoom(candidate) : 1;
|
|
2483
|
+
});
|
|
2484
|
+
const boostVotes = /* @__PURE__ */ new Map();
|
|
2485
|
+
candidates.forEach((candidate, index) => {
|
|
2486
|
+
const level = levels[index];
|
|
2487
|
+
if (level === 1) return;
|
|
2488
|
+
const weight = candidate.explicit !== void 0 ? 2 : 1;
|
|
2489
|
+
boostVotes.set(level, (boostVotes.get(level) ?? 0) + weight);
|
|
2490
|
+
});
|
|
2491
|
+
if (boostVotes.size <= 1) return levels;
|
|
2492
|
+
let winner = 2;
|
|
2493
|
+
let winnerVotes = -1;
|
|
2494
|
+
for (const [level, votes] of boostVotes) {
|
|
2495
|
+
if (votes > winnerVotes || votes === winnerVotes && level > winner) {
|
|
2496
|
+
winner = level;
|
|
2497
|
+
winnerVotes = votes;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
return candidates.map((candidate, index) => {
|
|
2501
|
+
if (candidate.explicit !== void 0) return candidate.explicit;
|
|
2502
|
+
return levels[index] === 1 ? 1 : winner;
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
// src/doc/dashboard/DashboardLayout.ts
|
|
2507
|
+
var MAX_CELLS = 32;
|
|
2508
|
+
var MAX_BLOCK_ASSIGNMENT = 64;
|
|
2509
|
+
var NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
2510
|
+
function parsePercent(value) {
|
|
2511
|
+
if (typeof value === "number") {
|
|
2512
|
+
return Number.isFinite(value) ? value : void 0;
|
|
2513
|
+
}
|
|
2514
|
+
if (typeof value !== "string") return void 0;
|
|
2515
|
+
const trimmed = value.trim().replace(/%$/, "");
|
|
2516
|
+
if (trimmed.length === 0) return void 0;
|
|
2517
|
+
const parsed = Number(trimmed);
|
|
2518
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2519
|
+
}
|
|
2520
|
+
function formatPercent(value) {
|
|
2521
|
+
const rounded = Math.round(value * 1e3) / 1e3;
|
|
2522
|
+
return `${rounded}%`;
|
|
2523
|
+
}
|
|
2524
|
+
function validateCell(input, path, errors) {
|
|
2525
|
+
if (!input || typeof input !== "object") {
|
|
2526
|
+
errors.push({ path, message: "cell must be an object" });
|
|
2527
|
+
return void 0;
|
|
2528
|
+
}
|
|
2529
|
+
const cell = input;
|
|
2530
|
+
const x = parsePercent(cell.x);
|
|
2531
|
+
const y = parsePercent(cell.y);
|
|
2532
|
+
const width = parsePercent(cell.width);
|
|
2533
|
+
const height = parsePercent(cell.height);
|
|
2534
|
+
if (x === void 0 || x < 0 || x > 100) {
|
|
2535
|
+
errors.push({ path: `${path}.x`, message: "x must be a percent between 0 and 100" });
|
|
2536
|
+
return void 0;
|
|
2537
|
+
}
|
|
2538
|
+
if (y === void 0 || y < 0 || y > 100) {
|
|
2539
|
+
errors.push({ path: `${path}.y`, message: "y must be a percent between 0 and 100" });
|
|
2540
|
+
return void 0;
|
|
2541
|
+
}
|
|
2542
|
+
if (width === void 0 || width <= 0 || width > 100) {
|
|
2543
|
+
errors.push({ path: `${path}.width`, message: "width must be a percent greater than 0" });
|
|
2544
|
+
return void 0;
|
|
2545
|
+
}
|
|
2546
|
+
if (height === void 0 || height <= 0 || height > 100) {
|
|
2547
|
+
errors.push({ path: `${path}.height`, message: "height must be a percent greater than 0" });
|
|
2548
|
+
return void 0;
|
|
2549
|
+
}
|
|
2550
|
+
const out = {
|
|
2551
|
+
x: formatPercent(x),
|
|
2552
|
+
y: formatPercent(y),
|
|
2553
|
+
width: formatPercent(width),
|
|
2554
|
+
height: formatPercent(height)
|
|
2555
|
+
};
|
|
2556
|
+
if (cell.block !== void 0) {
|
|
2557
|
+
const block = typeof cell.block === "string" ? Number(cell.block) : cell.block;
|
|
2558
|
+
if (typeof block !== "number" || !Number.isInteger(block) || block < 1 || block > MAX_BLOCK_ASSIGNMENT) {
|
|
2559
|
+
errors.push({
|
|
2560
|
+
path: `${path}.block`,
|
|
2561
|
+
message: `block must be an integer between 1 and ${MAX_BLOCK_ASSIGNMENT}`
|
|
2562
|
+
});
|
|
2563
|
+
return void 0;
|
|
2564
|
+
}
|
|
2565
|
+
out.block = block;
|
|
2566
|
+
}
|
|
2567
|
+
if (cell.zoom !== void 0) {
|
|
2568
|
+
const zoom = normalizeDashboardZoom(cell.zoom);
|
|
2569
|
+
if (zoom === void 0) {
|
|
2570
|
+
errors.push({
|
|
2571
|
+
path: `${path}.zoom`,
|
|
2572
|
+
message: "zoom must be 1, 1.5, or 2 (or 100/150/200 percent)"
|
|
2573
|
+
});
|
|
2574
|
+
return void 0;
|
|
2575
|
+
}
|
|
2576
|
+
out.zoom = zoom;
|
|
2577
|
+
}
|
|
2578
|
+
return out;
|
|
2579
|
+
}
|
|
2580
|
+
function validateCellArray(input, path, errors) {
|
|
2581
|
+
if (!Array.isArray(input)) {
|
|
2582
|
+
errors.push({ path, message: "must be an array of cells" });
|
|
2583
|
+
return void 0;
|
|
2584
|
+
}
|
|
2585
|
+
if (input.length < 1 || input.length > MAX_CELLS) {
|
|
2586
|
+
errors.push({ path, message: `must contain between 1 and ${MAX_CELLS} cells` });
|
|
2587
|
+
return void 0;
|
|
2588
|
+
}
|
|
2589
|
+
const cells = [];
|
|
2590
|
+
for (let i = 0; i < input.length; i++) {
|
|
2591
|
+
const cell = validateCell(input[i], `${path}[${i}]`, errors);
|
|
2592
|
+
if (!cell) return void 0;
|
|
2593
|
+
cells.push(cell);
|
|
2594
|
+
}
|
|
2595
|
+
return cells;
|
|
2596
|
+
}
|
|
2597
|
+
function validateTitleSlot(input, errors) {
|
|
2598
|
+
if (!input || typeof input !== "object") {
|
|
2599
|
+
errors.push({ path: "titleSlot", message: "titleSlot must be an object" });
|
|
2600
|
+
return void 0;
|
|
2601
|
+
}
|
|
2602
|
+
const slot = input;
|
|
2603
|
+
const placement = slot.placement === "bottom" ? "bottom" : slot.placement === "top" ? "top" : void 0;
|
|
2604
|
+
if (placement === void 0 && slot.placement !== void 0) {
|
|
2605
|
+
errors.push({ path: "titleSlot.placement", message: "placement must be 'top' or 'bottom'" });
|
|
2606
|
+
return void 0;
|
|
2607
|
+
}
|
|
2608
|
+
const height = parsePercent(slot.height);
|
|
2609
|
+
if (height === void 0 || height <= 0 || height > 40) {
|
|
2610
|
+
errors.push({ path: "titleSlot.height", message: "height must be a percent between 0 and 40" });
|
|
2611
|
+
return void 0;
|
|
2612
|
+
}
|
|
2613
|
+
return { placement: placement ?? "top", height: formatPercent(height) };
|
|
2614
|
+
}
|
|
2615
|
+
function validateDashboardLayoutDefinition(input) {
|
|
2616
|
+
const errors = [];
|
|
2617
|
+
if (!input || typeof input !== "object") {
|
|
2618
|
+
return { valid: false, errors: [{ path: "", message: "definition must be an object" }] };
|
|
2619
|
+
}
|
|
2620
|
+
const def = input;
|
|
2621
|
+
const name = typeof def.name === "string" ? def.name.trim().toLowerCase() : "";
|
|
2622
|
+
if (!NAME_PATTERN.test(name)) {
|
|
2623
|
+
errors.push({
|
|
2624
|
+
path: "name",
|
|
2625
|
+
message: "name must be a slug (lowercase letters, digits, hyphens)"
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
const label = typeof def.label === "string" ? def.label.trim() : "";
|
|
2629
|
+
if (label.length === 0) {
|
|
2630
|
+
errors.push({ path: "label", message: "label must be a non-empty string" });
|
|
2631
|
+
}
|
|
2632
|
+
const cellsInput = def.cells;
|
|
2633
|
+
let landscape;
|
|
2634
|
+
let portrait;
|
|
2635
|
+
let square;
|
|
2636
|
+
if (!cellsInput || typeof cellsInput !== "object") {
|
|
2637
|
+
errors.push({ path: "cells", message: "cells must be an object with a landscape array" });
|
|
2638
|
+
} else {
|
|
2639
|
+
landscape = validateCellArray(cellsInput.landscape, "cells.landscape", errors);
|
|
2640
|
+
if (cellsInput.portrait !== void 0) {
|
|
2641
|
+
portrait = validateCellArray(cellsInput.portrait, "cells.portrait", errors);
|
|
2642
|
+
}
|
|
2643
|
+
if (cellsInput.square !== void 0) {
|
|
2644
|
+
square = validateCellArray(cellsInput.square, "cells.square", errors);
|
|
2645
|
+
}
|
|
2646
|
+
if (landscape) {
|
|
2647
|
+
if (portrait && portrait.length !== landscape.length) {
|
|
2648
|
+
errors.push({
|
|
2649
|
+
path: "cells.portrait",
|
|
2650
|
+
message: "portrait must define the same number of cells as landscape"
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
if (square && square.length !== landscape.length) {
|
|
2654
|
+
errors.push({
|
|
2655
|
+
path: "cells.square",
|
|
2656
|
+
message: "square must define the same number of cells as landscape"
|
|
2657
|
+
});
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
let titleSlot;
|
|
2662
|
+
if (def.titleSlot !== void 0) {
|
|
2663
|
+
titleSlot = validateTitleSlot(def.titleSlot, errors);
|
|
2664
|
+
}
|
|
2665
|
+
if (errors.length > 0 || !landscape) {
|
|
2666
|
+
return { valid: false, errors };
|
|
2667
|
+
}
|
|
2668
|
+
const layout2 = {
|
|
2669
|
+
name,
|
|
2670
|
+
label,
|
|
2671
|
+
cells: {
|
|
2672
|
+
landscape,
|
|
2673
|
+
...portrait ? { portrait } : {},
|
|
2674
|
+
...square ? { square } : {}
|
|
2675
|
+
}
|
|
2676
|
+
};
|
|
2677
|
+
if (typeof def.description === "string" && def.description.trim()) {
|
|
2678
|
+
layout2.description = def.description.trim();
|
|
2679
|
+
}
|
|
2680
|
+
if (titleSlot) layout2.titleSlot = titleSlot;
|
|
2681
|
+
if (def.auto === false) layout2.auto = false;
|
|
2682
|
+
return { valid: true, errors, layout: layout2 };
|
|
2683
|
+
}
|
|
2684
|
+
function layoutCapacity(def) {
|
|
2685
|
+
return def.cells.landscape.length;
|
|
2686
|
+
}
|
|
2687
|
+
function transposeCells(cells) {
|
|
2688
|
+
return cells.map((cell) => ({
|
|
2689
|
+
x: cell.y,
|
|
2690
|
+
y: cell.x,
|
|
2691
|
+
width: cell.height,
|
|
2692
|
+
height: cell.width,
|
|
2693
|
+
...cell.block !== void 0 ? { block: cell.block } : {},
|
|
2694
|
+
...cell.zoom !== void 0 ? { zoom: cell.zoom } : {}
|
|
2695
|
+
}));
|
|
2696
|
+
}
|
|
2697
|
+
function resolveLayoutCells(def, orientation, contentRect) {
|
|
2698
|
+
const cells = orientation === "portrait" ? def.cells.portrait ?? transposeCells(def.cells.landscape) : orientation === "square" ? def.cells.square ?? def.cells.landscape : def.cells.landscape;
|
|
2699
|
+
return cells.map((cell) => {
|
|
2700
|
+
const x = parsePercent(cell.x) ?? 0;
|
|
2701
|
+
const y = parsePercent(cell.y) ?? 0;
|
|
2702
|
+
const width = parsePercent(cell.width) ?? 0;
|
|
2703
|
+
const height = parsePercent(cell.height) ?? 0;
|
|
2704
|
+
const resolved = {
|
|
2705
|
+
rect: {
|
|
2706
|
+
x: contentRect.x + x / 100 * contentRect.width,
|
|
2707
|
+
y: contentRect.y + y / 100 * contentRect.height,
|
|
2708
|
+
width: Math.max(1, width / 100 * contentRect.width),
|
|
2709
|
+
height: Math.max(1, height / 100 * contentRect.height)
|
|
2710
|
+
}
|
|
2711
|
+
};
|
|
2712
|
+
if (cell.block !== void 0) resolved.block = cell.block;
|
|
2713
|
+
const zoom = normalizeDashboardZoom(cell.zoom);
|
|
2714
|
+
if (zoom !== void 0) resolved.zoom = zoom;
|
|
2715
|
+
return resolved;
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
// src/doc/dashboard/dashboardLayoutsFrontmatter.ts
|
|
2720
|
+
var FRONTMATTER_DASHBOARD_LAYOUTS_KEY = "squisq-dashboard-layouts";
|
|
2721
|
+
var LONG_TO_SHORT = {
|
|
2722
|
+
label: "lb",
|
|
2723
|
+
description: "ds",
|
|
2724
|
+
cells: "ce",
|
|
2725
|
+
landscape: "ls",
|
|
2726
|
+
portrait: "pt",
|
|
2727
|
+
square: "sq",
|
|
2728
|
+
width: "wd",
|
|
2729
|
+
height: "hg",
|
|
2730
|
+
block: "bk",
|
|
2731
|
+
zoom: "zo",
|
|
2732
|
+
titleSlot: "ts",
|
|
2733
|
+
placement: "pl",
|
|
2734
|
+
auto: "au"
|
|
2735
|
+
};
|
|
2736
|
+
var SHORT_TO_LONG = Object.fromEntries(
|
|
2737
|
+
Object.entries(LONG_TO_SHORT).map(([long, short]) => [short, long])
|
|
2738
|
+
);
|
|
2739
|
+
function renameKeys(value, map2) {
|
|
2740
|
+
if (Array.isArray(value)) return value.map((v) => renameKeys(v, map2));
|
|
2741
|
+
if (value && typeof value === "object") {
|
|
2742
|
+
const out = {};
|
|
2743
|
+
for (const [k, v] of Object.entries(value)) {
|
|
2744
|
+
out[map2[k] ?? k] = renameKeys(v, map2);
|
|
2745
|
+
}
|
|
2746
|
+
return out;
|
|
2747
|
+
}
|
|
2748
|
+
return value;
|
|
2749
|
+
}
|
|
2750
|
+
function readDashboardLayoutsFromFrontmatter(frontmatter) {
|
|
2751
|
+
if (!frontmatter) return void 0;
|
|
2752
|
+
const candidates = normalizeCandidates(frontmatter[FRONTMATTER_DASHBOARD_LAYOUTS_KEY]);
|
|
2753
|
+
if (!candidates) return void 0;
|
|
2754
|
+
const out = [];
|
|
2755
|
+
for (const entry of candidates) {
|
|
2756
|
+
const result = validateDashboardLayoutDefinition(entry);
|
|
2757
|
+
if (result.layout) out.push(result.layout);
|
|
2758
|
+
}
|
|
2759
|
+
return out.length > 0 ? out : void 0;
|
|
2760
|
+
}
|
|
2761
|
+
function writeDashboardLayoutsToFrontmatter(layouts, options) {
|
|
2762
|
+
if (!layouts || layouts.length === 0) return void 0;
|
|
2763
|
+
const map2 = {};
|
|
2764
|
+
for (const def of layouts) {
|
|
2765
|
+
const { name, ...rest } = def;
|
|
2766
|
+
map2[name] = renameKeys(rest, LONG_TO_SHORT);
|
|
2767
|
+
}
|
|
2768
|
+
return JSON.stringify(map2, null, options?.pretty ? 2 : void 0);
|
|
2769
|
+
}
|
|
2770
|
+
function expandCompactMap(map2) {
|
|
2771
|
+
return Object.entries(map2).map(([name, raw]) => {
|
|
2772
|
+
const expanded = renameKeys(raw && typeof raw === "object" ? raw : {}, SHORT_TO_LONG);
|
|
2773
|
+
return { name, ...expanded };
|
|
2774
|
+
});
|
|
2775
|
+
}
|
|
2776
|
+
function fromParsed(parsed) {
|
|
2777
|
+
if (Array.isArray(parsed)) return parsed;
|
|
2778
|
+
if (parsed && typeof parsed === "object")
|
|
2779
|
+
return expandCompactMap(parsed);
|
|
2780
|
+
return null;
|
|
2781
|
+
}
|
|
2782
|
+
function tryJson(s) {
|
|
2783
|
+
try {
|
|
2784
|
+
return JSON.parse(s);
|
|
2785
|
+
} catch {
|
|
2786
|
+
return void 0;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
function normalizeCandidates(raw) {
|
|
2790
|
+
if (raw == null) return null;
|
|
2791
|
+
if (Array.isArray(raw)) return raw;
|
|
2792
|
+
if (typeof raw === "object") return expandCompactMap(raw);
|
|
2793
|
+
if (typeof raw !== "string") return null;
|
|
2794
|
+
const parsed = tryJson(raw.trim());
|
|
2795
|
+
if (parsed !== void 0) return fromParsed(parsed);
|
|
2796
|
+
return null;
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
// src/doc/dashboard/builtinDashboardLayouts.ts
|
|
2800
|
+
var GAP = 2;
|
|
2801
|
+
function pct(value) {
|
|
2802
|
+
return `${Math.round(value * 1e3) / 1e3}%`;
|
|
2803
|
+
}
|
|
2804
|
+
function gridCells(cols, rows, gap = GAP) {
|
|
2805
|
+
const cellW = (100 - (cols - 1) * gap) / cols;
|
|
2806
|
+
const cellH = (100 - (rows - 1) * gap) / rows;
|
|
2807
|
+
const cells = [];
|
|
2808
|
+
for (let row = 0; row < rows; row++) {
|
|
2809
|
+
for (let col = 0; col < cols; col++) {
|
|
2810
|
+
cells.push({
|
|
2811
|
+
x: pct(col * (cellW + gap)),
|
|
2812
|
+
y: pct(row * (cellH + gap)),
|
|
2813
|
+
width: pct(cellW),
|
|
2814
|
+
height: pct(cellH)
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
return cells;
|
|
2819
|
+
}
|
|
2820
|
+
function heroRow(heroWidth, companions, gap = GAP) {
|
|
2821
|
+
const railX = heroWidth + gap;
|
|
2822
|
+
const railW = 100 - railX;
|
|
2823
|
+
const cellH = (100 - (companions - 1) * gap) / companions;
|
|
2824
|
+
const cells = [
|
|
2825
|
+
{ x: "0%", y: "0%", width: pct(heroWidth), height: "100%" }
|
|
2826
|
+
];
|
|
2827
|
+
for (let i = 0; i < companions; i++) {
|
|
2828
|
+
cells.push({
|
|
2829
|
+
x: pct(railX),
|
|
2830
|
+
y: pct(i * (cellH + gap)),
|
|
2831
|
+
width: pct(railW),
|
|
2832
|
+
height: pct(cellH)
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
return cells;
|
|
2836
|
+
}
|
|
2837
|
+
function heroBand(heroHeight, below, gap = GAP) {
|
|
2838
|
+
const rowY = heroHeight + gap;
|
|
2839
|
+
const rowH = 100 - rowY;
|
|
2840
|
+
const cellW = (100 - (below - 1) * gap) / below;
|
|
2841
|
+
const cells = [
|
|
2842
|
+
{ x: "0%", y: "0%", width: "100%", height: pct(heroHeight) }
|
|
2843
|
+
];
|
|
2844
|
+
for (let i = 0; i < below; i++) {
|
|
2845
|
+
cells.push({
|
|
2846
|
+
x: pct(i * (cellW + gap)),
|
|
2847
|
+
y: pct(rowY),
|
|
2848
|
+
width: pct(cellW),
|
|
2849
|
+
height: pct(rowH)
|
|
2850
|
+
});
|
|
2851
|
+
}
|
|
2852
|
+
return cells;
|
|
2853
|
+
}
|
|
2854
|
+
function mosaicCells(heroWidth, gap = GAP) {
|
|
2855
|
+
const gridX = heroWidth + gap;
|
|
2856
|
+
const gridW = 100 - gridX;
|
|
2857
|
+
const cellW = (gridW - gap) / 2;
|
|
2858
|
+
const cellH = (100 - gap) / 2;
|
|
2859
|
+
const cells = [
|
|
2860
|
+
{ x: "0%", y: "0%", width: pct(heroWidth), height: "100%" }
|
|
2861
|
+
];
|
|
2862
|
+
for (let row = 0; row < 2; row++) {
|
|
2863
|
+
for (let col = 0; col < 2; col++) {
|
|
2864
|
+
cells.push({
|
|
2865
|
+
x: pct(gridX + col * (cellW + gap)),
|
|
2866
|
+
y: pct(row * (cellH + gap)),
|
|
2867
|
+
width: pct(cellW),
|
|
2868
|
+
height: pct(cellH)
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
return cells;
|
|
2873
|
+
}
|
|
2874
|
+
var BUILTIN_DASHBOARD_LAYOUTS = Object.freeze([
|
|
2875
|
+
{
|
|
2876
|
+
name: "focus-1",
|
|
2877
|
+
label: "Focus",
|
|
2878
|
+
description: "One block fills the dashboard.",
|
|
2879
|
+
cells: { landscape: [{ x: "0%", y: "0%", width: "100%", height: "100%" }] }
|
|
2880
|
+
},
|
|
2881
|
+
{
|
|
2882
|
+
name: "split-2",
|
|
2883
|
+
label: "Split",
|
|
2884
|
+
description: "Two blocks side by side.",
|
|
2885
|
+
cells: { landscape: gridCells(2, 1) }
|
|
2886
|
+
},
|
|
2887
|
+
{
|
|
2888
|
+
name: "hero-left",
|
|
2889
|
+
label: "Hero + 2",
|
|
2890
|
+
description: "A large lead block with two companions.",
|
|
2891
|
+
cells: {
|
|
2892
|
+
landscape: heroRow(58, 2),
|
|
2893
|
+
portrait: [
|
|
2894
|
+
{ x: "0%", y: "0%", width: "100%", height: pct(46) },
|
|
2895
|
+
{ x: "0%", y: pct(48), width: "100%", height: pct(25) },
|
|
2896
|
+
{ x: "0%", y: pct(75), width: "100%", height: pct(25) }
|
|
2897
|
+
]
|
|
2898
|
+
}
|
|
2899
|
+
},
|
|
2900
|
+
{
|
|
2901
|
+
name: "grid-2x2",
|
|
2902
|
+
label: "Grid 2\xD72",
|
|
2903
|
+
description: "Four blocks in a balanced grid.",
|
|
2904
|
+
cells: { landscape: gridCells(2, 2) }
|
|
2905
|
+
},
|
|
2906
|
+
{
|
|
2907
|
+
name: "hero-top",
|
|
2908
|
+
label: "Hero band",
|
|
2909
|
+
description: "A wide lead band with three blocks below.",
|
|
2910
|
+
cells: {
|
|
2911
|
+
landscape: heroBand(46, 3),
|
|
2912
|
+
portrait: [
|
|
2913
|
+
{ x: "0%", y: "0%", width: "100%", height: pct(40) },
|
|
2914
|
+
{ x: "0%", y: pct(42), width: "100%", height: pct(18) },
|
|
2915
|
+
{ x: "0%", y: pct(62), width: "100%", height: pct(18) },
|
|
2916
|
+
{ x: "0%", y: pct(82), width: "100%", height: pct(18) }
|
|
2917
|
+
]
|
|
2918
|
+
},
|
|
2919
|
+
auto: false
|
|
2920
|
+
},
|
|
2921
|
+
{
|
|
2922
|
+
name: "mosaic-5",
|
|
2923
|
+
label: "Mosaic",
|
|
2924
|
+
description: "A hero block beside a 2\xD72 grid.",
|
|
2925
|
+
cells: {
|
|
2926
|
+
landscape: mosaicCells(58),
|
|
2927
|
+
portrait: [
|
|
2928
|
+
{ x: "0%", y: "0%", width: "100%", height: pct(40) },
|
|
2929
|
+
{ x: "0%", y: pct(42), width: pct(49), height: pct(27) },
|
|
2930
|
+
{ x: pct(51), y: pct(42), width: pct(49), height: pct(27) },
|
|
2931
|
+
{ x: "0%", y: pct(71), width: pct(49), height: pct(29) },
|
|
2932
|
+
{ x: pct(51), y: pct(71), width: pct(49), height: pct(29) }
|
|
2933
|
+
]
|
|
2934
|
+
}
|
|
2935
|
+
},
|
|
2936
|
+
{
|
|
2937
|
+
name: "grid-3x2",
|
|
2938
|
+
label: "Grid 3\xD72",
|
|
2939
|
+
description: "Six blocks in three columns.",
|
|
2940
|
+
cells: { landscape: gridCells(3, 2) }
|
|
2941
|
+
},
|
|
2942
|
+
{
|
|
2943
|
+
name: "grid-3x3",
|
|
2944
|
+
label: "Grid 3\xD73",
|
|
2945
|
+
description: "Nine blocks in a square grid.",
|
|
2946
|
+
cells: { landscape: gridCells(3, 3), square: gridCells(3, 3) }
|
|
2947
|
+
},
|
|
2948
|
+
{
|
|
2949
|
+
name: "grid-4x3",
|
|
2950
|
+
label: "Grid 4\xD73",
|
|
2951
|
+
description: "Twelve blocks in four columns.",
|
|
2952
|
+
cells: { landscape: gridCells(4, 3) }
|
|
2953
|
+
},
|
|
2954
|
+
{
|
|
2955
|
+
name: "grid-4x4",
|
|
2956
|
+
label: "Grid 4\xD74",
|
|
2957
|
+
description: "Sixteen blocks \u2014 the densest wall.",
|
|
2958
|
+
cells: { landscape: gridCells(4, 4) }
|
|
2959
|
+
}
|
|
2960
|
+
]);
|
|
2961
|
+
function summarize(def, custom) {
|
|
2962
|
+
return {
|
|
2963
|
+
id: def.name,
|
|
2964
|
+
label: def.label,
|
|
2965
|
+
...def.description ? { description: def.description } : {},
|
|
2966
|
+
capacity: layoutCapacity(def),
|
|
2967
|
+
custom
|
|
2968
|
+
};
|
|
2969
|
+
}
|
|
2970
|
+
function getDashboardLayoutSummaries() {
|
|
2971
|
+
return BUILTIN_DASHBOARD_LAYOUTS.map((def) => summarize(def, false));
|
|
2972
|
+
}
|
|
2973
|
+
function listDashboardLayouts(doc) {
|
|
2974
|
+
const customs = readDashboardLayoutsFromFrontmatter(doc?.frontmatter) ?? [];
|
|
2975
|
+
const customNames = new Set(customs.map((def) => def.name));
|
|
2976
|
+
return [
|
|
2977
|
+
...customs.map((def) => summarize(def, true)),
|
|
2978
|
+
...BUILTIN_DASHBOARD_LAYOUTS.filter((def) => !customNames.has(def.name)).map(
|
|
2979
|
+
(def) => summarize(def, false)
|
|
2980
|
+
)
|
|
2981
|
+
];
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
// src/doc/dashboard/chooseDashboardLayout.ts
|
|
2985
|
+
var DASHBOARD_AUTO_LAYOUT_ID = "auto";
|
|
2986
|
+
function resolveDashboardLayoutDefinition(id, customLayouts) {
|
|
2987
|
+
const normalized = id.trim().toLowerCase();
|
|
2988
|
+
if (!normalized) return void 0;
|
|
2989
|
+
return customLayouts?.find((def) => def.name === normalized) ?? BUILTIN_DASHBOARD_LAYOUTS.find((def) => def.name === normalized);
|
|
2990
|
+
}
|
|
2991
|
+
function chooseDashboardLayout(blockCount, _orientation, customLayouts) {
|
|
2992
|
+
const pool = [
|
|
2993
|
+
...(customLayouts ?? []).filter((def) => def.auto !== false),
|
|
2994
|
+
...BUILTIN_DASHBOARD_LAYOUTS.filter((def) => def.auto !== false)
|
|
2995
|
+
];
|
|
2996
|
+
const ladder = [...pool].sort((a, b) => layoutCapacity(a) - layoutCapacity(b));
|
|
2997
|
+
const needed = Math.max(1, blockCount);
|
|
2998
|
+
const fit = ladder.find((def) => layoutCapacity(def) >= needed);
|
|
2999
|
+
if (fit) return fit;
|
|
3000
|
+
const max = ladder.reduce((acc, def) => Math.max(acc, layoutCapacity(def)), 0);
|
|
3001
|
+
return ladder.find((def) => layoutCapacity(def) === max) ?? BUILTIN_DASHBOARD_LAYOUTS[0];
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
// src/doc/dashboard/dashboardStyle.ts
|
|
3005
|
+
var DASHBOARD_STYLE_IDS = ["basic", "card", "panel", "accent"];
|
|
3006
|
+
var DEFAULT_DASHBOARD_STYLE = "basic";
|
|
3007
|
+
var DASHBOARD_STYLES = Object.freeze([
|
|
3008
|
+
{
|
|
3009
|
+
id: "basic",
|
|
3010
|
+
label: "Basic",
|
|
3011
|
+
description: "Blocks fill their cells edge to edge."
|
|
3012
|
+
},
|
|
3013
|
+
{
|
|
3014
|
+
id: "card",
|
|
3015
|
+
label: "Cards",
|
|
3016
|
+
description: "Each block sits on a raised, rounded card."
|
|
3017
|
+
},
|
|
3018
|
+
{
|
|
3019
|
+
id: "panel",
|
|
3020
|
+
label: "Panels",
|
|
3021
|
+
description: "Flat outlined panels with an accent rule."
|
|
3022
|
+
},
|
|
3023
|
+
{
|
|
3024
|
+
id: "accent",
|
|
3025
|
+
label: "Accent cards",
|
|
3026
|
+
description: "Cards tinted with the theme's accent colors in rotation."
|
|
3027
|
+
}
|
|
3028
|
+
]);
|
|
3029
|
+
function resolveDashboardStyleId(value) {
|
|
3030
|
+
if (typeof value !== "string") return void 0;
|
|
3031
|
+
const normalized = value.trim().toLowerCase();
|
|
3032
|
+
if (!normalized) return void 0;
|
|
3033
|
+
if (normalized === "none" || normalized === "default" || normalized === "flat") return "basic";
|
|
3034
|
+
if (normalized === "cards") return "card";
|
|
3035
|
+
if (normalized === "panels" || normalized === "outline") return "panel";
|
|
3036
|
+
if (normalized === "accents" || normalized === "accent-cards") return "accent";
|
|
3037
|
+
return DASHBOARD_STYLE_IDS.includes(normalized) ? normalized : void 0;
|
|
3038
|
+
}
|
|
3039
|
+
function clamp(value, min, max) {
|
|
3040
|
+
return Math.min(max, Math.max(min, value));
|
|
3041
|
+
}
|
|
3042
|
+
function isLight(color) {
|
|
3043
|
+
return relativeLuminance(color) > 0.45;
|
|
3044
|
+
}
|
|
3045
|
+
function dashboardCanvasFill(style, theme) {
|
|
3046
|
+
const background = theme.colors.background;
|
|
3047
|
+
if (style === "basic" || style === "panel") return background;
|
|
3048
|
+
return isLight(background) ? oklchDarken(background, 0.05) : oklchLighten(background, 0.04);
|
|
3049
|
+
}
|
|
3050
|
+
function dashboardCellAccent(theme, index) {
|
|
3051
|
+
const schemes = Object.values(theme.colorSchemes ?? {});
|
|
3052
|
+
if (schemes.length === 0) return theme.colors.primary;
|
|
3053
|
+
const scheme = schemes[(index % schemes.length + schemes.length) % schemes.length];
|
|
3054
|
+
return scheme.accent || scheme.text || theme.colors.primary;
|
|
3055
|
+
}
|
|
3056
|
+
function cellMetrics(style, theme, rect) {
|
|
3057
|
+
const minAxis = Math.max(1, Math.min(rect.width, rect.height));
|
|
3058
|
+
const themeRadius = theme.style?.borderRadius ?? 14;
|
|
3059
|
+
if (style === "panel") {
|
|
3060
|
+
return {
|
|
3061
|
+
edge: clamp(minAxis * 0.014, 3, 12),
|
|
3062
|
+
radius: clamp(themeRadius * 0.45, 0, minAxis * 0.05)
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
return {
|
|
3066
|
+
// Card styles reserve a wider ring: the elevation is painted inside the
|
|
3067
|
+
// layout cell (a cell renders as its own clipped SVG), so the shadow
|
|
3068
|
+
// needs room to fall without being cut flat at the cell boundary.
|
|
3069
|
+
edge: clamp(minAxis * 0.03, 6, 26),
|
|
3070
|
+
radius: clamp(themeRadius, 6, minAxis * 0.1)
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
function radiusPct(radius, box) {
|
|
3074
|
+
if (radius <= 0) return void 0;
|
|
3075
|
+
const x = Math.round(radius / Math.max(1, box.width) * 1e5) / 1e3;
|
|
3076
|
+
const y = Math.round(radius / Math.max(1, box.height) * 1e5) / 1e3;
|
|
3077
|
+
return `${x}% / ${y}%`;
|
|
3078
|
+
}
|
|
3079
|
+
function buildDashboardCellChrome(style, options) {
|
|
3080
|
+
const { theme, rect, index } = options;
|
|
3081
|
+
if (style === "basic") return null;
|
|
3082
|
+
const { edge, radius } = cellMetrics(style, theme, rect);
|
|
3083
|
+
const cardRect = {
|
|
3084
|
+
x: rect.x + edge,
|
|
3085
|
+
y: rect.y + edge,
|
|
3086
|
+
width: Math.max(1, rect.width - edge * 2),
|
|
3087
|
+
height: Math.max(1, rect.height - edge * 2)
|
|
3088
|
+
};
|
|
3089
|
+
const local = {
|
|
3090
|
+
x: edge,
|
|
3091
|
+
y: edge,
|
|
3092
|
+
width: cardRect.width,
|
|
3093
|
+
height: cardRect.height
|
|
3094
|
+
};
|
|
3095
|
+
const minAxis = Math.max(1, Math.min(rect.width, rect.height));
|
|
3096
|
+
const hairline = Math.max(1, Math.round(minAxis * 4e-3));
|
|
3097
|
+
const accent = dashboardCellAccent(theme, index);
|
|
3098
|
+
const layers = [];
|
|
3099
|
+
if (style !== "panel") {
|
|
3100
|
+
const steps = [
|
|
3101
|
+
{ spread: 0.45, dy: 0.5, alpha: 0.05 },
|
|
3102
|
+
{ spread: 0.25, dy: 0.42, alpha: 0.05 },
|
|
3103
|
+
{ spread: 0.08, dy: 0.3, alpha: 0.06 }
|
|
3104
|
+
];
|
|
3105
|
+
steps.forEach((step, stepIndex) => {
|
|
3106
|
+
const spread = edge * step.spread;
|
|
3107
|
+
const dy = edge * step.dy;
|
|
3108
|
+
layers.push({
|
|
3109
|
+
type: "shape",
|
|
3110
|
+
id: `cell-shadow-${stepIndex}`,
|
|
3111
|
+
content: {
|
|
3112
|
+
shape: "rect",
|
|
3113
|
+
fill: withAlpha(theme.colors.text, step.alpha),
|
|
3114
|
+
borderRadius: radius + spread
|
|
3115
|
+
},
|
|
3116
|
+
position: {
|
|
3117
|
+
x: local.x - spread,
|
|
3118
|
+
y: local.y - spread + dy,
|
|
3119
|
+
width: local.width + spread * 2,
|
|
3120
|
+
height: local.height + spread * 2
|
|
3121
|
+
}
|
|
3122
|
+
});
|
|
3123
|
+
});
|
|
3124
|
+
}
|
|
3125
|
+
layers.push({
|
|
3126
|
+
type: "shape",
|
|
3127
|
+
id: "cell-surface",
|
|
3128
|
+
content: {
|
|
3129
|
+
shape: "rect",
|
|
3130
|
+
// The surface stays the theme background; the canvas is what moves
|
|
3131
|
+
// (see `dashboardCanvasFill`), so cards read as raised paper in both
|
|
3132
|
+
// light and dark themes without inventing a palette slot.
|
|
3133
|
+
fill: theme.colors.background,
|
|
3134
|
+
borderRadius: radius
|
|
3135
|
+
},
|
|
3136
|
+
position: { x: local.x, y: local.y, width: local.width, height: local.height }
|
|
3137
|
+
});
|
|
3138
|
+
const overlayLayers = [];
|
|
3139
|
+
if (style === "accent") {
|
|
3140
|
+
overlayLayers.push({
|
|
3141
|
+
type: "shape",
|
|
3142
|
+
id: "cell-accent-wash",
|
|
3143
|
+
content: { shape: "rect", fill: withAlpha(accent, 0.1), borderRadius: radius },
|
|
3144
|
+
position: { x: 0, y: 0, width: "100%", height: "100%" }
|
|
3145
|
+
});
|
|
3146
|
+
}
|
|
3147
|
+
if (style === "accent" || style === "panel") {
|
|
3148
|
+
const barHeight = Math.max(2, Math.round(minAxis * 0.014));
|
|
3149
|
+
overlayLayers.push({
|
|
3150
|
+
type: "shape",
|
|
3151
|
+
id: "cell-accent-bar",
|
|
3152
|
+
content: { shape: "rect", fill: accent },
|
|
3153
|
+
position: { x: 0, y: 0, width: "100%", height: barHeight }
|
|
3154
|
+
});
|
|
3155
|
+
}
|
|
3156
|
+
const strokeWidth = style === "panel" ? hairline : Math.max(1, hairline * 0.6);
|
|
3157
|
+
overlayLayers.push({
|
|
3158
|
+
type: "shape",
|
|
3159
|
+
id: "cell-border",
|
|
3160
|
+
content: {
|
|
3161
|
+
shape: "rect",
|
|
3162
|
+
fill: "none",
|
|
3163
|
+
stroke: style === "panel" ? withAlpha(accent, 0.45) : withAlpha(theme.colors.text, 0.14),
|
|
3164
|
+
strokeWidth,
|
|
3165
|
+
borderRadius: radius
|
|
3166
|
+
},
|
|
3167
|
+
position: {
|
|
3168
|
+
x: strokeWidth / 2,
|
|
3169
|
+
y: strokeWidth / 2,
|
|
3170
|
+
width: Math.max(1, local.width - strokeWidth),
|
|
3171
|
+
height: Math.max(1, local.height - strokeWidth)
|
|
3172
|
+
}
|
|
3173
|
+
});
|
|
3174
|
+
const chrome = {
|
|
3175
|
+
cardRect,
|
|
3176
|
+
// The block fills the card: templates bring their own padding, and a
|
|
3177
|
+
// padded well inside the card reads as a box inside a box.
|
|
3178
|
+
contentRect: cardRect,
|
|
3179
|
+
layers,
|
|
3180
|
+
overlayLayers,
|
|
3181
|
+
radius
|
|
3182
|
+
};
|
|
3183
|
+
const contentRadius = radiusPct(radius, cardRect);
|
|
3184
|
+
if (contentRadius) chrome.contentRadiusPct = contentRadius;
|
|
3185
|
+
return chrome;
|
|
3186
|
+
}
|
|
3187
|
+
function stripsBlockBackdrop(style) {
|
|
3188
|
+
return style !== "basic";
|
|
3189
|
+
}
|
|
3190
|
+
function stripBlockBackdropLayer(layers, theme) {
|
|
3191
|
+
const first = layers[0];
|
|
3192
|
+
if (!first || first.type !== "shape") return [...layers];
|
|
3193
|
+
const { content: content2, position } = first;
|
|
3194
|
+
const fill = typeof content2.fill === "string" ? content2.fill.trim().toLowerCase() : "";
|
|
3195
|
+
const fullBleed = content2.shape === "rect" && (position.x === 0 || position.x === "0%") && (position.y === 0 || position.y === "0%") && position.width === "100%" && position.height === "100%";
|
|
3196
|
+
if (!fullBleed || content2.gradient || content2.pattern) return [...layers];
|
|
3197
|
+
if (fill !== theme.colors.background.trim().toLowerCase()) return [...layers];
|
|
3198
|
+
return layers.slice(1);
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
// src/doc/dashboard/dashboardSettings.ts
|
|
3202
|
+
var DASHBOARD_FRONTMATTER_KEYS = Object.freeze({
|
|
3203
|
+
layout: { canonical: "squisq-dashboard-layout", legacy: "dashboard-layout" },
|
|
3204
|
+
showTitle: { canonical: "squisq-dashboard-title", legacy: "dashboard-title" },
|
|
3205
|
+
zoom: { canonical: "squisq-dashboard-zoom", legacy: "dashboard-zoom" },
|
|
3206
|
+
style: { canonical: "squisq-dashboard-style", legacy: "dashboard-style" }
|
|
3207
|
+
});
|
|
3208
|
+
var DEFAULT_DASHBOARD_SETTINGS = Object.freeze({
|
|
3209
|
+
layout: DASHBOARD_AUTO_LAYOUT_ID,
|
|
3210
|
+
showTitle: true,
|
|
3211
|
+
zoom: "auto",
|
|
3212
|
+
style: DEFAULT_DASHBOARD_STYLE
|
|
3213
|
+
});
|
|
3214
|
+
function readSetting2(frontmatter, keys) {
|
|
3215
|
+
if (!frontmatter) return void 0;
|
|
3216
|
+
return Object.prototype.hasOwnProperty.call(frontmatter, keys.canonical) ? frontmatter[keys.canonical] : frontmatter[keys.legacy];
|
|
3217
|
+
}
|
|
3218
|
+
function resolveBoolean2(value) {
|
|
3219
|
+
if (typeof value === "boolean") return value;
|
|
3220
|
+
if (typeof value !== "string") return void 0;
|
|
3221
|
+
const normalized = value.trim().toLowerCase();
|
|
3222
|
+
if (["true", "yes", "on", "show", "visible"].includes(normalized)) return true;
|
|
3223
|
+
if (["false", "no", "off", "hide", "hidden"].includes(normalized)) return false;
|
|
3224
|
+
return void 0;
|
|
3225
|
+
}
|
|
3226
|
+
function resolveLayoutId(value) {
|
|
3227
|
+
if (typeof value !== "string") return void 0;
|
|
3228
|
+
const normalized = value.trim().toLowerCase();
|
|
3229
|
+
if (!normalized) return void 0;
|
|
3230
|
+
if (normalized === "auto" || normalized === "default") return DASHBOARD_AUTO_LAYOUT_ID;
|
|
3231
|
+
return normalized;
|
|
3232
|
+
}
|
|
3233
|
+
function resolveZoomMode(value) {
|
|
3234
|
+
if (typeof value === "boolean") return value ? "auto" : "off";
|
|
3235
|
+
if (typeof value !== "string") return void 0;
|
|
3236
|
+
const normalized = value.trim().toLowerCase();
|
|
3237
|
+
if (["auto", "on", "true", "yes"].includes(normalized)) return "auto";
|
|
3238
|
+
if (["off", "false", "no", "none", "100", "100%", "1"].includes(normalized)) return "off";
|
|
3239
|
+
return void 0;
|
|
3240
|
+
}
|
|
3241
|
+
function resolveDashboardSettings(frontmatter, overrides = {}) {
|
|
3242
|
+
const resolved = {
|
|
3243
|
+
layout: resolveLayoutId(readSetting2(frontmatter, DASHBOARD_FRONTMATTER_KEYS.layout)) ?? DEFAULT_DASHBOARD_SETTINGS.layout,
|
|
3244
|
+
showTitle: resolveBoolean2(readSetting2(frontmatter, DASHBOARD_FRONTMATTER_KEYS.showTitle)) ?? DEFAULT_DASHBOARD_SETTINGS.showTitle,
|
|
3245
|
+
zoom: resolveZoomMode(readSetting2(frontmatter, DASHBOARD_FRONTMATTER_KEYS.zoom)) ?? DEFAULT_DASHBOARD_SETTINGS.zoom,
|
|
3246
|
+
style: resolveDashboardStyleId(readSetting2(frontmatter, DASHBOARD_FRONTMATTER_KEYS.style)) ?? DEFAULT_DASHBOARD_SETTINGS.style
|
|
3247
|
+
};
|
|
3248
|
+
return {
|
|
3249
|
+
layout: resolveLayoutId(overrides.layout) ?? resolved.layout,
|
|
3250
|
+
showTitle: overrides.showTitle ?? resolved.showTitle,
|
|
3251
|
+
zoom: overrides.zoom ?? resolved.zoom,
|
|
3252
|
+
style: resolveDashboardStyleId(overrides.style) ?? resolved.style
|
|
3253
|
+
};
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
// src/doc/dashboard/materializeDashboard.ts
|
|
3257
|
+
var DEFAULT_TITLE_SLOT = { placement: "top", height: "9%" };
|
|
3258
|
+
var TITLE_CONTENT_GAP = 0.015;
|
|
3259
|
+
var CANVAS_MARGIN = 0.02;
|
|
3260
|
+
function percentNumber(value) {
|
|
3261
|
+
const parsed = Number(value.trim().replace(/%$/, ""));
|
|
3262
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
3263
|
+
}
|
|
3264
|
+
function pctOf(value, total) {
|
|
3265
|
+
return `${Math.round(value / Math.max(1, total) * 1e5) / 1e3}%`;
|
|
3266
|
+
}
|
|
3267
|
+
function rectToPct(rect, viewport) {
|
|
3268
|
+
return {
|
|
3269
|
+
left: pctOf(rect.x, viewport.width),
|
|
3270
|
+
top: pctOf(rect.y, viewport.height),
|
|
3271
|
+
width: pctOf(rect.width, viewport.width),
|
|
3272
|
+
height: pctOf(rect.height, viewport.height)
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
function resolveDashboardTitle(doc, provided) {
|
|
3276
|
+
const frontmatterTitle = doc.frontmatter?.title;
|
|
3277
|
+
if (typeof frontmatterTitle === "string" && frontmatterTitle.trim()) {
|
|
3278
|
+
return frontmatterTitle.trim();
|
|
3279
|
+
}
|
|
3280
|
+
return provided?.trim() ?? "";
|
|
3281
|
+
}
|
|
3282
|
+
function dedupeLeadingTitleBlock(candidates, titleText) {
|
|
3283
|
+
if (candidates.length === 0 || !titleText) return candidates;
|
|
3284
|
+
const first = candidates[0];
|
|
3285
|
+
const firstTitle = typeof first.title === "string" ? first.title.trim().toLowerCase() : "";
|
|
3286
|
+
if (!firstTitle || firstTitle !== titleText.trim().toLowerCase()) return candidates;
|
|
3287
|
+
const template = first.template;
|
|
3288
|
+
const titleShaped = template === "title" || template === "sectionHeader" || template === "cover";
|
|
3289
|
+
const hasBody = Array.isArray(first.contents) && first.contents.length > 0;
|
|
3290
|
+
return titleShaped || !hasBody ? candidates.slice(1) : candidates;
|
|
3291
|
+
}
|
|
3292
|
+
function buildTitleLayers(titleText, bandViewport, slot, theme, canvasViewport, margin) {
|
|
3293
|
+
const context = createTemplateContext(theme, 0, 1, canvasViewport);
|
|
3294
|
+
const canvasFontScale = calculateFontScale(canvasViewport);
|
|
3295
|
+
const fontSize = Math.min(44 * canvasFontScale, bandViewport.height * 0.52);
|
|
3296
|
+
const accentWidth = Math.max(4, Math.round(6 * canvasFontScale));
|
|
3297
|
+
const textX = margin + accentWidth + Math.max(10, Math.round(accentWidth * 2.5));
|
|
3298
|
+
return [
|
|
3299
|
+
{
|
|
3300
|
+
type: "shape",
|
|
3301
|
+
id: "dashboard-title-accent",
|
|
3302
|
+
content: { shape: "rect", fill: theme.colors.primary },
|
|
3303
|
+
position: {
|
|
3304
|
+
x: margin,
|
|
3305
|
+
y: "26%",
|
|
3306
|
+
width: accentWidth,
|
|
3307
|
+
height: "48%"
|
|
3308
|
+
}
|
|
3309
|
+
},
|
|
3310
|
+
{
|
|
3311
|
+
type: "text",
|
|
3312
|
+
id: "dashboard-title-text",
|
|
3313
|
+
content: {
|
|
3314
|
+
text: titleText,
|
|
3315
|
+
style: {
|
|
3316
|
+
fontSize,
|
|
3317
|
+
fontFamily: getThemeFont(context, "title"),
|
|
3318
|
+
fontWeight: "bold",
|
|
3319
|
+
color: theme.colors.text,
|
|
3320
|
+
textAlign: "left",
|
|
3321
|
+
verticalAlign: "middle",
|
|
3322
|
+
maxLines: 1
|
|
3323
|
+
}
|
|
3324
|
+
},
|
|
3325
|
+
position: {
|
|
3326
|
+
x: textX,
|
|
3327
|
+
y: 0,
|
|
3328
|
+
width: Math.max(1, bandViewport.width - textX - margin),
|
|
3329
|
+
height: "100%"
|
|
3330
|
+
}
|
|
3331
|
+
},
|
|
3332
|
+
{
|
|
3333
|
+
// Hairline separating the band from the content area.
|
|
3334
|
+
type: "shape",
|
|
3335
|
+
id: "dashboard-title-rule",
|
|
3336
|
+
content: { shape: "rect", fill: withAlpha(theme.colors.text, 0.14) },
|
|
3337
|
+
position: {
|
|
3338
|
+
x: 0,
|
|
3339
|
+
y: slot.placement === "bottom" ? 0 : bandViewport.height - 2,
|
|
3340
|
+
width: "100%",
|
|
3341
|
+
height: 2
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
];
|
|
3345
|
+
}
|
|
3346
|
+
function materializeDashboard(doc, options = {}) {
|
|
3347
|
+
const theme = options.theme ?? DEFAULT_THEME;
|
|
3348
|
+
const viewport = options.viewport ?? VIEWPORT_PRESETS.landscape;
|
|
3349
|
+
const orientation = getViewportOrientation(viewport);
|
|
3350
|
+
const customLayouts = options.customLayouts ?? readDashboardLayoutsFromFrontmatter(doc.frontmatter);
|
|
3351
|
+
const customTemplates = options.customTemplates ?? doc.customTemplates;
|
|
3352
|
+
const diagnostics = [];
|
|
3353
|
+
const settings = resolveDashboardSettings(doc.frontmatter, {
|
|
3354
|
+
layout: typeof options.layout === "string" ? options.layout : void 0,
|
|
3355
|
+
showTitle: options.showTitle,
|
|
3356
|
+
zoom: options.zoom,
|
|
3357
|
+
...options.style !== void 0 ? { style: options.style } : {}
|
|
3358
|
+
});
|
|
3359
|
+
const style = settings.style;
|
|
3360
|
+
let layoutDef;
|
|
3361
|
+
let layoutSource = "auto";
|
|
3362
|
+
if (options.layout && typeof options.layout === "object") {
|
|
3363
|
+
const validated = validateDashboardLayoutDefinition(options.layout);
|
|
3364
|
+
if (validated.layout) {
|
|
3365
|
+
layoutDef = validated.layout;
|
|
3366
|
+
layoutSource = "option";
|
|
3367
|
+
} else {
|
|
3368
|
+
diagnostics.push({
|
|
3369
|
+
type: "unknown-layout",
|
|
3370
|
+
message: "Inline dashboard layout definition is invalid; falling back to auto.",
|
|
3371
|
+
requestedLayout: options.layout.name
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
} else if (settings.layout !== DASHBOARD_AUTO_LAYOUT_ID) {
|
|
3375
|
+
const resolved = resolveDashboardLayoutDefinition(settings.layout, customLayouts);
|
|
3376
|
+
if (resolved) {
|
|
3377
|
+
layoutDef = resolved;
|
|
3378
|
+
layoutSource = typeof options.layout === "string" && options.layout.trim() ? "option" : "frontmatter";
|
|
3379
|
+
} else {
|
|
3380
|
+
diagnostics.push({
|
|
3381
|
+
type: "unknown-layout",
|
|
3382
|
+
message: `Unknown dashboard layout "${settings.layout}"; falling back to auto.`,
|
|
3383
|
+
requestedLayout: settings.layout
|
|
3384
|
+
});
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
const preview = buildPreviewDoc(doc, {
|
|
3388
|
+
documentTitle: options.documentTitle,
|
|
3389
|
+
interleaveImages: false
|
|
3390
|
+
});
|
|
3391
|
+
let candidates = preview.blocks ?? [];
|
|
3392
|
+
if (candidates.length === 0) {
|
|
3393
|
+
diagnostics.push({
|
|
3394
|
+
type: "empty-doc",
|
|
3395
|
+
message: "The document has no renderable blocks; the dashboard has no cells."
|
|
3396
|
+
});
|
|
3397
|
+
}
|
|
3398
|
+
const titleText = settings.showTitle ? resolveDashboardTitle(doc, options.documentTitle) : "";
|
|
3399
|
+
const hasTitleBand = titleText.length > 0;
|
|
3400
|
+
if (hasTitleBand) {
|
|
3401
|
+
candidates = dedupeLeadingTitleBlock(candidates, titleText);
|
|
3402
|
+
}
|
|
3403
|
+
if (!layoutDef) {
|
|
3404
|
+
layoutDef = chooseDashboardLayout(candidates.length, orientation, customLayouts);
|
|
3405
|
+
layoutSource = "auto";
|
|
3406
|
+
}
|
|
3407
|
+
const margin = CANVAS_MARGIN * Math.min(viewport.width, viewport.height);
|
|
3408
|
+
let contentRect = {
|
|
3409
|
+
x: margin,
|
|
3410
|
+
y: margin,
|
|
3411
|
+
width: Math.max(1, viewport.width - margin * 2),
|
|
3412
|
+
height: Math.max(1, viewport.height - margin * 2)
|
|
3413
|
+
};
|
|
3414
|
+
let title2 = null;
|
|
3415
|
+
if (hasTitleBand) {
|
|
3416
|
+
const slot = layoutDef.titleSlot ?? DEFAULT_TITLE_SLOT;
|
|
3417
|
+
const bandPct = Math.min(40, Math.max(2, percentNumber(slot.height)));
|
|
3418
|
+
const bandHeight = bandPct / 100 * viewport.height;
|
|
3419
|
+
const gap = TITLE_CONTENT_GAP * viewport.height;
|
|
3420
|
+
const titleRect = slot.placement === "bottom" ? { x: 0, y: viewport.height - bandHeight, width: viewport.width, height: bandHeight } : { x: 0, y: 0, width: viewport.width, height: bandHeight };
|
|
3421
|
+
contentRect = slot.placement === "bottom" ? {
|
|
3422
|
+
x: margin,
|
|
3423
|
+
y: margin,
|
|
3424
|
+
width: Math.max(1, viewport.width - margin * 2),
|
|
3425
|
+
height: Math.max(1, viewport.height - bandHeight - gap - margin)
|
|
3426
|
+
} : {
|
|
3427
|
+
x: margin,
|
|
3428
|
+
y: bandHeight + gap,
|
|
3429
|
+
width: Math.max(1, viewport.width - margin * 2),
|
|
3430
|
+
height: Math.max(1, viewport.height - bandHeight - gap - margin)
|
|
3431
|
+
};
|
|
3432
|
+
const bandViewport = {
|
|
3433
|
+
width: Math.max(1, Math.round(titleRect.width)),
|
|
3434
|
+
height: Math.max(1, Math.round(titleRect.height)),
|
|
3435
|
+
name: "Dashboard title"
|
|
3436
|
+
};
|
|
3437
|
+
title2 = {
|
|
3438
|
+
text: titleText,
|
|
3439
|
+
rect: titleRect,
|
|
3440
|
+
rectPct: rectToPct(titleRect, viewport),
|
|
3441
|
+
viewport: bandViewport,
|
|
3442
|
+
// The band spans the canvas, but its type starts on the cell area's
|
|
3443
|
+
// left edge so the title and the first column share one margin.
|
|
3444
|
+
layers: buildTitleLayers(titleText, bandViewport, slot, theme, viewport, margin)
|
|
3445
|
+
};
|
|
3446
|
+
}
|
|
3447
|
+
const resolvedCells = resolveLayoutCells(layoutDef, orientation, contentRect);
|
|
3448
|
+
const assignments = [];
|
|
3449
|
+
const explicitlyClaimed = /* @__PURE__ */ new Set();
|
|
3450
|
+
resolvedCells.forEach((cell, cellIndex) => {
|
|
3451
|
+
if (cell.block === void 0) return;
|
|
3452
|
+
const candidateIndex = cell.block - 1;
|
|
3453
|
+
if (candidateIndex < 0 || candidateIndex >= candidates.length) {
|
|
3454
|
+
diagnostics.push({
|
|
3455
|
+
type: "invalid-cell-assignment",
|
|
3456
|
+
message: `Cell ${cellIndex + 1} of layout "${layoutDef.name}" requests block ${cell.block}, but the document has ${candidates.length} block(s); the cell is left empty.`
|
|
3457
|
+
});
|
|
3458
|
+
return;
|
|
3459
|
+
}
|
|
3460
|
+
explicitlyClaimed.add(candidateIndex);
|
|
3461
|
+
assignments.push({ cellIndex, rect: cell.rect, candidateIndex, explicitZoom: cell.zoom });
|
|
3462
|
+
});
|
|
3463
|
+
const filled = /* @__PURE__ */ new Set();
|
|
3464
|
+
let cursor = 0;
|
|
3465
|
+
resolvedCells.forEach((cell, cellIndex) => {
|
|
3466
|
+
if (cell.block !== void 0) return;
|
|
3467
|
+
while (cursor < candidates.length && explicitlyClaimed.has(cursor)) cursor++;
|
|
3468
|
+
if (cursor >= candidates.length) return;
|
|
3469
|
+
filled.add(cursor);
|
|
3470
|
+
assignments.push({
|
|
3471
|
+
cellIndex,
|
|
3472
|
+
rect: cell.rect,
|
|
3473
|
+
candidateIndex: cursor,
|
|
3474
|
+
explicitZoom: cell.zoom
|
|
3475
|
+
});
|
|
3476
|
+
cursor++;
|
|
3477
|
+
});
|
|
3478
|
+
const hidden = [];
|
|
3479
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
3480
|
+
if (!explicitlyClaimed.has(i) && !filled.has(i)) hidden.push(i);
|
|
3481
|
+
}
|
|
3482
|
+
if (hidden.length > 0) {
|
|
3483
|
+
diagnostics.push({
|
|
3484
|
+
type: "overflow",
|
|
3485
|
+
message: `${hidden.length} block(s) exceed the "${layoutDef.name}" layout's ${resolvedCells.length} cell(s) and are not rendered.`,
|
|
3486
|
+
hiddenBlockIds: hidden.map((i) => String(candidates[i].id ?? i))
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
const orderedAssignments = assignments.sort((a, b) => a.cellIndex - b.cellIndex);
|
|
3490
|
+
const zooms = resolveDashboardZooms(
|
|
3491
|
+
orderedAssignments.map(({ candidateIndex, explicitZoom }) => {
|
|
3492
|
+
const block = candidates[candidateIndex];
|
|
3493
|
+
const title3 = typeof block.title === "string" ? block.title : "";
|
|
3494
|
+
return {
|
|
3495
|
+
template: resolveTemplateName(block.template ?? ""),
|
|
3496
|
+
textLength: title3.length + getBlockBodyText(block).length,
|
|
3497
|
+
...explicitZoom !== void 0 ? { explicit: explicitZoom } : {}
|
|
3498
|
+
};
|
|
3499
|
+
}),
|
|
3500
|
+
settings.zoom
|
|
3501
|
+
);
|
|
3502
|
+
const registry = customTemplates && customTemplates.length > 0 ? buildRegistry(customTemplates) : templateRegistry;
|
|
3503
|
+
const cells = orderedAssignments.map(
|
|
3504
|
+
({ cellIndex, rect, candidateIndex }, orderIndex) => {
|
|
3505
|
+
const block = candidates[candidateIndex];
|
|
3506
|
+
const zoom = zooms[orderIndex];
|
|
3507
|
+
const chrome = buildDashboardCellChrome(style, { theme, rect, index: cellIndex });
|
|
3508
|
+
const contentRect2 = chrome ? chrome.contentRect : rect;
|
|
3509
|
+
const cellViewport = {
|
|
3510
|
+
width: Math.max(1, Math.round(contentRect2.width)),
|
|
3511
|
+
height: Math.max(1, Math.round(contentRect2.height)),
|
|
3512
|
+
name: `Dashboard cell ${cellIndex + 1}`
|
|
3513
|
+
};
|
|
3514
|
+
const cellContext = createTemplateContext(
|
|
3515
|
+
theme,
|
|
3516
|
+
candidateIndex,
|
|
3517
|
+
candidates.length,
|
|
3518
|
+
cellViewport
|
|
3519
|
+
);
|
|
3520
|
+
if (zoom !== 1) cellContext.fontScale *= zoom;
|
|
3521
|
+
const result = materializeBlockLayersWithRuntime(
|
|
3522
|
+
block,
|
|
3523
|
+
{
|
|
3524
|
+
theme,
|
|
3525
|
+
viewport: cellViewport,
|
|
3526
|
+
persistentLayers: false,
|
|
3527
|
+
blockIndex: candidateIndex,
|
|
3528
|
+
totalBlocks: candidates.length,
|
|
3529
|
+
failureMode: options.failureMode
|
|
3530
|
+
},
|
|
3531
|
+
{ registry, templateContext: cellContext }
|
|
3532
|
+
);
|
|
3533
|
+
const layers = stripsBlockBackdrop(style) ? stripBlockBackdropLayer(result.layers, theme) : result.layers;
|
|
3534
|
+
let frame;
|
|
3535
|
+
if (chrome) {
|
|
3536
|
+
const frameViewport = {
|
|
3537
|
+
width: Math.max(1, Math.round(rect.width)),
|
|
3538
|
+
height: Math.max(1, Math.round(rect.height)),
|
|
3539
|
+
name: `Dashboard cell ${cellIndex + 1} frame`
|
|
3540
|
+
};
|
|
3541
|
+
frame = {
|
|
3542
|
+
rect,
|
|
3543
|
+
rectPct: rectToPct(rect, viewport),
|
|
3544
|
+
viewport: frameViewport,
|
|
3545
|
+
layers: chrome.layers,
|
|
3546
|
+
overlayLayers: chrome.overlayLayers,
|
|
3547
|
+
overlayViewport: {
|
|
3548
|
+
width: cellViewport.width,
|
|
3549
|
+
height: cellViewport.height,
|
|
3550
|
+
name: `Dashboard cell ${cellIndex + 1} overlay`
|
|
3551
|
+
},
|
|
3552
|
+
...chrome.contentRadiusPct ? { contentRadiusPct: chrome.contentRadiusPct } : {}
|
|
3553
|
+
};
|
|
3554
|
+
}
|
|
3555
|
+
return {
|
|
3556
|
+
index: cellIndex,
|
|
3557
|
+
block,
|
|
3558
|
+
blockIndex: candidateIndex,
|
|
3559
|
+
layers,
|
|
3560
|
+
rect: contentRect2,
|
|
3561
|
+
rectPct: rectToPct(contentRect2, viewport),
|
|
3562
|
+
...frame ? { frame } : {},
|
|
3563
|
+
viewport: cellViewport,
|
|
3564
|
+
zoom,
|
|
3565
|
+
source: result.source,
|
|
3566
|
+
...result.diagnostic ? { diagnostic: result.diagnostic } : {}
|
|
3567
|
+
};
|
|
3568
|
+
}
|
|
3569
|
+
);
|
|
3570
|
+
const persistent = resolvePersistentLayers(doc, theme);
|
|
3571
|
+
const canvasFill = dashboardCanvasFill(style, theme);
|
|
3572
|
+
const backdrop = {
|
|
3573
|
+
fill: canvasFill,
|
|
3574
|
+
bottomLayers: [
|
|
3575
|
+
{
|
|
3576
|
+
type: "shape",
|
|
3577
|
+
id: "dashboard-backdrop",
|
|
3578
|
+
content: { shape: "rect", fill: canvasFill },
|
|
3579
|
+
position: { x: 0, y: 0, width: "100%", height: "100%" }
|
|
3580
|
+
},
|
|
3581
|
+
...expandPersistentLayers(persistent?.bottomLayers, theme)
|
|
3582
|
+
],
|
|
3583
|
+
topLayers: expandPersistentLayers(persistent?.topLayers, theme)
|
|
3584
|
+
};
|
|
3585
|
+
return {
|
|
3586
|
+
layout: layoutDef,
|
|
3587
|
+
layoutSource,
|
|
3588
|
+
style,
|
|
3589
|
+
viewport,
|
|
3590
|
+
cells,
|
|
3591
|
+
title: title2,
|
|
3592
|
+
backdrop,
|
|
3593
|
+
diagnostics
|
|
3594
|
+
};
|
|
3595
|
+
}
|
|
3596
|
+
function composeDashboardLayers(materialization) {
|
|
3597
|
+
const layers = [...materialization.backdrop.bottomLayers];
|
|
3598
|
+
for (const cell of materialization.cells) {
|
|
3599
|
+
if (cell.frame) {
|
|
3600
|
+
layers.push(
|
|
3601
|
+
...placeLayersInRect(
|
|
3602
|
+
cell.frame.layers,
|
|
3603
|
+
cell.frame.viewport,
|
|
3604
|
+
cell.frame.rect,
|
|
3605
|
+
`cell-${cell.index}-frame`
|
|
3606
|
+
)
|
|
3607
|
+
);
|
|
3608
|
+
}
|
|
3609
|
+
layers.push(...placeLayersInRect(cell.layers, cell.viewport, cell.rect, `cell-${cell.index}`));
|
|
3610
|
+
if (cell.frame && cell.frame.overlayLayers.length > 0) {
|
|
3611
|
+
layers.push(
|
|
3612
|
+
...placeLayersInRect(
|
|
3613
|
+
cell.frame.overlayLayers,
|
|
3614
|
+
cell.frame.overlayViewport,
|
|
3615
|
+
cell.rect,
|
|
3616
|
+
`cell-${cell.index}-overlay`
|
|
3617
|
+
)
|
|
3618
|
+
);
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
if (materialization.title) {
|
|
3622
|
+
layers.push(
|
|
3623
|
+
...placeLayersInRect(
|
|
3624
|
+
materialization.title.layers,
|
|
3625
|
+
materialization.title.viewport,
|
|
3626
|
+
materialization.title.rect,
|
|
3627
|
+
"dashboard-title"
|
|
3628
|
+
)
|
|
3629
|
+
);
|
|
3630
|
+
}
|
|
3631
|
+
layers.push(...materialization.backdrop.topLayers);
|
|
3632
|
+
return layers;
|
|
3633
|
+
}
|
|
3634
|
+
|
|
2442
3635
|
// src/doc/applyNarrationTiming.ts
|
|
2443
3636
|
var MIN_BLOCK_SIMILARITY = 0.5;
|
|
2444
3637
|
var MIN_V1_SCRIPT_SIMILARITY = 0.8;
|
|
@@ -3553,7 +4746,7 @@ function middleOut(from, to) {
|
|
|
3553
4746
|
const mid = Math.floor((lo + hi) / 2);
|
|
3554
4747
|
return rows.sort((a, b) => Math.abs(a - mid) - Math.abs(b - mid) || a - b);
|
|
3555
4748
|
}
|
|
3556
|
-
function
|
|
4749
|
+
function clamp2(value, lo, hi) {
|
|
3557
4750
|
return Math.max(lo, Math.min(hi, value));
|
|
3558
4751
|
}
|
|
3559
4752
|
function drawVertical(args, s, t, exitCol, enterCol, dir) {
|
|
@@ -3570,8 +4763,8 @@ function drawVertical(args, s, t, exitCol, enterCol, dir) {
|
|
|
3570
4763
|
};
|
|
3571
4764
|
const straightOk = exitCol >= t.c0 + 1 && exitCol <= t.c1 - 1 && exitCol >= s.c0 + 1 && exitCol <= s.c1 - 1;
|
|
3572
4765
|
if (straightOk || gap < 2) {
|
|
3573
|
-
const sCol =
|
|
3574
|
-
const tCol =
|
|
4766
|
+
const sCol = clamp2(exitCol, s.c0 + 1, s.c1 - 1);
|
|
4767
|
+
const tCol = clamp2(exitCol, t.c0 + 1, t.c1 - 1);
|
|
3575
4768
|
junction(exitRow, sCol, dir === 1 ? "bottom" : "top");
|
|
3576
4769
|
junction(enterRow, tCol, dir === 1 ? "top" : "bottom");
|
|
3577
4770
|
if (sCol === tCol && gap >= 1) {
|
|
@@ -3603,7 +4796,7 @@ function drawVertical(args, s, t, exitCol, enterCol, dir) {
|
|
|
3603
4796
|
}
|
|
3604
4797
|
return;
|
|
3605
4798
|
}
|
|
3606
|
-
const midRow =
|
|
4799
|
+
const midRow = clamp2(
|
|
3607
4800
|
Math.floor((exitRow + enterRow) / 2) + stagger,
|
|
3608
4801
|
Math.min(firstFree, lastFree),
|
|
3609
4802
|
Math.max(firstFree, lastFree)
|
|
@@ -3704,8 +4897,8 @@ function drawHorizontal(args, s, t, exitRow, enterRow, dir) {
|
|
|
3704
4897
|
};
|
|
3705
4898
|
const straightOk = exitRow >= t.r0 + 1 && exitRow <= t.r1 - 1 && exitRow >= s.r0 + 1 && exitRow <= s.r1 - 1;
|
|
3706
4899
|
if (straightOk || gap < 2) {
|
|
3707
|
-
const sRow =
|
|
3708
|
-
const tRow =
|
|
4900
|
+
const sRow = clamp2(exitRow, s.r0 + 1, s.r1 - 1);
|
|
4901
|
+
const tRow = clamp2(exitRow, t.r0 + 1, t.r1 - 1);
|
|
3709
4902
|
junction(sRow, exitCol, dir === 1 ? "right" : "left");
|
|
3710
4903
|
junction(tRow, enterCol, dir === 1 ? "left" : "right");
|
|
3711
4904
|
if (sRow === tRow && gap >= 1) {
|
|
@@ -3738,7 +4931,7 @@ function drawHorizontal(args, s, t, exitRow, enterRow, dir) {
|
|
|
3738
4931
|
}
|
|
3739
4932
|
return;
|
|
3740
4933
|
}
|
|
3741
|
-
const midCol =
|
|
4934
|
+
const midCol = clamp2(
|
|
3742
4935
|
Math.floor((exitCol + enterCol) / 2) + args.stagger,
|
|
3743
4936
|
Math.min(firstFree, lastFree),
|
|
3744
4937
|
Math.max(firstFree, lastFree)
|
|
@@ -4379,6 +5572,37 @@ export {
|
|
|
4379
5572
|
pageStyleDataAttributes,
|
|
4380
5573
|
PAGE_BASE_CSS,
|
|
4381
5574
|
buildPageCss,
|
|
5575
|
+
DASHBOARD_ZOOM_LEVELS,
|
|
5576
|
+
normalizeDashboardZoom,
|
|
5577
|
+
desiredCellZoom,
|
|
5578
|
+
resolveDashboardZooms,
|
|
5579
|
+
validateDashboardLayoutDefinition,
|
|
5580
|
+
layoutCapacity,
|
|
5581
|
+
transposeCells,
|
|
5582
|
+
resolveLayoutCells,
|
|
5583
|
+
FRONTMATTER_DASHBOARD_LAYOUTS_KEY,
|
|
5584
|
+
readDashboardLayoutsFromFrontmatter,
|
|
5585
|
+
writeDashboardLayoutsToFrontmatter,
|
|
5586
|
+
BUILTIN_DASHBOARD_LAYOUTS,
|
|
5587
|
+
getDashboardLayoutSummaries,
|
|
5588
|
+
listDashboardLayouts,
|
|
5589
|
+
DASHBOARD_AUTO_LAYOUT_ID,
|
|
5590
|
+
resolveDashboardLayoutDefinition,
|
|
5591
|
+
chooseDashboardLayout,
|
|
5592
|
+
DASHBOARD_STYLE_IDS,
|
|
5593
|
+
DEFAULT_DASHBOARD_STYLE,
|
|
5594
|
+
DASHBOARD_STYLES,
|
|
5595
|
+
resolveDashboardStyleId,
|
|
5596
|
+
dashboardCanvasFill,
|
|
5597
|
+
dashboardCellAccent,
|
|
5598
|
+
buildDashboardCellChrome,
|
|
5599
|
+
stripsBlockBackdrop,
|
|
5600
|
+
stripBlockBackdropLayer,
|
|
5601
|
+
DASHBOARD_FRONTMATTER_KEYS,
|
|
5602
|
+
DEFAULT_DASHBOARD_SETTINGS,
|
|
5603
|
+
resolveDashboardSettings,
|
|
5604
|
+
materializeDashboard,
|
|
5605
|
+
composeDashboardLayers,
|
|
4382
5606
|
applyNarrationTiming,
|
|
4383
5607
|
scoreTextSimilarity,
|
|
4384
5608
|
resolveAudioMapping,
|