@stll/folio-core 0.41.0 → 0.43.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/README.md +16 -0
- package/dist/compat/eigenpal.d.ts +3 -3
- package/dist/controller/layoutPipeline.d.ts +2 -1
- package/dist/controller/layoutPipeline.js +5 -2
- package/dist/controller/layoutSession.d.ts +2 -1
- package/dist/controller/layoutSession.js +1 -0
- package/dist/docx/blockContentParser.js +2 -1
- package/dist/docx/commentIdNormalization.d.ts +8 -0
- package/dist/docx/commentIdNormalization.js +16 -0
- package/dist/docx/defaultParagraphStyle.d.ts +22 -0
- package/dist/docx/defaultParagraphStyle.js +32 -0
- package/dist/docx/footnoteParser.js +2 -0
- package/dist/docx/headerFooterRefParser.d.ts +12 -1
- package/dist/docx/headerFooterRefParser.js +9 -2
- package/dist/docx/numberingParser.js +2 -1
- package/dist/docx/numberingReference.d.ts +21 -0
- package/dist/docx/numberingReference.js +21 -0
- package/dist/docx/numberingReferenceNormalization.d.ts +11 -2
- package/dist/docx/numberingReferenceNormalization.js +32 -9
- package/dist/docx/paragraphParser.js +3 -2
- package/dist/docx/parser.js +10 -2
- package/dist/docx/rezip.d.ts +20 -3
- package/dist/docx/rezip.js +46 -18
- package/dist/docx/server/createBilingualDocument.js +5 -4
- package/dist/docx/styleParser.js +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/internal/pageBreakRunSourceDescendantIndex.d.ts +2 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +7 -5
- package/dist/layout-bridge/convert/templatePreviewFlow.d.ts +19 -11
- package/dist/layout-bridge/convert/templatePreviewFlow.js +103 -38
- package/dist/layout-bridge/convert/toFlowBlocks.js +12 -3
- package/dist/prosemirror/commands/pageBreak.js +12 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +2 -1
- package/dist/prosemirror/conversion/toProseDoc.js +27 -12
- package/dist/prosemirror/extensions/features/ListExtension.js +42 -4
- package/dist/prosemirror/listMarker.js +2 -1
- package/dist/prosemirror/numberedRefFields.js +2 -1
- package/dist/prosemirror/pageBreakRunProjection.d.ts +11 -3
- package/dist/prosemirror/pageBreakRunProjection.js +16 -8
- package/dist/prosemirror/plugins/templatePreviewValues.d.ts +42 -1
- package/dist/prosemirror/plugins/templatePreviewValues.js +217 -14
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -1
- package/dist/prosemirror/styles/styleResolver.js +3 -6
- package/dist/prosemirror/utils/visualLineNavigation.d.ts +22 -2
- package/dist/prosemirror/utils/visualLineNavigation.js +80 -63
- package/dist/server.d.ts +2 -2
- package/dist/style-engine/styleEngine.d.ts +1 -1
- package/dist/style-sets/extract.js +57 -11
- package/package.json +2 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveColorValueToHex } from "../../docx/drawingUtils.js";
|
|
2
|
+
import { isNumberingReference } from "../../docx/numberingReference.js";
|
|
2
3
|
import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, createProseParagraphWithPropertySource, getDocumentParagraphPropertySourceContract, recreateProseNodeWithParagraphPropertySource, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
|
|
3
4
|
import { buildPageBreakRunSourceDescendantIndex } from "../../internal/pageBreakRunSourceDescendantIndex.js";
|
|
4
5
|
import { createStyleEngine } from "../../style-engine/styleEngine.js";
|
|
@@ -453,7 +454,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
|
|
|
453
454
|
if (formatting?.spaceBefore === void 0 && tableParagraphOverlay?.spaceBefore === void 0 && paragraphStyle?.pPr?.spaceBefore === void 0 && docDefaultSpacing?.spaceBefore !== void 0) spacingFromDocDefaults.before = true;
|
|
454
455
|
if (formatting?.spaceAfter === void 0 && tableParagraphOverlay?.spaceAfter === void 0 && paragraphStyle?.pPr?.spaceAfter === void 0 && docDefaultSpacing?.spaceAfter !== void 0) spacingFromDocDefaults.after = true;
|
|
455
456
|
if (spacingFromDocDefaults.before || spacingFromDocDefaults.after) attrs.spacingFromDocDefaults = spacingFromDocDefaults;
|
|
456
|
-
const effectiveIndent = mergeParagraphFormatting(formatting?.numPr?.numId === 0 && stylePpr?.numPr
|
|
457
|
+
const effectiveIndent = mergeParagraphFormatting(formatting?.numPr?.numId === 0 && isNumberingReference(stylePpr?.numPr?.numId) ? void 0 : stylePpr, formatting);
|
|
457
458
|
set("indentLeft", effectiveIndent?.indentLeft);
|
|
458
459
|
set("indentRight", formatting?.indentRight ?? stylePpr?.indentRight);
|
|
459
460
|
set("indentFirstLine", effectiveIndent?.indentFirstLine);
|
|
@@ -473,7 +474,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
|
|
|
473
474
|
set("outlineLevel", formatting?.outlineLevel ?? stylePpr?.outlineLevel);
|
|
474
475
|
set("direction", directionFromBidi(formatting?.bidi ?? stylePpr?.bidi));
|
|
475
476
|
set("defaultTextFormatting", resolveParagraphDefaultTextFormatting(styleId, formatting, styleResolver, { includeParagraphMarkRunProperties: tableOfContentsLevel === void 0 && (styleId === void 0 || paragraph.content.length === 0) }));
|
|
476
|
-
if (stylePpr?.numPr && formatting?.numPr?.numId === void 0 && stylePpr.numPr.numId
|
|
477
|
+
if (stylePpr?.numPr && formatting?.numPr?.numId === void 0 && isNumberingReference(stylePpr.numPr.numId)) {
|
|
477
478
|
attrs.numPr = {
|
|
478
479
|
...stylePpr.numPr,
|
|
479
480
|
...formatting?.numPr
|
|
@@ -1216,19 +1217,27 @@ const PAGE_BREAK_PARAGRAPH_OWNERS = {
|
|
|
1216
1217
|
};
|
|
1217
1218
|
function assertSourceContainerHasNoPageBreakRun(content, owner, sourceDescendants) {
|
|
1218
1219
|
if (!sourceDescendants.containsPageBreakRun(content)) return;
|
|
1219
|
-
if (owner === "table-cell" && hasSingleLeadingTableCellPageBreak(content)) return;
|
|
1220
|
+
if (owner === "table-cell" && hasSingleLeadingTableCellPageBreak(content, sourceDescendants)) return;
|
|
1220
1221
|
throw new UnsupportedDocxToProseMirrorConversionError({
|
|
1221
1222
|
message: `${PAGE_BREAK_CONTAINER_DESCRIPTIONS[owner]} cannot be represented in the editor model`,
|
|
1222
1223
|
owner,
|
|
1223
1224
|
contentType: "break"
|
|
1224
1225
|
});
|
|
1225
1226
|
}
|
|
1226
|
-
/**
|
|
1227
|
-
*
|
|
1228
|
-
*
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1227
|
+
/**
|
|
1228
|
+
* A page break opening a cell advances the whole row during pagination.
|
|
1229
|
+
*
|
|
1230
|
+
* Word writes this shape routinely and the row's own `breakBefore` projects it
|
|
1231
|
+
* losslessly, so what follows the break inside the cell rides along: the row
|
|
1232
|
+
* moves as a unit. The break has to be the cell's only one and has to open its
|
|
1233
|
+
* first paragraph; an interior break still requires table-fragment ownership,
|
|
1234
|
+
* which cell-local flow cannot model. `convertTableCell` in the layout bridge
|
|
1235
|
+
* decides the same question over the projected runs.
|
|
1236
|
+
*/
|
|
1237
|
+
function hasSingleLeadingTableCellPageBreak(content, sourceDescendants) {
|
|
1238
|
+
const first = content.at(0);
|
|
1239
|
+
if (first?.type !== "paragraph" || !hasSingleLeadingParagraphPageBreak(first)) return false;
|
|
1240
|
+
return !content.slice(1).some((block) => sourceDescendants.blockContainsPageBreakRun(block));
|
|
1232
1241
|
}
|
|
1233
1242
|
const scanLeadingPageBreakRun = (run, scan) => {
|
|
1234
1243
|
for (const content of run.content) {
|
|
@@ -1237,6 +1246,7 @@ const scanLeadingPageBreakRun = (run, scan) => {
|
|
|
1237
1246
|
scan.pageBreaks += 1;
|
|
1238
1247
|
continue;
|
|
1239
1248
|
}
|
|
1249
|
+
if (content.type === "shape" && content.shape.textBody && scan.pageBreaks > 0) scan.textBoxShapeAfterBreak = true;
|
|
1240
1250
|
if (scan.pageBreaks === 0) scan.contentBeforeBreak = true;
|
|
1241
1251
|
}
|
|
1242
1252
|
};
|
|
@@ -1276,12 +1286,17 @@ const scanLeadingPageBreakContent = (content, scan) => {
|
|
|
1276
1286
|
default: return content;
|
|
1277
1287
|
}
|
|
1278
1288
|
};
|
|
1279
|
-
const
|
|
1289
|
+
const scanParagraphPageBreaks = (paragraph) => {
|
|
1280
1290
|
const scan = {
|
|
1281
1291
|
contentBeforeBreak: false,
|
|
1282
|
-
pageBreaks: 0
|
|
1292
|
+
pageBreaks: 0,
|
|
1293
|
+
textBoxShapeAfterBreak: false
|
|
1283
1294
|
};
|
|
1284
1295
|
for (const content of paragraph.content) scanLeadingPageBreakContent(content, scan);
|
|
1296
|
+
return scan;
|
|
1297
|
+
};
|
|
1298
|
+
const hasSingleLeadingParagraphPageBreak = (paragraph) => {
|
|
1299
|
+
const scan = scanParagraphPageBreaks(paragraph);
|
|
1285
1300
|
return !scan.contentBeforeBreak && scan.pageBreaks === 1;
|
|
1286
1301
|
};
|
|
1287
1302
|
function assertParagraphPageBreakCanBeProjected({ paragraph, attrs, effectiveFrame, sourceDescendants }) {
|
|
@@ -1290,7 +1305,7 @@ function assertParagraphPageBreakCanBeProjected({ paragraph, attrs, effectiveFra
|
|
|
1290
1305
|
const disposition = pageBreakRunParagraphProjectionDispositionForFeatures({
|
|
1291
1306
|
attrs,
|
|
1292
1307
|
effectiveFrame,
|
|
1293
|
-
|
|
1308
|
+
textBoxAnchorAfterPageBreak: sourceFeatures.hasTextBoxShape && !sourceFeatures.pageBreakSharesTextBoxShape && scanParagraphPageBreaks(paragraph).textBoxShapeAfterBreak
|
|
1294
1309
|
});
|
|
1295
1310
|
if (disposition.status === "supported") return;
|
|
1296
1311
|
if (disposition.reason !== "textBoxAnchor" && hasSingleLeadingParagraphPageBreak(paragraph)) return;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isNumberingReference } from "../../../docx/numberingReference.js";
|
|
1
2
|
import { expectParagraphAttrs } from "../../attrs/index.js";
|
|
2
3
|
import { PPR_CHANGE_SCOPED_ATTR_KEYS, hasSerializableParagraphPropertyChange } from "../../commands/propertyChangeScope.js";
|
|
3
4
|
import { CLEARED_LIST_RENDERING_ATTRS } from "../../listMarker.js";
|
|
@@ -9,6 +10,7 @@ import { createExtension } from "../create.js";
|
|
|
9
10
|
import { goToNextCell, goToPrevCell } from "../nodes/TableExtension.js";
|
|
10
11
|
import { Priority } from "../types.js";
|
|
11
12
|
import { panic } from "better-result";
|
|
13
|
+
import { InputRule, inputRules, undoInputRule } from "prosemirror-inputrules";
|
|
12
14
|
//#region src/prosemirror/extensions/features/ListExtension.ts
|
|
13
15
|
/**
|
|
14
16
|
* List Extension — list commands + keymaps
|
|
@@ -48,9 +50,9 @@ function getPreviousListFormatting(attrs) {
|
|
|
48
50
|
}
|
|
49
51
|
function clearListAttrs(attrs) {
|
|
50
52
|
const styleNumPr = attrs.numPrFromStyle;
|
|
51
|
-
const numPr = styleNumPr?.numId
|
|
53
|
+
const numPr = isNumberingReference(styleNumPr?.numId) ? {
|
|
52
54
|
numId: 0,
|
|
53
|
-
ilvl: attrs.numPr?.ilvl ?? styleNumPr
|
|
55
|
+
ilvl: attrs.numPr?.ilvl ?? styleNumPr?.ilvl ?? 0
|
|
54
56
|
} : null;
|
|
55
57
|
return {
|
|
56
58
|
...attrs,
|
|
@@ -59,7 +61,7 @@ function clearListAttrs(attrs) {
|
|
|
59
61
|
};
|
|
60
62
|
}
|
|
61
63
|
function hasActiveListNumbering(attrs) {
|
|
62
|
-
return attrs.numPr?.numId
|
|
64
|
+
return isNumberingReference(attrs.numPr?.numId);
|
|
63
65
|
}
|
|
64
66
|
function toggleList(numId) {
|
|
65
67
|
return (state, dispatch) => {
|
|
@@ -293,11 +295,47 @@ function insertTab() {
|
|
|
293
295
|
return true;
|
|
294
296
|
};
|
|
295
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Word's as-you-type markers. Each ends in the space that triggers the rule.
|
|
300
|
+
* `1.` starts a numbered list at one, like the toolbar button; another typed
|
|
301
|
+
* number stays text rather than being silently renumbered.
|
|
302
|
+
*/
|
|
303
|
+
const BULLET_AUTOFORMAT = /^[-*] $/u;
|
|
304
|
+
const NUMBERED_AUTOFORMAT = /^1\. $/u;
|
|
305
|
+
const captureTransaction = (command, state) => {
|
|
306
|
+
const captured = { transaction: null };
|
|
307
|
+
command(state, (transaction) => {
|
|
308
|
+
captured.transaction = transaction;
|
|
309
|
+
});
|
|
310
|
+
return captured.transaction;
|
|
311
|
+
};
|
|
312
|
+
/**
|
|
313
|
+
* Replace a typed marker with the list the toolbar button produces. Running the
|
|
314
|
+
* command itself (rather than writing `numPr` here) keeps an autoformatted list
|
|
315
|
+
* identical to a clicked one, down to what the document saves.
|
|
316
|
+
*/
|
|
317
|
+
const listAutoformat = (marker, toggleCommand) => new InputRule(marker, (state, _match, start, end) => {
|
|
318
|
+
const { $from } = state.selection;
|
|
319
|
+
if ($from.parent.type.name !== "paragraph") return null;
|
|
320
|
+
if (start !== $from.start()) return null;
|
|
321
|
+
if (expectParagraphAttrs($from.parent).numPr?.numId) return null;
|
|
322
|
+
if (makeRevisionInfo(state)) return null;
|
|
323
|
+
const toggled = captureTransaction(toggleCommand, state);
|
|
324
|
+
if (!toggled) return null;
|
|
325
|
+
const tr = state.tr.delete(start, end);
|
|
326
|
+
for (const step of toggled.steps) {
|
|
327
|
+
const mapped = step.map(tr.mapping);
|
|
328
|
+
if (mapped) tr.step(mapped);
|
|
329
|
+
}
|
|
330
|
+
return tr;
|
|
331
|
+
});
|
|
332
|
+
const listAutoformatRules = () => inputRules({ rules: [listAutoformat(BULLET_AUTOFORMAT, toggleBulletList), listAutoformat(NUMBERED_AUTOFORMAT, toggleNumberedList)] });
|
|
296
333
|
const ListExtension = createExtension({
|
|
297
334
|
name: "list",
|
|
298
335
|
priority: Priority.High,
|
|
299
336
|
onSchemaReady() {
|
|
300
337
|
return {
|
|
338
|
+
plugins: [listAutoformatRules()],
|
|
301
339
|
commands: {
|
|
302
340
|
toggleBulletList: () => toggleBulletList,
|
|
303
341
|
toggleNumberedList: () => toggleNumberedList,
|
|
@@ -310,7 +348,7 @@ const ListExtension = createExtension({
|
|
|
310
348
|
"Shift-Tab": chainCommands(goToPrevCell(), decreaseListIndent()),
|
|
311
349
|
"Shift-Enter": () => false,
|
|
312
350
|
Enter: chainCommands(exitListOnEmptyEnter(), splitListItem()),
|
|
313
|
-
Backspace: backspaceExitList()
|
|
351
|
+
Backspace: chainCommands(undoInputRule, backspaceExitList())
|
|
314
352
|
}
|
|
315
353
|
};
|
|
316
354
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { convertBulletToUnicode } from "../docx/bulletMarkers.js";
|
|
2
|
+
import { isNumberingReference } from "../docx/numberingReference.js";
|
|
2
3
|
import { formatOoxmlCounter } from "../docx/ooxmlCounterFormatter.js";
|
|
3
4
|
import { LIST_RENDERING_ATTR_KEYS } from "./listRenderingAttrs.js";
|
|
4
5
|
//#region src/prosemirror/listMarker.ts
|
|
@@ -179,7 +180,7 @@ function advanceListMarker(attrs, state) {
|
|
|
179
180
|
const level = attrs.numPr?.ilvl ?? 0;
|
|
180
181
|
if (!Number.isInteger(level) || level < 0 || level > 8) return null;
|
|
181
182
|
const numId = attrs.numPr?.numId;
|
|
182
|
-
if (numId
|
|
183
|
+
if (!isNumberingReference(numId)) {
|
|
183
184
|
let marker = null;
|
|
184
185
|
if (markerTemplate?.includes("%") && !attrs.listIsBullet) {
|
|
185
186
|
const counters = getLastListCounters(state);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isNumberingReference } from "../docx/numberingReference.js";
|
|
1
2
|
import { expectFieldAttrs, expectParagraphAttrs } from "./attrs/index.js";
|
|
2
3
|
import { expectBookmarkBoundaryAttrs } from "./bookmarkBoundaryAttrs.js";
|
|
3
4
|
import { advanceVisibleListMarker, createListCounterState, resolveListTemplateWithComponents } from "./listMarker.js";
|
|
@@ -79,7 +80,7 @@ function numberTargetForParagraph(node, contextStreams, streams) {
|
|
|
79
80
|
}
|
|
80
81
|
function numberTargetForAdvancedMarker(attrs, resolvedMarker, state, contexts) {
|
|
81
82
|
const numId = attrs.numPr?.numId;
|
|
82
|
-
if (numId
|
|
83
|
+
if (!isNumberingReference(numId) || attrs.listIsBullet || attrs.listMarkerHidden || attrs.listNumFmt === "none" || !resolvedMarker) return null;
|
|
83
84
|
const marker = attrs.listMarkerAllCaps ? resolvedMarker.toLocaleUpperCase() : resolvedMarker;
|
|
84
85
|
const level = attrs.numPr?.ilvl ?? 0;
|
|
85
86
|
if (!Number.isInteger(level) || level < 0 || level > 8) return null;
|
|
@@ -6,7 +6,7 @@ declare const PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES: {
|
|
|
6
6
|
readonly borders: "A bordered paragraph containing an explicit page-break run cannot be projected";
|
|
7
7
|
readonly frame: "A framed paragraph containing an explicit page-break run cannot be projected";
|
|
8
8
|
readonly outline: "An outline paragraph containing an explicit page-break run cannot be projected";
|
|
9
|
-
readonly textBoxAnchor: "A paragraph
|
|
9
|
+
readonly textBoxAnchor: "A paragraph whose text-box anchor follows an explicit page-break run cannot be projected";
|
|
10
10
|
};
|
|
11
11
|
type PageBreakRunParagraphProjectionReason = keyof typeof PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES;
|
|
12
12
|
type PageBreakRunParagraphProjectionDisposition = {
|
|
@@ -19,10 +19,18 @@ type PageBreakRunParagraphProjectionDisposition = {
|
|
|
19
19
|
type PageBreakRunParagraphFeatures = {
|
|
20
20
|
attrs: ParagraphAttrs;
|
|
21
21
|
effectiveFrame: document_d_exports.ParagraphFormatting["frame"];
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* A text-box anchor that the paragraph's first page break precedes.
|
|
24
|
+
*
|
|
25
|
+
* Layout splits such a paragraph into fragments at its breaks, and only the
|
|
26
|
+
* first fragment keeps the paragraph's block id, which is what an anchor
|
|
27
|
+
* resolves its host through. An anchor before the first break is therefore
|
|
28
|
+
* projected faithfully; one after it would lose its host.
|
|
29
|
+
*/
|
|
30
|
+
textBoxAnchorAfterPageBreak: boolean;
|
|
23
31
|
};
|
|
24
32
|
/** Keep source-import and ProseMirror-layout ownership decisions on one predicate. */
|
|
25
|
-
declare const pageBreakRunParagraphProjectionDispositionForFeatures: ({ attrs, effectiveFrame,
|
|
33
|
+
declare const pageBreakRunParagraphProjectionDispositionForFeatures: ({ attrs, effectiveFrame, textBoxAnchorAfterPageBreak }: PageBreakRunParagraphFeatures) => PageBreakRunParagraphProjectionDisposition;
|
|
26
34
|
declare const pageBreakRunParagraphProjectionDisposition: (paragraph: Node) => PageBreakRunParagraphProjectionDisposition;
|
|
27
35
|
//#endregion
|
|
28
36
|
export { PageBreakRunParagraphProjectionDisposition, PageBreakRunParagraphProjectionReason, pageBreakRunParagraphProjectionDisposition, pageBreakRunParagraphProjectionDispositionForFeatures };
|
|
@@ -4,10 +4,10 @@ const PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES = {
|
|
|
4
4
|
borders: "A bordered paragraph containing an explicit page-break run cannot be projected",
|
|
5
5
|
frame: "A framed paragraph containing an explicit page-break run cannot be projected",
|
|
6
6
|
outline: "An outline paragraph containing an explicit page-break run cannot be projected",
|
|
7
|
-
textBoxAnchor: "A paragraph
|
|
7
|
+
textBoxAnchor: "A paragraph whose text-box anchor follows an explicit page-break run cannot be projected"
|
|
8
8
|
};
|
|
9
9
|
/** Keep source-import and ProseMirror-layout ownership decisions on one predicate. */
|
|
10
|
-
const pageBreakRunParagraphProjectionDispositionForFeatures = ({ attrs, effectiveFrame,
|
|
10
|
+
const pageBreakRunParagraphProjectionDispositionForFeatures = ({ attrs, effectiveFrame, textBoxAnchorAfterPageBreak }) => {
|
|
11
11
|
if (effectiveFrame !== void 0 && effectiveFrame.dropCap !== "drop" && effectiveFrame.dropCap !== "margin") return {
|
|
12
12
|
status: "unsupported",
|
|
13
13
|
reason: "frame",
|
|
@@ -23,7 +23,7 @@ const pageBreakRunParagraphProjectionDispositionForFeatures = ({ attrs, effectiv
|
|
|
23
23
|
reason: "borders",
|
|
24
24
|
message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.borders
|
|
25
25
|
};
|
|
26
|
-
if (
|
|
26
|
+
if (textBoxAnchorAfterPageBreak) return {
|
|
27
27
|
status: "unsupported",
|
|
28
28
|
reason: "textBoxAnchor",
|
|
29
29
|
message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.textBoxAnchor
|
|
@@ -32,15 +32,23 @@ const pageBreakRunParagraphProjectionDispositionForFeatures = ({ attrs, effectiv
|
|
|
32
32
|
};
|
|
33
33
|
const pageBreakRunParagraphProjectionDisposition = (paragraph) => {
|
|
34
34
|
const attrs = expectParagraphAttrs(paragraph);
|
|
35
|
-
let
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
let firstPageBreakPos;
|
|
36
|
+
let textBoxAnchorAfterPageBreak = false;
|
|
37
|
+
paragraph.descendants((descendant, pos) => {
|
|
38
|
+
if (descendant.type.name === "pageBreakRun") {
|
|
39
|
+
firstPageBreakPos ??= pos;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (descendant.type.name === "textBoxAnchor" && firstPageBreakPos !== void 0) {
|
|
43
|
+
textBoxAnchorAfterPageBreak = true;
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return !textBoxAnchorAfterPageBreak;
|
|
39
47
|
});
|
|
40
48
|
return pageBreakRunParagraphProjectionDispositionForFeatures({
|
|
41
49
|
attrs,
|
|
42
50
|
effectiveFrame: attrs._originalFormatting?.frame,
|
|
43
|
-
|
|
51
|
+
textBoxAnchorAfterPageBreak
|
|
44
52
|
});
|
|
45
53
|
};
|
|
46
54
|
//#endregion
|
|
@@ -25,6 +25,19 @@ type TemplatePreviewValues = {
|
|
|
25
25
|
values: Record<string, TemplatePreviewValue>;
|
|
26
26
|
/** `highlighted` marks substitutions with the preview accent. */
|
|
27
27
|
mode: "highlighted" | "plain";
|
|
28
|
+
/**
|
|
29
|
+
* `if` expression → whether its block applies. The key is the expression
|
|
30
|
+
* exactly as written between `{% if` and `%}`, trimmed; a tag that carries a
|
|
31
|
+
* filter chain (`{% if consented | checkbox | label("…") %}`) also answers to
|
|
32
|
+
* the bare path in front of the chain, so a host that keys by field path need
|
|
33
|
+
* not repeat the chain. An expression the map does not mention is left as
|
|
34
|
+
* authored. The host decides truthiness: folio evaluates nothing.
|
|
35
|
+
*
|
|
36
|
+
* `false` hides the block, opener through closer. `true` keeps the body and,
|
|
37
|
+
* in `plain` mode, drops the block's tag paragraphs so the reader sees what
|
|
38
|
+
* will be generated; `highlighted` mode keeps them.
|
|
39
|
+
*/
|
|
40
|
+
conditions?: Record<string, boolean>;
|
|
28
41
|
};
|
|
29
42
|
/**
|
|
30
43
|
* One `{{path}}` marker matched by an active preview value. Exposed on
|
|
@@ -41,9 +54,37 @@ type TemplatePreviewEntry = {
|
|
|
41
54
|
/** The typed value displayed in place of the marker. */
|
|
42
55
|
value: TemplatePreviewValue;
|
|
43
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* One span the preview hides: a `false` block from its opener through its
|
|
59
|
+
* closer, or a single directive tag of a `true` block in `plain` mode. Exposed
|
|
60
|
+
* on the plugin state beside {@link TemplatePreviewEntry} so a surface that
|
|
61
|
+
* never sees PM decorations can drop the same spans.
|
|
62
|
+
*/
|
|
63
|
+
type TemplatePreviewHiddenRange = {
|
|
64
|
+
/** Inclusive PM doc position of the span start. */
|
|
65
|
+
from: number;
|
|
66
|
+
/** Exclusive PM doc position of the span end. */
|
|
67
|
+
to: number;
|
|
68
|
+
/** The `if` expression of the block this span belongs to, as authored. */
|
|
69
|
+
expr: string;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Whether a hidden span swallows a block whole, i.e. covers every position of
|
|
73
|
+
* its content, leaving it nothing to render. `pmStart`/`pmEnd` are the block
|
|
74
|
+
* node's own positions, so its content is `[pmStart + 1, pmEnd - 1)`.
|
|
75
|
+
*
|
|
76
|
+
* The decoration path and the paged flow both drop a block on this predicate,
|
|
77
|
+
* so the two surfaces hide exactly the same blocks; a block only partly covered
|
|
78
|
+
* fails it and keeps its text, and the covered span alone is hidden.
|
|
79
|
+
*/
|
|
80
|
+
declare const templatePreviewHidesWholeBlock: (range: TemplatePreviewHiddenRange, block: {
|
|
81
|
+
pmStart: number;
|
|
82
|
+
pmEnd: number;
|
|
83
|
+
}) => boolean;
|
|
44
84
|
type TemplatePreviewState = {
|
|
45
85
|
preview: TemplatePreviewValues | null;
|
|
46
86
|
entries: TemplatePreviewEntry[];
|
|
87
|
+
hidden: TemplatePreviewHiddenRange[];
|
|
47
88
|
decorationSet: DecorationSet;
|
|
48
89
|
};
|
|
49
90
|
declare const templatePreviewValuesKey: PluginKey<TemplatePreviewState>;
|
|
@@ -51,4 +92,4 @@ declare function createTemplatePreviewValuesPlugin(): Plugin<TemplatePreviewStat
|
|
|
51
92
|
/** Push (or clear, with `null`) the live fill preview into the editor. */
|
|
52
93
|
declare const setTemplatePreviewValues: (view: EditorView, preview: TemplatePreviewValues | null) => void;
|
|
53
94
|
//#endregion
|
|
54
|
-
export { TemplatePreviewEntry, TemplatePreviewSpan, TemplatePreviewValue, TemplatePreviewValues, createTemplatePreviewValuesPlugin, setTemplatePreviewValues, templatePreviewValueFingerprint, templatePreviewValueText, templatePreviewValuesKey };
|
|
95
|
+
export { TemplatePreviewEntry, TemplatePreviewHiddenRange, TemplatePreviewSpan, TemplatePreviewValue, TemplatePreviewValues, createTemplatePreviewValuesPlugin, setTemplatePreviewValues, templatePreviewHidesWholeBlock, templatePreviewValueFingerprint, templatePreviewValueText, templatePreviewValuesKey };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { scanDirectives } from "./templateDirectives.js";
|
|
2
2
|
import { Plugin, PluginKey } from "prosemirror-state";
|
|
3
3
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
4
|
+
import { classifyMarker, isFieldPath } from "@stll/template-conditions";
|
|
4
5
|
//#region src/prosemirror/plugins/templatePreviewValues.ts
|
|
5
6
|
/** Concatenated plain text of a preview value. */
|
|
6
7
|
const templatePreviewValueText = (value) => typeof value === "string" ? value : value.runs.map((run) => run.text).join("");
|
|
@@ -11,6 +12,20 @@ const templatePreviewValueFingerprint = (value) => {
|
|
|
11
12
|
if (typeof value === "string") return value;
|
|
12
13
|
return value.runs.map((run) => `${run.bold === true ? "b" : ""}${run.italic === true ? "i" : ""}:${run.text}`).join("\0");
|
|
13
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Whether a hidden span swallows a block whole, i.e. covers every position of
|
|
17
|
+
* its content, leaving it nothing to render. `pmStart`/`pmEnd` are the block
|
|
18
|
+
* node's own positions, so its content is `[pmStart + 1, pmEnd - 1)`.
|
|
19
|
+
*
|
|
20
|
+
* The decoration path and the paged flow both drop a block on this predicate,
|
|
21
|
+
* so the two surfaces hide exactly the same blocks; a block only partly covered
|
|
22
|
+
* fails it and keeps its text, and the covered span alone is hidden.
|
|
23
|
+
*/
|
|
24
|
+
const templatePreviewHidesWholeBlock = (range, block) => range.from <= block.pmStart + 1 && range.to >= block.pmEnd - 1;
|
|
25
|
+
/** Hides the marker text a preview value stands in for. */
|
|
26
|
+
const ORIGINAL_CLASS = "folio-template-preview-original";
|
|
27
|
+
/** Hides a conditional block the host reported as not applying. */
|
|
28
|
+
const HIDDEN_CLASS = "folio-template-preview-hidden";
|
|
14
29
|
const KEY_HOLDER_SYMBOL = Symbol.for("stll.folio.templatePreviewValuesKey");
|
|
15
30
|
const keyHolder = globalThis;
|
|
16
31
|
const templatePreviewValuesKey = keyHolder[KEY_HOLDER_SYMBOL] ?? (keyHolder[KEY_HOLDER_SYMBOL] = new PluginKey("templatePreviewValues"));
|
|
@@ -29,6 +44,13 @@ function buildSpanNode(span) {
|
|
|
29
44
|
}
|
|
30
45
|
return node;
|
|
31
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* The widget standing in for a marker. A value carrying newlines breaks its
|
|
49
|
+
* lines inside the paragraph, not into new ones: here through the widget class's
|
|
50
|
+
* `white-space: pre-wrap`, and on the paged canvas through the line break runs
|
|
51
|
+
* `applyTemplatePreviewToBlocks` emits. The two must keep agreeing — an inline
|
|
52
|
+
* `{{ path }}` placeholder cannot introduce paragraphs on either surface.
|
|
53
|
+
*/
|
|
32
54
|
function buildValueWidget(value, mode) {
|
|
33
55
|
return () => {
|
|
34
56
|
const span = document.createElement("span");
|
|
@@ -42,13 +64,142 @@ function buildValueWidget(value, mode) {
|
|
|
42
64
|
return span;
|
|
43
65
|
};
|
|
44
66
|
}
|
|
45
|
-
|
|
46
|
-
|
|
67
|
+
/** Loop alias the bare-path probe binds. Any identifier does; it is discarded. */
|
|
68
|
+
const CHAIN_PROBE_ALIAS = "__folioCondition";
|
|
69
|
+
/** A field path opens with a letter or underscore, so `9x` and `-x` are not one. */
|
|
70
|
+
const FIELD_PATH_HEAD_RE = /^[\p{L}_]/u;
|
|
71
|
+
/**
|
|
72
|
+
* The bare field path a condition's filter chain hangs off, or undefined when
|
|
73
|
+
* the expression is not a path plus a chain.
|
|
74
|
+
*
|
|
75
|
+
* A host may write the tag the way `{% for x in xs | chain %}` already carries
|
|
76
|
+
* one — `{% if buyer_is_a_consumer | checkbox | ai("Is the buyer …") %}` — while
|
|
77
|
+
* keying `conditions` by the bare path. Splitting on `|` would cut a quoted
|
|
78
|
+
* argument such as `label("a | b")` in half, so the expression is classified as
|
|
79
|
+
* that very `for` chain instead and the grammar's own argument-aware scan
|
|
80
|
+
* reports the path. An expression that is not a path plus a chain of known
|
|
81
|
+
* filters (`a and b`, `items|length > 0`, `ai("a|b")`) classifies as nothing,
|
|
82
|
+
* which is what keeps a real expression on exact matching.
|
|
83
|
+
*/
|
|
84
|
+
const filterChainPath = (expr) => {
|
|
85
|
+
const meta = classifyMarker(`for ${CHAIN_PROBE_ALIAS} in ${expr}`, "statement");
|
|
86
|
+
if (meta?.kind !== "for" || !isFieldPath(meta.path) || !FIELD_PATH_HEAD_RE.test(meta.path)) return;
|
|
87
|
+
return meta.path;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* The verdict stored under one key. Only a key the host set itself, holding a
|
|
91
|
+
* real boolean, counts: an inherited property (`constructor`, or a boolean the
|
|
92
|
+
* host's prototype carries) and a value from an untyped host are read as
|
|
93
|
+
* silence rather than as a verdict.
|
|
94
|
+
*/
|
|
95
|
+
const ownVerdict = (conditions, key) => {
|
|
96
|
+
if (!Object.hasOwn(conditions, key)) return;
|
|
97
|
+
const value = conditions[key];
|
|
98
|
+
return typeof value === "boolean" ? value : void 0;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* The host's verdict on one condition: keyed by the expression as written, else
|
|
102
|
+
* by the bare path its filter chain hangs off. `undefined` means the host said
|
|
103
|
+
* nothing about this block, which leaves it exactly as authored.
|
|
104
|
+
*/
|
|
105
|
+
const conditionVerdict = (conditions, expr) => {
|
|
106
|
+
const exact = ownVerdict(conditions, expr);
|
|
107
|
+
if (exact !== void 0) return exact;
|
|
108
|
+
const path = filterChainPath(expr);
|
|
109
|
+
return path === void 0 ? void 0 : ownVerdict(conditions, path);
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* The spans the host's verdicts hide, in document order and without their
|
|
113
|
+
* nested hidden blocks: hiding a block already hides everything inside it.
|
|
114
|
+
*
|
|
115
|
+
* A `false` block is hidden whole, opener through closer. A `true` block keeps
|
|
116
|
+
* its body, and in `plain` mode loses its tag paragraphs — opener, any
|
|
117
|
+
* `{% elif %}` / `{% else %}`, closer — so the reader sees the document as it
|
|
118
|
+
* will be generated rather than its scaffolding. `highlighted` mode exists to
|
|
119
|
+
* show that scaffolding, so it keeps the tags. A block no verdict mentions is
|
|
120
|
+
* left exactly as authored.
|
|
121
|
+
*
|
|
122
|
+
* Openers and closers pair off a kind-aware stack — `{% endif %}` closes the
|
|
123
|
+
* nearest open `{% if %}`, `{% endfor %}` the nearest `{% for %}` — so a
|
|
124
|
+
* mid-edit template with an unpaired opener or a stray closer hides nothing
|
|
125
|
+
* rather than guessing a span. Inline markers pair like block ones: the fill
|
|
126
|
+
* engine resolves an inline conditional within its paragraph, so the preview
|
|
127
|
+
* follows. An `{% elif %}` or `{% else %}` branch of a `false` block sits
|
|
128
|
+
* inside its span and is hidden with it.
|
|
129
|
+
*/
|
|
130
|
+
function collectHiddenRanges(ranges, conditions, mode) {
|
|
131
|
+
if (conditions === void 0) return [];
|
|
132
|
+
const open = [];
|
|
133
|
+
const paired = [];
|
|
134
|
+
for (const range of [...ranges].sort((a, b) => a.from - b.from)) {
|
|
135
|
+
if (range.kind === "if" || range.kind === "for") {
|
|
136
|
+
open.push({
|
|
137
|
+
opener: range,
|
|
138
|
+
branches: []
|
|
139
|
+
});
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (range.kind === "elif" || range.kind === "else") {
|
|
143
|
+
const innermost = open.at(-1);
|
|
144
|
+
if (innermost?.opener.kind === "if") innermost.branches.push(range);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (range.kind !== "endif" && range.kind !== "endfor") continue;
|
|
148
|
+
const wanted = range.kind === "endif" ? "if" : "for";
|
|
149
|
+
let block;
|
|
150
|
+
for (let index = open.length - 1; index >= 0; index -= 1) {
|
|
151
|
+
const candidate = open[index];
|
|
152
|
+
if (candidate?.opener.kind === wanted) {
|
|
153
|
+
block = candidate;
|
|
154
|
+
open.length = index;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (block === void 0 || block.opener.kind !== "if") continue;
|
|
159
|
+
const { opener, branches } = block;
|
|
160
|
+
const verdict = conditionVerdict(conditions, opener.expr);
|
|
161
|
+
if (verdict === void 0) continue;
|
|
162
|
+
if (!verdict) {
|
|
163
|
+
paired.push({
|
|
164
|
+
from: opener.from,
|
|
165
|
+
to: range.to,
|
|
166
|
+
expr: opener.expr
|
|
167
|
+
});
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (mode !== "plain") continue;
|
|
171
|
+
paired.push({
|
|
172
|
+
from: opener.from,
|
|
173
|
+
to: opener.to,
|
|
174
|
+
expr: opener.expr
|
|
175
|
+
});
|
|
176
|
+
for (const branch of branches) paired.push({
|
|
177
|
+
from: branch.from,
|
|
178
|
+
to: branch.to,
|
|
179
|
+
expr: opener.expr
|
|
180
|
+
});
|
|
181
|
+
paired.push({
|
|
182
|
+
from: range.from,
|
|
183
|
+
to: range.to,
|
|
184
|
+
expr: opener.expr
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
paired.sort((a, b) => a.from - b.from || b.to - a.to);
|
|
188
|
+
const outermost = [];
|
|
189
|
+
for (const range of paired) {
|
|
190
|
+
const previous = outermost.at(-1);
|
|
191
|
+
if (previous && range.to <= previous.to) continue;
|
|
192
|
+
outermost.push(range);
|
|
193
|
+
}
|
|
194
|
+
return outermost;
|
|
195
|
+
}
|
|
196
|
+
function collectPreviewEntries(ranges, values, hidden) {
|
|
47
197
|
const entries = [];
|
|
48
|
-
for (const range of
|
|
198
|
+
for (const range of ranges) {
|
|
49
199
|
if (range.kind !== "placeholder" && range.kind !== "clause") continue;
|
|
50
|
-
const value =
|
|
200
|
+
const value = values[range.expr];
|
|
51
201
|
if (value === void 0 || templatePreviewValueText(value) === "") continue;
|
|
202
|
+
if (hidden.some((block) => range.from >= block.from && range.from < block.to)) continue;
|
|
52
203
|
entries.push({
|
|
53
204
|
from: range.from,
|
|
54
205
|
to: range.to,
|
|
@@ -58,10 +209,59 @@ function collectPreviewEntries(doc, preview) {
|
|
|
58
209
|
}
|
|
59
210
|
return entries;
|
|
60
211
|
}
|
|
61
|
-
function
|
|
62
|
-
if (
|
|
212
|
+
function projectPreview(doc, preview) {
|
|
213
|
+
if (!preview) return {
|
|
214
|
+
entries: [],
|
|
215
|
+
hidden: []
|
|
216
|
+
};
|
|
217
|
+
const hasValues = Object.keys(preview.values).length > 0;
|
|
218
|
+
const hasConditions = preview.conditions !== void 0 && Object.keys(preview.conditions).length > 0;
|
|
219
|
+
if (!hasValues && !hasConditions) return {
|
|
220
|
+
entries: [],
|
|
221
|
+
hidden: []
|
|
222
|
+
};
|
|
223
|
+
const ranges = scanDirectives(doc);
|
|
224
|
+
const hidden = collectHiddenRanges(ranges, preview.conditions, preview.mode);
|
|
225
|
+
return {
|
|
226
|
+
entries: hasValues ? collectPreviewEntries(ranges, preview.values, hidden) : [],
|
|
227
|
+
hidden
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Hide one span: a block the span covers whole is hidden as a node, leaving no
|
|
232
|
+
* empty line behind; a block it covers in part is hidden over the covered text
|
|
233
|
+
* only, so an inline `{% if %}` in a paragraph of running text takes its own
|
|
234
|
+
* span and nothing else.
|
|
235
|
+
*/
|
|
236
|
+
function pushHiddenDecorations(doc, range, out) {
|
|
237
|
+
const { from, to } = range;
|
|
238
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
239
|
+
if (!node.isBlock) return false;
|
|
240
|
+
const nodeEnd = pos + node.nodeSize;
|
|
241
|
+
if (templatePreviewHidesWholeBlock(range, {
|
|
242
|
+
pmStart: pos,
|
|
243
|
+
pmEnd: nodeEnd
|
|
244
|
+
})) {
|
|
245
|
+
out.push(Decoration.node(pos, nodeEnd, { class: HIDDEN_CLASS }));
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
const contentFrom = pos + 1;
|
|
249
|
+
const contentTo = nodeEnd - 1;
|
|
250
|
+
if (!node.isTextblock) return true;
|
|
251
|
+
const spanFrom = Math.max(from, contentFrom);
|
|
252
|
+
const spanTo = Math.min(to, contentTo);
|
|
253
|
+
if (spanTo > spanFrom) out.push(Decoration.inline(spanFrom, spanTo, { class: HIDDEN_CLASS }, {
|
|
254
|
+
inclusiveStart: false,
|
|
255
|
+
inclusiveEnd: false
|
|
256
|
+
}));
|
|
257
|
+
return false;
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function buildDecorationSet(doc, { entries, hidden }, mode) {
|
|
261
|
+
if (entries.length === 0 && hidden.length === 0) return DecorationSet.empty;
|
|
63
262
|
const decorations = [];
|
|
64
|
-
for (const
|
|
263
|
+
for (const range of hidden) pushHiddenDecorations(doc, range, decorations);
|
|
264
|
+
for (const entry of entries) decorations.push(Decoration.inline(entry.from, entry.to, { class: ORIGINAL_CLASS }, {
|
|
65
265
|
inclusiveStart: false,
|
|
66
266
|
inclusiveEnd: false
|
|
67
267
|
}), Decoration.widget(entry.from, buildValueWidget(entry.value, mode), {
|
|
@@ -80,25 +280,28 @@ function createTemplatePreviewValuesPlugin() {
|
|
|
80
280
|
return {
|
|
81
281
|
preview: null,
|
|
82
282
|
entries: [],
|
|
283
|
+
hidden: [],
|
|
83
284
|
decorationSet: DecorationSet.empty
|
|
84
285
|
};
|
|
85
286
|
},
|
|
86
287
|
apply(tr, prev, _oldState, newState) {
|
|
87
288
|
const meta = tr.getMeta(templatePreviewValuesKey);
|
|
88
289
|
if (meta !== void 0) {
|
|
89
|
-
const
|
|
290
|
+
const projection = projectPreview(newState.doc, meta.preview);
|
|
90
291
|
return {
|
|
91
292
|
preview: meta.preview,
|
|
92
|
-
entries,
|
|
93
|
-
|
|
293
|
+
entries: projection.entries,
|
|
294
|
+
hidden: projection.hidden,
|
|
295
|
+
decorationSet: buildDecorationSet(newState.doc, projection, meta.preview?.mode ?? "plain")
|
|
94
296
|
};
|
|
95
297
|
}
|
|
96
298
|
if (tr.docChanged && prev.preview) {
|
|
97
|
-
const
|
|
299
|
+
const projection = projectPreview(newState.doc, prev.preview);
|
|
98
300
|
return {
|
|
99
301
|
preview: prev.preview,
|
|
100
|
-
entries,
|
|
101
|
-
|
|
302
|
+
entries: projection.entries,
|
|
303
|
+
hidden: projection.hidden,
|
|
304
|
+
decorationSet: buildDecorationSet(newState.doc, projection, prev.preview.mode)
|
|
102
305
|
};
|
|
103
306
|
}
|
|
104
307
|
return prev;
|
|
@@ -114,4 +317,4 @@ const setTemplatePreviewValues = (view, preview) => {
|
|
|
114
317
|
view.dispatch(view.state.tr.setMeta(templatePreviewValuesKey, { preview }));
|
|
115
318
|
};
|
|
116
319
|
//#endregion
|
|
117
|
-
export { createTemplatePreviewValuesPlugin, setTemplatePreviewValues, templatePreviewValueFingerprint, templatePreviewValueText, templatePreviewValuesKey };
|
|
320
|
+
export { createTemplatePreviewValuesPlugin, setTemplatePreviewValues, templatePreviewHidesWholeBlock, templatePreviewValueFingerprint, templatePreviewValueText, templatePreviewValuesKey };
|