@json-to-office/shared 2.5.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-6TNT7DGC.js → chunk-LFHU4EOV.js} +5 -2
- package/dist/chunk-LFHU4EOV.js.map +1 -0
- package/dist/fonts/node.d.ts +9 -1
- package/dist/fonts/node.js +23 -0
- package/dist/fonts/node.js.map +1 -1
- package/dist/index.d.ts +411 -6
- package/dist/index.js +2021 -25
- package/dist/index.js.map +1 -1
- package/dist/{schema-utils-C5Qdzsgy.d.ts → schema-utils-B2i75lAT.d.ts} +7 -0
- package/dist/schemas/schema-utils.d.ts +1 -1
- package/dist/schemas/schema-utils.js +1 -1
- package/dist/schemas/slide-content.d.ts +16 -0
- package/dist/schemas/slide-content.js +22 -0
- package/dist/schemas/slide-content.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-6TNT7DGC.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
fixSchemaReferences,
|
|
59
59
|
restructureNameDiscriminatedUnions,
|
|
60
60
|
unionBranches
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-LFHU4EOV.js";
|
|
62
62
|
import {
|
|
63
63
|
CANVASES,
|
|
64
64
|
ChromeSchema,
|
|
@@ -240,21 +240,21 @@ var WEIGHT_LABELS = {
|
|
|
240
240
|
800: "ExtraBold",
|
|
241
241
|
900: "Black"
|
|
242
242
|
};
|
|
243
|
-
function synthesizeFamilyName(family,
|
|
244
|
-
if (
|
|
243
|
+
function synthesizeFamilyName(family, weight2, italic) {
|
|
244
|
+
if (weight2 == null) {
|
|
245
245
|
return { family, bold: false, italic, nonCanonicalWeight: false };
|
|
246
246
|
}
|
|
247
|
-
if (
|
|
247
|
+
if (weight2 === 400) {
|
|
248
248
|
return { family, bold: false, italic, nonCanonicalWeight: false };
|
|
249
249
|
}
|
|
250
|
-
if (
|
|
250
|
+
if (weight2 === 700) {
|
|
251
251
|
return { family, bold: true, italic, nonCanonicalWeight: false };
|
|
252
252
|
}
|
|
253
|
-
const label = WEIGHT_LABELS[
|
|
253
|
+
const label = WEIGHT_LABELS[weight2];
|
|
254
254
|
if (!label) {
|
|
255
255
|
return {
|
|
256
256
|
family,
|
|
257
|
-
bold:
|
|
257
|
+
bold: weight2 >= 600,
|
|
258
258
|
italic,
|
|
259
259
|
nonCanonicalWeight: true
|
|
260
260
|
};
|
|
@@ -353,8 +353,8 @@ function parseCssFaces(css) {
|
|
|
353
353
|
}
|
|
354
354
|
return out;
|
|
355
355
|
}
|
|
356
|
-
function cacheKey(family,
|
|
357
|
-
return `google|${family}|${
|
|
356
|
+
function cacheKey(family, weight2, italic) {
|
|
357
|
+
return `google|${family}|${weight2}|${italic ? "i" : "r"}`;
|
|
358
358
|
}
|
|
359
359
|
async function fetchGoogleFontSources(opts) {
|
|
360
360
|
const weights = opts.weights?.length ? opts.weights : [400, 700];
|
|
@@ -464,8 +464,8 @@ async function fetchGoogleFontSources(opts) {
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
// src/fonts/sources/url-fetcher.ts
|
|
467
|
-
function cacheKey2(url,
|
|
468
|
-
return `url|${url}|${
|
|
467
|
+
function cacheKey2(url, weight2, italic) {
|
|
468
|
+
return `url|${url}|${weight2}|${italic ? "i" : "r"}`;
|
|
469
469
|
}
|
|
470
470
|
async function fetchUrlFontSource(opts) {
|
|
471
471
|
if (!isAllowedFontUrl(opts.url)) {
|
|
@@ -592,25 +592,25 @@ function readUsWeightClass(ttf) {
|
|
|
592
592
|
if (os2.off + 6 > ttf.length) return null;
|
|
593
593
|
return ttf.readUInt16BE(os2.off + 4);
|
|
594
594
|
}
|
|
595
|
-
function validateFontMetadata(ttf,
|
|
595
|
+
function validateFontMetadata(ttf, weight2, italic, familyLabel) {
|
|
596
596
|
const diags = [];
|
|
597
597
|
const usWeight = readUsWeightClass(ttf);
|
|
598
|
-
if (usWeight != null && usWeight !==
|
|
598
|
+
if (usWeight != null && usWeight !== weight2) {
|
|
599
599
|
diags.push({
|
|
600
600
|
code: "WEIGHT_CLASS_MISMATCH",
|
|
601
|
-
message: `Font "${familyLabel}" weight ${
|
|
601
|
+
message: `Font "${familyLabel}" weight ${weight2}: OS/2.usWeightClass reports ${usWeight}. Likely a defective redistribution \u2014 consider adding an upstream override.`
|
|
602
602
|
});
|
|
603
603
|
}
|
|
604
604
|
const declaredFamilies = readFontFamilyNames(ttf);
|
|
605
605
|
if (declaredFamilies.length > 0 && !declaredFamilies.includes(familyLabel.trim())) {
|
|
606
606
|
diags.push({
|
|
607
607
|
code: "FAMILY_MISMATCH",
|
|
608
|
-
message: `Font "${familyLabel}" weight ${
|
|
608
|
+
message: `Font "${familyLabel}" weight ${weight2}${italic ? " italic" : ""}: name table declares ${declaredFamilies.map((f) => `"${f}"`).join(
|
|
609
609
|
" / "
|
|
610
610
|
)}, not "${familyLabel}". Referencing runs will not resolve this face.`
|
|
611
611
|
});
|
|
612
612
|
}
|
|
613
|
-
const std = standardSubfamilyNames(
|
|
613
|
+
const std = standardSubfamilyNames(weight2, italic);
|
|
614
614
|
if (!std) return diags;
|
|
615
615
|
const expected17 = std.typographic;
|
|
616
616
|
const expected2 = std.legacy;
|
|
@@ -619,13 +619,13 @@ function validateFontMetadata(ttf, weight, italic, familyLabel) {
|
|
|
619
619
|
if (n.nameID === 17 && n.value !== expected17) {
|
|
620
620
|
diags.push({
|
|
621
621
|
code: "SUBFAMILY_MISMATCH",
|
|
622
|
-
message: `Font "${familyLabel}" weight ${
|
|
622
|
+
message: `Font "${familyLabel}" weight ${weight2}${italic ? " italic" : ""}: name record (platform ${n.platformID}) nameID 17 = "${n.value}", expected "${expected17}".`
|
|
623
623
|
});
|
|
624
624
|
}
|
|
625
625
|
if (n.nameID === 2 && n.value !== expected2) {
|
|
626
626
|
diags.push({
|
|
627
627
|
code: "LEGACY_SUBFAMILY_MISMATCH",
|
|
628
|
-
message: `Font "${familyLabel}" weight ${
|
|
628
|
+
message: `Font "${familyLabel}" weight ${weight2}${italic ? " italic" : ""}: name record (platform ${n.platformID}) nameID 2 = "${n.value}", expected "${expected2}".`
|
|
629
629
|
});
|
|
630
630
|
}
|
|
631
631
|
}
|
|
@@ -645,8 +645,8 @@ var SFNT_VERSIONS = /* @__PURE__ */ new Set([
|
|
|
645
645
|
]);
|
|
646
646
|
var HEADER_SIZE2 = 12;
|
|
647
647
|
var TABLE_RECORD_SIZE2 = 16;
|
|
648
|
-
function validateFontStructure(ttf,
|
|
649
|
-
const face = `Font "${familyLabel}" weight ${
|
|
648
|
+
function validateFontStructure(ttf, weight2, italic, familyLabel) {
|
|
649
|
+
const face = `Font "${familyLabel}" weight ${weight2}${italic ? " italic" : ""}`;
|
|
650
650
|
const unreadable = (reason) => ({
|
|
651
651
|
code: "FONT_UNREADABLE",
|
|
652
652
|
message: `${face}: ${reason} The face will not resolve; text referencing it renders in a fallback.`
|
|
@@ -1160,16 +1160,16 @@ var INTER_VARIABLE_URL = "https://cdn.jsdelivr.net/gh/rsms/inter@v4.1/docs/font-
|
|
|
1160
1160
|
var INTER_VARIABLE_ITALIC_URL = "https://cdn.jsdelivr.net/gh/rsms/inter@v4.1/docs/font-files/InterVariable-Italic.woff2";
|
|
1161
1161
|
function interVariants() {
|
|
1162
1162
|
const weights = [100, 200, 300, 400, 500, 600, 700, 800, 900];
|
|
1163
|
-
const upright = weights.map((
|
|
1163
|
+
const upright = weights.map((weight2) => ({
|
|
1164
1164
|
kind: "variable",
|
|
1165
1165
|
url: INTER_VARIABLE_URL,
|
|
1166
|
-
weight,
|
|
1166
|
+
weight: weight2,
|
|
1167
1167
|
italic: false
|
|
1168
1168
|
}));
|
|
1169
|
-
const italic = weights.map((
|
|
1169
|
+
const italic = weights.map((weight2) => ({
|
|
1170
1170
|
kind: "variable",
|
|
1171
1171
|
url: INTER_VARIABLE_ITALIC_URL,
|
|
1172
|
-
weight,
|
|
1172
|
+
weight: weight2,
|
|
1173
1173
|
italic: true
|
|
1174
1174
|
}));
|
|
1175
1175
|
return [...upright, ...italic];
|
|
@@ -1371,6 +1371,1962 @@ var DEFAULT_CHART_THEME_COLORS = [
|
|
|
1371
1371
|
"accent6"
|
|
1372
1372
|
];
|
|
1373
1373
|
|
|
1374
|
+
// src/theme/chart-typography.ts
|
|
1375
|
+
var POINTS_PER_PIXEL_96DPI = 0.75;
|
|
1376
|
+
function chartPointsPerPixel(chartWidthPx, placedWidthPt) {
|
|
1377
|
+
if (!Number.isFinite(chartWidthPx) || chartWidthPx <= 0 || placedWidthPt === void 0 || !Number.isFinite(placedWidthPt) || placedWidthPt <= 0) {
|
|
1378
|
+
return POINTS_PER_PIXEL_96DPI;
|
|
1379
|
+
}
|
|
1380
|
+
return placedWidthPt / chartWidthPx;
|
|
1381
|
+
}
|
|
1382
|
+
var SERIF_FAMILIES = /* @__PURE__ */ new Set(["georgia", "times new roman", "cambria"]);
|
|
1383
|
+
var MONO_FAMILIES = /* @__PURE__ */ new Set(["consolas", "courier new", "menlo", "monaco"]);
|
|
1384
|
+
function cssFontFamily(family, category) {
|
|
1385
|
+
const generic = category === "serif" ? "serif" : category === "mono" ? "monospace" : category === "handwriting" ? "cursive" : category === void 0 && SERIF_FAMILIES.has(family.toLowerCase()) ? "serif" : category === void 0 && MONO_FAMILIES.has(family.toLowerCase()) ? "monospace" : "sans-serif";
|
|
1386
|
+
return `"${family.replace(/["\\]/g, "\\$&")}", ${generic}`;
|
|
1387
|
+
}
|
|
1388
|
+
function chartFamilyResolver(theme) {
|
|
1389
|
+
const categories = new Map(
|
|
1390
|
+
themeFontRegistry(theme).map((entry) => [
|
|
1391
|
+
entry.family.toLowerCase(),
|
|
1392
|
+
entry.category
|
|
1393
|
+
])
|
|
1394
|
+
);
|
|
1395
|
+
return (family) => cssFontFamily(family, categories.get(family.toLowerCase()));
|
|
1396
|
+
}
|
|
1397
|
+
function isPlainObject(value) {
|
|
1398
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1399
|
+
}
|
|
1400
|
+
function fill(authored, defaults) {
|
|
1401
|
+
if (authored !== void 0 && !isPlainObject(authored)) return authored;
|
|
1402
|
+
const base = isPlainObject(authored) ? { ...authored } : {};
|
|
1403
|
+
for (const [key, value] of Object.entries(defaults)) {
|
|
1404
|
+
if (value === void 0) continue;
|
|
1405
|
+
const current = base[key];
|
|
1406
|
+
if (current === void 0) {
|
|
1407
|
+
base[key] = isPlainObject(value) ? fill(void 0, value) : value;
|
|
1408
|
+
} else if (isPlainObject(current) && isPlainObject(value)) {
|
|
1409
|
+
base[key] = fill(current, value);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return base;
|
|
1413
|
+
}
|
|
1414
|
+
function fillAxis(authored, defaults) {
|
|
1415
|
+
if (Array.isArray(authored)) {
|
|
1416
|
+
return authored.map((axis) => fill(axis, defaults));
|
|
1417
|
+
}
|
|
1418
|
+
return fill(authored, defaults);
|
|
1419
|
+
}
|
|
1420
|
+
function weight(value) {
|
|
1421
|
+
return value === void 0 ? void 0 : String(value);
|
|
1422
|
+
}
|
|
1423
|
+
function withChartTypography(options, typography, ptPerPx) {
|
|
1424
|
+
const px = (points) => `${Math.round(points / ptPerPx * 10) / 10}px`;
|
|
1425
|
+
const labelPx = px(typography.labelPt);
|
|
1426
|
+
const sourcePx = px(typography.sourcePt);
|
|
1427
|
+
const mutedText = { fontSize: labelPx, color: typography.mutedColor };
|
|
1428
|
+
const mutedSource = { fontSize: sourcePx, color: typography.mutedColor };
|
|
1429
|
+
const labelText = {
|
|
1430
|
+
fontSize: labelPx,
|
|
1431
|
+
color: typography.textColor,
|
|
1432
|
+
fontWeight: weight(typography.labelWeight)
|
|
1433
|
+
};
|
|
1434
|
+
const axis = { labels: { style: mutedText }, title: { style: mutedText } };
|
|
1435
|
+
return {
|
|
1436
|
+
...options,
|
|
1437
|
+
chart: fill(options.chart, {
|
|
1438
|
+
style: { fontFamily: typography.bodyFamily }
|
|
1439
|
+
}),
|
|
1440
|
+
title: fill(options.title, {
|
|
1441
|
+
style: {
|
|
1442
|
+
fontFamily: typography.headingFamily,
|
|
1443
|
+
fontSize: px(typography.titlePt),
|
|
1444
|
+
fontWeight: weight(typography.titleWeight),
|
|
1445
|
+
color: typography.textColor
|
|
1446
|
+
}
|
|
1447
|
+
}),
|
|
1448
|
+
subtitle: fill(options.subtitle, { style: mutedText }),
|
|
1449
|
+
caption: fill(options.caption, { style: mutedSource }),
|
|
1450
|
+
xAxis: fillAxis(options.xAxis, axis),
|
|
1451
|
+
yAxis: fillAxis(options.yAxis, axis),
|
|
1452
|
+
legend: fill(options.legend, { itemStyle: labelText }),
|
|
1453
|
+
plotOptions: fill(options.plotOptions, {
|
|
1454
|
+
series: { dataLabels: { style: labelText } }
|
|
1455
|
+
}),
|
|
1456
|
+
credits: fill(options.credits, { style: mutedSource })
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
var FONT_FORMATS = {
|
|
1460
|
+
ttf: { mime: "font/ttf", format: "truetype" },
|
|
1461
|
+
otf: { mime: "font/otf", format: "opentype" },
|
|
1462
|
+
woff: { mime: "font/woff", format: "woff" },
|
|
1463
|
+
woff2: { mime: "font/woff2", format: "woff2" }
|
|
1464
|
+
};
|
|
1465
|
+
function chartFontFaceCss(faces, families) {
|
|
1466
|
+
const wanted = new Set(families.map((family) => family.toLowerCase()));
|
|
1467
|
+
return faces.filter((face) => wanted.has(face.family.toLowerCase())).map((face) => {
|
|
1468
|
+
const { mime, format } = FONT_FORMATS[face.format ?? "ttf"];
|
|
1469
|
+
return `@font-face{font-family:"${face.family.replace(/["\\]/g, "\\$&")}";font-weight:${face.weight};font-style:${face.italic ? "italic" : "normal"};src:url(data:${mime};base64,${face.data}) format("${format}")}`;
|
|
1470
|
+
}).join("\n");
|
|
1471
|
+
}
|
|
1472
|
+
function withChartFontFaceCss(props, faces, families) {
|
|
1473
|
+
const css = chartFontFaceCss(faces, families);
|
|
1474
|
+
if (!css) return props;
|
|
1475
|
+
const authored = props.resources?.css;
|
|
1476
|
+
return {
|
|
1477
|
+
...props,
|
|
1478
|
+
resources: {
|
|
1479
|
+
...props.resources,
|
|
1480
|
+
css: authored ? `${css}
|
|
1481
|
+
${authored}` : css
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// src/blocks/schema.ts
|
|
1487
|
+
import { Type } from "@sinclair/typebox";
|
|
1488
|
+
var BLOCK_SLOT_ROLES = [
|
|
1489
|
+
"actionTitle",
|
|
1490
|
+
"takeaway",
|
|
1491
|
+
"source",
|
|
1492
|
+
"tracker",
|
|
1493
|
+
"footer"
|
|
1494
|
+
];
|
|
1495
|
+
var BlockSlotSchema = Type.Recursive(
|
|
1496
|
+
(Self) => Type.Object(
|
|
1497
|
+
{
|
|
1498
|
+
type: Type.Union(
|
|
1499
|
+
[
|
|
1500
|
+
"string",
|
|
1501
|
+
"number",
|
|
1502
|
+
"integer",
|
|
1503
|
+
"boolean",
|
|
1504
|
+
"object",
|
|
1505
|
+
"array",
|
|
1506
|
+
"component"
|
|
1507
|
+
].map((v) => Type.Literal(v)),
|
|
1508
|
+
{
|
|
1509
|
+
description: "Content type accepted by this slot. Use component for a document component or registered plugin."
|
|
1510
|
+
}
|
|
1511
|
+
),
|
|
1512
|
+
description: Type.Optional(
|
|
1513
|
+
Type.String({
|
|
1514
|
+
description: "Explain this slot\u2019s content and purpose to authors."
|
|
1515
|
+
})
|
|
1516
|
+
),
|
|
1517
|
+
required: Type.Optional(
|
|
1518
|
+
Type.Boolean({
|
|
1519
|
+
description: "Require a value when no default is provided. Defaults to false."
|
|
1520
|
+
})
|
|
1521
|
+
),
|
|
1522
|
+
default: Type.Optional(
|
|
1523
|
+
Type.Unknown({
|
|
1524
|
+
description: "Value used when the caller omits this slot. Must satisfy the slot\u2019s type and constraints."
|
|
1525
|
+
})
|
|
1526
|
+
),
|
|
1527
|
+
enum: Type.Optional(
|
|
1528
|
+
Type.Array(
|
|
1529
|
+
Type.Union([Type.String(), Type.Number(), Type.Boolean()]),
|
|
1530
|
+
{
|
|
1531
|
+
minItems: 1,
|
|
1532
|
+
description: "Allowed scalar values for this slot."
|
|
1533
|
+
}
|
|
1534
|
+
)
|
|
1535
|
+
),
|
|
1536
|
+
minItems: Type.Optional(
|
|
1537
|
+
Type.Integer({
|
|
1538
|
+
minimum: 0,
|
|
1539
|
+
description: "Minimum number of array entries, inclusive."
|
|
1540
|
+
})
|
|
1541
|
+
),
|
|
1542
|
+
maxItems: Type.Optional(
|
|
1543
|
+
Type.Integer({
|
|
1544
|
+
minimum: 0,
|
|
1545
|
+
description: "Maximum number of array entries, inclusive."
|
|
1546
|
+
})
|
|
1547
|
+
),
|
|
1548
|
+
minLength: Type.Optional(
|
|
1549
|
+
Type.Integer({
|
|
1550
|
+
minimum: 0,
|
|
1551
|
+
description: "Minimum string length in characters, inclusive."
|
|
1552
|
+
})
|
|
1553
|
+
),
|
|
1554
|
+
maxLength: Type.Optional(
|
|
1555
|
+
Type.Integer({
|
|
1556
|
+
minimum: 0,
|
|
1557
|
+
description: "Maximum string length in characters, inclusive."
|
|
1558
|
+
})
|
|
1559
|
+
),
|
|
1560
|
+
minimum: Type.Optional(
|
|
1561
|
+
Type.Number({ description: "Minimum numeric value, inclusive." })
|
|
1562
|
+
),
|
|
1563
|
+
maximum: Type.Optional(
|
|
1564
|
+
Type.Number({ description: "Maximum numeric value, inclusive." })
|
|
1565
|
+
),
|
|
1566
|
+
maxWords: Type.Optional(
|
|
1567
|
+
Type.Integer({
|
|
1568
|
+
minimum: 1,
|
|
1569
|
+
description: "Maximum whitespace-separated word count. Exceeding it fails validation."
|
|
1570
|
+
})
|
|
1571
|
+
),
|
|
1572
|
+
oneLine: Type.Optional(
|
|
1573
|
+
Type.Boolean({
|
|
1574
|
+
description: "Reject newline characters in string values. Does not prevent visual line wrapping."
|
|
1575
|
+
})
|
|
1576
|
+
),
|
|
1577
|
+
items: Type.Optional({
|
|
1578
|
+
...Self,
|
|
1579
|
+
description: "Slot type and constraints for each array entry."
|
|
1580
|
+
}),
|
|
1581
|
+
properties: Type.Optional(
|
|
1582
|
+
Type.Record(Type.String(), Self, {
|
|
1583
|
+
description: "Named child slots accepted by an object slot. Undeclared properties are rejected."
|
|
1584
|
+
})
|
|
1585
|
+
),
|
|
1586
|
+
role: Type.Optional(
|
|
1587
|
+
Type.Union(
|
|
1588
|
+
BLOCK_SLOT_ROLES.map((role) => Type.Literal(role)),
|
|
1589
|
+
{
|
|
1590
|
+
description: "Content role for quality profiles: actionTitle, takeaway, source, tracker or footer. A profile may require or measure it; the theme only styles it."
|
|
1591
|
+
}
|
|
1592
|
+
)
|
|
1593
|
+
)
|
|
1594
|
+
},
|
|
1595
|
+
{ additionalProperties: false }
|
|
1596
|
+
),
|
|
1597
|
+
// Named so the export hoists it under a stable definition rather than a
|
|
1598
|
+
// TypeBox ordinal that shifts with what the process built before it.
|
|
1599
|
+
{ $id: "BlockSlot" }
|
|
1600
|
+
);
|
|
1601
|
+
var JsonBlockDefinitionSchema = Type.Unsafe(
|
|
1602
|
+
Type.Object(
|
|
1603
|
+
{
|
|
1604
|
+
description: Type.Optional(
|
|
1605
|
+
Type.String({
|
|
1606
|
+
description: "Describe what this reusable block renders and when to use it."
|
|
1607
|
+
})
|
|
1608
|
+
),
|
|
1609
|
+
slots: Type.Record(Type.String(), BlockSlotSchema, {
|
|
1610
|
+
description: "Named inputs and their types, defaults and constraints. Use an empty object for a block with no inputs."
|
|
1611
|
+
}),
|
|
1612
|
+
body: Type.Array(Type.Unknown(), {
|
|
1613
|
+
description: "Components and binding directives expanded in order when this block is invoked."
|
|
1614
|
+
}),
|
|
1615
|
+
section: Type.Optional(
|
|
1616
|
+
Type.Object(
|
|
1617
|
+
{
|
|
1618
|
+
tracker: Type.Optional(
|
|
1619
|
+
Type.Unknown({
|
|
1620
|
+
description: "Section tracker value or binding, available to headers and footers through $context at /section/tracker."
|
|
1621
|
+
})
|
|
1622
|
+
),
|
|
1623
|
+
header: Type.Optional(
|
|
1624
|
+
Type.Array(Type.Unknown(), {
|
|
1625
|
+
description: "Header component templates. Explicit header settings on the section take precedence."
|
|
1626
|
+
})
|
|
1627
|
+
),
|
|
1628
|
+
footer: Type.Optional(
|
|
1629
|
+
Type.Array(Type.Unknown(), {
|
|
1630
|
+
description: "Footer component templates. Explicit footer settings on the section take precedence."
|
|
1631
|
+
})
|
|
1632
|
+
),
|
|
1633
|
+
pageBreak: Type.Optional(
|
|
1634
|
+
Type.Boolean({
|
|
1635
|
+
description: "Start the containing section on a new page. An explicit section pageBreak setting takes precedence."
|
|
1636
|
+
})
|
|
1637
|
+
),
|
|
1638
|
+
scope: Type.Optional(
|
|
1639
|
+
Type.Union([Type.Literal("section"), Type.Literal("following")], {
|
|
1640
|
+
description: "Apply header/footer templates to this section only, or inherit them in following sections. Defaults to section."
|
|
1641
|
+
})
|
|
1642
|
+
)
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
additionalProperties: false,
|
|
1646
|
+
description: "DOCX section tracker, header/footer templates and page-break behavior. Place this block at the section boundary."
|
|
1647
|
+
}
|
|
1648
|
+
)
|
|
1649
|
+
),
|
|
1650
|
+
slide: Type.Optional(
|
|
1651
|
+
Type.Object(
|
|
1652
|
+
{
|
|
1653
|
+
background: Type.Optional(
|
|
1654
|
+
Type.Unknown({
|
|
1655
|
+
description: "Slide background (color, gradient or image) or a binding. A background the slide states itself takes precedence."
|
|
1656
|
+
})
|
|
1657
|
+
),
|
|
1658
|
+
grid: Type.Optional(
|
|
1659
|
+
Type.Unknown({
|
|
1660
|
+
description: "Grid configuration merged over the presentation grid when resolving grid placements in this block\u2019s body."
|
|
1661
|
+
})
|
|
1662
|
+
),
|
|
1663
|
+
notes: Type.Optional(
|
|
1664
|
+
Type.Unknown({
|
|
1665
|
+
description: "Speaker notes or a binding. Notes the slide states itself take precedence."
|
|
1666
|
+
})
|
|
1667
|
+
)
|
|
1668
|
+
},
|
|
1669
|
+
{
|
|
1670
|
+
additionalProperties: false,
|
|
1671
|
+
description: "PPTX slide background, grid and notes supplied by this block. Invoke the block as a direct child of a slide."
|
|
1672
|
+
}
|
|
1673
|
+
)
|
|
1674
|
+
)
|
|
1675
|
+
},
|
|
1676
|
+
{ additionalProperties: false }
|
|
1677
|
+
)
|
|
1678
|
+
);
|
|
1679
|
+
var BlockDefinitionsSchema = Type.Record(
|
|
1680
|
+
Type.String({ pattern: "^[a-zA-Z][a-zA-Z0-9_-]*$" }),
|
|
1681
|
+
JsonBlockDefinitionSchema,
|
|
1682
|
+
{
|
|
1683
|
+
description: "Document-local JSON block definitions. Names are not built into the engine."
|
|
1684
|
+
}
|
|
1685
|
+
);
|
|
1686
|
+
var BlockInvocationPropsSchema = Type.Object(
|
|
1687
|
+
{
|
|
1688
|
+
ref: Type.String({
|
|
1689
|
+
minLength: 1,
|
|
1690
|
+
description: "Name in this document\u2019s props.blocks."
|
|
1691
|
+
}),
|
|
1692
|
+
slots: Type.Optional(
|
|
1693
|
+
Type.Record(Type.String(), Type.Unknown(), {
|
|
1694
|
+
description: "Input values keyed by the slot names declared in the referenced block definition."
|
|
1695
|
+
})
|
|
1696
|
+
)
|
|
1697
|
+
},
|
|
1698
|
+
{ additionalProperties: false }
|
|
1699
|
+
);
|
|
1700
|
+
function blockSlotJsonSchema(slot) {
|
|
1701
|
+
const { oneLine, properties, items, role: _role, ...rest } = slot;
|
|
1702
|
+
delete rest.required;
|
|
1703
|
+
delete rest.maxWords;
|
|
1704
|
+
if (slot.type === "component") {
|
|
1705
|
+
return {
|
|
1706
|
+
type: "object",
|
|
1707
|
+
properties: { name: { type: "string" } },
|
|
1708
|
+
required: ["name"],
|
|
1709
|
+
description: slot.description
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
return {
|
|
1713
|
+
...rest,
|
|
1714
|
+
...oneLine && { pattern: "^[^\\r\\n]*$" },
|
|
1715
|
+
...items && { items: blockSlotJsonSchema(items) },
|
|
1716
|
+
...properties && {
|
|
1717
|
+
properties: Object.fromEntries(
|
|
1718
|
+
Object.entries(properties).map(([key, value]) => [
|
|
1719
|
+
key,
|
|
1720
|
+
blockSlotJsonSchema(value)
|
|
1721
|
+
])
|
|
1722
|
+
),
|
|
1723
|
+
required: Object.entries(properties).filter(([, value]) => value.required && value.default === void 0).map(([key]) => key),
|
|
1724
|
+
additionalProperties: false
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
// src/blocks/directives.ts
|
|
1730
|
+
var BLOCK_DIRECTIVES = {
|
|
1731
|
+
$slot: { keys: ["$slot", "default", "props"], result: "dynamic" },
|
|
1732
|
+
$item: { keys: ["$item", "default", "props"], result: "dynamic" },
|
|
1733
|
+
$theme: { keys: ["$theme", "default"], result: "dynamic" },
|
|
1734
|
+
$context: { keys: ["$context", "default"], result: "dynamic" },
|
|
1735
|
+
$count: { keys: ["$count"], result: "number" },
|
|
1736
|
+
$if: { keys: ["$if", "then", "else"], result: "dynamic" },
|
|
1737
|
+
$each: { keys: ["$each", "template"], result: "array" },
|
|
1738
|
+
$join: { keys: ["$join", "separator", "keepEmpty"], result: "string" },
|
|
1739
|
+
$measure: { keys: ["$measure", "fraction", "unit"], result: "number" }
|
|
1740
|
+
};
|
|
1741
|
+
|
|
1742
|
+
// src/blocks/evaluator.ts
|
|
1743
|
+
import { Value } from "@sinclair/typebox/value";
|
|
1744
|
+
var BlockEvaluationError = class extends Error {
|
|
1745
|
+
constructor(issues) {
|
|
1746
|
+
super(issues.map((i) => `${i.path}: ${i.message}`).join("\n"));
|
|
1747
|
+
this.issues = issues;
|
|
1748
|
+
this.name = "BlockEvaluationError";
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1751
|
+
var isBlockRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1752
|
+
var blockPointerKey = (s) => s.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
1753
|
+
var own = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
1754
|
+
function blockValueAt(root, path) {
|
|
1755
|
+
if (path === "") return root;
|
|
1756
|
+
if (!path.startsWith("/")) return void 0;
|
|
1757
|
+
let value = root;
|
|
1758
|
+
for (const part of path.slice(1).split("/")) {
|
|
1759
|
+
const key = part.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
1760
|
+
if (!isBlockRecord(value) && !Array.isArray(value) || !own(value, key))
|
|
1761
|
+
return void 0;
|
|
1762
|
+
value = value[key];
|
|
1763
|
+
}
|
|
1764
|
+
return value;
|
|
1765
|
+
}
|
|
1766
|
+
function toAuthoredBlockPointer(map, pointer2) {
|
|
1767
|
+
let best;
|
|
1768
|
+
for (const path of Object.keys(map)) {
|
|
1769
|
+
if ((pointer2 === path || pointer2.startsWith(`${path}/`)) && (best === void 0 || path.length > best.length))
|
|
1770
|
+
best = path;
|
|
1771
|
+
}
|
|
1772
|
+
return best === void 0 ? pointer2 : `${map[best]}${pointer2.slice(best.length)}`;
|
|
1773
|
+
}
|
|
1774
|
+
var BLOCK_SLOT_PLACEMENT_PROPS = [
|
|
1775
|
+
"x",
|
|
1776
|
+
"y",
|
|
1777
|
+
"w",
|
|
1778
|
+
"h",
|
|
1779
|
+
"position",
|
|
1780
|
+
"grid",
|
|
1781
|
+
"gridConfig",
|
|
1782
|
+
"direction",
|
|
1783
|
+
"gap",
|
|
1784
|
+
"weights",
|
|
1785
|
+
"alignment",
|
|
1786
|
+
"spacing"
|
|
1787
|
+
];
|
|
1788
|
+
var blockWordCount = (text) => text.trim() === "" ? 0 : text.trim().split(/\s+/).length;
|
|
1789
|
+
var present = (value) => value !== void 0 && value !== null && value !== "" && value !== false && (!Array.isArray(value) || value.length > 0);
|
|
1790
|
+
var fail = (path, code, message) => {
|
|
1791
|
+
throw new BlockEvaluationError([{ path, code, message }]);
|
|
1792
|
+
};
|
|
1793
|
+
function resolveBlockSlot(slot, input, path, issues) {
|
|
1794
|
+
const value = input === void 0 && slot.default !== void 0 ? structuredClone(slot.default) : input;
|
|
1795
|
+
if (value === void 0) {
|
|
1796
|
+
if (slot.required)
|
|
1797
|
+
issues.push({
|
|
1798
|
+
path,
|
|
1799
|
+
code: "block_required_slot",
|
|
1800
|
+
message: "Required block slot is missing."
|
|
1801
|
+
});
|
|
1802
|
+
return void 0;
|
|
1803
|
+
}
|
|
1804
|
+
const validType = slot.type === "array" ? Array.isArray(value) : slot.type === "component" ? isBlockRecord(value) && typeof value.name === "string" : slot.type === "object" ? isBlockRecord(value) : slot.type === "integer" ? typeof value === "number" && Number.isInteger(value) : typeof value === slot.type && (typeof value !== "number" || Number.isFinite(value));
|
|
1805
|
+
if (!validType) {
|
|
1806
|
+
issues.push({
|
|
1807
|
+
path,
|
|
1808
|
+
code: "block_slot_type",
|
|
1809
|
+
message: `Expected ${slot.type}.`
|
|
1810
|
+
});
|
|
1811
|
+
return value;
|
|
1812
|
+
}
|
|
1813
|
+
const issue = (message) => issues.push({ path, code: "block_slot_budget", message });
|
|
1814
|
+
if (slot.enum && !slot.enum.includes(value))
|
|
1815
|
+
issue("Value is not one of the declared choices.");
|
|
1816
|
+
if (typeof value === "string") {
|
|
1817
|
+
if (slot.oneLine && /[\r\n]/.test(value))
|
|
1818
|
+
issue("Slot must contain one line.");
|
|
1819
|
+
if (slot.minLength !== void 0 && value.length < slot.minLength)
|
|
1820
|
+
issue(`Minimum length is ${slot.minLength}.`);
|
|
1821
|
+
if (slot.maxLength !== void 0 && value.length > slot.maxLength)
|
|
1822
|
+
issue(`Maximum length is ${slot.maxLength}.`);
|
|
1823
|
+
if (slot.maxWords !== void 0 && blockWordCount(value) > slot.maxWords)
|
|
1824
|
+
issue(`Maximum word count is ${slot.maxWords}.`);
|
|
1825
|
+
}
|
|
1826
|
+
if (typeof value === "number") {
|
|
1827
|
+
if (slot.minimum !== void 0 && value < slot.minimum)
|
|
1828
|
+
issue(`Minimum value is ${slot.minimum}.`);
|
|
1829
|
+
if (slot.maximum !== void 0 && value > slot.maximum)
|
|
1830
|
+
issue(`Maximum value is ${slot.maximum}.`);
|
|
1831
|
+
}
|
|
1832
|
+
if (Array.isArray(value)) {
|
|
1833
|
+
if (slot.minItems !== void 0 && value.length < slot.minItems)
|
|
1834
|
+
issue(`Minimum item count is ${slot.minItems}.`);
|
|
1835
|
+
if (slot.maxItems !== void 0 && value.length > slot.maxItems)
|
|
1836
|
+
issue(`Maximum item count is ${slot.maxItems}.`);
|
|
1837
|
+
return slot.items ? value.map(
|
|
1838
|
+
(v, i) => resolveBlockSlot(slot.items, v, `${path}/${i}`, issues)
|
|
1839
|
+
) : value;
|
|
1840
|
+
}
|
|
1841
|
+
if (slot.type === "component" && isBlockRecord(value)) {
|
|
1842
|
+
const checkPlacement = (node, pointer2, depth = 0) => {
|
|
1843
|
+
if (depth > 64) {
|
|
1844
|
+
issues.push({
|
|
1845
|
+
path: pointer2,
|
|
1846
|
+
code: "block_expansion_limit",
|
|
1847
|
+
message: "Component slot exceeds 64 levels."
|
|
1848
|
+
});
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
if (Array.isArray(node)) {
|
|
1852
|
+
node.forEach(
|
|
1853
|
+
(item, i) => checkPlacement(item, `${pointer2}/${i}`, depth + 1)
|
|
1854
|
+
);
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
if (!isBlockRecord(node)) return;
|
|
1858
|
+
const props = typeof node.name === "string" && isBlockRecord(node.props) ? node.props : {};
|
|
1859
|
+
for (const key of BLOCK_SLOT_PLACEMENT_PROPS) {
|
|
1860
|
+
if (own(props, key))
|
|
1861
|
+
issues.push({
|
|
1862
|
+
path: `${pointer2}/props/${key}`,
|
|
1863
|
+
code: "block_slot_placement",
|
|
1864
|
+
message: "Block placement belongs in the definition, not in a component slot."
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
for (const [key, item] of Object.entries(node))
|
|
1868
|
+
checkPlacement(item, `${pointer2}/${blockPointerKey(key)}`, depth + 1);
|
|
1869
|
+
};
|
|
1870
|
+
checkPlacement(value, path);
|
|
1871
|
+
}
|
|
1872
|
+
if (slot.type === "object" && isBlockRecord(value) && slot.properties)
|
|
1873
|
+
return resolveBlockSlots(slot.properties, value, path, issues);
|
|
1874
|
+
return value;
|
|
1875
|
+
}
|
|
1876
|
+
function resolveBlockSlots(slots, values, path, issues) {
|
|
1877
|
+
const out = {};
|
|
1878
|
+
for (const key of Object.keys(values)) {
|
|
1879
|
+
if (!own(slots, key))
|
|
1880
|
+
issues.push({
|
|
1881
|
+
path: `${path}/${blockPointerKey(key)}`,
|
|
1882
|
+
code: "block_unknown_slot",
|
|
1883
|
+
message: `Unknown slot '${key}'. Expected: ${Object.keys(slots).join(", ")}.`
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
for (const [key, slot] of Object.entries(slots)) {
|
|
1887
|
+
const value = resolveBlockSlot(
|
|
1888
|
+
slot,
|
|
1889
|
+
own(values, key) ? values[key] : void 0,
|
|
1890
|
+
`${path}/${blockPointerKey(key)}`,
|
|
1891
|
+
issues
|
|
1892
|
+
);
|
|
1893
|
+
if (value !== void 0)
|
|
1894
|
+
Object.defineProperty(out, key, {
|
|
1895
|
+
value,
|
|
1896
|
+
enumerable: true,
|
|
1897
|
+
writable: true,
|
|
1898
|
+
configurable: true
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
return out;
|
|
1902
|
+
}
|
|
1903
|
+
var DIRECTIVES = Object.fromEntries(
|
|
1904
|
+
Object.entries(BLOCK_DIRECTIVES).map(([key, directive]) => [
|
|
1905
|
+
key,
|
|
1906
|
+
directive.keys
|
|
1907
|
+
])
|
|
1908
|
+
);
|
|
1909
|
+
function slotDescriptorAt(slots, pointer2) {
|
|
1910
|
+
let descriptor = { type: "object", properties: slots };
|
|
1911
|
+
for (const escaped of pointer2.slice(1).split("/")) {
|
|
1912
|
+
const key = escaped.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
1913
|
+
if (descriptor?.type === "object") {
|
|
1914
|
+
if (!descriptor.properties) return { type: "object" };
|
|
1915
|
+
descriptor = own(descriptor.properties, key) ? descriptor.properties[key] : void 0;
|
|
1916
|
+
} else if (descriptor?.type === "array" && /^(0|[1-9]\d*)$/.test(key))
|
|
1917
|
+
descriptor = descriptor.items ?? { type: "object" };
|
|
1918
|
+
else if (descriptor?.type === "component") return { type: "object" };
|
|
1919
|
+
else return void 0;
|
|
1920
|
+
}
|
|
1921
|
+
return descriptor;
|
|
1922
|
+
}
|
|
1923
|
+
function checkTemplate(value, path, slots, issues, repeated = false, depth = 0) {
|
|
1924
|
+
if (depth > 64) {
|
|
1925
|
+
issues.push({
|
|
1926
|
+
path,
|
|
1927
|
+
code: "block_depth",
|
|
1928
|
+
message: "Definition exceeds 64 levels."
|
|
1929
|
+
});
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
if (Array.isArray(value)) {
|
|
1933
|
+
value.forEach(
|
|
1934
|
+
(v, i) => checkTemplate(v, `${path}/${i}`, slots, issues, repeated, depth + 1)
|
|
1935
|
+
);
|
|
1936
|
+
return;
|
|
1937
|
+
}
|
|
1938
|
+
if (!isBlockRecord(value)) return;
|
|
1939
|
+
const keys = Object.keys(value).filter((k) => k.startsWith("$"));
|
|
1940
|
+
if (keys.length) {
|
|
1941
|
+
const key = keys[0];
|
|
1942
|
+
const allowed = DIRECTIVES[key];
|
|
1943
|
+
if (!allowed || keys.length !== 1 || Object.keys(value).some((k) => !allowed.includes(k))) {
|
|
1944
|
+
issues.push({
|
|
1945
|
+
path,
|
|
1946
|
+
code: "block_invalid_binding",
|
|
1947
|
+
message: "Unknown or malformed block directive."
|
|
1948
|
+
});
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
|
+
if ([
|
|
1952
|
+
"$slot",
|
|
1953
|
+
"$item",
|
|
1954
|
+
"$theme",
|
|
1955
|
+
"$context",
|
|
1956
|
+
"$if",
|
|
1957
|
+
"$each",
|
|
1958
|
+
"$count"
|
|
1959
|
+
].includes(key)) {
|
|
1960
|
+
const pointer2 = value[key];
|
|
1961
|
+
if (typeof pointer2 !== "string" || pointer2 !== "" && !pointer2.startsWith("/"))
|
|
1962
|
+
issues.push({
|
|
1963
|
+
path,
|
|
1964
|
+
code: "block_invalid_binding",
|
|
1965
|
+
message: "Bindings use JSON Pointers, e.g. /title."
|
|
1966
|
+
});
|
|
1967
|
+
else if (["$slot", "$if", "$each", "$count"].includes(key)) {
|
|
1968
|
+
const descriptor = slotDescriptorAt(slots, pointer2);
|
|
1969
|
+
if (!descriptor)
|
|
1970
|
+
issues.push({
|
|
1971
|
+
path,
|
|
1972
|
+
code: "block_unknown_binding",
|
|
1973
|
+
message: `No slot field '${pointer2}' is declared.`
|
|
1974
|
+
});
|
|
1975
|
+
else if (["$each", "$count"].includes(key) && descriptor.type !== "array")
|
|
1976
|
+
issues.push({
|
|
1977
|
+
path,
|
|
1978
|
+
code: "block_invalid_binding",
|
|
1979
|
+
message: `${key} requires an array slot.`
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
if (key === "$item" && !repeated)
|
|
1983
|
+
issues.push({
|
|
1984
|
+
path,
|
|
1985
|
+
code: "block_invalid_binding",
|
|
1986
|
+
message: "$item is only available inside $each."
|
|
1987
|
+
});
|
|
1988
|
+
if ((key === "$slot" || key === "$item") && own(value, "props") && !isBlockRecord(value.props))
|
|
1989
|
+
issues.push({
|
|
1990
|
+
path: `${path}/props`,
|
|
1991
|
+
code: "block_invalid_binding",
|
|
1992
|
+
message: "props must be an object of component props merged beneath a component-slot value."
|
|
1993
|
+
});
|
|
1994
|
+
}
|
|
1995
|
+
if (key === "$join" && value.keepEmpty !== void 0 && typeof value.keepEmpty !== "boolean")
|
|
1996
|
+
issues.push({
|
|
1997
|
+
path,
|
|
1998
|
+
code: "block_invalid_binding",
|
|
1999
|
+
message: "keepEmpty must be boolean."
|
|
2000
|
+
});
|
|
2001
|
+
if (key === "$if" && !own(value, "then"))
|
|
2002
|
+
issues.push({
|
|
2003
|
+
path,
|
|
2004
|
+
code: "block_invalid_binding",
|
|
2005
|
+
message: "$if requires then."
|
|
2006
|
+
});
|
|
2007
|
+
if (key === "$each" && (!own(value, "template") || Array.isArray(value.template)))
|
|
2008
|
+
issues.push({
|
|
2009
|
+
path,
|
|
2010
|
+
code: "block_invalid_binding",
|
|
2011
|
+
message: "$each requires one template value; use a group for multiple flow children."
|
|
2012
|
+
});
|
|
2013
|
+
if (key === "$join" && (!Array.isArray(value.$join) || value.separator !== void 0 && typeof value.separator !== "string"))
|
|
2014
|
+
issues.push({
|
|
2015
|
+
path,
|
|
2016
|
+
code: "block_invalid_binding",
|
|
2017
|
+
message: "$join requires an array and an optional string separator."
|
|
2018
|
+
});
|
|
2019
|
+
if (key === "$measure" && (!["width", "height"].includes(String(value.$measure)) || !["pt", "twip", "in"].includes(String(value.unit ?? "pt")) || value.fraction !== void 0 && (typeof value.fraction !== "number" || value.fraction < 0 || value.fraction > 1)))
|
|
2020
|
+
issues.push({
|
|
2021
|
+
path,
|
|
2022
|
+
code: "block_invalid_binding",
|
|
2023
|
+
message: "$measure requires width/height, pt/twip/in and a fraction between 0 and 1."
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
for (const [key, item] of Object.entries(value)) {
|
|
2027
|
+
if (key.startsWith("$") && key !== "$join") continue;
|
|
2028
|
+
checkTemplate(
|
|
2029
|
+
item,
|
|
2030
|
+
`${path}/${blockPointerKey(key)}`,
|
|
2031
|
+
slots,
|
|
2032
|
+
issues,
|
|
2033
|
+
repeated || own(value, "$each"),
|
|
2034
|
+
depth + 1
|
|
2035
|
+
);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
function readBlockDefinitions(document) {
|
|
2039
|
+
const value = isBlockRecord(document) && isBlockRecord(document.props) ? document.props.blocks : void 0;
|
|
2040
|
+
return value ?? {};
|
|
2041
|
+
}
|
|
2042
|
+
function validateBlockDefinitions(definitions, format, reservedNames = []) {
|
|
2043
|
+
if (!Value.Check(BlockDefinitionsSchema, definitions))
|
|
2044
|
+
return [...Value.Errors(BlockDefinitionsSchema, definitions)].slice(0, 100).map((e) => ({
|
|
2045
|
+
path: `/props/blocks${e.path}`,
|
|
2046
|
+
code: "block_invalid_definition",
|
|
2047
|
+
message: e.message
|
|
2048
|
+
}));
|
|
2049
|
+
const issues = [];
|
|
2050
|
+
for (const [name, def] of Object.entries(definitions)) {
|
|
2051
|
+
const path = `/props/blocks/${blockPointerKey(name)}`;
|
|
2052
|
+
if (reservedNames.includes(name))
|
|
2053
|
+
issues.push({
|
|
2054
|
+
path,
|
|
2055
|
+
code: "block_name_collision",
|
|
2056
|
+
message: `Block '${name}' conflicts with a registered component.`
|
|
2057
|
+
});
|
|
2058
|
+
if (format !== "docx" && def.section)
|
|
2059
|
+
issues.push({
|
|
2060
|
+
path: `${path}/section`,
|
|
2061
|
+
code: "block_format",
|
|
2062
|
+
message: "Section effects are DOCX-only."
|
|
2063
|
+
});
|
|
2064
|
+
if (format !== "pptx" && def.slide)
|
|
2065
|
+
issues.push({
|
|
2066
|
+
path: `${path}/slide`,
|
|
2067
|
+
code: "block_format",
|
|
2068
|
+
message: "Slide effects are PPTX-only."
|
|
2069
|
+
});
|
|
2070
|
+
const checkSlot = (slot, pointer2) => {
|
|
2071
|
+
if (slot.default !== void 0)
|
|
2072
|
+
resolveBlockSlot(slot, slot.default, `${pointer2}/default`, issues);
|
|
2073
|
+
for (const [minimum, maximum] of [
|
|
2074
|
+
["minItems", "maxItems"],
|
|
2075
|
+
["minLength", "maxLength"],
|
|
2076
|
+
["minimum", "maximum"]
|
|
2077
|
+
]) {
|
|
2078
|
+
if (slot[minimum] !== void 0 && slot[maximum] !== void 0 && slot[minimum] > slot[maximum])
|
|
2079
|
+
issues.push({
|
|
2080
|
+
path: pointer2,
|
|
2081
|
+
code: "block_invalid_definition",
|
|
2082
|
+
message: `${minimum} exceeds ${maximum}.`
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
if (slot.items) checkSlot(slot.items, `${pointer2}/items`);
|
|
2086
|
+
for (const [key, nested] of Object.entries(slot.properties ?? {}))
|
|
2087
|
+
checkSlot(nested, `${pointer2}/properties/${blockPointerKey(key)}`);
|
|
2088
|
+
};
|
|
2089
|
+
for (const [key, slot] of Object.entries(def.slots))
|
|
2090
|
+
checkSlot(slot, `${path}/slots/${blockPointerKey(key)}`);
|
|
2091
|
+
checkTemplate(def.body, `${path}/body`, def.slots, issues);
|
|
2092
|
+
if (def.section)
|
|
2093
|
+
checkTemplate(def.section, `${path}/section`, def.slots, issues);
|
|
2094
|
+
if (def.slide) checkTemplate(def.slide, `${path}/slide`, def.slots, issues);
|
|
2095
|
+
}
|
|
2096
|
+
return issues;
|
|
2097
|
+
}
|
|
2098
|
+
function validateBlockInvocations(document, definitions, format, reservedNames = []) {
|
|
2099
|
+
const issues = validateBlockDefinitions(definitions, format, reservedNames);
|
|
2100
|
+
if (issues.length) return issues;
|
|
2101
|
+
const walk = (v, path) => {
|
|
2102
|
+
if (Array.isArray(v)) {
|
|
2103
|
+
v.forEach((item, i) => walk(item, `${path}/${i}`));
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
if (!isBlockRecord(v) || v.enabled === false) return;
|
|
2107
|
+
if (v.name === "block" && isBlockRecord(v.props) && typeof v.props.ref === "string") {
|
|
2108
|
+
const def = own(definitions, v.props.ref) ? definitions[v.props.ref] : void 0;
|
|
2109
|
+
if (!def)
|
|
2110
|
+
issues.push({
|
|
2111
|
+
path: `${path}/props/ref`,
|
|
2112
|
+
code: "block_unknown_reference",
|
|
2113
|
+
message: `Block '${v.props.ref}' is not defined in this document.`
|
|
2114
|
+
});
|
|
2115
|
+
else {
|
|
2116
|
+
if (v.props.slots === void 0 || isBlockRecord(v.props.slots))
|
|
2117
|
+
resolveBlockSlots(
|
|
2118
|
+
def.slots,
|
|
2119
|
+
v.props.slots ?? {},
|
|
2120
|
+
`${path}/props/slots`,
|
|
2121
|
+
issues
|
|
2122
|
+
);
|
|
2123
|
+
if (def.section && !/^\/children\/\d+\/children\/\d+$/.test(path))
|
|
2124
|
+
issues.push({
|
|
2125
|
+
path,
|
|
2126
|
+
code: "invalid_placement",
|
|
2127
|
+
message: "A block with section effects must be a direct child of a top-level section."
|
|
2128
|
+
});
|
|
2129
|
+
if (def.slide && !/^\/children\/\d+\/children\/\d+$/.test(path))
|
|
2130
|
+
issues.push({
|
|
2131
|
+
path,
|
|
2132
|
+
code: "invalid_placement",
|
|
2133
|
+
message: "A block with slide effects must be a direct child of a slide."
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
for (const [key, item] of Object.entries(v)) {
|
|
2138
|
+
if (path === "/props" && key === "blocks") continue;
|
|
2139
|
+
walk(item, `${path}/${blockPointerKey(key)}`);
|
|
2140
|
+
}
|
|
2141
|
+
};
|
|
2142
|
+
walk(document, "");
|
|
2143
|
+
return issues;
|
|
2144
|
+
}
|
|
2145
|
+
var JsonBlockEvaluator = class {
|
|
2146
|
+
constructor(definitions, options) {
|
|
2147
|
+
this.definitions = definitions;
|
|
2148
|
+
this.options = options;
|
|
2149
|
+
const issues = validateBlockDefinitions(
|
|
2150
|
+
definitions,
|
|
2151
|
+
options.format,
|
|
2152
|
+
options.reservedNames
|
|
2153
|
+
);
|
|
2154
|
+
if (issues.length) throw new BlockEvaluationError(issues);
|
|
2155
|
+
}
|
|
2156
|
+
sourceMap = {};
|
|
2157
|
+
blocks = [];
|
|
2158
|
+
nodes = 0;
|
|
2159
|
+
guard(path, depth) {
|
|
2160
|
+
if (depth > 64 || ++this.nodes > 5e4)
|
|
2161
|
+
fail(
|
|
2162
|
+
path,
|
|
2163
|
+
"block_expansion_limit",
|
|
2164
|
+
"Block expansion exceeds the depth/node limit (64/50000)."
|
|
2165
|
+
);
|
|
2166
|
+
}
|
|
2167
|
+
evaluate(value, env, out, definitionPath, depth = 0) {
|
|
2168
|
+
this.guard(env.source, depth);
|
|
2169
|
+
this.sourceMap[out] = env.source;
|
|
2170
|
+
if (Array.isArray(value)) {
|
|
2171
|
+
const result2 = [];
|
|
2172
|
+
value.forEach((v, i) => {
|
|
2173
|
+
const evaluated = this.evaluate(
|
|
2174
|
+
v,
|
|
2175
|
+
env,
|
|
2176
|
+
`${out}/${result2.length}`,
|
|
2177
|
+
`${definitionPath}/${i}`,
|
|
2178
|
+
depth + 1
|
|
2179
|
+
);
|
|
2180
|
+
if (evaluated !== void 0) {
|
|
2181
|
+
if (isBlockRecord(v) && ("$if" in v || "$each" in v) && Array.isArray(evaluated)) {
|
|
2182
|
+
const base = `${out}/${result2.length}`;
|
|
2183
|
+
const maps = Object.entries(this.sourceMap).filter(
|
|
2184
|
+
([key]) => key.startsWith(`${base}/`)
|
|
2185
|
+
);
|
|
2186
|
+
for (const [key] of maps) delete this.sourceMap[key];
|
|
2187
|
+
for (const [key, source] of maps) {
|
|
2188
|
+
const rest = key.slice(base.length + 1);
|
|
2189
|
+
const [index, ...suffix] = rest.split("/");
|
|
2190
|
+
this.sourceMap[`${out}/${result2.length + Number(index)}${suffix.length ? "/" + suffix.join("/") : ""}`] = source;
|
|
2191
|
+
}
|
|
2192
|
+
result2.push(...evaluated);
|
|
2193
|
+
} else result2.push(evaluated);
|
|
2194
|
+
}
|
|
2195
|
+
});
|
|
2196
|
+
return result2;
|
|
2197
|
+
}
|
|
2198
|
+
if (!isBlockRecord(value)) return value;
|
|
2199
|
+
if ("$slot" in value || "$item" in value || "$theme" in value || "$context" in value) {
|
|
2200
|
+
const key = ["$slot", "$item", "$theme", "$context"].find(
|
|
2201
|
+
(k) => k in value
|
|
2202
|
+
);
|
|
2203
|
+
const pointer2 = value[key];
|
|
2204
|
+
const root = key === "$slot" ? env.slots : key === "$item" ? env.item : key === "$theme" ? this.options.theme : env.context;
|
|
2205
|
+
const found = blockValueAt(root, pointer2);
|
|
2206
|
+
if (key === "$slot")
|
|
2207
|
+
this.sourceMap[out] = env.slotSources ? toAuthoredBlockPointer(env.slotSources, pointer2) : `${env.source}/props/slots${pointer2}`;
|
|
2208
|
+
if (key === "$item")
|
|
2209
|
+
this.sourceMap[out] = `${env.itemSource ?? env.source}${pointer2}`;
|
|
2210
|
+
if (key === "$context")
|
|
2211
|
+
this.sourceMap[out] = toAuthoredBlockPointer(env.contextSources ?? {}, pointer2) === pointer2 ? env.source : toAuthoredBlockPointer(env.contextSources ?? {}, pointer2);
|
|
2212
|
+
let result2 = found !== void 0 ? structuredClone(found) : void 0;
|
|
2213
|
+
if (result2 === void 0 && own(value, "default"))
|
|
2214
|
+
result2 = this.evaluate(
|
|
2215
|
+
value.default,
|
|
2216
|
+
env,
|
|
2217
|
+
out,
|
|
2218
|
+
`${definitionPath}/default`,
|
|
2219
|
+
depth + 1
|
|
2220
|
+
);
|
|
2221
|
+
if (result2 === void 0 && key === "$theme")
|
|
2222
|
+
return fail(
|
|
2223
|
+
definitionPath,
|
|
2224
|
+
"block_unknown_theme_binding",
|
|
2225
|
+
`Theme value '${pointer2}' is missing; declare a fallback or use an existing token.`
|
|
2226
|
+
);
|
|
2227
|
+
if ((key === "$slot" || key === "$item") && own(value, "props") && isBlockRecord(result2) && typeof result2.name === "string") {
|
|
2228
|
+
const origin = this.sourceMap[out];
|
|
2229
|
+
const defaults = this.evaluate(
|
|
2230
|
+
value.props,
|
|
2231
|
+
env,
|
|
2232
|
+
`${out}/props`,
|
|
2233
|
+
`${definitionPath}/props`,
|
|
2234
|
+
depth + 1
|
|
2235
|
+
);
|
|
2236
|
+
const authored = isBlockRecord(result2.props) ? result2.props : {};
|
|
2237
|
+
for (const propKey of Object.keys(authored)) {
|
|
2238
|
+
const pointerKey = `${out}/props/${blockPointerKey(propKey)}`;
|
|
2239
|
+
for (const mapped of Object.keys(this.sourceMap))
|
|
2240
|
+
if (mapped === pointerKey || mapped.startsWith(`${pointerKey}/`))
|
|
2241
|
+
delete this.sourceMap[mapped];
|
|
2242
|
+
this.sourceMap[pointerKey] = `${origin}/props/${blockPointerKey(propKey)}`;
|
|
2243
|
+
}
|
|
2244
|
+
result2 = {
|
|
2245
|
+
...result2,
|
|
2246
|
+
props: {
|
|
2247
|
+
...isBlockRecord(defaults) ? defaults : {},
|
|
2248
|
+
...authored
|
|
2249
|
+
}
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
return result2;
|
|
2253
|
+
}
|
|
2254
|
+
if ("$if" in value)
|
|
2255
|
+
return this.evaluate(
|
|
2256
|
+
present(blockValueAt(env.slots, value.$if)) ? value.then : value.else,
|
|
2257
|
+
env,
|
|
2258
|
+
out,
|
|
2259
|
+
definitionPath,
|
|
2260
|
+
depth + 1
|
|
2261
|
+
);
|
|
2262
|
+
if ("$count" in value) {
|
|
2263
|
+
const list = blockValueAt(env.slots, value.$count);
|
|
2264
|
+
if (!Array.isArray(list))
|
|
2265
|
+
return fail(
|
|
2266
|
+
`${env.source}/props/slots${value.$count}`,
|
|
2267
|
+
"block_slot_type",
|
|
2268
|
+
"$count requires an array slot."
|
|
2269
|
+
);
|
|
2270
|
+
return list.length;
|
|
2271
|
+
}
|
|
2272
|
+
if ("$each" in value) {
|
|
2273
|
+
const list = blockValueAt(env.slots, value.$each);
|
|
2274
|
+
if (!Array.isArray(list))
|
|
2275
|
+
return fail(
|
|
2276
|
+
`${env.source}/props/slots${value.$each}`,
|
|
2277
|
+
"block_slot_type",
|
|
2278
|
+
"$each requires an array slot."
|
|
2279
|
+
);
|
|
2280
|
+
const result2 = [];
|
|
2281
|
+
list.forEach((item, i) => {
|
|
2282
|
+
const pointer2 = `${out}/${result2.length}`;
|
|
2283
|
+
const evaluated = this.evaluate(
|
|
2284
|
+
value.template,
|
|
2285
|
+
{
|
|
2286
|
+
...env,
|
|
2287
|
+
item,
|
|
2288
|
+
itemSource: env.slotSources ? toAuthoredBlockPointer(env.slotSources, `${value.$each}/${i}`) : `${env.source}/props/slots${value.$each}/${i}`
|
|
2289
|
+
},
|
|
2290
|
+
pointer2,
|
|
2291
|
+
`${definitionPath}/template`,
|
|
2292
|
+
depth + 1
|
|
2293
|
+
);
|
|
2294
|
+
if (evaluated !== void 0) result2.push(evaluated);
|
|
2295
|
+
else
|
|
2296
|
+
for (const key of Object.keys(this.sourceMap)) {
|
|
2297
|
+
if (key === pointer2 || key.startsWith(`${pointer2}/`))
|
|
2298
|
+
delete this.sourceMap[key];
|
|
2299
|
+
}
|
|
2300
|
+
});
|
|
2301
|
+
return result2;
|
|
2302
|
+
}
|
|
2303
|
+
if ("$join" in value) {
|
|
2304
|
+
const values = value.$join.map(
|
|
2305
|
+
(v, i) => this.evaluate(
|
|
2306
|
+
v,
|
|
2307
|
+
env,
|
|
2308
|
+
`${out}/${i}`,
|
|
2309
|
+
`${definitionPath}/$join/${i}`,
|
|
2310
|
+
depth + 1
|
|
2311
|
+
)
|
|
2312
|
+
);
|
|
2313
|
+
const first = values.findIndex(present);
|
|
2314
|
+
if (first >= 0) this.sourceMap[out] = this.sourceMap[`${out}/${first}`];
|
|
2315
|
+
return (value.keepEmpty === true ? values : values.filter(present)).map((v) => String(v ?? "")).join(String(value.separator ?? ""));
|
|
2316
|
+
}
|
|
2317
|
+
if ("$measure" in value) {
|
|
2318
|
+
if (!this.options.measure)
|
|
2319
|
+
return fail(
|
|
2320
|
+
definitionPath,
|
|
2321
|
+
"block_unsupported_operation",
|
|
2322
|
+
"This format does not support $measure."
|
|
2323
|
+
);
|
|
2324
|
+
return this.options.measure(
|
|
2325
|
+
value.$measure,
|
|
2326
|
+
value.unit ?? "pt",
|
|
2327
|
+
env.context
|
|
2328
|
+
) * Number(value.fraction ?? 1);
|
|
2329
|
+
}
|
|
2330
|
+
const result = {};
|
|
2331
|
+
for (const [key, item] of Object.entries(value)) {
|
|
2332
|
+
const evaluated = this.evaluate(
|
|
2333
|
+
item,
|
|
2334
|
+
env,
|
|
2335
|
+
`${out}/${blockPointerKey(key)}`,
|
|
2336
|
+
`${definitionPath}/${blockPointerKey(key)}`,
|
|
2337
|
+
depth + 1
|
|
2338
|
+
);
|
|
2339
|
+
if (evaluated !== void 0)
|
|
2340
|
+
Object.defineProperty(result, key, {
|
|
2341
|
+
value: evaluated,
|
|
2342
|
+
enumerable: true,
|
|
2343
|
+
configurable: true,
|
|
2344
|
+
writable: true
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
return result;
|
|
2348
|
+
}
|
|
2349
|
+
expand(value, path = "", depth = 0) {
|
|
2350
|
+
this.guard(path, depth);
|
|
2351
|
+
if (Array.isArray(value))
|
|
2352
|
+
return value.map((v, i) => this.expand(v, `${path}/${i}`, depth + 1));
|
|
2353
|
+
if (!isBlockRecord(value)) return value;
|
|
2354
|
+
if (value.name === "block" && value.enabled !== false) {
|
|
2355
|
+
if (!isBlockRecord(value.props) || typeof value.props.ref !== "string")
|
|
2356
|
+
return fail(
|
|
2357
|
+
path,
|
|
2358
|
+
"block_invalid_invocation",
|
|
2359
|
+
"A block requires props.ref and optional props.slots."
|
|
2360
|
+
);
|
|
2361
|
+
if (Object.keys(value.props).some(
|
|
2362
|
+
(key) => !["ref", "slots"].includes(key)
|
|
2363
|
+
) || value.props.slots !== void 0 && !isBlockRecord(value.props.slots))
|
|
2364
|
+
return fail(
|
|
2365
|
+
path,
|
|
2366
|
+
"block_invalid_invocation",
|
|
2367
|
+
"Block props accept only ref and an object of slots."
|
|
2368
|
+
);
|
|
2369
|
+
const def = own(this.definitions, value.props.ref) ? this.definitions[value.props.ref] : void 0;
|
|
2370
|
+
if (!def)
|
|
2371
|
+
return fail(
|
|
2372
|
+
`${path}/props/ref`,
|
|
2373
|
+
"block_unknown_reference",
|
|
2374
|
+
`Block '${value.props.ref}' is not defined in this document.`
|
|
2375
|
+
);
|
|
2376
|
+
const issues = [];
|
|
2377
|
+
const source = toAuthoredBlockPointer(this.sourceMap, path);
|
|
2378
|
+
const slotsPath = `${path}/props/slots`;
|
|
2379
|
+
const slots = resolveBlockSlots(
|
|
2380
|
+
def.slots,
|
|
2381
|
+
value.props.slots ?? {},
|
|
2382
|
+
slotsPath,
|
|
2383
|
+
issues
|
|
2384
|
+
);
|
|
2385
|
+
if (issues.length)
|
|
2386
|
+
throw new BlockEvaluationError(
|
|
2387
|
+
issues.map((issue) => ({
|
|
2388
|
+
...issue,
|
|
2389
|
+
path: toAuthoredBlockPointer(this.sourceMap, issue.path)
|
|
2390
|
+
}))
|
|
2391
|
+
);
|
|
2392
|
+
const slotSources = Object.fromEntries([
|
|
2393
|
+
["", toAuthoredBlockPointer(this.sourceMap, slotsPath)],
|
|
2394
|
+
...Object.entries(this.sourceMap).filter(([key]) => key.startsWith(`${slotsPath}/`)).map(([key, origin]) => [key.slice(slotsPath.length), origin])
|
|
2395
|
+
]);
|
|
2396
|
+
const env = {
|
|
2397
|
+
slots,
|
|
2398
|
+
slotSources,
|
|
2399
|
+
source,
|
|
2400
|
+
definition: `/props/blocks/${blockPointerKey(value.props.ref)}`,
|
|
2401
|
+
context: this.options.contextAt?.(path) ?? this.options.context ?? {},
|
|
2402
|
+
contextSources: this.options.contextSources
|
|
2403
|
+
};
|
|
2404
|
+
if (def.section)
|
|
2405
|
+
this.options.onSection?.({
|
|
2406
|
+
settings: def.section,
|
|
2407
|
+
environment: env,
|
|
2408
|
+
path
|
|
2409
|
+
});
|
|
2410
|
+
if (def.slide)
|
|
2411
|
+
this.options.onSlide?.({ settings: def.slide, environment: env, path });
|
|
2412
|
+
this.blocks.push(source);
|
|
2413
|
+
const children = this.evaluate(
|
|
2414
|
+
def.body,
|
|
2415
|
+
env,
|
|
2416
|
+
`${path}/children`,
|
|
2417
|
+
`${env.definition}/body`,
|
|
2418
|
+
depth + 1
|
|
2419
|
+
);
|
|
2420
|
+
return {
|
|
2421
|
+
name: "group",
|
|
2422
|
+
...value.id !== void 0 && { id: value.id },
|
|
2423
|
+
children: this.expand(children, `${path}/children`, depth + 1)
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
if (value.enabled === false) return { ...value };
|
|
2427
|
+
const result = { ...value };
|
|
2428
|
+
for (const [key, item] of Object.entries(value)) {
|
|
2429
|
+
if (path === "/props" && key === "blocks") continue;
|
|
2430
|
+
Object.defineProperty(result, key, {
|
|
2431
|
+
value: this.expand(item, `${path}/${blockPointerKey(key)}`, depth + 1),
|
|
2432
|
+
enumerable: true,
|
|
2433
|
+
configurable: true,
|
|
2434
|
+
writable: true
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2437
|
+
return result;
|
|
2438
|
+
}
|
|
2439
|
+
};
|
|
2440
|
+
|
|
2441
|
+
// src/blocks/metadata.ts
|
|
2442
|
+
import { Value as Value2 } from "@sinclair/typebox/value";
|
|
2443
|
+
function blockSlotsJsonSchema(definition) {
|
|
2444
|
+
return {
|
|
2445
|
+
type: "object",
|
|
2446
|
+
additionalProperties: false,
|
|
2447
|
+
properties: Object.fromEntries(
|
|
2448
|
+
Object.entries(definition.slots).map(([key, slot]) => [
|
|
2449
|
+
key,
|
|
2450
|
+
blockSlotJsonSchema(slot)
|
|
2451
|
+
])
|
|
2452
|
+
),
|
|
2453
|
+
required: Object.entries(definition.slots).filter(([, slot]) => slot.required && slot.default === void 0).map(([key]) => key)
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
function documentBlockMetadata(document) {
|
|
2457
|
+
const definitions = readBlockDefinitions(document);
|
|
2458
|
+
if (!Value2.Check(BlockDefinitionsSchema, definitions))
|
|
2459
|
+
return { definitions: [], invocations: [], invalidDefinitions: true };
|
|
2460
|
+
const invocations2 = [];
|
|
2461
|
+
const walk = (value, path) => {
|
|
2462
|
+
if (Array.isArray(value)) {
|
|
2463
|
+
value.forEach((item, i) => walk(item, `${path}/${i}`));
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2466
|
+
if (!isBlockRecord(value)) return;
|
|
2467
|
+
if (value.name === "block" && isBlockRecord(value.props) && typeof value.props.ref === "string")
|
|
2468
|
+
invocations2.push({
|
|
2469
|
+
ref: value.props.ref,
|
|
2470
|
+
path,
|
|
2471
|
+
slotsPath: `${path}/props/slots`,
|
|
2472
|
+
defined: Object.prototype.hasOwnProperty.call(
|
|
2473
|
+
definitions,
|
|
2474
|
+
value.props.ref
|
|
2475
|
+
)
|
|
2476
|
+
});
|
|
2477
|
+
for (const [key, item] of Object.entries(value)) {
|
|
2478
|
+
if (path === "/props" && key === "blocks") continue;
|
|
2479
|
+
walk(item, `${path}/${blockPointerKey(key)}`);
|
|
2480
|
+
}
|
|
2481
|
+
};
|
|
2482
|
+
walk(document, "");
|
|
2483
|
+
return {
|
|
2484
|
+
definitions: Object.entries(definitions).map(([name, definition]) => ({
|
|
2485
|
+
name,
|
|
2486
|
+
definitionPointer: `/props/blocks/${blockPointerKey(name)}`,
|
|
2487
|
+
definition,
|
|
2488
|
+
slotsSchema: blockSlotsJsonSchema(definition)
|
|
2489
|
+
})),
|
|
2490
|
+
invocations: invocations2,
|
|
2491
|
+
invalidDefinitions: false
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
function visitInvocationSlots(document, blocks, visit) {
|
|
2495
|
+
const definitions = readBlockDefinitions(document);
|
|
2496
|
+
for (const path of blocks) {
|
|
2497
|
+
const node = blockValueAt(document, path);
|
|
2498
|
+
if (!isBlockRecord(node) || !isBlockRecord(node.props) || typeof node.props.ref !== "string")
|
|
2499
|
+
continue;
|
|
2500
|
+
const ref = node.props.ref;
|
|
2501
|
+
const definition = definitions[ref];
|
|
2502
|
+
if (!definition) continue;
|
|
2503
|
+
const walk = (slot, value, pointer2, name) => {
|
|
2504
|
+
visit(ref, slot, value, pointer2, name);
|
|
2505
|
+
if (isBlockRecord(value) && slot.properties) {
|
|
2506
|
+
for (const [key, property] of Object.entries(slot.properties)) {
|
|
2507
|
+
walk(
|
|
2508
|
+
property,
|
|
2509
|
+
blockValueAt(value, `/${blockPointerKey(key)}`),
|
|
2510
|
+
`${pointer2}/${blockPointerKey(key)}`,
|
|
2511
|
+
`${name}.${key}`
|
|
2512
|
+
);
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
if (Array.isArray(value) && slot.items)
|
|
2516
|
+
value.forEach(
|
|
2517
|
+
(item, i) => walk(slot.items, item, `${pointer2}/${i}`, name)
|
|
2518
|
+
);
|
|
2519
|
+
};
|
|
2520
|
+
for (const [name, slot] of Object.entries(definition.slots)) {
|
|
2521
|
+
const authored = blockValueAt(
|
|
2522
|
+
node.props.slots,
|
|
2523
|
+
`/${blockPointerKey(name)}`
|
|
2524
|
+
);
|
|
2525
|
+
walk(
|
|
2526
|
+
slot,
|
|
2527
|
+
authored === void 0 && slot.default !== void 0 ? slot.default : authored,
|
|
2528
|
+
`${path}/props/slots/${blockPointerKey(name)}`,
|
|
2529
|
+
name
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
function blockSlotBudgets(document, blocks) {
|
|
2535
|
+
const result = [];
|
|
2536
|
+
visitInvocationSlots(document, blocks, (ref, slot, value, pointer2, name) => {
|
|
2537
|
+
if (typeof value === "string" && slot.maxWords !== void 0)
|
|
2538
|
+
result.push({
|
|
2539
|
+
block: ref,
|
|
2540
|
+
slot: name,
|
|
2541
|
+
path: pointer2,
|
|
2542
|
+
words: blockWordCount(value),
|
|
2543
|
+
maxWords: slot.maxWords
|
|
2544
|
+
});
|
|
2545
|
+
});
|
|
2546
|
+
return result;
|
|
2547
|
+
}
|
|
2548
|
+
function blockSlotRoles(document, blocks) {
|
|
2549
|
+
const result = [];
|
|
2550
|
+
visitInvocationSlots(document, blocks, (ref, slot, value, pointer2, name) => {
|
|
2551
|
+
if (!slot.role) return;
|
|
2552
|
+
result.push({
|
|
2553
|
+
block: ref,
|
|
2554
|
+
invocation: pointer2.replace(/\/props\/slots\/.*$/, ""),
|
|
2555
|
+
slot: name,
|
|
2556
|
+
role: slot.role,
|
|
2557
|
+
path: pointer2,
|
|
2558
|
+
value
|
|
2559
|
+
});
|
|
2560
|
+
});
|
|
2561
|
+
return result;
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
// src/blocks/schema-types.ts
|
|
2565
|
+
var allTypes = [
|
|
2566
|
+
"null",
|
|
2567
|
+
"boolean",
|
|
2568
|
+
"number",
|
|
2569
|
+
"string",
|
|
2570
|
+
"array",
|
|
2571
|
+
"object"
|
|
2572
|
+
];
|
|
2573
|
+
var intersection = (a, b) => new Set([...a].filter((value) => b.has(value)));
|
|
2574
|
+
var union = (sets) => new Set(sets.flatMap((set) => [...set]));
|
|
2575
|
+
var typeOf = (value) => value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
2576
|
+
function possibleValueTypes(schema, resolve, seen = /* @__PURE__ */ new Set()) {
|
|
2577
|
+
if (schema === false) return /* @__PURE__ */ new Set();
|
|
2578
|
+
if (schema === true || seen.has(schema)) return new Set(allTypes);
|
|
2579
|
+
const negated = schema.not;
|
|
2580
|
+
if (negated === true || negated && typeof negated === "object" && Object.keys(negated).length === 0)
|
|
2581
|
+
return /* @__PURE__ */ new Set();
|
|
2582
|
+
const next = new Set(seen).add(schema);
|
|
2583
|
+
let types = new Set(allTypes);
|
|
2584
|
+
if (schema.type) {
|
|
2585
|
+
const declared = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
2586
|
+
types = intersection(
|
|
2587
|
+
types,
|
|
2588
|
+
new Set(
|
|
2589
|
+
allTypes.filter(
|
|
2590
|
+
(type) => declared.includes(type) || type === "number" && declared.includes("integer")
|
|
2591
|
+
)
|
|
2592
|
+
)
|
|
2593
|
+
);
|
|
2594
|
+
}
|
|
2595
|
+
if (Object.hasOwn(schema, "const"))
|
|
2596
|
+
types = intersection(types, /* @__PURE__ */ new Set([typeOf(schema.const)]));
|
|
2597
|
+
if (Array.isArray(schema.enum))
|
|
2598
|
+
types = intersection(types, new Set(schema.enum.map(typeOf)));
|
|
2599
|
+
if (typeof schema.$ref === "string") {
|
|
2600
|
+
const target = resolve(schema.$ref);
|
|
2601
|
+
if (target !== void 0)
|
|
2602
|
+
types = intersection(types, possibleValueTypes(target, resolve, next));
|
|
2603
|
+
}
|
|
2604
|
+
for (const key of ["anyOf", "oneOf"]) {
|
|
2605
|
+
if (Array.isArray(schema[key]))
|
|
2606
|
+
types = intersection(
|
|
2607
|
+
types,
|
|
2608
|
+
union(
|
|
2609
|
+
schema[key].map(
|
|
2610
|
+
(branch) => possibleValueTypes(branch, resolve, next)
|
|
2611
|
+
)
|
|
2612
|
+
)
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
if (Array.isArray(schema.allOf))
|
|
2616
|
+
for (const branch of schema.allOf)
|
|
2617
|
+
types = intersection(types, possibleValueTypes(branch, resolve, next));
|
|
2618
|
+
if (negated && typeof negated === "object" && negated.type && Object.keys(negated).every(
|
|
2619
|
+
(key) => ["type", "description", "title", "$comment"].includes(key)
|
|
2620
|
+
)) {
|
|
2621
|
+
const excluded = (Array.isArray(negated.type) ? negated.type : [negated.type]).filter((type) => type !== "integer");
|
|
2622
|
+
types = new Set([...types].filter((type) => !excluded.includes(type)));
|
|
2623
|
+
}
|
|
2624
|
+
return types;
|
|
2625
|
+
}
|
|
2626
|
+
function arrayItemSchema(schema, resolve, seen = /* @__PURE__ */ new Set()) {
|
|
2627
|
+
if (schema === false) return false;
|
|
2628
|
+
if (schema === true || seen.has(schema)) return {};
|
|
2629
|
+
const next = new Set(seen).add(schema);
|
|
2630
|
+
const constraints = [];
|
|
2631
|
+
if (typeof schema.$ref === "string") {
|
|
2632
|
+
const target = resolve(schema.$ref);
|
|
2633
|
+
if (target !== void 0)
|
|
2634
|
+
constraints.push(arrayItemSchema(target, resolve, next));
|
|
2635
|
+
}
|
|
2636
|
+
if (schema.items !== void 0)
|
|
2637
|
+
constraints.push(
|
|
2638
|
+
Array.isArray(schema.items) ? {
|
|
2639
|
+
anyOf: [
|
|
2640
|
+
...schema.items,
|
|
2641
|
+
...schema.additionalItems === false ? [] : [schema.additionalItems ?? {}]
|
|
2642
|
+
]
|
|
2643
|
+
} : schema.items
|
|
2644
|
+
);
|
|
2645
|
+
for (const key of ["anyOf", "oneOf"])
|
|
2646
|
+
if (Array.isArray(schema[key])) {
|
|
2647
|
+
constraints.push({
|
|
2648
|
+
anyOf: schema[key].filter(
|
|
2649
|
+
(branch) => possibleValueTypes(branch, resolve).has("array")
|
|
2650
|
+
).map(
|
|
2651
|
+
(branch) => arrayItemSchema(branch, resolve, next)
|
|
2652
|
+
)
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
if (Array.isArray(schema.allOf))
|
|
2656
|
+
constraints.push(
|
|
2657
|
+
...schema.allOf.map(
|
|
2658
|
+
(branch) => arrayItemSchema(branch, resolve, next)
|
|
2659
|
+
)
|
|
2660
|
+
);
|
|
2661
|
+
return constraints.length === 0 ? {} : constraints.length === 1 ? constraints[0] : { allOf: constraints };
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
// src/blocks/authoring-schema.ts
|
|
2665
|
+
var object = (properties, required) => ({
|
|
2666
|
+
type: "object",
|
|
2667
|
+
properties,
|
|
2668
|
+
required,
|
|
2669
|
+
additionalProperties: false
|
|
2670
|
+
});
|
|
2671
|
+
var pointer = (description) => ({
|
|
2672
|
+
type: "string",
|
|
2673
|
+
pattern: "^(|/.*)$",
|
|
2674
|
+
description
|
|
2675
|
+
});
|
|
2676
|
+
var referenceDescriptions = (format) => ({
|
|
2677
|
+
$slot: "Read a named input slot by JSON Pointer, e.g. /title or /client/name.",
|
|
2678
|
+
$item: "Read the current $each entry by JSON Pointer. Use an empty string for the whole entry or /title for a property.",
|
|
2679
|
+
$theme: "Read the active theme by JSON Pointer, e.g. /colors/primary. A missing value requires a default.",
|
|
2680
|
+
$context: format === "pptx" ? "Read deck or slide context by JSON Pointer, e.g. /document/title, /slide/width or /slide/index." : "Read document or section context by JSON Pointer, e.g. /document/title or /section/tracker."
|
|
2681
|
+
});
|
|
2682
|
+
var measureDescriptions = (format) => format === "pptx" ? {
|
|
2683
|
+
axis: "Measure the slide canvas width or height, in the unit given.",
|
|
2684
|
+
unit: "Measurement unit: points, twentieths of a point, or inches. Defaults to pt; use in for frame coordinates."
|
|
2685
|
+
} : {
|
|
2686
|
+
axis: "Measure the usable page width or height after margins, using the containing section\u2019s page settings.",
|
|
2687
|
+
unit: "Measurement unit: points, twentieths of a point, or inches. Defaults to pt."
|
|
2688
|
+
};
|
|
2689
|
+
var describe = (schema, description) => ({
|
|
2690
|
+
...typeof schema === "boolean" ? { allOf: [schema] } : schema,
|
|
2691
|
+
description
|
|
2692
|
+
});
|
|
2693
|
+
var metadata = (schema) => typeof schema === "boolean" ? {} : {
|
|
2694
|
+
...schema.description && { description: schema.description },
|
|
2695
|
+
...schema.markdownDescription && {
|
|
2696
|
+
markdownDescription: schema.markdownDescription
|
|
2697
|
+
}
|
|
2698
|
+
};
|
|
2699
|
+
var hasKey = (key) => ({ type: "object", required: [key] });
|
|
2700
|
+
var directiveNames = Object.keys(BLOCK_DIRECTIVES);
|
|
2701
|
+
function createBlockAuthoringSchema(definitions, componentDefinition, excludedComponents = [], format = "docx") {
|
|
2702
|
+
const prefix = `BlockTemplate_${componentDefinition}`;
|
|
2703
|
+
const references = referenceDescriptions(format);
|
|
2704
|
+
const measure = measureDescriptions(format);
|
|
2705
|
+
const bodyName = `${prefix}_Body`;
|
|
2706
|
+
const ref = (name) => ({ $ref: `#/definitions/${name}` });
|
|
2707
|
+
if (definitions[bodyName]) return ref(bodyName);
|
|
2708
|
+
const originals = { ...definitions };
|
|
2709
|
+
const source = originals[componentDefinition];
|
|
2710
|
+
originals[componentDefinition] = {
|
|
2711
|
+
...source,
|
|
2712
|
+
anyOf: (source.anyOf ?? [source]).filter(
|
|
2713
|
+
(branch) => !excludedComponents.includes(branch.properties?.name?.const)
|
|
2714
|
+
)
|
|
2715
|
+
};
|
|
2716
|
+
const resolve = (pointer2) => {
|
|
2717
|
+
if (!pointer2.startsWith("#/definitions/")) return void 0;
|
|
2718
|
+
let node = originals;
|
|
2719
|
+
for (const key of pointer2.slice("#/definitions/".length).split("/")) {
|
|
2720
|
+
const decoded = key.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
2721
|
+
if (!node || typeof node !== "object" || !Object.hasOwn(node, decoded))
|
|
2722
|
+
return void 0;
|
|
2723
|
+
node = node[decoded];
|
|
2724
|
+
}
|
|
2725
|
+
return typeof node === "boolean" || node && typeof node === "object" ? node : void 0;
|
|
2726
|
+
};
|
|
2727
|
+
const values = /* @__PURE__ */ new Map();
|
|
2728
|
+
const literals = /* @__PURE__ */ new Map();
|
|
2729
|
+
let nextId = 0;
|
|
2730
|
+
const componentRef = ref(componentDefinition);
|
|
2731
|
+
const shared = /* @__PURE__ */ new Map();
|
|
2732
|
+
const share = (schema) => {
|
|
2733
|
+
const key = JSON.stringify(schema);
|
|
2734
|
+
let name = shared.get(key);
|
|
2735
|
+
if (!name) {
|
|
2736
|
+
name = `${prefix}_Shared${nextId++}`;
|
|
2737
|
+
shared.set(key, name);
|
|
2738
|
+
definitions[name] = schema;
|
|
2739
|
+
}
|
|
2740
|
+
return ref(name);
|
|
2741
|
+
};
|
|
2742
|
+
const presence = Object.fromEntries(
|
|
2743
|
+
directiveNames.map((key) => [key, share(hasKey(key))])
|
|
2744
|
+
);
|
|
2745
|
+
const anyDirective = share({ anyOf: Object.values(presence) });
|
|
2746
|
+
const starterPrefixes = [
|
|
2747
|
+
.../* @__PURE__ */ new Set([
|
|
2748
|
+
"",
|
|
2749
|
+
...directiveNames.flatMap(
|
|
2750
|
+
(key) => Array.from(
|
|
2751
|
+
{ length: key.length - 1 },
|
|
2752
|
+
(_, index) => key.slice(0, index + 1)
|
|
2753
|
+
)
|
|
2754
|
+
)
|
|
2755
|
+
])
|
|
2756
|
+
];
|
|
2757
|
+
const starterObject = share({
|
|
2758
|
+
type: "object",
|
|
2759
|
+
// An enum here would itself become a list of bogus property suggestions.
|
|
2760
|
+
propertyNames: {
|
|
2761
|
+
pattern: `^(?:${starterPrefixes.map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`
|
|
2762
|
+
}
|
|
2763
|
+
});
|
|
2764
|
+
function literal(schema) {
|
|
2765
|
+
if (typeof schema === "boolean") return schema;
|
|
2766
|
+
const key = JSON.stringify(schema);
|
|
2767
|
+
const cached = literals.get(key);
|
|
2768
|
+
if (cached) return { ...ref(cached), ...metadata(schema) };
|
|
2769
|
+
const name = `${prefix}_Literal${nextId++}`;
|
|
2770
|
+
literals.set(key, name);
|
|
2771
|
+
definitions[name] = {};
|
|
2772
|
+
const result = { ...schema };
|
|
2773
|
+
if (typeof schema.$ref === "string") {
|
|
2774
|
+
const target = resolve(schema.$ref);
|
|
2775
|
+
if (target !== void 0) {
|
|
2776
|
+
const transformed = literal(target);
|
|
2777
|
+
if (typeof transformed === "object") result.$ref = transformed.$ref;
|
|
2778
|
+
else {
|
|
2779
|
+
delete result.$ref;
|
|
2780
|
+
if (!transformed) result.not = {};
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
if (schema.properties)
|
|
2785
|
+
result.properties = Object.fromEntries(
|
|
2786
|
+
Object.entries(
|
|
2787
|
+
schema.properties
|
|
2788
|
+
).map(([key2, value]) => [
|
|
2789
|
+
key2,
|
|
2790
|
+
// Literal discriminators retain canonical component/version dispatch
|
|
2791
|
+
// and their individual choice descriptions.
|
|
2792
|
+
["name", "version"].includes(key2) && typeof value === "object" && typeof value.const === "string" ? value : author(value)
|
|
2793
|
+
])
|
|
2794
|
+
);
|
|
2795
|
+
if (schema.patternProperties)
|
|
2796
|
+
result.patternProperties = Object.fromEntries(
|
|
2797
|
+
Object.entries(
|
|
2798
|
+
schema.patternProperties
|
|
2799
|
+
).map(([key2, value]) => [key2, author(value)])
|
|
2800
|
+
);
|
|
2801
|
+
if (schema.items !== void 0)
|
|
2802
|
+
result.items = Array.isArray(schema.items) ? schema.items.map((item) => author(item, true)) : author(schema.items, true);
|
|
2803
|
+
for (const key2 of ["additionalProperties", "additionalItems"])
|
|
2804
|
+
if (typeof schema[key2] === "object")
|
|
2805
|
+
result[key2] = author(schema[key2], key2 === "additionalItems");
|
|
2806
|
+
for (const key2 of ["anyOf", "oneOf", "allOf"])
|
|
2807
|
+
if (Array.isArray(schema[key2]))
|
|
2808
|
+
result[key2] = schema[key2].map((branch) => {
|
|
2809
|
+
const transformed = literal(branch);
|
|
2810
|
+
return typeof branch === "object" && typeof branch.properties?.name?.const === "string" && typeof transformed === "object" && transformed.$ref ? definitions[transformed.$ref.slice("#/definitions/".length)] : transformed;
|
|
2811
|
+
});
|
|
2812
|
+
for (const key2 of ["then", "else"])
|
|
2813
|
+
if (schema[key2] !== void 0) result[key2] = literal(schema[key2]);
|
|
2814
|
+
definitions[name] = result;
|
|
2815
|
+
return { ...ref(name), ...metadata(schema) };
|
|
2816
|
+
}
|
|
2817
|
+
function author(input, sequence = false) {
|
|
2818
|
+
if (input === false) return false;
|
|
2819
|
+
const annotations = metadata(input);
|
|
2820
|
+
const schema = typeof input === "object" ? { ...input } : input;
|
|
2821
|
+
if (typeof schema === "object") {
|
|
2822
|
+
delete schema.description;
|
|
2823
|
+
delete schema.markdownDescription;
|
|
2824
|
+
}
|
|
2825
|
+
const key = `${sequence ? "sequence" : "value"}:${JSON.stringify(schema)}`;
|
|
2826
|
+
const cached = values.get(key);
|
|
2827
|
+
if (cached) return { ...ref(cached), ...annotations };
|
|
2828
|
+
const name = `${prefix}_Value${nextId++}`;
|
|
2829
|
+
values.set(key, name);
|
|
2830
|
+
definitions[name] = {};
|
|
2831
|
+
const self = ref(name);
|
|
2832
|
+
const types = possibleValueTypes(schema, resolve);
|
|
2833
|
+
if (types.size === 0) {
|
|
2834
|
+
definitions[name] = { allOf: [literal(schema)] };
|
|
2835
|
+
return self;
|
|
2836
|
+
}
|
|
2837
|
+
const value = () => sequence ? author(schema) : self;
|
|
2838
|
+
const branch = () => sequence ? { anyOf: [self, { type: "array", items: self }] } : self;
|
|
2839
|
+
const item = () => sequence ? value() : author(arrayItemSchema(schema, resolve));
|
|
2840
|
+
const specs = {};
|
|
2841
|
+
for (const directive of directiveNames) {
|
|
2842
|
+
const result = BLOCK_DIRECTIVES[directive].result;
|
|
2843
|
+
if (result !== "dynamic" && !types.has(result) && !(sequence && result === "array"))
|
|
2844
|
+
continue;
|
|
2845
|
+
switch (directive) {
|
|
2846
|
+
case "$slot":
|
|
2847
|
+
case "$item":
|
|
2848
|
+
case "$theme":
|
|
2849
|
+
case "$context":
|
|
2850
|
+
specs[directive] = object(
|
|
2851
|
+
{
|
|
2852
|
+
[directive]: pointer(references[directive]),
|
|
2853
|
+
default: describe(
|
|
2854
|
+
value(),
|
|
2855
|
+
"Fallback value or binding used only when the referenced value is missing. Null, false and empty values do not trigger it."
|
|
2856
|
+
),
|
|
2857
|
+
...directive === "$slot" || directive === "$item" ? {
|
|
2858
|
+
props: {
|
|
2859
|
+
type: "object",
|
|
2860
|
+
description: "Component props merged beneath a component-slot value. Put placement (x, y, w, h, grid) and styling defaults here; the slot content may override styling but never placement."
|
|
2861
|
+
}
|
|
2862
|
+
} : {}
|
|
2863
|
+
},
|
|
2864
|
+
[directive]
|
|
2865
|
+
);
|
|
2866
|
+
break;
|
|
2867
|
+
case "$if":
|
|
2868
|
+
specs[directive] = object(
|
|
2869
|
+
{
|
|
2870
|
+
$if: pointer(
|
|
2871
|
+
"Test a slot by JSON Pointer, e.g. /subtitle. Missing, null, false, empty text and empty arrays select else; zero selects then."
|
|
2872
|
+
),
|
|
2873
|
+
then: describe(
|
|
2874
|
+
branch(),
|
|
2875
|
+
"Value or components to emit when the slot tested by $if is present."
|
|
2876
|
+
),
|
|
2877
|
+
else: describe(
|
|
2878
|
+
branch(),
|
|
2879
|
+
"Value or components to emit otherwise. Omit to produce no output."
|
|
2880
|
+
)
|
|
2881
|
+
},
|
|
2882
|
+
["$if", "then"]
|
|
2883
|
+
);
|
|
2884
|
+
break;
|
|
2885
|
+
case "$each":
|
|
2886
|
+
specs[directive] = object(
|
|
2887
|
+
{
|
|
2888
|
+
$each: pointer(
|
|
2889
|
+
"Repeat template for each entry in an array slot, e.g. /items. Read the current entry with $item."
|
|
2890
|
+
),
|
|
2891
|
+
template: describe(
|
|
2892
|
+
item(),
|
|
2893
|
+
"One template evaluated per array entry. Use $item for the current entry and a group for multiple components."
|
|
2894
|
+
)
|
|
2895
|
+
},
|
|
2896
|
+
["$each", "template"]
|
|
2897
|
+
);
|
|
2898
|
+
break;
|
|
2899
|
+
case "$count":
|
|
2900
|
+
specs[directive] = object(
|
|
2901
|
+
{
|
|
2902
|
+
$count: pointer(
|
|
2903
|
+
"Return the number of entries in an array slot, e.g. /items."
|
|
2904
|
+
)
|
|
2905
|
+
},
|
|
2906
|
+
["$count"]
|
|
2907
|
+
);
|
|
2908
|
+
break;
|
|
2909
|
+
case "$join":
|
|
2910
|
+
specs[directive] = object(
|
|
2911
|
+
{
|
|
2912
|
+
$join: {
|
|
2913
|
+
type: "array",
|
|
2914
|
+
items: author({}),
|
|
2915
|
+
description: "Evaluate these values or bindings and join them as text. Empty values are skipped unless keepEmpty is true."
|
|
2916
|
+
},
|
|
2917
|
+
separator: {
|
|
2918
|
+
type: "string",
|
|
2919
|
+
description: "Text inserted between joined values. Defaults to an empty string."
|
|
2920
|
+
},
|
|
2921
|
+
keepEmpty: {
|
|
2922
|
+
type: "boolean",
|
|
2923
|
+
description: "Keep missing, null, false, empty text and empty arrays in the join. Defaults to false."
|
|
2924
|
+
}
|
|
2925
|
+
},
|
|
2926
|
+
["$join"]
|
|
2927
|
+
);
|
|
2928
|
+
break;
|
|
2929
|
+
case "$measure":
|
|
2930
|
+
specs[directive] = object(
|
|
2931
|
+
{
|
|
2932
|
+
$measure: {
|
|
2933
|
+
enum: ["width", "height"],
|
|
2934
|
+
description: measure.axis
|
|
2935
|
+
},
|
|
2936
|
+
fraction: {
|
|
2937
|
+
type: "number",
|
|
2938
|
+
minimum: 0,
|
|
2939
|
+
maximum: 1,
|
|
2940
|
+
description: "Fraction of the measured dimension, from 0 to 1. Defaults to 1."
|
|
2941
|
+
},
|
|
2942
|
+
unit: {
|
|
2943
|
+
enum: ["pt", "twip", "in"],
|
|
2944
|
+
description: measure.unit
|
|
2945
|
+
}
|
|
2946
|
+
},
|
|
2947
|
+
["$measure"]
|
|
2948
|
+
);
|
|
2949
|
+
break;
|
|
2950
|
+
default: {
|
|
2951
|
+
const exhaustive = directive;
|
|
2952
|
+
throw new Error(
|
|
2953
|
+
`Missing authoring schema for directive ${exhaustive}`
|
|
2954
|
+
);
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
definitions[name] = {
|
|
2959
|
+
allOf: [
|
|
2960
|
+
{
|
|
2961
|
+
if: anyDirective,
|
|
2962
|
+
then: {
|
|
2963
|
+
allOf: directiveNames.map((directive) => ({
|
|
2964
|
+
if: presence[directive],
|
|
2965
|
+
then: specs[directive] ? share({
|
|
2966
|
+
...specs[directive],
|
|
2967
|
+
properties: Object.fromEntries(
|
|
2968
|
+
Object.entries(specs[directive].properties).map(
|
|
2969
|
+
([key2, property]) => [key2, share(property)]
|
|
2970
|
+
)
|
|
2971
|
+
)
|
|
2972
|
+
}) : false
|
|
2973
|
+
}))
|
|
2974
|
+
},
|
|
2975
|
+
else: literal(schema)
|
|
2976
|
+
},
|
|
2977
|
+
{
|
|
2978
|
+
// Keep starters while the first key is empty or a partial directive
|
|
2979
|
+
// ("$", "$sl", ...). Any ordinary or completed key ends this phase.
|
|
2980
|
+
// Prefixes come from the evaluator's directive registry.
|
|
2981
|
+
if: starterObject,
|
|
2982
|
+
then: {
|
|
2983
|
+
properties: Object.fromEntries(
|
|
2984
|
+
Object.entries(specs).map(([key2, spec]) => [
|
|
2985
|
+
key2,
|
|
2986
|
+
share(spec.properties[key2])
|
|
2987
|
+
])
|
|
2988
|
+
)
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
]
|
|
2992
|
+
};
|
|
2993
|
+
return { ...self, ...annotations };
|
|
2994
|
+
}
|
|
2995
|
+
definitions[bodyName] = author(componentRef, true);
|
|
2996
|
+
return ref(bodyName);
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
// src/blocks/compose.ts
|
|
3000
|
+
async function composeBlocksWithPlugins(evaluator, document, options) {
|
|
3001
|
+
const preserve = options.preserve ?? /* @__PURE__ */ new Set();
|
|
3002
|
+
let visited = 0;
|
|
3003
|
+
const walk = async (value, path, depth) => {
|
|
3004
|
+
if (depth > 64 || ++visited > 1e5)
|
|
3005
|
+
throw new BlockEvaluationError([
|
|
3006
|
+
{
|
|
3007
|
+
path: toAuthoredBlockPointer(evaluator.sourceMap, path),
|
|
3008
|
+
code: "block_expansion_limit",
|
|
3009
|
+
message: "Combined plugin/block expansion exceeds depth/node limits (64/100000)."
|
|
3010
|
+
}
|
|
3011
|
+
]);
|
|
3012
|
+
if (Array.isArray(value)) {
|
|
3013
|
+
const children = [];
|
|
3014
|
+
for (let i = 0; i < value.length; i++)
|
|
3015
|
+
children.push(await walk(value[i], `${path}/${i}`, depth + 1));
|
|
3016
|
+
return {
|
|
3017
|
+
standard: children.map((c) => c.standard),
|
|
3018
|
+
preserved: children.map((c) => c.preserved)
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3021
|
+
if (!isBlockRecord(value) || value.enabled === false)
|
|
3022
|
+
return { standard: value, preserved: value };
|
|
3023
|
+
if (value.name === "block")
|
|
3024
|
+
return walk(evaluator.expand(value, path, depth), path, depth + 1);
|
|
3025
|
+
const standard = { ...value };
|
|
3026
|
+
const kept = { ...value };
|
|
3027
|
+
for (const [key, item] of Object.entries(value)) {
|
|
3028
|
+
if (path === "/props" && key === "blocks") continue;
|
|
3029
|
+
const processed = await walk(item, `${path}/${key}`, depth + 1);
|
|
3030
|
+
Object.defineProperty(standard, key, {
|
|
3031
|
+
value: processed.standard,
|
|
3032
|
+
enumerable: true,
|
|
3033
|
+
configurable: true,
|
|
3034
|
+
writable: true
|
|
3035
|
+
});
|
|
3036
|
+
Object.defineProperty(kept, key, {
|
|
3037
|
+
value: processed.preserved,
|
|
3038
|
+
enumerable: true,
|
|
3039
|
+
configurable: true,
|
|
3040
|
+
writable: true
|
|
3041
|
+
});
|
|
3042
|
+
}
|
|
3043
|
+
if (typeof value.name === "string" && options.plugins.has(value.name)) {
|
|
3044
|
+
const source = toAuthoredBlockPointer(evaluator.sourceMap, path);
|
|
3045
|
+
const emitted = await options.render(standard, source);
|
|
3046
|
+
evaluator.sourceMap[`${path}/children`] = source;
|
|
3047
|
+
const processed = await walk(emitted, `${path}/children`, depth + 1);
|
|
3048
|
+
return {
|
|
3049
|
+
standard: { name: "group", children: processed.standard },
|
|
3050
|
+
preserved: preserve.has(value.name) ? value : { name: "group", children: processed.preserved }
|
|
3051
|
+
};
|
|
3052
|
+
}
|
|
3053
|
+
return { standard, preserved: kept };
|
|
3054
|
+
};
|
|
3055
|
+
return walk(document, "", 0);
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
// src/blocks/editor.ts
|
|
3059
|
+
import { Value as Value3 } from "@sinclair/typebox/value";
|
|
3060
|
+
var clone = (value) => JSON.parse(JSON.stringify(value));
|
|
3061
|
+
function range(minimum, maximum, unit) {
|
|
3062
|
+
if (minimum !== void 0 && maximum !== void 0)
|
|
3063
|
+
return minimum === maximum ? `${minimum} ${unit}` : `${minimum}\u2013${maximum} ${unit}`;
|
|
3064
|
+
if (minimum !== void 0) return `at least ${minimum} ${unit}`;
|
|
3065
|
+
if (maximum !== void 0) return `at most ${maximum} ${unit}`;
|
|
3066
|
+
return void 0;
|
|
3067
|
+
}
|
|
3068
|
+
function blockSlotFacts(slot) {
|
|
3069
|
+
const facts = [];
|
|
3070
|
+
if (slot.required && slot.default === void 0) facts.push("Required");
|
|
3071
|
+
if (slot.default !== void 0)
|
|
3072
|
+
facts.push(`Default: \`${JSON.stringify(slot.default)}\``);
|
|
3073
|
+
if (slot.type === "component")
|
|
3074
|
+
facts.push("A component; placement stays in the definition");
|
|
3075
|
+
if (slot.enum)
|
|
3076
|
+
facts.push(
|
|
3077
|
+
`One of ${slot.enum.map((value) => `\`${JSON.stringify(value)}\``).join(", ")}`
|
|
3078
|
+
);
|
|
3079
|
+
const length = range(slot.minLength, slot.maxLength, "characters");
|
|
3080
|
+
if (length) facts.push(length);
|
|
3081
|
+
if (slot.maxWords !== void 0) facts.push(`at most ${slot.maxWords} words`);
|
|
3082
|
+
if (slot.oneLine) facts.push("one line");
|
|
3083
|
+
const bounds = range(slot.minimum, slot.maximum, "");
|
|
3084
|
+
if (bounds) facts.push(bounds.trim());
|
|
3085
|
+
const entries = range(slot.minItems, slot.maxItems, "entries");
|
|
3086
|
+
if (entries) facts.push(entries);
|
|
3087
|
+
if (slot.role) facts.push(`Role: ${slot.role}`);
|
|
3088
|
+
return facts;
|
|
3089
|
+
}
|
|
3090
|
+
function blockSlotMarkdown(slot) {
|
|
3091
|
+
return [slot.description, blockSlotFacts(slot).join(" \xB7 ")].filter(Boolean).join("\n\n");
|
|
3092
|
+
}
|
|
3093
|
+
function blockSlotEditorSchema(slot, componentRef) {
|
|
3094
|
+
let schema;
|
|
3095
|
+
if (slot.type === "component") {
|
|
3096
|
+
schema = componentRef ? {
|
|
3097
|
+
allOf: [
|
|
3098
|
+
componentRef,
|
|
3099
|
+
{
|
|
3100
|
+
properties: {
|
|
3101
|
+
props: {
|
|
3102
|
+
propertyNames: {
|
|
3103
|
+
not: { enum: [...BLOCK_SLOT_PLACEMENT_PROPS] },
|
|
3104
|
+
errorMessage: "Block placement belongs in the definition, not in a component slot."
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
]
|
|
3110
|
+
} : {
|
|
3111
|
+
type: "object",
|
|
3112
|
+
properties: { name: { type: "string" } },
|
|
3113
|
+
required: ["name"]
|
|
3114
|
+
};
|
|
3115
|
+
} else {
|
|
3116
|
+
const { oneLine, properties, items, ...rest } = slot;
|
|
3117
|
+
for (const key of ["role", "required", "maxWords", "description"])
|
|
3118
|
+
delete rest[key];
|
|
3119
|
+
schema = { ...rest };
|
|
3120
|
+
if (oneLine) schema.pattern = "^[^\\r\\n]*$";
|
|
3121
|
+
if (items) schema.items = blockSlotEditorSchema(items, componentRef);
|
|
3122
|
+
if (properties) {
|
|
3123
|
+
schema.properties = Object.fromEntries(
|
|
3124
|
+
Object.entries(properties).map(([key, value]) => [
|
|
3125
|
+
key,
|
|
3126
|
+
blockSlotEditorSchema(value, componentRef)
|
|
3127
|
+
])
|
|
3128
|
+
);
|
|
3129
|
+
schema.required = Object.entries(properties).filter(([, value]) => value.required && value.default === void 0).map(([key]) => key);
|
|
3130
|
+
schema.additionalProperties = false;
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
if (slot.description) schema.description = slot.description;
|
|
3134
|
+
const markdown = blockSlotMarkdown(slot);
|
|
3135
|
+
if (markdown) schema.markdownDescription = markdown;
|
|
3136
|
+
return schema;
|
|
3137
|
+
}
|
|
3138
|
+
function blockSlotsEditorSchema(definition, componentRef) {
|
|
3139
|
+
return {
|
|
3140
|
+
type: "object",
|
|
3141
|
+
additionalProperties: false,
|
|
3142
|
+
description: "Input values keyed by the slot names declared in the referenced block definition.",
|
|
3143
|
+
properties: Object.fromEntries(
|
|
3144
|
+
Object.entries(definition.slots).map(([key, slot]) => [
|
|
3145
|
+
key,
|
|
3146
|
+
blockSlotEditorSchema(slot, componentRef)
|
|
3147
|
+
])
|
|
3148
|
+
),
|
|
3149
|
+
required: Object.entries(definition.slots).filter(([, slot]) => slot.required && slot.default === void 0).map(([key]) => key)
|
|
3150
|
+
};
|
|
3151
|
+
}
|
|
3152
|
+
function blockInvocationPropsSchema(definitions, componentRef) {
|
|
3153
|
+
const names = Object.keys(definitions);
|
|
3154
|
+
const schema = {
|
|
3155
|
+
type: "object",
|
|
3156
|
+
additionalProperties: false,
|
|
3157
|
+
required: ["ref"],
|
|
3158
|
+
properties: {
|
|
3159
|
+
ref: {
|
|
3160
|
+
type: "string",
|
|
3161
|
+
minLength: 1,
|
|
3162
|
+
description: "Name in this document\u2019s props.blocks.",
|
|
3163
|
+
...names.length && {
|
|
3164
|
+
anyOf: names.map((name) => ({
|
|
3165
|
+
const: name,
|
|
3166
|
+
type: "string",
|
|
3167
|
+
description: definitions[name].description ?? `Block "${name}", defined in this document.`
|
|
3168
|
+
}))
|
|
3169
|
+
}
|
|
3170
|
+
},
|
|
3171
|
+
slots: {
|
|
3172
|
+
type: "object",
|
|
3173
|
+
description: "Input values keyed by the slot names declared in the referenced block definition."
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
};
|
|
3177
|
+
if (names.length)
|
|
3178
|
+
schema.allOf = names.map((name) => ({
|
|
3179
|
+
if: { properties: { ref: { const: name } }, required: ["ref"] },
|
|
3180
|
+
then: {
|
|
3181
|
+
properties: {
|
|
3182
|
+
slots: blockSlotsEditorSchema(definitions[name], componentRef)
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
}));
|
|
3186
|
+
return schema;
|
|
3187
|
+
}
|
|
3188
|
+
function applyDocumentBlocksToSchema(schema, definitions, targets) {
|
|
3189
|
+
for (const target of targets) {
|
|
3190
|
+
const definition = schema.definitions?.[target.name];
|
|
3191
|
+
if (!definition) continue;
|
|
3192
|
+
const props = blockInvocationPropsSchema(definitions, target.componentRef);
|
|
3193
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3194
|
+
const walk = (node) => {
|
|
3195
|
+
if (!node || typeof node !== "object" || seen.has(node)) return;
|
|
3196
|
+
seen.add(node);
|
|
3197
|
+
if (Array.isArray(node)) {
|
|
3198
|
+
node.forEach(walk);
|
|
3199
|
+
return;
|
|
3200
|
+
}
|
|
3201
|
+
const value = node;
|
|
3202
|
+
if (value.properties?.name?.const === "block" && value.properties.props) {
|
|
3203
|
+
value.properties.props = clone(props);
|
|
3204
|
+
return;
|
|
3205
|
+
}
|
|
3206
|
+
for (const [key, child] of Object.entries(value))
|
|
3207
|
+
if (key !== "$ref") walk(child);
|
|
3208
|
+
};
|
|
3209
|
+
walk(definition);
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
function invocations(node, visit) {
|
|
3213
|
+
if (Array.isArray(node)) {
|
|
3214
|
+
node.forEach((item) => invocations(item, visit));
|
|
3215
|
+
return;
|
|
3216
|
+
}
|
|
3217
|
+
if (!isBlockRecord(node)) return;
|
|
3218
|
+
if (node.name === "block" && isBlockRecord(node.props) && typeof node.props.ref === "string")
|
|
3219
|
+
visit(node.props.ref, node);
|
|
3220
|
+
for (const value of Object.values(node)) invocations(value, visit);
|
|
3221
|
+
}
|
|
3222
|
+
function blockDependencies(definitions, name) {
|
|
3223
|
+
const order = [];
|
|
3224
|
+
const seen = /* @__PURE__ */ new Set([name]);
|
|
3225
|
+
const walk = (current) => {
|
|
3226
|
+
const definition = Object.prototype.hasOwnProperty.call(
|
|
3227
|
+
definitions,
|
|
3228
|
+
current
|
|
3229
|
+
) ? definitions[current] : void 0;
|
|
3230
|
+
if (!definition) return;
|
|
3231
|
+
invocations(
|
|
3232
|
+
[definition.body, definition.section, definition.slide],
|
|
3233
|
+
(ref) => {
|
|
3234
|
+
if (seen.has(ref)) return;
|
|
3235
|
+
seen.add(ref);
|
|
3236
|
+
if (!Object.prototype.hasOwnProperty.call(definitions, ref)) return;
|
|
3237
|
+
walk(ref);
|
|
3238
|
+
order.push(ref);
|
|
3239
|
+
}
|
|
3240
|
+
);
|
|
3241
|
+
};
|
|
3242
|
+
walk(name);
|
|
3243
|
+
return order;
|
|
3244
|
+
}
|
|
3245
|
+
function exampleValue(slot, name, format) {
|
|
3246
|
+
if (slot.default !== void 0) return clone(slot.default);
|
|
3247
|
+
if (slot.enum?.length) return slot.enum[0];
|
|
3248
|
+
switch (slot.type) {
|
|
3249
|
+
case "string":
|
|
3250
|
+
return name;
|
|
3251
|
+
case "number":
|
|
3252
|
+
case "integer": {
|
|
3253
|
+
const minimum = slot.minimum ?? 0;
|
|
3254
|
+
return slot.maximum !== void 0 && slot.maximum < minimum ? slot.maximum : minimum;
|
|
3255
|
+
}
|
|
3256
|
+
case "boolean":
|
|
3257
|
+
return true;
|
|
3258
|
+
case "array": {
|
|
3259
|
+
const count = Math.min(
|
|
3260
|
+
Math.max(3, slot.minItems ?? 0),
|
|
3261
|
+
slot.maxItems ?? Number.POSITIVE_INFINITY
|
|
3262
|
+
);
|
|
3263
|
+
const item = slot.items ?? { type: "string" };
|
|
3264
|
+
return Array.from(
|
|
3265
|
+
{ length: count },
|
|
3266
|
+
(_, index) => exampleValue(item, `${name} ${index + 1}`, format)
|
|
3267
|
+
);
|
|
3268
|
+
}
|
|
3269
|
+
case "object":
|
|
3270
|
+
return exampleSlots(slot.properties ?? {}, format);
|
|
3271
|
+
case "component":
|
|
3272
|
+
return format === "docx" ? { name: "paragraph", props: { text: name } } : { name: "text", props: { text: name } };
|
|
3273
|
+
default:
|
|
3274
|
+
return name;
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
function exampleSlots(slots, format) {
|
|
3278
|
+
return Object.fromEntries(
|
|
3279
|
+
Object.entries(slots).filter(
|
|
3280
|
+
([, slot]) => slot.required && slot.default === void 0 || slot.role
|
|
3281
|
+
).map(([key, slot]) => [key, exampleValue(slot, key, format)])
|
|
3282
|
+
);
|
|
3283
|
+
}
|
|
3284
|
+
function blockInvocationExample(name, definition, options) {
|
|
3285
|
+
let found;
|
|
3286
|
+
if (isBlockRecord(options.document)) {
|
|
3287
|
+
const authored = Object.fromEntries(
|
|
3288
|
+
Object.entries(options.document).filter(([key]) => key !== "props")
|
|
3289
|
+
);
|
|
3290
|
+
invocations(authored, (ref, invocation) => {
|
|
3291
|
+
if (found || ref !== name) return;
|
|
3292
|
+
const props = invocation.props;
|
|
3293
|
+
found = {
|
|
3294
|
+
name: "block",
|
|
3295
|
+
props: {
|
|
3296
|
+
ref,
|
|
3297
|
+
...isBlockRecord(props.slots) && { slots: clone(props.slots) }
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
3300
|
+
});
|
|
3301
|
+
}
|
|
3302
|
+
return found ?? {
|
|
3303
|
+
name: "block",
|
|
3304
|
+
props: {
|
|
3305
|
+
ref: name,
|
|
3306
|
+
slots: exampleSlots(definition.slots, options.format)
|
|
3307
|
+
}
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
function blockReferencesFromDocument(document, source) {
|
|
3311
|
+
const definitions = readBlockDefinitions(document);
|
|
3312
|
+
if (!Value3.Check(BlockDefinitionsSchema, definitions) || validateBlockDefinitions(definitions, source.format).length > 0)
|
|
3313
|
+
return [];
|
|
3314
|
+
return Object.entries(definitions).map(([name, definition]) => ({
|
|
3315
|
+
name,
|
|
3316
|
+
format: source.format,
|
|
3317
|
+
template: source.template,
|
|
3318
|
+
definitionPointer: `/props/blocks/${blockPointerKey(name)}`,
|
|
3319
|
+
description: definition.description ?? "",
|
|
3320
|
+
definition,
|
|
3321
|
+
slotsSchema: blockSlotsJsonSchema(definition),
|
|
3322
|
+
example: blockInvocationExample(name, definition, {
|
|
3323
|
+
document,
|
|
3324
|
+
format: source.format
|
|
3325
|
+
}),
|
|
3326
|
+
dependencies: blockDependencies(definitions, name)
|
|
3327
|
+
}));
|
|
3328
|
+
}
|
|
3329
|
+
|
|
1374
3330
|
// src/utils/deepMerge.ts
|
|
1375
3331
|
function isObject(item) {
|
|
1376
3332
|
return item !== null && typeof item === "object" && !Array.isArray(item);
|
|
@@ -1396,6 +3352,12 @@ function mergeWithDefaults(userConfig, themeDefaults) {
|
|
|
1396
3352
|
return deepMerge(themeDefaults, userConfig);
|
|
1397
3353
|
}
|
|
1398
3354
|
export {
|
|
3355
|
+
BLOCK_SLOT_PLACEMENT_PROPS,
|
|
3356
|
+
BLOCK_SLOT_ROLES,
|
|
3357
|
+
BlockDefinitionsSchema,
|
|
3358
|
+
BlockEvaluationError,
|
|
3359
|
+
BlockInvocationPropsSchema,
|
|
3360
|
+
BlockSlotSchema,
|
|
1399
3361
|
CANVASES,
|
|
1400
3362
|
ChromeSchema,
|
|
1401
3363
|
ComponentValidationError,
|
|
@@ -1414,12 +3376,15 @@ export {
|
|
|
1414
3376
|
FontRegistryEntrySchema,
|
|
1415
3377
|
FontRegistrySchema,
|
|
1416
3378
|
FontSourceSchema,
|
|
3379
|
+
JsonBlockDefinitionSchema,
|
|
3380
|
+
JsonBlockEvaluator,
|
|
1417
3381
|
MAX_RASTERIZE_BATCH_SLIDES,
|
|
1418
3382
|
MAX_RASTERIZE_FONTS,
|
|
1419
3383
|
MAX_RASTERIZE_FONT_BYTES,
|
|
1420
3384
|
MAX_VISUAL_DPI,
|
|
1421
3385
|
MIN_VISUAL_DPI,
|
|
1422
3386
|
MotifSchema,
|
|
3387
|
+
POINTS_PER_PIXEL_96DPI,
|
|
1423
3388
|
POPULAR_GOOGLE_FONTS,
|
|
1424
3389
|
PaletteSchema,
|
|
1425
3390
|
RENDERER_DEPENDENCY_MISSING,
|
|
@@ -1436,30 +3401,53 @@ export {
|
|
|
1436
3401
|
UnknownPreservedComponentError,
|
|
1437
3402
|
UnsupportedRendererFeatureError,
|
|
1438
3403
|
WEIGHT_LABELS,
|
|
3404
|
+
applyDocumentBlocksToSchema,
|
|
1439
3405
|
applyExportMode,
|
|
1440
3406
|
applyFontSubstitution,
|
|
1441
3407
|
assertNever,
|
|
1442
3408
|
assertRendererSupports,
|
|
3409
|
+
blockDependencies,
|
|
3410
|
+
blockInvocationExample,
|
|
3411
|
+
blockInvocationPropsSchema,
|
|
3412
|
+
blockPointerKey,
|
|
3413
|
+
blockReferencesFromDocument,
|
|
3414
|
+
blockSlotBudgets,
|
|
3415
|
+
blockSlotEditorSchema,
|
|
3416
|
+
blockSlotFacts,
|
|
3417
|
+
blockSlotJsonSchema,
|
|
3418
|
+
blockSlotMarkdown,
|
|
3419
|
+
blockSlotRoles,
|
|
3420
|
+
blockSlotsEditorSchema,
|
|
3421
|
+
blockSlotsJsonSchema,
|
|
3422
|
+
blockValueAt,
|
|
3423
|
+
blockWordCount,
|
|
1443
3424
|
buildDefaultSubstitutionMap,
|
|
1444
3425
|
calculatePosition,
|
|
1445
3426
|
capsFormatting,
|
|
3427
|
+
chartFamilyResolver,
|
|
3428
|
+
chartFontFaceCss,
|
|
3429
|
+
chartPointsPerPixel,
|
|
1446
3430
|
clampVisualDpi,
|
|
1447
3431
|
clearComponentNamesCache,
|
|
1448
3432
|
collectFontNamesFromDocx,
|
|
1449
3433
|
collectFontNamesFromPptx,
|
|
1450
3434
|
compareSemver,
|
|
3435
|
+
composeBlocksWithPlugins,
|
|
1451
3436
|
convertToJsonSchema,
|
|
3437
|
+
createBlockAuthoringSchema,
|
|
1452
3438
|
createComponent,
|
|
1453
3439
|
createComponentSchema,
|
|
1454
3440
|
createComponentSchemaObject,
|
|
1455
3441
|
createErrorConfig,
|
|
1456
3442
|
createJsonParseError,
|
|
1457
3443
|
createVersion,
|
|
3444
|
+
cssFontFamily,
|
|
1458
3445
|
defaultSubstituteFor,
|
|
1459
3446
|
designCanvas,
|
|
1460
3447
|
designColors,
|
|
1461
3448
|
detectFontFormat,
|
|
1462
3449
|
diagnoseUnsupportedFeatures,
|
|
3450
|
+
documentBlockMetadata,
|
|
1463
3451
|
documentFontRegistry,
|
|
1464
3452
|
exportSchemaToFile,
|
|
1465
3453
|
extractStandardComponentNames,
|
|
@@ -1474,6 +3462,7 @@ export {
|
|
|
1474
3462
|
getValidationSummary,
|
|
1475
3463
|
groupErrorsByPath,
|
|
1476
3464
|
isAllowedFontUrl,
|
|
3465
|
+
isBlockRecord,
|
|
1477
3466
|
isLiteralSchema,
|
|
1478
3467
|
isObjectSchema,
|
|
1479
3468
|
isSafeFont,
|
|
@@ -1485,8 +3474,10 @@ export {
|
|
|
1485
3474
|
mergeWithDefaults,
|
|
1486
3475
|
parseSemver,
|
|
1487
3476
|
partitionDiagnostics,
|
|
3477
|
+
readBlockDefinitions,
|
|
1488
3478
|
rendererError,
|
|
1489
3479
|
rendererWarning,
|
|
3480
|
+
resolveBlockSlot,
|
|
1490
3481
|
resolveComponentVersion,
|
|
1491
3482
|
resolveDesignColor,
|
|
1492
3483
|
resolveTypeRoles,
|
|
@@ -1494,11 +3485,16 @@ export {
|
|
|
1494
3485
|
rewriteFontFamilyName,
|
|
1495
3486
|
synthesizeFamilyName,
|
|
1496
3487
|
themeFontRegistry,
|
|
3488
|
+
toAuthoredBlockPointer,
|
|
1497
3489
|
transformValueError,
|
|
1498
3490
|
transformValueErrors,
|
|
1499
3491
|
unionBranches,
|
|
3492
|
+
validateBlockDefinitions,
|
|
3493
|
+
validateBlockInvocations,
|
|
1500
3494
|
validateCustomComponentProps,
|
|
1501
3495
|
validateDesignColors,
|
|
1502
|
-
validateFontReferences
|
|
3496
|
+
validateFontReferences,
|
|
3497
|
+
withChartFontFaceCss,
|
|
3498
|
+
withChartTypography
|
|
1503
3499
|
};
|
|
1504
3500
|
//# sourceMappingURL=index.js.map
|