@attentionform/astra 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +132 -84
- package/README.md +1 -1
- package/dist/cli.js +886 -371
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -5662,6 +5662,23 @@ function writeCorePropertiesPart(adapter, xml) {
|
|
|
5662
5662
|
} catch {
|
|
5663
5663
|
}
|
|
5664
5664
|
}
|
|
5665
|
+
function registerExistingCorePropertiesPart(adapter) {
|
|
5666
|
+
let present = false;
|
|
5667
|
+
try {
|
|
5668
|
+
present = adapter.readTextOrNull(CORE_PROPERTIES_PART) !== null;
|
|
5669
|
+
} catch {
|
|
5670
|
+
return;
|
|
5671
|
+
}
|
|
5672
|
+
if (!present) return;
|
|
5673
|
+
try {
|
|
5674
|
+
ensureContentTypeOverride(adapter);
|
|
5675
|
+
} catch {
|
|
5676
|
+
}
|
|
5677
|
+
try {
|
|
5678
|
+
ensurePackageRel(adapter);
|
|
5679
|
+
} catch {
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5665
5682
|
function patchToMetadata(patch) {
|
|
5666
5683
|
const out = {};
|
|
5667
5684
|
if (patch.title !== void 0) out.title = patch.title;
|
|
@@ -9907,6 +9924,41 @@ function hasLiveNumberingForSync(ast) {
|
|
|
9907
9924
|
}
|
|
9908
9925
|
}
|
|
9909
9926
|
|
|
9927
|
+
// ../engine/src/ooxml/quote-style.ts
|
|
9928
|
+
var QUOTE_STYLE_ID = "Quote";
|
|
9929
|
+
var QUOTE_INDENT_TWIPS = 720;
|
|
9930
|
+
var QUOTE_STYLE_BODY = `<w:style w:type="paragraph" w:styleId="Quote">
|
|
9931
|
+
<w:name w:val="Quote"/>
|
|
9932
|
+
<w:basedOn w:val="Normal"/>
|
|
9933
|
+
<w:next w:val="Normal"/>
|
|
9934
|
+
<w:uiPriority w:val="29"/>
|
|
9935
|
+
<w:qFormat/>
|
|
9936
|
+
<w:pPr>
|
|
9937
|
+
<w:pBdr>
|
|
9938
|
+
<w:left w:val="single" w:sz="18" w:space="10" w:color="94A3B8"/>
|
|
9939
|
+
</w:pBdr>
|
|
9940
|
+
<w:spacing w:before="120" w:after="120"/>
|
|
9941
|
+
<w:ind w:left="${QUOTE_INDENT_TWIPS}"/>
|
|
9942
|
+
</w:pPr>
|
|
9943
|
+
<w:rPr>
|
|
9944
|
+
<w:i/>
|
|
9945
|
+
<w:color w:val="475569"/>
|
|
9946
|
+
</w:rPr>
|
|
9947
|
+
</w:style>`;
|
|
9948
|
+
var QUOTE_STYLE_XML = QUOTE_STYLE_BODY.replace("<w:style ", '<w:style xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" ');
|
|
9949
|
+
function ensureQuoteStyle(stylesDom) {
|
|
9950
|
+
if (!stylesDom) return false;
|
|
9951
|
+
const root = stylesDom.getElementsByTagName("w:styles")[0];
|
|
9952
|
+
if (!root) return false;
|
|
9953
|
+
for (const style of Array.from(stylesDom.getElementsByTagName("w:style"))) {
|
|
9954
|
+
if (style.getAttribute("w:styleId") === QUOTE_STYLE_ID) return false;
|
|
9955
|
+
}
|
|
9956
|
+
const fragment = new DOMParser().parseFromString(QUOTE_STYLE_XML, "text/xml").documentElement;
|
|
9957
|
+
if (!fragment) return false;
|
|
9958
|
+
root.appendChild(stylesDom.importNode(fragment, true));
|
|
9959
|
+
return true;
|
|
9960
|
+
}
|
|
9961
|
+
|
|
9910
9962
|
// ../engine/src/parser.ts
|
|
9911
9963
|
function hasAncestorTag(el, tag) {
|
|
9912
9964
|
for (let n = el.parentNode; n; n = n.parentNode) {
|
|
@@ -10574,6 +10626,7 @@ var DocxParser = class _DocxParser {
|
|
|
10574
10626
|
} else {
|
|
10575
10627
|
this.stylesDom = null;
|
|
10576
10628
|
}
|
|
10629
|
+
registerExistingCorePropertiesPart(this.getZipAdapter());
|
|
10577
10630
|
this.invalidateMediaCache();
|
|
10578
10631
|
this.assignStableIds();
|
|
10579
10632
|
this._bumpNodePagesRevision();
|
|
@@ -11139,6 +11192,27 @@ var DocxParser = class _DocxParser {
|
|
|
11139
11192
|
return tracked;
|
|
11140
11193
|
}
|
|
11141
11194
|
// --- AST layer: stable id assignment ---
|
|
11195
|
+
/** Orders AST blocks by where their element sits in the body; blocks inside one body child keep their order. */
|
|
11196
|
+
sortBlocksInDocumentOrder(nodes) {
|
|
11197
|
+
const body = this.documentDom.getElementsByTagName("w:body")[0];
|
|
11198
|
+
if (!body || nodes.length < 2) return;
|
|
11199
|
+
const topIndex = /* @__PURE__ */ new Map();
|
|
11200
|
+
let position2 = 0;
|
|
11201
|
+
for (let child = body.firstChild; child; child = child.nextSibling) {
|
|
11202
|
+
if (child.nodeType === 1) topIndex.set(child, position2++);
|
|
11203
|
+
}
|
|
11204
|
+
const keyOf = (element) => {
|
|
11205
|
+
let current = element;
|
|
11206
|
+
while (current && current.parentNode !== body) current = current.parentNode;
|
|
11207
|
+
return current ? topIndex.get(current) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
11208
|
+
};
|
|
11209
|
+
const keyed = nodes.map((node, index) => ({ node, index, key: keyOf(node.xml) }));
|
|
11210
|
+
if (keyed.every((entry, i) => i === 0 || keyed[i - 1].key <= entry.key)) return;
|
|
11211
|
+
keyed.sort((a, b) => a.key - b.key || a.index - b.index);
|
|
11212
|
+
keyed.forEach((entry, i) => {
|
|
11213
|
+
nodes[i] = entry.node;
|
|
11214
|
+
});
|
|
11215
|
+
}
|
|
11142
11216
|
async getAST() {
|
|
11143
11217
|
const ast = {
|
|
11144
11218
|
metadata: {},
|
|
@@ -11516,6 +11590,7 @@ var DocxParser = class _DocxParser {
|
|
|
11516
11590
|
ast.sectPrFlags = { titlePg: false, evenAndOddHeaders: false };
|
|
11517
11591
|
}
|
|
11518
11592
|
ast.nextChangeId = firstFreeRevisionId(this.maxPackageRevisionId());
|
|
11593
|
+
this.sortBlocksInDocumentOrder(rootNodes);
|
|
11519
11594
|
ast.sections.push(...splitIntoSections(rootNodes, fileSections));
|
|
11520
11595
|
const prevNumberingAst = this.lastAstForNumbering;
|
|
11521
11596
|
if (prevNumberingAst && prevNumberingAst !== ast) {
|
|
@@ -13290,6 +13365,7 @@ ${validation.errors.join("\n")}`, "repair the part XML or re-save the document i
|
|
|
13290
13365
|
<w:spacing w:before="120" w:after="120" w:line="240" w:lineRule="auto"/>
|
|
13291
13366
|
</w:pPr>
|
|
13292
13367
|
</w:style>
|
|
13368
|
+
${QUOTE_STYLE_BODY}
|
|
13293
13369
|
<w:style w:type="character" w:styleId="Code">
|
|
13294
13370
|
<w:name w:val="Code"/>
|
|
13295
13371
|
<w:rPr>
|
|
@@ -13672,15 +13748,26 @@ var import_isomorphic_git = __toESM(require("isomorphic-git"));
|
|
|
13672
13748
|
var import_fs3 = __toESM(require("fs"));
|
|
13673
13749
|
var import_promises = __toESM(require("fs/promises"));
|
|
13674
13750
|
var import_path2 = __toESM(require("path"));
|
|
13751
|
+
function hasUriScheme(ref) {
|
|
13752
|
+
if (/^[a-zA-Z]:[\\/]/.test(ref)) return false;
|
|
13753
|
+
return /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(ref);
|
|
13754
|
+
}
|
|
13755
|
+
function withoutTrailingSeparator(p, normalized) {
|
|
13756
|
+
const root = p.parse(normalized).root;
|
|
13757
|
+
let out = normalized;
|
|
13758
|
+
while (out.length > root.length && out.endsWith(p.sep)) out = out.slice(0, -1);
|
|
13759
|
+
return out;
|
|
13760
|
+
}
|
|
13675
13761
|
function isPathInsideDir(candidate, dir, platform = process.platform) {
|
|
13676
|
-
const
|
|
13677
|
-
|
|
13762
|
+
const p = platform === "win32" ? import_path2.default.win32 : import_path2.default.posix;
|
|
13763
|
+
let c = withoutTrailingSeparator(p, p.normalize(candidate));
|
|
13764
|
+
let d = withoutTrailingSeparator(p, p.normalize(dir));
|
|
13678
13765
|
if (platform === "win32") {
|
|
13679
|
-
|
|
13680
|
-
|
|
13681
|
-
return lc === ld || lc.startsWith(ld + import_path2.default.sep.toLowerCase());
|
|
13766
|
+
c = c.toLowerCase();
|
|
13767
|
+
d = d.toLowerCase();
|
|
13682
13768
|
}
|
|
13683
|
-
|
|
13769
|
+
if (c === d) return true;
|
|
13770
|
+
return c.startsWith(d.endsWith(p.sep) ? d : d + p.sep);
|
|
13684
13771
|
}
|
|
13685
13772
|
function canonicalizeForAsset(resolved) {
|
|
13686
13773
|
try {
|
|
@@ -14928,6 +15015,19 @@ function warnIfLowContrast(warnings, seen, fill, textColor, where) {
|
|
|
14928
15015
|
warnings.push(msg);
|
|
14929
15016
|
}
|
|
14930
15017
|
}
|
|
15018
|
+
function autoTextForFill(fill, current) {
|
|
15019
|
+
if (!/^[0-9A-Fa-f]{6}$/.test(fill)) return void 0;
|
|
15020
|
+
if (contrastRatio((current ?? "000000").toUpperCase(), fill.toUpperCase()) >= TABLE_CONTRAST_MIN_RATIO) return void 0;
|
|
15021
|
+
return contrastRatio("FFFFFF", fill.toUpperCase()) > contrastRatio("000000", fill.toUpperCase()) ? "FFFFFF" : "000000";
|
|
15022
|
+
}
|
|
15023
|
+
function noteAutoText(warnings, seen, fill, text2, where) {
|
|
15024
|
+
const key = `auto:${fill}:${text2}`;
|
|
15025
|
+
if (seen.has(key)) return;
|
|
15026
|
+
seen.add(key);
|
|
15027
|
+
warnings.push(
|
|
15028
|
+
`Table note: text set to #${text2} at ${where} because black text is not readable on fill #${fill} (contrast ${contrastRatio(text2, fill).toFixed(2)}:1). Pass textColor to choose another colour.`
|
|
15029
|
+
);
|
|
15030
|
+
}
|
|
14931
15031
|
function paintCellTextColor(tc, doc, normColor) {
|
|
14932
15032
|
let ps = directChildren5(tc, "w:p");
|
|
14933
15033
|
if (ps.length === 0) {
|
|
@@ -15554,6 +15654,13 @@ async function executeTable(opIn, ast, guide) {
|
|
|
15554
15654
|
let effectiveText;
|
|
15555
15655
|
if (rIdx === 0) {
|
|
15556
15656
|
effectiveText = explicitTextColor ?? guideTextForFill(rowFill);
|
|
15657
|
+
if (effectiveText === void 0) {
|
|
15658
|
+
const auto = autoTextForFill(rowFill, firstExplicitCellTextColor(tc));
|
|
15659
|
+
if (auto) {
|
|
15660
|
+
effectiveText = auto;
|
|
15661
|
+
noteAutoText(warnings, warnedContrastPairs, rowFill, auto, `header cell (${rIdx + 1},${cIdx + 1}) of table '${tableNode.id}'`);
|
|
15662
|
+
}
|
|
15663
|
+
}
|
|
15557
15664
|
if (effectiveText && tblEl.ownerDocument) {
|
|
15558
15665
|
paintCellTextColor(tc, tblEl.ownerDocument, effectiveText);
|
|
15559
15666
|
}
|
|
@@ -16080,7 +16187,14 @@ async function executeTable(opIn, ast, guide) {
|
|
|
16080
16187
|
shd.setAttribute("w:color", "auto");
|
|
16081
16188
|
shd.setAttribute("w:fill", normFill);
|
|
16082
16189
|
sortChildrenBySchema(tcPr, TCPR_CHILD_ORDER);
|
|
16083
|
-
|
|
16190
|
+
let effectiveText = explicitTextColor ?? guideTextForFill(normFill);
|
|
16191
|
+
if (effectiveText === void 0) {
|
|
16192
|
+
const auto = autoTextForFill(normFill, firstExplicitCellTextColor(tc));
|
|
16193
|
+
if (auto) {
|
|
16194
|
+
effectiveText = auto;
|
|
16195
|
+
noteAutoText(warnings, warnedContrastPairs, normFill, auto, `cell (${shown(rowIndex)},${shown(colIndex)}) of table '${tableNode.id}'`);
|
|
16196
|
+
}
|
|
16197
|
+
}
|
|
16084
16198
|
if (effectiveText && tblEl.ownerDocument) {
|
|
16085
16199
|
paintCellTextColor(tc, tblEl.ownerDocument, effectiveText);
|
|
16086
16200
|
}
|
|
@@ -16364,7 +16478,7 @@ async function executeTable(opIn, ast, guide) {
|
|
|
16364
16478
|
break;
|
|
16365
16479
|
}
|
|
16366
16480
|
case "setCellTextColor": {
|
|
16367
|
-
const colorInput = raw.color ?? op.color;
|
|
16481
|
+
const colorInput = raw.color ?? op.color ?? raw.textColor ?? op.textColor;
|
|
16368
16482
|
if (colorInput === void 0 || colorInput === null || String(colorInput).trim() === "") {
|
|
16369
16483
|
throw new Error(
|
|
16370
16484
|
`E_VALIDATION: setCellTextColor requires color (got no color). Remedy: pass color as 6 hex digits without '#', e.g. color:'FFFFFF' for white header text.`
|
|
@@ -16820,6 +16934,13 @@ function sanitizeXml(xmlStr) {
|
|
|
16820
16934
|
// ../engine/src/operation-schema.ts
|
|
16821
16935
|
var import_zod2 = require("zod");
|
|
16822
16936
|
|
|
16937
|
+
// ../engine/src/anchor-alias.ts
|
|
16938
|
+
function anchorAliasOf(value) {
|
|
16939
|
+
if (value === "start" || value === "@start") return "start";
|
|
16940
|
+
if (value === "end" || value === "@end") return "end";
|
|
16941
|
+
return null;
|
|
16942
|
+
}
|
|
16943
|
+
|
|
16823
16944
|
// ../engine/src/operation-params.ts
|
|
16824
16945
|
var STRUCTURAL = /* @__PURE__ */ new Set(["action", "target", "tableId", "operation", "op", "params", "trackChanges", "expectedVersion"]);
|
|
16825
16946
|
var ROW = ["rowIndex", "row"];
|
|
@@ -16845,9 +16966,17 @@ var TABLE_SUBOP_KEYS = {
|
|
|
16845
16966
|
setCellAlignment: ["align", "alignment", ...ROW, ...COL],
|
|
16846
16967
|
setTableAlignment: ["align", "alignment"],
|
|
16847
16968
|
setCellPadding: ["padding", ...ROW, ...COL],
|
|
16848
|
-
setCellTextColor: ["color", ...ROW, ...COL],
|
|
16969
|
+
setCellTextColor: ["color", "textColor", ...ROW, ...COL],
|
|
16849
16970
|
setKeepTogether: []
|
|
16850
16971
|
};
|
|
16972
|
+
function describeTableSubOpKeys() {
|
|
16973
|
+
const aliases = /* @__PURE__ */ new Set(["rowIndex", "colIndex", "columnIndex"]);
|
|
16974
|
+
const parts = Object.entries(TABLE_SUBOP_KEYS).map(([sub, keys]) => {
|
|
16975
|
+
const shown2 = keys.filter((k) => !aliases.has(k));
|
|
16976
|
+
return shown2.length > 0 ? `${sub}(${shown2.join(",")})` : sub;
|
|
16977
|
+
});
|
|
16978
|
+
return `Parameters each operation reads (row and col count from 1): ${parts.join("; ")}.`;
|
|
16979
|
+
}
|
|
16851
16980
|
function isRecord(value) {
|
|
16852
16981
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
16853
16982
|
}
|
|
@@ -20801,6 +20930,8 @@ var expectedVersion = import_zod2.z.number().int().nonnegative().optional();
|
|
|
20801
20930
|
var nestedParams = import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional();
|
|
20802
20931
|
var sectionNumber = import_zod2.z.number().int().positive("sections count from 1 (1 is the first section)").describe("Section number, counting from 1 (1 is the first section).");
|
|
20803
20932
|
var position = import_zod2.z.enum(["before", "after"]);
|
|
20933
|
+
var ANCHOR_REF_DESCRIPTION = "A node id from inspect_document, or 'start' / 'end' for the first / last block of the document (no inspect needed; '@start' and '@end' work too). With 'start', position defaults to 'before'; with 'end', to 'after'.";
|
|
20934
|
+
var ANCHOR_REF_SHORT = "A node id, or 'start' / 'end' for the first / last block.";
|
|
20804
20935
|
var operationInsertPosition = import_zod2.z.enum(["before", "after"]).optional();
|
|
20805
20936
|
var FONT_SIZE_PT_REGEX = /^\s*(\d+(?:\.\d+)?)\s*pt\s*$/i;
|
|
20806
20937
|
function parseFontSize(sz) {
|
|
@@ -20903,7 +21034,7 @@ var insertOp = import_zod2.z.object({
|
|
|
20903
21034
|
// engine appends in call order without ever reading them) need no dummy
|
|
20904
21035
|
// anchor. Body inserts still require both — enforced by the strict
|
|
20905
21036
|
// validators below, not by this shape.
|
|
20906
|
-
anchor: import_zod2.z.string().min(1).max(256).optional(),
|
|
21037
|
+
anchor: import_zod2.z.string().min(1).max(256).describe(ANCHOR_REF_DESCRIPTION).optional(),
|
|
20907
21038
|
position: operationInsertPosition,
|
|
20908
21039
|
text: import_zod2.z.string().max(5e5).optional(),
|
|
20909
21040
|
xml: import_zod2.z.string().max(5e5).optional(),
|
|
@@ -21025,7 +21156,7 @@ var clearFormatOp = import_zod2.z.object({
|
|
|
21025
21156
|
var moveOp = import_zod2.z.object({
|
|
21026
21157
|
action: import_zod2.z.literal("move"),
|
|
21027
21158
|
target: nodeIdOrList,
|
|
21028
|
-
anchor: import_zod2.z.string().min(1).max(256),
|
|
21159
|
+
anchor: import_zod2.z.string().min(1).max(256).describe(ANCHOR_REF_SHORT),
|
|
21029
21160
|
position,
|
|
21030
21161
|
expectedVersion,
|
|
21031
21162
|
params: nestedParams
|
|
@@ -21062,8 +21193,8 @@ var tableOp = import_zod2.z.object({
|
|
|
21062
21193
|
action: import_zod2.z.literal("table"),
|
|
21063
21194
|
target: import_zod2.z.string().min(1).max(256).optional(),
|
|
21064
21195
|
tableId: import_zod2.z.string().min(1).max(256).optional(),
|
|
21065
|
-
operation: tableSubOp.optional(),
|
|
21066
|
-
op: tableSubOp.optional(),
|
|
21196
|
+
operation: tableSubOp.describe(describeTableSubOpKeys()).optional(),
|
|
21197
|
+
op: tableSubOp.describe("Alias of operation.").optional(),
|
|
21067
21198
|
trackChanges: import_zod2.z.boolean().optional(),
|
|
21068
21199
|
author: revisionAuthor,
|
|
21069
21200
|
date: revisionDate,
|
|
@@ -21093,7 +21224,6 @@ var tableOp = import_zod2.z.object({
|
|
|
21093
21224
|
border: import_zod2.z.string().max(32).optional(),
|
|
21094
21225
|
color: import_zod2.z.string().max(16).optional(),
|
|
21095
21226
|
sz: fontSizeOrSzSchema.optional(),
|
|
21096
|
-
fontSize: fontSizeOrSzSchema.optional(),
|
|
21097
21227
|
vAlign: import_zod2.z.string().max(16).optional(),
|
|
21098
21228
|
align: tableAlignmentSchema.optional(),
|
|
21099
21229
|
alignment: tableAlignmentSchema.optional(),
|
|
@@ -21155,7 +21285,7 @@ var namedStyleOp = import_zod2.z.object({
|
|
|
21155
21285
|
var fieldOp = import_zod2.z.object({
|
|
21156
21286
|
action: import_zod2.z.literal("field"),
|
|
21157
21287
|
operation: import_zod2.z.enum(["insertTOC", "insertPageNumber", "insertWordCount", "updateFields"]),
|
|
21158
|
-
anchor: import_zod2.z.string().min(1).max(256).optional(),
|
|
21288
|
+
anchor: import_zod2.z.string().min(1).max(256).describe(ANCHOR_REF_SHORT).optional(),
|
|
21159
21289
|
position: operationInsertPosition,
|
|
21160
21290
|
title: import_zod2.z.string().max(1e3).optional(),
|
|
21161
21291
|
levels: import_zod2.z.number().int().min(1).max(9).optional(),
|
|
@@ -21171,7 +21301,7 @@ var equationOp = import_zod2.z.object({
|
|
|
21171
21301
|
action: import_zod2.z.literal("equation"),
|
|
21172
21302
|
operation: import_zod2.z.enum(["insert", "replace"]),
|
|
21173
21303
|
latex: import_zod2.z.string().min(1).max(5e4),
|
|
21174
|
-
anchor: import_zod2.z.string().min(1).max(256).optional(),
|
|
21304
|
+
anchor: import_zod2.z.string().min(1).max(256).describe(ANCHOR_REF_SHORT).optional(),
|
|
21175
21305
|
position: operationInsertPosition,
|
|
21176
21306
|
target: import_zod2.z.string().min(1).max(256).optional(),
|
|
21177
21307
|
id: import_zod2.z.string().min(1).max(256).optional(),
|
|
@@ -21285,7 +21415,7 @@ var chartOp = import_zod2.z.object({
|
|
|
21285
21415
|
categories: import_zod2.z.array(import_zod2.z.string().max(500)).max(1e3).optional(),
|
|
21286
21416
|
series: import_zod2.z.array(chartSeries).max(100).optional(),
|
|
21287
21417
|
style: chartStyle.optional(),
|
|
21288
|
-
anchor: import_zod2.z.string().min(1).max(256).optional(),
|
|
21418
|
+
anchor: import_zod2.z.string().min(1).max(256).describe(ANCHOR_REF_SHORT).optional(),
|
|
21289
21419
|
id: import_zod2.z.string().min(1).max(256).optional(),
|
|
21290
21420
|
widthEmu: import_zod2.z.number().positive().max(1e8).optional(),
|
|
21291
21421
|
heightEmu: import_zod2.z.number().positive().max(1e8).optional(),
|
|
@@ -21295,7 +21425,7 @@ var chartOp = import_zod2.z.object({
|
|
|
21295
21425
|
var imageOp = import_zod2.z.object({
|
|
21296
21426
|
action: import_zod2.z.literal("image"),
|
|
21297
21427
|
operation: import_zod2.z.enum(["insert", "resize", "reposition", "setWrap", "setCaption", "delete", "setAltText"]),
|
|
21298
|
-
anchor: import_zod2.z.string().min(1).max(256).optional(),
|
|
21428
|
+
anchor: import_zod2.z.string().min(1).max(256).describe(ANCHOR_REF_SHORT).optional(),
|
|
21299
21429
|
target: import_zod2.z.string().min(1).max(256).optional(),
|
|
21300
21430
|
fileData: import_zod2.z.string().max(2e7).optional(),
|
|
21301
21431
|
filePath: import_zod2.z.string().min(1).max(1024).optional(),
|
|
@@ -21309,7 +21439,8 @@ var imageOp = import_zod2.z.object({
|
|
|
21309
21439
|
var calloutOp = import_zod2.z.object({
|
|
21310
21440
|
action: import_zod2.z.literal("callout"),
|
|
21311
21441
|
text: import_zod2.z.string().min(1).max(1e5),
|
|
21312
|
-
anchor: import_zod2.z.string().min(1).max(256).optional(),
|
|
21442
|
+
anchor: import_zod2.z.string().min(1).max(256).describe(ANCHOR_REF_SHORT).optional(),
|
|
21443
|
+
id: import_zod2.z.string().min(1).max(256).optional(),
|
|
21313
21444
|
title: import_zod2.z.string().max(1e3).optional(),
|
|
21314
21445
|
theme: import_zod2.z.string().max(32).optional(),
|
|
21315
21446
|
position: operationInsertPosition,
|
|
@@ -21319,7 +21450,7 @@ var calloutOp = import_zod2.z.object({
|
|
|
21319
21450
|
var footnoteOp = import_zod2.z.object({
|
|
21320
21451
|
action: import_zod2.z.literal("footnote"),
|
|
21321
21452
|
operation: import_zod2.z.enum(["insert"]).optional(),
|
|
21322
|
-
anchor: import_zod2.z.string().min(1).max(256),
|
|
21453
|
+
anchor: import_zod2.z.string().min(1).max(256).describe(ANCHOR_REF_SHORT),
|
|
21323
21454
|
text: import_zod2.z.string().min(1).max(5e4),
|
|
21324
21455
|
id: import_zod2.z.string().min(1).max(256).optional(),
|
|
21325
21456
|
offset: import_zod2.z.number().int().nonnegative().optional(),
|
|
@@ -21328,7 +21459,7 @@ var footnoteOp = import_zod2.z.object({
|
|
|
21328
21459
|
var endnoteOp = import_zod2.z.object({
|
|
21329
21460
|
action: import_zod2.z.literal("endnote"),
|
|
21330
21461
|
operation: import_zod2.z.enum(["insert"]).optional(),
|
|
21331
|
-
anchor: import_zod2.z.string().min(1).max(256),
|
|
21462
|
+
anchor: import_zod2.z.string().min(1).max(256).describe(ANCHOR_REF_SHORT),
|
|
21332
21463
|
text: import_zod2.z.string().min(1).max(5e4),
|
|
21333
21464
|
id: import_zod2.z.string().min(1).max(256).optional(),
|
|
21334
21465
|
offset: import_zod2.z.number().int().nonnegative().optional(),
|
|
@@ -21507,11 +21638,11 @@ function insertBodyAnchorDetail(op) {
|
|
|
21507
21638
|
if (effectiveTarget === "header" || effectiveTarget === "footer") return null;
|
|
21508
21639
|
const missing = [];
|
|
21509
21640
|
if (typeof rec["anchor"] !== "string" || rec["anchor"].length < 1) {
|
|
21510
|
-
missing.push("anchor: anchor is required for body inserts [invalid_type]");
|
|
21641
|
+
missing.push("anchor: anchor is required for body inserts: a node id from inspect_document, or 'start' / 'end' for the first / last block [invalid_type]");
|
|
21511
21642
|
}
|
|
21512
21643
|
const pos = rec["position"];
|
|
21513
|
-
if (pos !== "before" && pos !== "after") {
|
|
21514
|
-
missing.push("position: position is required for body inserts [invalid_type]");
|
|
21644
|
+
if (pos !== "before" && pos !== "after" && anchorAliasOf(rec["anchor"]) === null) {
|
|
21645
|
+
missing.push("position: position is required for body inserts (or use anchor 'start' / 'end', which imply it) [invalid_type]");
|
|
21515
21646
|
}
|
|
21516
21647
|
return missing.length > 0 ? missing.join("; ") : null;
|
|
21517
21648
|
}
|
|
@@ -28225,7 +28356,7 @@ function resolveImageFilePath(allowedDir, filePath) {
|
|
|
28225
28356
|
"use a plain relative file path inside the allowed directory"
|
|
28226
28357
|
);
|
|
28227
28358
|
}
|
|
28228
|
-
if (
|
|
28359
|
+
if (hasUriScheme(trimmed)) {
|
|
28229
28360
|
throw toError(
|
|
28230
28361
|
"E_SANDBOX",
|
|
28231
28362
|
"image filePath points outside the allowed directory",
|
|
@@ -28396,7 +28527,7 @@ function resolveAssetPath(allowedDir, refPath, line2, what) {
|
|
|
28396
28527
|
if (trimmed.includes("\0") || /%00/i.test(trimmed) || trimmed.includes("\n") || trimmed.includes("\r")) {
|
|
28397
28528
|
fail(line2, `${what} reference path is not usable`, "use a plain relative file path inside the allowed directory");
|
|
28398
28529
|
}
|
|
28399
|
-
if (
|
|
28530
|
+
if (hasUriScheme(trimmed)) {
|
|
28400
28531
|
fail(line2, `${what} reference points outside the allowed directory`, "keep referenced files inside the allowed directory and use a relative path");
|
|
28401
28532
|
}
|
|
28402
28533
|
const candidate = import_path5.default.isAbsolute(trimmed) ? import_path5.default.resolve(trimmed) : import_path5.default.resolve(allowedDir, trimmed);
|
|
@@ -29666,7 +29797,8 @@ async function executeCallout(op, ast, parser2, guide) {
|
|
|
29666
29797
|
if (!targetSection) throw new Error("No section available for callout box insertion");
|
|
29667
29798
|
const ownerDoc = targetSection.nodes[0]?.xml?.ownerDocument || ownerDocumentOf(ast.sections);
|
|
29668
29799
|
if (!ownerDoc) throw new Error("Cannot resolve XML DOM document");
|
|
29669
|
-
const
|
|
29800
|
+
const clientId = pick("id", op.id);
|
|
29801
|
+
const calloutId = typeof clientId === "string" && clientId.length > 0 ? clientId : isCalloutReproducible() ? (() => {
|
|
29670
29802
|
const base = `callout_${calloutHash8(`${resolvedAnchor ?? ""}:${resolvedPosition}:${themeKey}:${resolvedTitle ?? ""}:${resolvedText}`)}`;
|
|
29671
29803
|
const live = /* @__PURE__ */ new Set();
|
|
29672
29804
|
try {
|
|
@@ -30977,6 +31109,208 @@ function executePageBorder(op, ast, parser2) {
|
|
|
30977
31109
|
section.version += 1;
|
|
30978
31110
|
}
|
|
30979
31111
|
|
|
31112
|
+
// ../engine/src/compose/pristine-seed.ts
|
|
31113
|
+
function pristineTemplateSeedIds(parser2, requestedTemplate) {
|
|
31114
|
+
if (!parser2.hasPristineBuiltinSeed()) return null;
|
|
31115
|
+
let summary;
|
|
31116
|
+
try {
|
|
31117
|
+
summary = parser2.getStructuralSummary();
|
|
31118
|
+
} catch {
|
|
31119
|
+
return null;
|
|
31120
|
+
}
|
|
31121
|
+
const nodes = summary.nodes ?? [];
|
|
31122
|
+
if (![1, 2, 4, 5, 6].includes(nodes.length)) return null;
|
|
31123
|
+
if (summary.hasTrackedChanges === true) return null;
|
|
31124
|
+
if (Object.keys(summary.mediaFiles ?? {}).length > 0) return null;
|
|
31125
|
+
if ((summary.headers ?? []).length > 0) return null;
|
|
31126
|
+
if ((summary.footers ?? []).length > 0) return null;
|
|
31127
|
+
if ((summary.charts ?? []).length > 0) return null;
|
|
31128
|
+
try {
|
|
31129
|
+
const zip = parser2.getZipAdapter?.();
|
|
31130
|
+
if (typeof zip?.hasFile === "function") {
|
|
31131
|
+
for (const part of [
|
|
31132
|
+
"word/footnotes.xml",
|
|
31133
|
+
"word/endnotes.xml",
|
|
31134
|
+
"word/comments.xml",
|
|
31135
|
+
"word/commentsExtended.xml"
|
|
31136
|
+
]) {
|
|
31137
|
+
try {
|
|
31138
|
+
if (zip.hasFile(part)) return null;
|
|
31139
|
+
} catch {
|
|
31140
|
+
return null;
|
|
31141
|
+
}
|
|
31142
|
+
}
|
|
31143
|
+
} else {
|
|
31144
|
+
return null;
|
|
31145
|
+
}
|
|
31146
|
+
} catch {
|
|
31147
|
+
return null;
|
|
31148
|
+
}
|
|
31149
|
+
for (const n of nodes) {
|
|
31150
|
+
if (n.hasTrackedChanges === true) return null;
|
|
31151
|
+
const raw = n.rawXml ?? "";
|
|
31152
|
+
if (raw.includes("w:footnoteReference") || raw.includes("w:endnoteReference") || raw.includes("w:commentReference") || raw.includes("w:commentRangeStart") || raw.includes("w:commentRangeEnd")) {
|
|
31153
|
+
return null;
|
|
31154
|
+
}
|
|
31155
|
+
}
|
|
31156
|
+
const ids = nodes.map((n) => n.id ?? "").filter((id) => id.length > 0);
|
|
31157
|
+
if (ids.length !== nodes.length) return null;
|
|
31158
|
+
if (nodes.length === 1) {
|
|
31159
|
+
const node = nodes[0];
|
|
31160
|
+
if (node.type !== "paragraph") return null;
|
|
31161
|
+
if ((node.text ?? "").trim() !== "") return null;
|
|
31162
|
+
return ids;
|
|
31163
|
+
}
|
|
31164
|
+
if (nodes.length === 2) {
|
|
31165
|
+
const [first, second] = nodes;
|
|
31166
|
+
const t0 = (first.text ?? "").trim();
|
|
31167
|
+
const t1 = (second.text ?? "").trim();
|
|
31168
|
+
if (t0 === BLANK_SEED_TITLE && t1 === BLANK_SEED_HINT) {
|
|
31169
|
+
const firstRaw = first.rawXml ?? "";
|
|
31170
|
+
const secondRaw = second.rawXml ?? "";
|
|
31171
|
+
if (!firstRaw.includes('w:val="Title"') || !secondRaw.includes('w:val="Subtitle"')) return null;
|
|
31172
|
+
return ids;
|
|
31173
|
+
}
|
|
31174
|
+
const tmpl = requestedTemplate?.toLowerCase();
|
|
31175
|
+
if (t0 === "MEMORANDUM" && tmpl === "memo") return ids;
|
|
31176
|
+
if (t0 === "Letter" && tmpl === "letter") return ids;
|
|
31177
|
+
if (t0 === "Proposal" && tmpl === "proposal") return ids;
|
|
31178
|
+
if (t0 === "INVOICE" && tmpl === "invoice") return ids;
|
|
31179
|
+
return null;
|
|
31180
|
+
}
|
|
31181
|
+
if (nodes.length === 4) {
|
|
31182
|
+
const t0 = (nodes[0].text ?? "").trim();
|
|
31183
|
+
const tmpl = requestedTemplate?.toLowerCase();
|
|
31184
|
+
if (t0 === "Research Benchmark Report" && tmpl === "research") return ids;
|
|
31185
|
+
if (t0 === "Corporate Strategy Roadmap" && tmpl === "corporate") return ids;
|
|
31186
|
+
if (t0 === "Velocity Audit & Performance" && tmpl === "bar-chart-3d") return ids;
|
|
31187
|
+
return null;
|
|
31188
|
+
}
|
|
31189
|
+
if (nodes.length === 5) {
|
|
31190
|
+
const t0 = (nodes[0].text ?? "").trim();
|
|
31191
|
+
const tmpl = requestedTemplate?.toLowerCase();
|
|
31192
|
+
if (t0 === "Financial Report & Audit" && tmpl === "financial") return ids;
|
|
31193
|
+
return null;
|
|
31194
|
+
}
|
|
31195
|
+
if (nodes.length === 6) {
|
|
31196
|
+
const t0 = (nodes[0].text ?? "").trim();
|
|
31197
|
+
const t2 = (nodes[2].text ?? "").trim();
|
|
31198
|
+
const tmpl = requestedTemplate?.toLowerCase();
|
|
31199
|
+
if (t0 === "Q3 Executive Financial Performance Report" && t2 === "1. Executive Summary") {
|
|
31200
|
+
if (tmpl === "report") return ids;
|
|
31201
|
+
return null;
|
|
31202
|
+
}
|
|
31203
|
+
if (t0 === "Manuscript Title" && t2 === "Chapter 1: The Beginning" && tmpl === "novel") {
|
|
31204
|
+
return ids;
|
|
31205
|
+
}
|
|
31206
|
+
if (t0 === "Neural Document Architectures: A Comparative Benchmark" && tmpl === "academic") {
|
|
31207
|
+
return ids;
|
|
31208
|
+
}
|
|
31209
|
+
if (t0 === "The ASTra Dispatch \u2014 Monthly Engineering Gazette" && tmpl === "newsletter") return ids;
|
|
31210
|
+
if (t0 === "ASTra Document Engine API Specification" && (tmpl === "api" || tmpl === "api-spec")) return ids;
|
|
31211
|
+
return null;
|
|
31212
|
+
}
|
|
31213
|
+
return null;
|
|
31214
|
+
}
|
|
31215
|
+
var SEED_TEMPLATE_NAMES = [
|
|
31216
|
+
"memo",
|
|
31217
|
+
"letter",
|
|
31218
|
+
"proposal",
|
|
31219
|
+
"invoice",
|
|
31220
|
+
"research",
|
|
31221
|
+
"corporate",
|
|
31222
|
+
"bar-chart-3d",
|
|
31223
|
+
"financial",
|
|
31224
|
+
"report",
|
|
31225
|
+
"novel",
|
|
31226
|
+
"academic",
|
|
31227
|
+
"newsletter",
|
|
31228
|
+
"api-spec"
|
|
31229
|
+
];
|
|
31230
|
+
function pristineSeedIdsOfAnyTemplate(parser2) {
|
|
31231
|
+
const blank = pristineTemplateSeedIds(parser2);
|
|
31232
|
+
if (blank !== null) return blank;
|
|
31233
|
+
for (const name2 of SEED_TEMPLATE_NAMES) {
|
|
31234
|
+
const ids = pristineTemplateSeedIds(parser2, name2);
|
|
31235
|
+
if (ids !== null) return ids;
|
|
31236
|
+
}
|
|
31237
|
+
return null;
|
|
31238
|
+
}
|
|
31239
|
+
|
|
31240
|
+
// ../engine/src/document-title.ts
|
|
31241
|
+
var TEMPLATE_NAMES = [
|
|
31242
|
+
"blank",
|
|
31243
|
+
"memo",
|
|
31244
|
+
"report",
|
|
31245
|
+
"letter",
|
|
31246
|
+
"proposal",
|
|
31247
|
+
"invoice",
|
|
31248
|
+
"research",
|
|
31249
|
+
"corporate",
|
|
31250
|
+
"financial",
|
|
31251
|
+
"novel",
|
|
31252
|
+
"academic",
|
|
31253
|
+
"newsletter",
|
|
31254
|
+
"api-spec",
|
|
31255
|
+
"bar-chart-3d"
|
|
31256
|
+
];
|
|
31257
|
+
var PLACEHOLDER_TITLES = new Set(TEMPLATE_NAMES.map((name2) => newDocumentTitle(name2).toLowerCase()));
|
|
31258
|
+
var DERIVED_TITLE_MAX = 200;
|
|
31259
|
+
var DEFAULT_CREATOR = "ASTra";
|
|
31260
|
+
function isPlaceholderTitle(title) {
|
|
31261
|
+
if (typeof title !== "string") return true;
|
|
31262
|
+
const clean = title.replace(/\s+/g, " ").trim().toLowerCase();
|
|
31263
|
+
return clean === "" || PLACEHOLDER_TITLES.has(clean);
|
|
31264
|
+
}
|
|
31265
|
+
function cleanTitle(text2) {
|
|
31266
|
+
const plain = typeof text2 === "string" ? Array.from(text2, (ch) => ch.charCodeAt(0) < 32 || ch.charCodeAt(0) === 127 ? " " : ch).join("").replace(/\s+/g, " ").trim() : "";
|
|
31267
|
+
return plain.length > DERIVED_TITLE_MAX ? plain.slice(0, DERIVED_TITLE_MAX).trimEnd() : plain;
|
|
31268
|
+
}
|
|
31269
|
+
function deriveTitleFromContent(ast) {
|
|
31270
|
+
const nodes = ast.sections.flatMap((section) => section.nodes);
|
|
31271
|
+
const styleOf = (node) => {
|
|
31272
|
+
const pPr = Array.from(node.xml.childNodes).find((c) => c.nodeType === 1 && c.tagName === "w:pPr");
|
|
31273
|
+
const pStyle = pPr ? Array.from(pPr.childNodes).find((c) => c.nodeType === 1 && c.tagName === "w:pStyle") : void 0;
|
|
31274
|
+
return pStyle?.getAttribute("w:val") ?? node.metadata?.namedStyle ?? void 0;
|
|
31275
|
+
};
|
|
31276
|
+
const textOf4 = (node) => {
|
|
31277
|
+
const runs = node.xml.getElementsByTagName("w:t");
|
|
31278
|
+
let text2 = "";
|
|
31279
|
+
for (let i = 0; i < runs.length; i++) text2 += runs[i].textContent ?? "";
|
|
31280
|
+
return text2 !== "" ? text2 : node.metadata?.text ?? "";
|
|
31281
|
+
};
|
|
31282
|
+
const pick = (match, from) => {
|
|
31283
|
+
for (const node of nodes) {
|
|
31284
|
+
if (node.type !== "paragraph" || !match(node)) continue;
|
|
31285
|
+
const title = cleanTitle(textOf4(node));
|
|
31286
|
+
if (title !== "" && !isPlaceholderTitle(title)) return { title, from };
|
|
31287
|
+
}
|
|
31288
|
+
return null;
|
|
31289
|
+
};
|
|
31290
|
+
return pick((n) => n.id.startsWith("cover_title_"), "cover page") ?? pick((n) => styleOf(n) === "Title", "Title") ?? pick((n) => styleOf(n) === "Heading1", "Heading 1");
|
|
31291
|
+
}
|
|
31292
|
+
function applyDocumentIdentity(ast, parser2) {
|
|
31293
|
+
const result = {};
|
|
31294
|
+
const patch = {};
|
|
31295
|
+
if (isPlaceholderTitle(ast.metadata?.title)) {
|
|
31296
|
+
const derived = deriveTitleFromContent(ast);
|
|
31297
|
+
if (derived) {
|
|
31298
|
+
patch.title = derived.title;
|
|
31299
|
+
result.title = derived.title;
|
|
31300
|
+
result.titleSetFrom = derived.from;
|
|
31301
|
+
}
|
|
31302
|
+
}
|
|
31303
|
+
const author = typeof ast.settings?.defaultAuthor === "string" ? ast.settings.defaultAuthor.trim() : "";
|
|
31304
|
+
if (author !== "" && author !== DEFAULT_CREATOR && ast.metadata?.creator === DEFAULT_CREATOR) {
|
|
31305
|
+
patch.creator = author;
|
|
31306
|
+
patch.lastModifiedBy = author;
|
|
31307
|
+
result.creatorSet = author;
|
|
31308
|
+
}
|
|
31309
|
+
if (Object.keys(patch).length === 0) return result;
|
|
31310
|
+
new CorePropertiesManipulator(ast, parser2?.getZipAdapter()).update(patch);
|
|
31311
|
+
return result;
|
|
31312
|
+
}
|
|
31313
|
+
|
|
30980
31314
|
// ../engine/src/operations/coverPage.ts
|
|
30981
31315
|
function isCoverReproducible(explicit, env) {
|
|
30982
31316
|
try {
|
|
@@ -31041,6 +31375,7 @@ function executeCoverPage(op, ast, parser2) {
|
|
|
31041
31375
|
if (!firstSection || firstSection.nodes.length === 0) {
|
|
31042
31376
|
throw new Error("Cannot insert cover page into an empty document");
|
|
31043
31377
|
}
|
|
31378
|
+
const seedIds = parser2 ? pristineSeedIdsOfAnyTemplate(parser2) : null;
|
|
31044
31379
|
const doc = firstSection.nodes[0].xml.ownerDocument;
|
|
31045
31380
|
if (!doc) throw new Error("Document not found on first section");
|
|
31046
31381
|
const sectPr = doc.getElementsByTagName("w:sectPr")[0];
|
|
@@ -31107,8 +31442,8 @@ function executeCoverPage(op, ast, parser2) {
|
|
|
31107
31442
|
titleColor.setAttribute("w:val", accentColor);
|
|
31108
31443
|
titleRPr.appendChild(titleFont);
|
|
31109
31444
|
titleRPr.appendChild(titleBold);
|
|
31110
|
-
titleRPr.appendChild(titleSz);
|
|
31111
31445
|
titleRPr.appendChild(titleColor);
|
|
31446
|
+
titleRPr.appendChild(titleSz);
|
|
31112
31447
|
titleRun.appendChild(titleRPr);
|
|
31113
31448
|
titleRun.appendChild(createTextElement(doc, titleText));
|
|
31114
31449
|
titleP.appendChild(titleRun);
|
|
@@ -31134,8 +31469,8 @@ function executeCoverPage(op, ast, parser2) {
|
|
|
31134
31469
|
subSz.setAttribute("w:val", "28");
|
|
31135
31470
|
const subColor = createWordElement(doc, "w:color");
|
|
31136
31471
|
subColor.setAttribute("w:val", "64748B");
|
|
31137
|
-
subRPr.appendChild(subSz);
|
|
31138
31472
|
subRPr.appendChild(subColor);
|
|
31473
|
+
subRPr.appendChild(subSz);
|
|
31139
31474
|
subRun.appendChild(subRPr);
|
|
31140
31475
|
subRun.appendChild(createTextElement(doc, subtitleText));
|
|
31141
31476
|
subP.appendChild(subRun);
|
|
@@ -31185,8 +31520,8 @@ function executeCoverPage(op, ast, parser2) {
|
|
|
31185
31520
|
metaSz.setAttribute("w:val", "22");
|
|
31186
31521
|
const metaColor = createWordElement(doc, "w:color");
|
|
31187
31522
|
metaColor.setAttribute("w:val", "475569");
|
|
31188
|
-
metaRPr.appendChild(metaSz);
|
|
31189
31523
|
metaRPr.appendChild(metaColor);
|
|
31524
|
+
metaRPr.appendChild(metaSz);
|
|
31190
31525
|
metaRun.appendChild(metaRPr);
|
|
31191
31526
|
const metaLines = [authorText, orgText, dateText].filter(Boolean).join(" \u2022 ");
|
|
31192
31527
|
metaRun.appendChild(createTextElement(doc, metaLines || dateText));
|
|
@@ -31231,6 +31566,13 @@ function executeCoverPage(op, ast, parser2) {
|
|
|
31231
31566
|
}
|
|
31232
31567
|
}
|
|
31233
31568
|
}
|
|
31569
|
+
if (seedIds !== null && seedIds.length > 0) {
|
|
31570
|
+
executeDelete({ action: "delete", target: seedIds }, ast, parser2);
|
|
31571
|
+
}
|
|
31572
|
+
try {
|
|
31573
|
+
applyDocumentIdentity(ast, parser2);
|
|
31574
|
+
} catch {
|
|
31575
|
+
}
|
|
31234
31576
|
}
|
|
31235
31577
|
|
|
31236
31578
|
// ../engine/src/operations/metadata.ts
|
|
@@ -31402,8 +31744,9 @@ function collectBatchTouchedIds(ops, ast) {
|
|
|
31402
31744
|
const touch = (raw) => {
|
|
31403
31745
|
if (typeof raw !== "string" || raw.length === 0) return;
|
|
31404
31746
|
let resolved = raw;
|
|
31405
|
-
|
|
31406
|
-
|
|
31747
|
+
const alias = anchorAliasOf(raw);
|
|
31748
|
+
if (alias === "start") resolved = order[0] ?? raw;
|
|
31749
|
+
else if (alias === "end") resolved = order[order.length - 1] ?? raw;
|
|
31407
31750
|
else {
|
|
31408
31751
|
const cell = parseCellAddress(raw);
|
|
31409
31752
|
if (cell && order.includes(cell.tableId)) resolved = cell.tableId;
|
|
@@ -31996,13 +32339,13 @@ var OperationEngine = class _OperationEngine {
|
|
|
31996
32339
|
/**
|
|
31997
32340
|
* R-4 helper: true when the op mints a live node id from the client id.
|
|
31998
32341
|
* Only these routes use `op.id` verbatim as the node id; other actions
|
|
31999
|
-
* either ignore `id` (footnote/
|
|
32342
|
+
* either ignore `id` (footnote/field generate their own) or are
|
|
32000
32343
|
* rejected at the schema gate, so live collision applies here only.
|
|
32001
32344
|
*/
|
|
32002
32345
|
static mintsNodeId(op) {
|
|
32003
32346
|
const fields = op;
|
|
32004
32347
|
const action = typeof fields["action"] === "string" ? String(fields["action"]) : "";
|
|
32005
|
-
if (action === "insert") return true;
|
|
32348
|
+
if (action === "insert" || action === "callout") return true;
|
|
32006
32349
|
if (action === "equation") {
|
|
32007
32350
|
const sub = fields["operation"] ?? fields["params"]?.["operation"];
|
|
32008
32351
|
return sub === "insert";
|
|
@@ -32102,7 +32445,7 @@ var OperationEngine = class _OperationEngine {
|
|
|
32102
32445
|
* E_VALIDATION + remedy.
|
|
32103
32446
|
*/
|
|
32104
32447
|
static isAnchorAlias(value) {
|
|
32105
|
-
return value
|
|
32448
|
+
return anchorAliasOf(value) !== null;
|
|
32106
32449
|
}
|
|
32107
32450
|
resolveAnchorAlias(op) {
|
|
32108
32451
|
const fields = op;
|
|
@@ -32118,6 +32461,10 @@ var OperationEngine = class _OperationEngine {
|
|
|
32118
32461
|
}
|
|
32119
32462
|
}
|
|
32120
32463
|
if (aliasSeen === null) return;
|
|
32464
|
+
for (const h of holders) {
|
|
32465
|
+
const alias = anchorAliasOf(h["anchor"]);
|
|
32466
|
+
if (alias !== null && h["position"] === void 0) h["position"] = alias === "start" ? "before" : "after";
|
|
32467
|
+
}
|
|
32121
32468
|
let first;
|
|
32122
32469
|
let last;
|
|
32123
32470
|
for (const section of this.ast.sections ?? []) {
|
|
@@ -32134,8 +32481,9 @@ var OperationEngine = class _OperationEngine {
|
|
|
32134
32481
|
);
|
|
32135
32482
|
}
|
|
32136
32483
|
for (const h of holders) {
|
|
32137
|
-
|
|
32138
|
-
|
|
32484
|
+
const alias = anchorAliasOf(h["anchor"]);
|
|
32485
|
+
if (alias === "start") h["anchor"] = first;
|
|
32486
|
+
else if (alias === "end") h["anchor"] = last;
|
|
32139
32487
|
}
|
|
32140
32488
|
}
|
|
32141
32489
|
async routeOperation(op, allowedDir) {
|
|
@@ -33392,7 +33740,11 @@ ${textLines.map((l) => `> ${l}`).join("\n")}
|
|
|
33392
33740
|
`);
|
|
33393
33741
|
} else {
|
|
33394
33742
|
const textLines = text2 ? text2.split("\n") : [];
|
|
33395
|
-
|
|
33743
|
+
const ind = node.xml?.getElementsByTagName?.("w:pPr")?.[0]?.getElementsByTagName?.("w:ind")?.[0] ?? null;
|
|
33744
|
+
const left = ind ? parseInt(ind.getAttribute("w:left") || "0", 10) : 0;
|
|
33745
|
+
const depth = style === "quote" && Number.isFinite(left) && left > 0 ? Math.min(3, Math.max(1, Math.round(left / 720))) : 1;
|
|
33746
|
+
const marker = ">".repeat(1).concat(" >".repeat(depth - 1));
|
|
33747
|
+
lines.push(`${textLines.map((l) => `${marker} ${l}`).join("\n")}
|
|
33396
33748
|
`);
|
|
33397
33749
|
}
|
|
33398
33750
|
} else {
|
|
@@ -33896,17 +34248,19 @@ function directKids(parent, tag) {
|
|
|
33896
34248
|
return out;
|
|
33897
34249
|
}
|
|
33898
34250
|
function cellTextOf(tc) {
|
|
33899
|
-
|
|
34251
|
+
const parts = [];
|
|
33900
34252
|
try {
|
|
33901
34253
|
const paras = directKids(tc, "w:p");
|
|
33902
34254
|
for (const p of paras) {
|
|
34255
|
+
let text2 = "";
|
|
33903
34256
|
const ts = p.getElementsByTagName("w:t");
|
|
33904
34257
|
for (let i = 0; i < ts.length; i++) text2 += ts[i].textContent || "";
|
|
34258
|
+
if (text2.trim().length > 0) parts.push(text2);
|
|
33905
34259
|
}
|
|
33906
34260
|
} catch {
|
|
33907
34261
|
return "";
|
|
33908
34262
|
}
|
|
33909
|
-
return
|
|
34263
|
+
return parts.join(" ");
|
|
33910
34264
|
}
|
|
33911
34265
|
function displayLen(value) {
|
|
33912
34266
|
return [...value].length;
|
|
@@ -38075,6 +38429,134 @@ var HeadlessVisualRenderer = class {
|
|
|
38075
38429
|
};
|
|
38076
38430
|
var headlessScreenshotProvider = new HeadlessVisualRenderer();
|
|
38077
38431
|
|
|
38432
|
+
// ../engine/src/stats.ts
|
|
38433
|
+
function nearestOwner(el, tagName) {
|
|
38434
|
+
let cur = el.parentNode;
|
|
38435
|
+
while (cur && typeof cur === "object") {
|
|
38436
|
+
const e = cur;
|
|
38437
|
+
if (e.tagName === tagName) return e;
|
|
38438
|
+
cur = e.parentNode;
|
|
38439
|
+
}
|
|
38440
|
+
return null;
|
|
38441
|
+
}
|
|
38442
|
+
function tableCellTextSpaced(node) {
|
|
38443
|
+
try {
|
|
38444
|
+
const xml = node.xml;
|
|
38445
|
+
if (!xml || typeof xml.getElementsByTagName !== "function") return void 0;
|
|
38446
|
+
const tcs = xml.getElementsByTagName("w:tc");
|
|
38447
|
+
if (!tcs || tcs.length === 0) return void 0;
|
|
38448
|
+
const cells = [];
|
|
38449
|
+
for (let i = 0; i < tcs.length; i++) {
|
|
38450
|
+
const tc = tcs[i];
|
|
38451
|
+
const paras = tc.getElementsByTagName("w:p");
|
|
38452
|
+
if (paras.length > 0) {
|
|
38453
|
+
const parts = [];
|
|
38454
|
+
for (let p = 0; p < paras.length; p++) {
|
|
38455
|
+
const para = paras[p];
|
|
38456
|
+
if (nearestOwner(para, "w:tc") !== tc) continue;
|
|
38457
|
+
const runs = para.getElementsByTagName("w:t");
|
|
38458
|
+
let t = "";
|
|
38459
|
+
for (let r = 0; r < runs.length; r++) {
|
|
38460
|
+
const run2 = runs[r];
|
|
38461
|
+
if (nearestOwner(run2, "w:tc") !== tc) continue;
|
|
38462
|
+
if (nearestOwner(run2, "w:p") !== para) continue;
|
|
38463
|
+
t += run2.textContent || "";
|
|
38464
|
+
}
|
|
38465
|
+
if (t.trim().length > 0) parts.push(t.trim());
|
|
38466
|
+
}
|
|
38467
|
+
if (parts.length > 0) cells.push(parts.join(" "));
|
|
38468
|
+
} else {
|
|
38469
|
+
const runs = tc.getElementsByTagName("w:t");
|
|
38470
|
+
let t = "";
|
|
38471
|
+
for (let r = 0; r < runs.length; r++) {
|
|
38472
|
+
const run2 = runs[r];
|
|
38473
|
+
if (nearestOwner(run2, "w:tc") !== tc) continue;
|
|
38474
|
+
t += run2.textContent || "";
|
|
38475
|
+
}
|
|
38476
|
+
if (t.trim().length > 0) cells.push(t.trim());
|
|
38477
|
+
}
|
|
38478
|
+
}
|
|
38479
|
+
if (cells.length === 0) return "";
|
|
38480
|
+
return cells.join(" ");
|
|
38481
|
+
} catch {
|
|
38482
|
+
return void 0;
|
|
38483
|
+
}
|
|
38484
|
+
}
|
|
38485
|
+
function nestedTableCount(node) {
|
|
38486
|
+
try {
|
|
38487
|
+
const xml = node.xml;
|
|
38488
|
+
if (!xml || typeof xml.getElementsByTagName !== "function") return 0;
|
|
38489
|
+
return xml.getElementsByTagName("w:tbl").length;
|
|
38490
|
+
} catch {
|
|
38491
|
+
return 0;
|
|
38492
|
+
}
|
|
38493
|
+
}
|
|
38494
|
+
function isCalloutTable(node, dims) {
|
|
38495
|
+
try {
|
|
38496
|
+
const xml = node.xml;
|
|
38497
|
+
if (!xml || typeof xml.getElementsByTagName !== "function") return false;
|
|
38498
|
+
if (dims) {
|
|
38499
|
+
if (dims.rows !== 1 || dims.cols !== 1) return false;
|
|
38500
|
+
} else {
|
|
38501
|
+
const rows = Array.from(xml.childNodes).filter((n) => n.nodeType === 1 && n.tagName === "w:tr");
|
|
38502
|
+
if (rows.length !== 1) return false;
|
|
38503
|
+
const cells = Array.from(rows[0].childNodes).filter((n) => n.nodeType === 1 && n.tagName === "w:tc");
|
|
38504
|
+
if (cells.length !== 1) return false;
|
|
38505
|
+
}
|
|
38506
|
+
const fill = xml.getElementsByTagName("w:shd")[0]?.getAttribute("w:fill") ?? "";
|
|
38507
|
+
return /^[0-9A-Fa-f]{6}$/.test(fill);
|
|
38508
|
+
} catch {
|
|
38509
|
+
return false;
|
|
38510
|
+
}
|
|
38511
|
+
}
|
|
38512
|
+
function countExplicitPageBreaks(node) {
|
|
38513
|
+
if (node.type === "pageBreak") return 1;
|
|
38514
|
+
const text2 = node.metadata?.text || "";
|
|
38515
|
+
let count = text2 === "[Page Break]" ? 1 : 0;
|
|
38516
|
+
try {
|
|
38517
|
+
const xml = node.xml;
|
|
38518
|
+
if (xml && typeof xml.getElementsByTagName === "function") {
|
|
38519
|
+
const brs = xml.getElementsByTagName("w:br");
|
|
38520
|
+
let brCount = 0;
|
|
38521
|
+
for (let i = 0; i < brs.length; i++) {
|
|
38522
|
+
if (brs[i].getAttribute("w:type") === "page") brCount++;
|
|
38523
|
+
}
|
|
38524
|
+
if (brCount > count) count = brCount;
|
|
38525
|
+
}
|
|
38526
|
+
} catch {
|
|
38527
|
+
}
|
|
38528
|
+
return count;
|
|
38529
|
+
}
|
|
38530
|
+
function computeDocStats(ast) {
|
|
38531
|
+
let words = 0;
|
|
38532
|
+
let paragraphs = 0;
|
|
38533
|
+
let tables = 0;
|
|
38534
|
+
let callouts = 0;
|
|
38535
|
+
let images = 0;
|
|
38536
|
+
let pageBreaks = 0;
|
|
38537
|
+
for (const sec of ast.sections) {
|
|
38538
|
+
for (const node of sec.nodes) {
|
|
38539
|
+
if (node.type === "paragraph") paragraphs += 1;
|
|
38540
|
+
if (node.type === "table") {
|
|
38541
|
+
if (isCalloutTable(node)) callouts += 1;
|
|
38542
|
+
else tables += 1;
|
|
38543
|
+
tables += nestedTableCount(node);
|
|
38544
|
+
}
|
|
38545
|
+
if (node.type === "image") images += 1;
|
|
38546
|
+
pageBreaks += countExplicitPageBreaks(node);
|
|
38547
|
+
let text2 = node.metadata?.text ?? "";
|
|
38548
|
+
if (node.type === "table") {
|
|
38549
|
+
const spaced = tableCellTextSpaced(node);
|
|
38550
|
+
if (spaced !== void 0) text2 = spaced;
|
|
38551
|
+
}
|
|
38552
|
+
const hits = text2.match(/\S+/g);
|
|
38553
|
+
if (hits) words += hits.length;
|
|
38554
|
+
}
|
|
38555
|
+
}
|
|
38556
|
+
const pageBudget = words / 450 + (tables + callouts) * 0.45 + images * 0.5 + pageBreaks;
|
|
38557
|
+
return { words, paragraphs, pages: Math.max(1, Math.ceil(pageBudget)), tables, callouts, images };
|
|
38558
|
+
}
|
|
38559
|
+
|
|
38078
38560
|
// ../engine/src/visual/telemetry.ts
|
|
38079
38561
|
var VISUAL_TELEMETRY_TOKEN_BUDGET = 500;
|
|
38080
38562
|
var VISUAL_WIREFRAME_WIDTH = 68;
|
|
@@ -38171,17 +38653,6 @@ var WIREFRAME_TABLE_ROWS = 3;
|
|
|
38171
38653
|
function flatText(value) {
|
|
38172
38654
|
return value.replace(/\s+/g, " ").trim();
|
|
38173
38655
|
}
|
|
38174
|
-
function isCalloutTable(node, grid) {
|
|
38175
|
-
if (!grid || grid.rows !== 1 || grid.cols !== 1) return false;
|
|
38176
|
-
try {
|
|
38177
|
-
const xml = node.xml;
|
|
38178
|
-
const shd = xml?.getElementsByTagName("w:shd")[0];
|
|
38179
|
-
const fill = shd?.getAttribute("w:fill") ?? "";
|
|
38180
|
-
return /^[0-9A-Fa-f]{6}$/.test(fill);
|
|
38181
|
-
} catch {
|
|
38182
|
-
return false;
|
|
38183
|
-
}
|
|
38184
|
-
}
|
|
38185
38656
|
function tableRowLines(grid, width) {
|
|
38186
38657
|
const byRow = /* @__PURE__ */ new Map();
|
|
38187
38658
|
for (const cell of grid.cells) {
|
|
@@ -38404,7 +38875,7 @@ function generateVisualWireframe(ast, targetPage = 1, pageMap, maxNodes = 24, op
|
|
|
38404
38875
|
} else if (node.type === "table") {
|
|
38405
38876
|
lines.push(emptyLine);
|
|
38406
38877
|
const grid = tableGridOf(node);
|
|
38407
|
-
if (isCalloutTable(node, grid)) {
|
|
38878
|
+
if (isCalloutTable(node, grid ? { rows: grid.rows, cols: grid.cols } : null)) {
|
|
38408
38879
|
const note = flatText(grid?.cells[0]?.text ?? "");
|
|
38409
38880
|
const body = note.length > 48 ? note.slice(0, 45) + "..." : note;
|
|
38410
38881
|
lines.push(`| [CALLOUT] ${body}`.slice(0, W2 - 2).padEnd(W2 - 1) + "|");
|
|
@@ -38591,6 +39062,10 @@ function buildLineIndex(src, tokens) {
|
|
|
38591
39062
|
}
|
|
38592
39063
|
return out;
|
|
38593
39064
|
}
|
|
39065
|
+
var QUOTE_INDENT = QUOTE_INDENT_TWIPS;
|
|
39066
|
+
var QUOTE_LIST_INDENT = 360;
|
|
39067
|
+
var QUOTE_MAX_LEVEL = 3;
|
|
39068
|
+
var CODE_BLOCK_BASE_INDENT = 80;
|
|
38594
39069
|
function subLine(blockRaw, blockLine, excerpt) {
|
|
38595
39070
|
const found = blockRaw.indexOf(excerpt);
|
|
38596
39071
|
if (found === -1) return blockLine;
|
|
@@ -38762,12 +39237,17 @@ function compileMarkdownDetailed(markdown, options) {
|
|
|
38762
39237
|
const pendingNotes = [];
|
|
38763
39238
|
let seq = 0;
|
|
38764
39239
|
let lastInsertId = null;
|
|
39240
|
+
const idPrefix = options?.idPrefix ?? "md";
|
|
38765
39241
|
const nextId = () => {
|
|
38766
39242
|
seq += 1;
|
|
38767
|
-
return
|
|
39243
|
+
return `${idPrefix}_${String(seq).padStart(4, "0")}`;
|
|
39244
|
+
};
|
|
39245
|
+
const startAnchor = {
|
|
39246
|
+
anchor: options?.anchor ?? "end",
|
|
39247
|
+
position: options?.position ?? "after"
|
|
38768
39248
|
};
|
|
38769
39249
|
const insertAnchor = () => {
|
|
38770
|
-
if (lastInsertId === null) return {
|
|
39250
|
+
if (lastInsertId === null) return { ...startAnchor };
|
|
38771
39251
|
return { anchor: lastInsertId, position: "after" };
|
|
38772
39252
|
};
|
|
38773
39253
|
const pushInsertParagraph = (text2, spans) => {
|
|
@@ -38895,12 +39375,12 @@ function compileMarkdownDetailed(markdown, options) {
|
|
|
38895
39375
|
const inlineMime = inlineKind === "jpeg" ? "image/jpeg" : inlineKind === "svg" ? "image/svg+xml" : inlineKind === "tiff" ? "image/tiff" : `image/${inlineKind}`;
|
|
38896
39376
|
const inlineFitted = fitImageEmus(decodeImagePixels(inlineBuf, inlineKind));
|
|
38897
39377
|
const inlineId = nextId();
|
|
38898
|
-
const inlineAnchor =
|
|
39378
|
+
const inlineAnchor = insertAnchor();
|
|
38899
39379
|
ops.push({
|
|
38900
39380
|
action: "insert",
|
|
38901
39381
|
type: "image",
|
|
38902
|
-
anchor: inlineAnchor,
|
|
38903
|
-
position:
|
|
39382
|
+
anchor: inlineAnchor.anchor,
|
|
39383
|
+
position: inlineAnchor.position,
|
|
38904
39384
|
id: inlineId,
|
|
38905
39385
|
fileData: `data:${inlineMime};base64,${inlineBuf.toString("base64")}`,
|
|
38906
39386
|
...inlineFitted ? { width: inlineFitted.width, height: inlineFitted.height } : {},
|
|
@@ -38909,7 +39389,7 @@ function compileMarkdownDetailed(markdown, options) {
|
|
|
38909
39389
|
lastInsertId = inlineId;
|
|
38910
39390
|
return { id: inlineId, kind: "image" };
|
|
38911
39391
|
}
|
|
38912
|
-
if (
|
|
39392
|
+
if (hasUriScheme(target)) {
|
|
38913
39393
|
fail(line2, "image reference points outside the allowed directory", "keep referenced files inside the allowed directory and use a relative path");
|
|
38914
39394
|
}
|
|
38915
39395
|
if (allowedDir === void 0) {
|
|
@@ -38918,14 +39398,17 @@ function compileMarkdownDetailed(markdown, options) {
|
|
|
38918
39398
|
const isChart = target.toLowerCase().endsWith(CHART_JSON_SUFFIX);
|
|
38919
39399
|
const absPath = resolveAssetPath(allowedDir, target, line2, isChart ? "chart" : "image");
|
|
38920
39400
|
const id = nextId();
|
|
38921
|
-
const
|
|
39401
|
+
const chartAnchor = insertAnchor();
|
|
38922
39402
|
if (isChart) {
|
|
39403
|
+
if (chartAnchor.position === "before") {
|
|
39404
|
+
fail(line2, "a chart cannot be the first block when the compose goes before a node", "start the Markdown with a paragraph, or place the compose after the node");
|
|
39405
|
+
}
|
|
38923
39406
|
const asset2 = readChartAsset(absPath, line2, allowedDir);
|
|
38924
39407
|
const title = asset2.title ?? (alt.trim().length > 0 ? alt.trim() : "Chart");
|
|
38925
39408
|
ops.push({
|
|
38926
39409
|
action: "chart",
|
|
38927
39410
|
operation: "insert",
|
|
38928
|
-
anchor:
|
|
39411
|
+
anchor: chartAnchor.anchor,
|
|
38929
39412
|
id,
|
|
38930
39413
|
chartType: asset2.chartType,
|
|
38931
39414
|
title,
|
|
@@ -38936,7 +39419,7 @@ function compileMarkdownDetailed(markdown, options) {
|
|
|
38936
39419
|
return { id, kind: "chart" };
|
|
38937
39420
|
}
|
|
38938
39421
|
const asset = readImageAsset(absPath, line2, allowedDir);
|
|
38939
|
-
const anchor =
|
|
39422
|
+
const anchor = insertAnchor();
|
|
38940
39423
|
ops.push({
|
|
38941
39424
|
action: "insert",
|
|
38942
39425
|
type: "image",
|
|
@@ -38954,15 +39437,11 @@ function compileMarkdownDetailed(markdown, options) {
|
|
|
38954
39437
|
const walkAlert = (list) => {
|
|
38955
39438
|
for (const tok of list) {
|
|
38956
39439
|
const t = tok;
|
|
38957
|
-
if (t.type === "strong" || t.type === "em" || t.type === "codespan") {
|
|
38958
|
-
fail(line2, "rich text inside alerts is out of scope", "move bold, italic, or code into a body paragraph");
|
|
38959
|
-
}
|
|
38960
39440
|
if (t.type === "link") {
|
|
38961
39441
|
const raw = t.raw;
|
|
38962
39442
|
if (typeof raw === "string" && raw.startsWith("<")) {
|
|
38963
39443
|
fail(line2, "raw HTML is out of scope in an alert", "use plain Markdown blocks instead of HTML tags");
|
|
38964
39444
|
}
|
|
38965
|
-
fail(line2, "links inside alerts are out of scope", "move the link into a body paragraph");
|
|
38966
39445
|
}
|
|
38967
39446
|
if (t.type === "image") {
|
|
38968
39447
|
fail(line2, "images are out of scope in an alert", "put images on their own block line instead");
|
|
@@ -38980,7 +39459,7 @@ function compileMarkdownDetailed(markdown, options) {
|
|
|
38980
39459
|
if (alertTokens) walkAlert(alertTokens);
|
|
38981
39460
|
};
|
|
38982
39461
|
const handleAlertBlockquote = (token, line2) => {
|
|
38983
|
-
const inner = token.tokens ?? [];
|
|
39462
|
+
const inner = (token.tokens ?? []).filter((child) => child.type !== "space");
|
|
38984
39463
|
if (inner.length === 0) {
|
|
38985
39464
|
fail(line2, "blockquotes are out of scope", "use a plain paragraph");
|
|
38986
39465
|
}
|
|
@@ -39006,6 +39485,7 @@ ${rest}` : sameLineBody;
|
|
|
39006
39485
|
}
|
|
39007
39486
|
const restLines = rest.split("\n");
|
|
39008
39487
|
let title;
|
|
39488
|
+
let titleSegments;
|
|
39009
39489
|
let bodySrc = rest;
|
|
39010
39490
|
if (restLines.length > 0 && ALERT_TITLE_PATTERN.test(restLines[0].trim())) {
|
|
39011
39491
|
const innerTitle = (ALERT_TITLE_PATTERN.exec(restLines[0].trim())?.[1] ?? "").trim();
|
|
@@ -39028,6 +39508,22 @@ ${rest}` : sameLineBody;
|
|
|
39028
39508
|
CITATION_REF_PATTERN.lastIndex = 0;
|
|
39029
39509
|
title = innerTitle;
|
|
39030
39510
|
bodySrc = restLines.slice(1).join("\n");
|
|
39511
|
+
let titleTokens;
|
|
39512
|
+
try {
|
|
39513
|
+
titleTokens = lexMarkdown(restLines[0].trim(), { dollarMath: options?.dollarMath });
|
|
39514
|
+
} catch (err) {
|
|
39515
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
39516
|
+
throw toError("E_VALIDATION", `line ${line2}: alert title rejected (${detail})`, "keep the alert title to one line of inline text");
|
|
39517
|
+
}
|
|
39518
|
+
const titleBlocks = titleTokens.filter((t) => t.type !== "space");
|
|
39519
|
+
if (titleBlocks.length !== 1 || titleBlocks[0].type !== "paragraph") {
|
|
39520
|
+
fail(line2, `alert [!${alertType}] title must be one line of inline text`, "keep the title to one bold line");
|
|
39521
|
+
}
|
|
39522
|
+
const titleInlineTokens = titleBlocks[0].tokens;
|
|
39523
|
+
failOnSpanTokensInAlert(titleInlineTokens, line2);
|
|
39524
|
+
const parsedTitle = parseInlineTokens(titleInlineTokens, line2, "an alert title", citationDefs, citationOrder, citationNum, usedCitations);
|
|
39525
|
+
title = parsedTitle.plainText.length > 0 ? parsedTitle.plainText : innerTitle;
|
|
39526
|
+
titleSegments = parsedTitle.segments;
|
|
39031
39527
|
}
|
|
39032
39528
|
for (const bodyLine of bodySrc.split("\n")) {
|
|
39033
39529
|
if (CONTAINER_FENCE_PATTERN.test(bodyLine)) {
|
|
@@ -39048,6 +39544,7 @@ ${rest}` : sameLineBody;
|
|
|
39048
39544
|
throw toError("E_VALIDATION", `line ${line2}: alert body rejected (${detail})`, "keep alert bodies plain text");
|
|
39049
39545
|
}
|
|
39050
39546
|
const cleanParas = [];
|
|
39547
|
+
const bodySegments = [];
|
|
39051
39548
|
for (const bodyToken of bodyTokens) {
|
|
39052
39549
|
if (bodyToken.type === "space") continue;
|
|
39053
39550
|
if (bodyToken.type !== "paragraph") {
|
|
@@ -39063,31 +39560,214 @@ ${rest}` : sameLineBody;
|
|
|
39063
39560
|
FOOTNOTE_REF_PATTERN.lastIndex = 0;
|
|
39064
39561
|
failOnImageOrHtml(paraText2, paraLine, "an alert");
|
|
39065
39562
|
failOnSpanTokensInAlert(para.tokens, paraLine);
|
|
39066
|
-
|
|
39563
|
+
const parsedPara = parseInlineTokens(para.tokens, paraLine, "an alert", citationDefs, citationOrder, citationNum, usedCitations);
|
|
39564
|
+
if (parsedPara.footnoteRefs.length > 0) {
|
|
39565
|
+
fail(paraLine, "footnote markers inside alerts are out of scope", "move the note reference into a body paragraph");
|
|
39566
|
+
}
|
|
39567
|
+
for (const key of parsedPara.citationKeys) usedCitations.add(key);
|
|
39568
|
+
if (cleanParas.length > 0) bodySegments.push({ text: " ", bold: false, italic: false, code: false });
|
|
39569
|
+
bodySegments.push(...parsedPara.segments);
|
|
39570
|
+
cleanParas.push(parsedPara.plainText);
|
|
39067
39571
|
}
|
|
39068
39572
|
const text2 = plainJoin(cleanParas.join(" "));
|
|
39069
39573
|
if (text2.length === 0) {
|
|
39070
39574
|
fail(line2, `alert [!${alertType}] needs body text`, "write the alert text on > lines after the marker");
|
|
39071
39575
|
}
|
|
39072
|
-
const
|
|
39576
|
+
const calloutAnchor = insertAnchor();
|
|
39073
39577
|
const alertTag = alertType.toLowerCase();
|
|
39074
39578
|
const tagThemeRaw = styleGuide?.callouts?.tagThemes?.[alertTag];
|
|
39075
39579
|
const tagTheme = typeof tagThemeRaw === "string" && tagThemeRaw.length > 0 ? tagThemeRaw : void 0;
|
|
39076
39580
|
const guideThemeRaw = styleGuide?.callouts?.defaultTheme;
|
|
39077
39581
|
const guideTheme = typeof guideThemeRaw === "string" && guideThemeRaw.length > 0 ? guideThemeRaw : void 0;
|
|
39582
|
+
const calloutId = nextId();
|
|
39078
39583
|
const op = {
|
|
39079
39584
|
action: "callout",
|
|
39080
|
-
|
|
39081
|
-
|
|
39585
|
+
id: calloutId,
|
|
39586
|
+
anchor: calloutAnchor.anchor,
|
|
39587
|
+
position: calloutAnchor.position,
|
|
39082
39588
|
text: text2,
|
|
39083
39589
|
theme: tagTheme ?? guideTheme ?? ALERT_TYPE_TO_THEME[alertType] ?? "indigo"
|
|
39084
39590
|
};
|
|
39085
39591
|
if (title !== void 0) op["title"] = title;
|
|
39592
|
+
const spanParams = {};
|
|
39593
|
+
if (titleSegments !== void 0 && segmentsNeedSplit(titleSegments)) spanParams["mdTitleSpans"] = titleSegments;
|
|
39594
|
+
if (segmentsNeedSplit(bodySegments)) spanParams["mdBodySpans"] = bodySegments;
|
|
39595
|
+
if (Object.keys(spanParams).length > 0) op["params"] = spanParams;
|
|
39086
39596
|
ops.push(op);
|
|
39087
|
-
lastInsertId =
|
|
39597
|
+
lastInsertId = calloutId;
|
|
39088
39598
|
};
|
|
39089
39599
|
let underTitle = false;
|
|
39090
39600
|
let lastBlockKind = "none";
|
|
39601
|
+
const compileList = (token, line2, quoteLevel = 0) => {
|
|
39602
|
+
const listType = token.ordered === true ? "numbered" : "bullet";
|
|
39603
|
+
const items = token.items ?? [];
|
|
39604
|
+
if (items.length === 0) {
|
|
39605
|
+
fail(line2, "empty list block", "add at least one list item");
|
|
39606
|
+
}
|
|
39607
|
+
const itemIds = [];
|
|
39608
|
+
const itemBaseLine = line2;
|
|
39609
|
+
for (const item of items) {
|
|
39610
|
+
const itemLine = subLine(token.raw, itemBaseLine, item.raw);
|
|
39611
|
+
if (item.task === true) {
|
|
39612
|
+
fail(itemLine, "task-list items are out of scope", "use a plain bullet or numbered item");
|
|
39613
|
+
}
|
|
39614
|
+
for (const child of item.tokens ?? []) {
|
|
39615
|
+
if (child.type === "list") {
|
|
39616
|
+
const nestedRaw = child.raw;
|
|
39617
|
+
const nestedLine = typeof nestedRaw === "string" && nestedRaw ? subLine(token.raw, itemBaseLine, nestedRaw) : itemLine;
|
|
39618
|
+
fail(nestedLine, "nested lists are out of scope", "flatten nested items into the top-level list");
|
|
39619
|
+
}
|
|
39620
|
+
if (child.type !== "text" && child.type !== "paragraph" && child.type !== "space") {
|
|
39621
|
+
fail(itemLine, `list items carry ${child.type} blocks, out of scope`, "keep list items plain text");
|
|
39622
|
+
}
|
|
39623
|
+
}
|
|
39624
|
+
if (item.text.split("\n").some((entry) => FOOTNOTE_DEF_PATTERN.test(entry))) {
|
|
39625
|
+
fail(itemLine, "footnote definition must stand alone as its own block", "move the definition out of the list");
|
|
39626
|
+
}
|
|
39627
|
+
if (item.text.split("\n").some((entry) => CITATION_DEF_PATTERN.test(entry))) {
|
|
39628
|
+
fail(itemLine, "citation definition must stand alone as its own block", "move the definition out of the list");
|
|
39629
|
+
}
|
|
39630
|
+
failOnImageOrHtml(item.text, itemLine, "a list item");
|
|
39631
|
+
const firstChild = (item.tokens ?? [])[0];
|
|
39632
|
+
const inlineTokens = firstChild?.tokens ?? [];
|
|
39633
|
+
if (inlineTokens.length === 1 && inlineTokens[0].type === "image") {
|
|
39634
|
+
fail(itemLine, "images are out of scope in a list item", "put images on their own block line instead of inside a list");
|
|
39635
|
+
}
|
|
39636
|
+
for (const it of inlineTokens) {
|
|
39637
|
+
if (it.type === "image") {
|
|
39638
|
+
fail(itemLine, "images are out of scope in a list item", "put images on their own block line instead of inside a list");
|
|
39639
|
+
}
|
|
39640
|
+
}
|
|
39641
|
+
const inline = parseInlineTokens(inlineTokens, itemLine, "a list item", citationDefs, citationOrder, citationNum, usedCitations);
|
|
39642
|
+
for (const key of inline.citationKeys) usedCitations.add(key);
|
|
39643
|
+
for (const label of inline.footnoteRefs) {
|
|
39644
|
+
if (!defs.has(label)) {
|
|
39645
|
+
fail(itemLine, `undefined footnote reference [^${label}]`, "add a matching [^label]: body definition");
|
|
39646
|
+
}
|
|
39647
|
+
}
|
|
39648
|
+
const id = pushInsertParagraph(inline.plainText, inline.segments);
|
|
39649
|
+
trackRefs(id, inline.footnoteEntries ?? inline.footnoteRefs, itemLine);
|
|
39650
|
+
itemIds.push(id);
|
|
39651
|
+
}
|
|
39652
|
+
ops.push({ action: "list", operation: "create", target: itemIds, listType });
|
|
39653
|
+
if (lastBlockKind === "table" && itemIds.length > 0) {
|
|
39654
|
+
ops.push({ action: "style", target: itemIds[0], spacingBefore: 240 });
|
|
39655
|
+
}
|
|
39656
|
+
if (quoteLevel > 0) {
|
|
39657
|
+
for (const itemId of itemIds) {
|
|
39658
|
+
ops.push({ action: "namedStyle", operation: "apply", styleName: "Quote", target: itemId });
|
|
39659
|
+
ops.push({ action: "style", target: itemId, indentLeft: quoteLevel * QUOTE_INDENT + QUOTE_LIST_INDENT, indentHanging: QUOTE_LIST_INDENT });
|
|
39660
|
+
}
|
|
39661
|
+
}
|
|
39662
|
+
};
|
|
39663
|
+
const compileCode = (token, line2, quoteLevel = 0) => {
|
|
39664
|
+
const lang = (token.lang ?? "").trim();
|
|
39665
|
+
if (CHART_FENCE_LANGUAGES.has(lang.toLowerCase())) {
|
|
39666
|
+
fail(line2, `fenced code tagged '${lang}' requests a chart`, "describe the chart with a chart path reference instead");
|
|
39667
|
+
}
|
|
39668
|
+
const id = nextId();
|
|
39669
|
+
const anchor = insertAnchor();
|
|
39670
|
+
ops.push({
|
|
39671
|
+
action: "insert",
|
|
39672
|
+
type: "paragraph",
|
|
39673
|
+
anchor: anchor.anchor,
|
|
39674
|
+
position: anchor.position,
|
|
39675
|
+
text: token.text ?? "",
|
|
39676
|
+
id,
|
|
39677
|
+
params: { codeBlock: true, ...lang ? { codeLanguage: lang } : {} }
|
|
39678
|
+
});
|
|
39679
|
+
lastInsertId = id;
|
|
39680
|
+
if (lastBlockKind === "table") {
|
|
39681
|
+
ops.push({ action: "style", target: id, spacingBefore: 240 });
|
|
39682
|
+
}
|
|
39683
|
+
if (quoteLevel > 0) {
|
|
39684
|
+
ops.push({ action: "style", target: id, indentLeft: quoteLevel * QUOTE_INDENT + CODE_BLOCK_BASE_INDENT });
|
|
39685
|
+
}
|
|
39686
|
+
};
|
|
39687
|
+
const compileQuote = (token, line2, level) => {
|
|
39688
|
+
const effective = Math.min(level, QUOTE_MAX_LEVEL);
|
|
39689
|
+
if (level > QUOTE_MAX_LEVEL) {
|
|
39690
|
+
warnings.push(`line ${line2}: quote nesting deeper than ${QUOTE_MAX_LEVEL} levels was written at level ${QUOTE_MAX_LEVEL}.`);
|
|
39691
|
+
}
|
|
39692
|
+
const children2 = (token.tokens ?? []).filter((child) => child.type !== "space");
|
|
39693
|
+
if (children2.length === 0) {
|
|
39694
|
+
fail(line2, "blockquote is empty", "write the quote text after the > marker");
|
|
39695
|
+
}
|
|
39696
|
+
const strippedRaw = token.raw.split("\n").map((entry) => entry.replace(/^ {0,3}>[ ]?/, "")).join("\n");
|
|
39697
|
+
for (const child of children2) {
|
|
39698
|
+
const childRaw = child.raw ?? "";
|
|
39699
|
+
const childLine = subLine(strippedRaw, line2, childRaw.split("\n")[0] ?? "");
|
|
39700
|
+
switch (child.type) {
|
|
39701
|
+
case "paragraph": {
|
|
39702
|
+
const para = child;
|
|
39703
|
+
const paraText2 = para.text ?? "";
|
|
39704
|
+
const paraLines = childRaw.replace(/\n+$/, "").split("\n");
|
|
39705
|
+
if (paraLines.some((entry) => FOOTNOTE_DEF_PATTERN.test(entry) || CITATION_DEF_PATTERN.test(entry))) {
|
|
39706
|
+
fail(childLine, "note and citation definitions must stand alone as their own block", "move the definition out of the quote");
|
|
39707
|
+
}
|
|
39708
|
+
if (CONTAINER_FENCE_PATTERN.test(paraLines[0] ?? "")) {
|
|
39709
|
+
fail(childLine, "container fences (:::) are out of scope as custom styles", "use plain Markdown blocks");
|
|
39710
|
+
}
|
|
39711
|
+
const paraTokens = para.tokens ?? [];
|
|
39712
|
+
for (const it of paraTokens) {
|
|
39713
|
+
if (it.type === "image") {
|
|
39714
|
+
fail(childLine, "images are out of scope in a quote", "put images on their own block line after the quote");
|
|
39715
|
+
}
|
|
39716
|
+
}
|
|
39717
|
+
failOnImageOrHtml(paraText2, childLine, "a quote");
|
|
39718
|
+
const inline = parseInlineTokens(para.tokens, childLine, "a quote", citationDefs, citationOrder, citationNum, usedCitations);
|
|
39719
|
+
for (const key of inline.citationKeys) usedCitations.add(key);
|
|
39720
|
+
for (const label of inline.footnoteRefs) {
|
|
39721
|
+
if (!defs.has(label)) {
|
|
39722
|
+
fail(childLine, `undefined footnote reference [^${label}]`, "add a matching [^label]: body definition");
|
|
39723
|
+
}
|
|
39724
|
+
}
|
|
39725
|
+
const id = pushInsertParagraph(inline.plainText, inline.segments);
|
|
39726
|
+
trackRefs(id, inline.footnoteEntries ?? inline.footnoteRefs, childLine);
|
|
39727
|
+
ops.push({ action: "namedStyle", operation: "apply", styleName: "Quote", target: id });
|
|
39728
|
+
if (effective > 1) {
|
|
39729
|
+
ops.push({ action: "style", target: id, indentLeft: effective * QUOTE_INDENT });
|
|
39730
|
+
}
|
|
39731
|
+
if (lastBlockKind === "table") {
|
|
39732
|
+
ops.push({ action: "style", target: id, spacingBefore: 240 });
|
|
39733
|
+
}
|
|
39734
|
+
lastBlockKind = "quote";
|
|
39735
|
+
underTitle = false;
|
|
39736
|
+
break;
|
|
39737
|
+
}
|
|
39738
|
+
case "blockquote":
|
|
39739
|
+
compileQuote(child, childLine, level + 1);
|
|
39740
|
+
break;
|
|
39741
|
+
case "list":
|
|
39742
|
+
compileList(child, childLine, effective);
|
|
39743
|
+
lastBlockKind = "list";
|
|
39744
|
+
break;
|
|
39745
|
+
case "code":
|
|
39746
|
+
compileCode(child, childLine, effective);
|
|
39747
|
+
lastBlockKind = "code";
|
|
39748
|
+
break;
|
|
39749
|
+
case "table":
|
|
39750
|
+
fail(childLine, "tables inside a quote are out of scope", "Word cannot indent a table as a quote: put the table after the quote");
|
|
39751
|
+
break;
|
|
39752
|
+
case "heading":
|
|
39753
|
+
fail(childLine, "headings inside a quote are out of scope", "put the heading before the quote");
|
|
39754
|
+
break;
|
|
39755
|
+
default:
|
|
39756
|
+
fail(childLine, `${child.type} blocks inside a quote are out of scope`, "keep quotes to paragraphs, flat lists and code blocks");
|
|
39757
|
+
}
|
|
39758
|
+
}
|
|
39759
|
+
};
|
|
39760
|
+
const quoteOpensWithAlertMarker = (token, line2) => {
|
|
39761
|
+
const first = (token.tokens ?? []).find((child) => child.type !== "space");
|
|
39762
|
+
if (!first || first.type !== "paragraph") return false;
|
|
39763
|
+
const head = (first.text ?? "").split("\n")[0]?.trim() ?? "";
|
|
39764
|
+
if (ALERT_MARKER_PATTERN.test(head)) return true;
|
|
39765
|
+
const unknown = /^\[!([A-Za-z][A-Za-z0-9_-]*)\]/.exec(head);
|
|
39766
|
+
if (unknown) {
|
|
39767
|
+
fail(line2, `unknown alert type [!${unknown[1]}]`, "use NOTE, TIP, IMPORTANT, WARNING or CAUTION, or drop the [!...] marker for a plain quote");
|
|
39768
|
+
}
|
|
39769
|
+
return false;
|
|
39770
|
+
};
|
|
39091
39771
|
for (const rawToken of tokens) {
|
|
39092
39772
|
const token = rawToken;
|
|
39093
39773
|
const line2 = lineOf.get(rawToken) ?? 1;
|
|
@@ -39197,60 +39877,7 @@ ${rest}` : sameLineBody;
|
|
|
39197
39877
|
break;
|
|
39198
39878
|
}
|
|
39199
39879
|
case "list": {
|
|
39200
|
-
|
|
39201
|
-
const items = token.items ?? [];
|
|
39202
|
-
if (items.length === 0) {
|
|
39203
|
-
fail(line2, "empty list block", "add at least one list item");
|
|
39204
|
-
}
|
|
39205
|
-
const itemIds = [];
|
|
39206
|
-
const itemBaseLine = line2;
|
|
39207
|
-
for (const item of items) {
|
|
39208
|
-
const itemLine = subLine(token.raw, itemBaseLine, item.raw);
|
|
39209
|
-
if (item.task === true) {
|
|
39210
|
-
fail(itemLine, "task-list items are out of scope", "use a plain bullet or numbered item");
|
|
39211
|
-
}
|
|
39212
|
-
for (const child of item.tokens ?? []) {
|
|
39213
|
-
if (child.type === "list") {
|
|
39214
|
-
const nestedRaw = child.raw;
|
|
39215
|
-
const nestedLine = typeof nestedRaw === "string" && nestedRaw ? subLine(token.raw, itemBaseLine, nestedRaw) : itemLine;
|
|
39216
|
-
fail(nestedLine, "nested lists are out of scope", "flatten nested items into the top-level list");
|
|
39217
|
-
}
|
|
39218
|
-
if (child.type !== "text" && child.type !== "paragraph" && child.type !== "space") {
|
|
39219
|
-
fail(itemLine, `list items carry ${child.type} blocks, out of scope`, "keep list items plain text");
|
|
39220
|
-
}
|
|
39221
|
-
}
|
|
39222
|
-
if (item.text.split("\n").some((entry) => FOOTNOTE_DEF_PATTERN.test(entry))) {
|
|
39223
|
-
fail(itemLine, "footnote definition must stand alone as its own block", "move the definition out of the list");
|
|
39224
|
-
}
|
|
39225
|
-
if (item.text.split("\n").some((entry) => CITATION_DEF_PATTERN.test(entry))) {
|
|
39226
|
-
fail(itemLine, "citation definition must stand alone as its own block", "move the definition out of the list");
|
|
39227
|
-
}
|
|
39228
|
-
failOnImageOrHtml(item.text, itemLine, "a list item");
|
|
39229
|
-
const firstChild = (item.tokens ?? [])[0];
|
|
39230
|
-
const inlineTokens = firstChild?.tokens ?? [];
|
|
39231
|
-
if (inlineTokens.length === 1 && inlineTokens[0].type === "image") {
|
|
39232
|
-
fail(itemLine, "images are out of scope in a list item", "put images on their own block line instead of inside a list");
|
|
39233
|
-
}
|
|
39234
|
-
for (const it of inlineTokens) {
|
|
39235
|
-
if (it.type === "image") {
|
|
39236
|
-
fail(itemLine, "images are out of scope in a list item", "put images on their own block line instead of inside a list");
|
|
39237
|
-
}
|
|
39238
|
-
}
|
|
39239
|
-
const inline = parseInlineTokens(inlineTokens, itemLine, "a list item", citationDefs, citationOrder, citationNum, usedCitations);
|
|
39240
|
-
for (const key of inline.citationKeys) usedCitations.add(key);
|
|
39241
|
-
for (const label of inline.footnoteRefs) {
|
|
39242
|
-
if (!defs.has(label)) {
|
|
39243
|
-
fail(itemLine, `undefined footnote reference [^${label}]`, "add a matching [^label]: body definition");
|
|
39244
|
-
}
|
|
39245
|
-
}
|
|
39246
|
-
const id = pushInsertParagraph(inline.plainText, inline.segments);
|
|
39247
|
-
trackRefs(id, inline.footnoteEntries ?? inline.footnoteRefs, itemLine);
|
|
39248
|
-
itemIds.push(id);
|
|
39249
|
-
}
|
|
39250
|
-
ops.push({ action: "list", operation: "create", target: itemIds, listType });
|
|
39251
|
-
if (lastBlockKind === "table" && itemIds.length > 0) {
|
|
39252
|
-
ops.push({ action: "style", target: itemIds[0], spacingBefore: 240 });
|
|
39253
|
-
}
|
|
39880
|
+
compileList(token, line2);
|
|
39254
39881
|
lastBlockKind = "list";
|
|
39255
39882
|
break;
|
|
39256
39883
|
}
|
|
@@ -39363,25 +39990,7 @@ ${rest}` : sameLineBody;
|
|
|
39363
39990
|
break;
|
|
39364
39991
|
}
|
|
39365
39992
|
case "code": {
|
|
39366
|
-
|
|
39367
|
-
if (CHART_FENCE_LANGUAGES.has(lang.toLowerCase())) {
|
|
39368
|
-
fail(line2, `fenced code tagged '${lang}' requests a chart`, "describe the chart with a chart path reference instead");
|
|
39369
|
-
}
|
|
39370
|
-
const id = nextId();
|
|
39371
|
-
const anchor = insertAnchor();
|
|
39372
|
-
ops.push({
|
|
39373
|
-
action: "insert",
|
|
39374
|
-
type: "paragraph",
|
|
39375
|
-
anchor: anchor.anchor,
|
|
39376
|
-
position: anchor.position,
|
|
39377
|
-
text: token.text ?? "",
|
|
39378
|
-
id,
|
|
39379
|
-
params: { codeBlock: true, ...lang ? { codeLanguage: lang } : {} }
|
|
39380
|
-
});
|
|
39381
|
-
lastInsertId = id;
|
|
39382
|
-
if (lastBlockKind === "table") {
|
|
39383
|
-
ops.push({ action: "style", target: id, spacingBefore: 240 });
|
|
39384
|
-
}
|
|
39993
|
+
compileCode(token, line2);
|
|
39385
39994
|
lastBlockKind = "code";
|
|
39386
39995
|
break;
|
|
39387
39996
|
}
|
|
@@ -39400,8 +40009,12 @@ ${rest}` : sameLineBody;
|
|
|
39400
40009
|
fail(line2, "raw HTML blocks are out of scope", "use plain Markdown blocks instead of HTML tags");
|
|
39401
40010
|
break;
|
|
39402
40011
|
case "blockquote":
|
|
39403
|
-
|
|
39404
|
-
|
|
40012
|
+
if (quoteOpensWithAlertMarker(token, line2)) {
|
|
40013
|
+
handleAlertBlockquote(token, line2);
|
|
40014
|
+
lastBlockKind = "callout";
|
|
40015
|
+
} else {
|
|
40016
|
+
compileQuote(token, line2, 1);
|
|
40017
|
+
}
|
|
39405
40018
|
break;
|
|
39406
40019
|
case "image": {
|
|
39407
40020
|
const img = token;
|
|
@@ -39485,107 +40098,15 @@ ${rest}` : sameLineBody;
|
|
|
39485
40098
|
}
|
|
39486
40099
|
|
|
39487
40100
|
// ../engine/src/compose/markdown.ts
|
|
39488
|
-
function
|
|
39489
|
-
|
|
39490
|
-
|
|
39491
|
-
|
|
39492
|
-
|
|
39493
|
-
|
|
39494
|
-
|
|
39495
|
-
|
|
39496
|
-
|
|
39497
|
-
if (![1, 2, 4, 5, 6].includes(nodes.length)) return null;
|
|
39498
|
-
if (summary.hasTrackedChanges === true) return null;
|
|
39499
|
-
if (Object.keys(summary.mediaFiles ?? {}).length > 0) return null;
|
|
39500
|
-
if ((summary.headers ?? []).length > 0) return null;
|
|
39501
|
-
if ((summary.footers ?? []).length > 0) return null;
|
|
39502
|
-
if ((summary.charts ?? []).length > 0) return null;
|
|
39503
|
-
try {
|
|
39504
|
-
const zip = parser2.getZipAdapter?.();
|
|
39505
|
-
if (typeof zip?.hasFile === "function") {
|
|
39506
|
-
for (const part of [
|
|
39507
|
-
"word/footnotes.xml",
|
|
39508
|
-
"word/endnotes.xml",
|
|
39509
|
-
"word/comments.xml",
|
|
39510
|
-
"word/commentsExtended.xml"
|
|
39511
|
-
]) {
|
|
39512
|
-
try {
|
|
39513
|
-
if (zip.hasFile(part)) return null;
|
|
39514
|
-
} catch {
|
|
39515
|
-
return null;
|
|
39516
|
-
}
|
|
39517
|
-
}
|
|
39518
|
-
} else {
|
|
39519
|
-
return null;
|
|
39520
|
-
}
|
|
39521
|
-
} catch {
|
|
39522
|
-
return null;
|
|
39523
|
-
}
|
|
39524
|
-
for (const n of nodes) {
|
|
39525
|
-
if (n.hasTrackedChanges === true) return null;
|
|
39526
|
-
const raw = n.rawXml ?? "";
|
|
39527
|
-
if (raw.includes("w:footnoteReference") || raw.includes("w:endnoteReference") || raw.includes("w:commentReference") || raw.includes("w:commentRangeStart") || raw.includes("w:commentRangeEnd")) {
|
|
39528
|
-
return null;
|
|
39529
|
-
}
|
|
39530
|
-
}
|
|
39531
|
-
const ids = nodes.map((n) => n.id ?? "").filter((id) => id.length > 0);
|
|
39532
|
-
if (ids.length !== nodes.length) return null;
|
|
39533
|
-
if (nodes.length === 1) {
|
|
39534
|
-
const node = nodes[0];
|
|
39535
|
-
if (node.type !== "paragraph") return null;
|
|
39536
|
-
if ((node.text ?? "").trim() !== "") return null;
|
|
39537
|
-
return ids;
|
|
39538
|
-
}
|
|
39539
|
-
if (nodes.length === 2) {
|
|
39540
|
-
const [first, second] = nodes;
|
|
39541
|
-
const t0 = (first.text ?? "").trim();
|
|
39542
|
-
const t1 = (second.text ?? "").trim();
|
|
39543
|
-
if (t0 === BLANK_SEED_TITLE && t1 === BLANK_SEED_HINT) {
|
|
39544
|
-
const firstRaw = first.rawXml ?? "";
|
|
39545
|
-
const secondRaw = second.rawXml ?? "";
|
|
39546
|
-
if (!firstRaw.includes('w:val="Title"') || !secondRaw.includes('w:val="Subtitle"')) return null;
|
|
39547
|
-
return ids;
|
|
39548
|
-
}
|
|
39549
|
-
const tmpl = requestedTemplate?.toLowerCase();
|
|
39550
|
-
if (t0 === "MEMORANDUM" && tmpl === "memo") return ids;
|
|
39551
|
-
if (t0 === "Letter" && tmpl === "letter") return ids;
|
|
39552
|
-
if (t0 === "Proposal" && tmpl === "proposal") return ids;
|
|
39553
|
-
if (t0 === "INVOICE" && tmpl === "invoice") return ids;
|
|
39554
|
-
return null;
|
|
39555
|
-
}
|
|
39556
|
-
if (nodes.length === 4) {
|
|
39557
|
-
const t0 = (nodes[0].text ?? "").trim();
|
|
39558
|
-
const tmpl = requestedTemplate?.toLowerCase();
|
|
39559
|
-
if (t0 === "Research Benchmark Report" && tmpl === "research") return ids;
|
|
39560
|
-
if (t0 === "Corporate Strategy Roadmap" && tmpl === "corporate") return ids;
|
|
39561
|
-
if (t0 === "Velocity Audit & Performance" && tmpl === "bar-chart-3d") return ids;
|
|
39562
|
-
return null;
|
|
39563
|
-
}
|
|
39564
|
-
if (nodes.length === 5) {
|
|
39565
|
-
const t0 = (nodes[0].text ?? "").trim();
|
|
39566
|
-
const tmpl = requestedTemplate?.toLowerCase();
|
|
39567
|
-
if (t0 === "Financial Report & Audit" && tmpl === "financial") return ids;
|
|
39568
|
-
return null;
|
|
39569
|
-
}
|
|
39570
|
-
if (nodes.length === 6) {
|
|
39571
|
-
const t0 = (nodes[0].text ?? "").trim();
|
|
39572
|
-
const t2 = (nodes[2].text ?? "").trim();
|
|
39573
|
-
const tmpl = requestedTemplate?.toLowerCase();
|
|
39574
|
-
if (t0 === "Q3 Executive Financial Performance Report" && t2 === "1. Executive Summary") {
|
|
39575
|
-
if (tmpl === "report") return ids;
|
|
39576
|
-
return null;
|
|
39577
|
-
}
|
|
39578
|
-
if (t0 === "Manuscript Title" && t2 === "Chapter 1: The Beginning" && tmpl === "novel") {
|
|
39579
|
-
return ids;
|
|
39580
|
-
}
|
|
39581
|
-
if (t0 === "Neural Document Architectures: A Comparative Benchmark" && tmpl === "academic") {
|
|
39582
|
-
return ids;
|
|
39583
|
-
}
|
|
39584
|
-
if (t0 === "TECH HORIZONS" && tmpl === "newsletter") return ids;
|
|
39585
|
-
if (t0 === "Platform Core API Reference" && (tmpl === "api" || tmpl === "api-spec")) return ids;
|
|
39586
|
-
return null;
|
|
39587
|
-
}
|
|
39588
|
-
return null;
|
|
40101
|
+
function nextComposeIdPrefix(ast) {
|
|
40102
|
+
const live = /* @__PURE__ */ new Set();
|
|
40103
|
+
for (const section of ast.sections ?? []) for (const node of section.nodes ?? []) live.add(node.id);
|
|
40104
|
+
const used = (prefix) => {
|
|
40105
|
+
for (const id of live) if (id.startsWith(`${prefix}_`)) return true;
|
|
40106
|
+
return false;
|
|
40107
|
+
};
|
|
40108
|
+
if (!used("md")) return "md";
|
|
40109
|
+
for (let n = 2; ; n++) if (!used(`md${n}`)) return `md${n}`;
|
|
39589
40110
|
}
|
|
39590
40111
|
var pristineBlankSeedIds = pristineTemplateSeedIds;
|
|
39591
40112
|
function assertTemplateSeed(parser2, template) {
|
|
@@ -39629,15 +40150,26 @@ function assertTemplateSeed(parser2, template) {
|
|
|
39629
40150
|
function applyInlineSpans(parser2, ast, operations) {
|
|
39630
40151
|
const docParser = parser2;
|
|
39631
40152
|
const liveAst = ast;
|
|
40153
|
+
const targets = [];
|
|
39632
40154
|
for (const op of operations) {
|
|
39633
40155
|
const rec = op;
|
|
39634
|
-
if (rec["action"] !== "insert" || rec["type"] !== "paragraph") continue;
|
|
39635
40156
|
const params = rec["params"];
|
|
39636
|
-
const rawSpans = params?.["mdSpans"];
|
|
39637
|
-
if (!Array.isArray(rawSpans) || !segmentsNeedSplit(rawSpans)) continue;
|
|
39638
|
-
const spans = rawSpans;
|
|
39639
40157
|
const id = String(rec["id"] ?? "");
|
|
39640
40158
|
if (!id) continue;
|
|
40159
|
+
if (rec["action"] === "callout") {
|
|
40160
|
+
const titleSpans = params?.["mdTitleSpans"];
|
|
40161
|
+
const bodySpans = params?.["mdBodySpans"];
|
|
40162
|
+
if (Array.isArray(titleSpans) && segmentsNeedSplit(titleSpans)) targets.push({ id, spans: titleSpans, part: "calloutTitle" });
|
|
40163
|
+
if (Array.isArray(bodySpans) && segmentsNeedSplit(bodySpans)) targets.push({ id, spans: bodySpans, part: "calloutBody" });
|
|
40164
|
+
continue;
|
|
40165
|
+
}
|
|
40166
|
+
if (rec["action"] !== "insert" || rec["type"] !== "paragraph") continue;
|
|
40167
|
+
const rawSpans = params?.["mdSpans"];
|
|
40168
|
+
if (!Array.isArray(rawSpans) || !segmentsNeedSplit(rawSpans)) continue;
|
|
40169
|
+
targets.push({ id, spans: rawSpans, part: "paragraph" });
|
|
40170
|
+
}
|
|
40171
|
+
for (const target of targets) {
|
|
40172
|
+
const { id, spans, part } = target;
|
|
39641
40173
|
let node;
|
|
39642
40174
|
for (const section of liveAst.sections) {
|
|
39643
40175
|
const found = section.nodes.find((n) => n.id === id);
|
|
@@ -39651,7 +40183,18 @@ function applyInlineSpans(parser2, ast, operations) {
|
|
|
39651
40183
|
`E_VALIDATION: line 1: span target paragraph '${id}' is missing after compose. Remedy: retry the compose without manual edits in between.`
|
|
39652
40184
|
);
|
|
39653
40185
|
}
|
|
39654
|
-
|
|
40186
|
+
let para = node.xml;
|
|
40187
|
+
let baseRPr;
|
|
40188
|
+
if (part !== "paragraph") {
|
|
40189
|
+
const cellParas = Array.from(para.getElementsByTagName("w:p"));
|
|
40190
|
+
const hasTitle = cellParas.length > 1;
|
|
40191
|
+
const picked = part === "calloutTitle" ? hasTitle ? cellParas[0] : void 0 : cellParas[cellParas.length - 1];
|
|
40192
|
+
if (!picked) continue;
|
|
40193
|
+
para = picked;
|
|
40194
|
+
const firstRun = Array.from(para.childNodes).find((k) => k.nodeType === 1 && k.tagName === "w:r");
|
|
40195
|
+
const rPrEl = firstRun ? Array.from(firstRun.childNodes).find((k) => k.nodeType === 1 && k.tagName === "w:rPr") : void 0;
|
|
40196
|
+
baseRPr = rPrEl ? rPrEl : void 0;
|
|
40197
|
+
}
|
|
39655
40198
|
const doc = para.ownerDocument;
|
|
39656
40199
|
if (!doc) continue;
|
|
39657
40200
|
const keptFootRefs = [];
|
|
@@ -39683,10 +40226,12 @@ function applyInlineSpans(parser2, ast, operations) {
|
|
|
39683
40226
|
}
|
|
39684
40227
|
const buildRun = (seg) => {
|
|
39685
40228
|
const run2 = createWordElement(doc, "w:r");
|
|
39686
|
-
const needsRPr = seg.bold || seg.italic || seg.strike || seg.code || seg.linkUrl !== void 0;
|
|
40229
|
+
const needsRPr = seg.bold || seg.italic || seg.strike || seg.code || seg.linkUrl !== void 0 || baseRPr !== void 0;
|
|
39687
40230
|
if (needsRPr) {
|
|
39688
40231
|
const rPr = createWordElement(doc, "w:rPr");
|
|
39689
|
-
|
|
40232
|
+
const baseBold = baseRPr !== void 0 && baseRPr.getElementsByTagName("w:b").length > 0;
|
|
40233
|
+
const baseColor = baseRPr?.getElementsByTagName("w:color")[0]?.getAttribute("w:val") ?? void 0;
|
|
40234
|
+
if (seg.bold || baseBold) rPr.appendChild(createWordElement(doc, "w:b"));
|
|
39690
40235
|
if (seg.italic) rPr.appendChild(createWordElement(doc, "w:i"));
|
|
39691
40236
|
if (seg.strike) rPr.appendChild(createWordElement(doc, "w:strike"));
|
|
39692
40237
|
if (seg.code) {
|
|
@@ -39695,6 +40240,11 @@ function applyInlineSpans(parser2, ast, operations) {
|
|
|
39695
40240
|
rFonts.setAttribute("w:hAnsi", COMPOSE_CODE_FONT);
|
|
39696
40241
|
rPr.appendChild(rFonts);
|
|
39697
40242
|
}
|
|
40243
|
+
if (seg.linkUrl === void 0 && baseColor) {
|
|
40244
|
+
const color = createWordElement(doc, "w:color");
|
|
40245
|
+
color.setAttribute("w:val", baseColor);
|
|
40246
|
+
rPr.appendChild(color);
|
|
40247
|
+
}
|
|
39698
40248
|
if (seg.linkUrl !== void 0) {
|
|
39699
40249
|
const color = createWordElement(doc, "w:color");
|
|
39700
40250
|
color.setAttribute("w:val", "0066CC");
|
|
@@ -39780,35 +40330,76 @@ function applyInlineSpans(parser2, ast, operations) {
|
|
|
39780
40330
|
);
|
|
39781
40331
|
for (let i = 0; i < refSibs.length; i++) {
|
|
39782
40332
|
const refEl = refSibs[i];
|
|
39783
|
-
const
|
|
39784
|
-
insertRunAtOffset(para, refEl,
|
|
40333
|
+
const op = noteOps[i];
|
|
40334
|
+
insertRunAtOffset(para, refEl, op?.offset);
|
|
39785
40335
|
}
|
|
39786
40336
|
node.version += 1;
|
|
39787
40337
|
}
|
|
39788
40338
|
}
|
|
40339
|
+
function validateComposedOperations(operations) {
|
|
40340
|
+
validateOperationsStrict(
|
|
40341
|
+
operations.map((op) => {
|
|
40342
|
+
const rec = op;
|
|
40343
|
+
const params = rec["params"];
|
|
40344
|
+
if (rec["action"] !== "callout" || !params) return op;
|
|
40345
|
+
const { mdTitleSpans: _t, mdBodySpans: _b, ...publicParams } = params;
|
|
40346
|
+
void _t;
|
|
40347
|
+
void _b;
|
|
40348
|
+
const { params: _p, ...rest } = rec;
|
|
40349
|
+
void _p;
|
|
40350
|
+
return Object.keys(publicParams).length > 0 ? { ...rest, params: publicParams } : rest;
|
|
40351
|
+
})
|
|
40352
|
+
);
|
|
40353
|
+
}
|
|
39789
40354
|
async function composeDocument(parser2, markdown, options) {
|
|
39790
40355
|
if (!parser2 || typeof parser2.getAST !== "function") {
|
|
39791
40356
|
throw new Error(
|
|
39792
40357
|
"E_VALIDATION: line 1: composeDocument needs an open document parser. Remedy: open a document first, then compose into it."
|
|
39793
40358
|
);
|
|
39794
40359
|
}
|
|
40360
|
+
const liveAst = await parser2.getAST();
|
|
40361
|
+
const idPrefix = options?.idPrefix ?? nextComposeIdPrefix(liveAst);
|
|
39795
40362
|
if (options?.template !== void 0) {
|
|
40363
|
+
if (idPrefix !== "md") {
|
|
40364
|
+
throw new Error(
|
|
40365
|
+
"E_VALIDATION: line 1: template is accepted only on the first compose of a document. Remedy: omit template on this compose; the document already holds composed content."
|
|
40366
|
+
);
|
|
40367
|
+
}
|
|
39796
40368
|
assertTemplateSeed(parser2, String(options.template));
|
|
39797
40369
|
}
|
|
40370
|
+
if (options?.position !== void 0 && options.anchor === void 0) {
|
|
40371
|
+
throw new Error(
|
|
40372
|
+
"E_VALIDATION: line 1: position needs an anchor. Remedy: pass anchor (a node id, start or end) together with position, or omit position to append at the end."
|
|
40373
|
+
);
|
|
40374
|
+
}
|
|
40375
|
+
if (options?.anchor !== void 0 && anchorAliasOf(options.anchor) === null) {
|
|
40376
|
+
const known = liveAst.sections.some((section) => section.nodes.some((node) => node.id === options.anchor));
|
|
40377
|
+
if (!known) {
|
|
40378
|
+
throw new Error(
|
|
40379
|
+
`E_VALIDATION: line 1: anchor '${options.anchor}' is not a node of this document. Remedy: pass a node id from inspect_document, or start or end.`
|
|
40380
|
+
);
|
|
40381
|
+
}
|
|
40382
|
+
}
|
|
39798
40383
|
const styleGuide = resolveComposeStyleGuide(options?.styleGuide);
|
|
39799
40384
|
const { operations, warnings: compileWarnings } = compileMarkdownDetailed(markdown, {
|
|
40385
|
+
idPrefix,
|
|
40386
|
+
...options?.anchor !== void 0 ? { anchor: options.anchor } : {},
|
|
40387
|
+
...options?.position !== void 0 ? { position: options.position } : {},
|
|
39800
40388
|
allowedDir: options?.allowedDir,
|
|
39801
40389
|
...options?.styleGuide !== void 0 ? { styleGuide: options.styleGuide } : {},
|
|
39802
40390
|
...options?.dollarMath ? { dollarMath: true } : {}
|
|
39803
40391
|
});
|
|
39804
|
-
|
|
40392
|
+
validateComposedOperations(operations);
|
|
40393
|
+
if (operations.some((op) => op.action === "namedStyle" && op.styleName === QUOTE_STYLE_ID)) {
|
|
40394
|
+
ensureQuoteStyle(parser2.getStylesDom());
|
|
40395
|
+
}
|
|
39805
40396
|
const chunkSize = options?.chunkSize !== void 0 ? Math.floor(options.chunkSize) : ENGINE_MAX_OPS;
|
|
39806
40397
|
if (!Number.isInteger(chunkSize) || chunkSize < 1 || chunkSize > ENGINE_MAX_OPS) {
|
|
39807
40398
|
throw new Error(
|
|
39808
40399
|
`E_VALIDATION: line 1: chunkSize must be an integer in [1, ${ENGINE_MAX_OPS}]. Remedy: omit chunkSize or pass a value inside that range.`
|
|
39809
40400
|
);
|
|
39810
40401
|
}
|
|
39811
|
-
const ast =
|
|
40402
|
+
const ast = liveAst;
|
|
39812
40403
|
const blankSeedIds = pristineBlankSeedIds(parser2, options?.template);
|
|
39813
40404
|
let preBatchSnapshot = null;
|
|
39814
40405
|
try {
|
|
@@ -39861,7 +40452,17 @@ async function composeDocument(parser2, markdown, options) {
|
|
|
39861
40452
|
}
|
|
39862
40453
|
base += chunk.length;
|
|
39863
40454
|
}
|
|
39864
|
-
if (!aggregate.success)
|
|
40455
|
+
if (!aggregate.success) {
|
|
40456
|
+
if (aggregate.appliedCount > 0 && preBatchSnapshot !== null) {
|
|
40457
|
+
try {
|
|
40458
|
+
parser2.restorePackageSnapshot(preBatchSnapshot);
|
|
40459
|
+
aggregate.appliedCount = 0;
|
|
40460
|
+
aggregate.rollbackTriggered = true;
|
|
40461
|
+
} catch {
|
|
40462
|
+
}
|
|
40463
|
+
}
|
|
40464
|
+
return aggregate;
|
|
40465
|
+
}
|
|
39865
40466
|
if (blankSeedIds !== null) {
|
|
39866
40467
|
const seedCleanup = await engine.executeBatch({
|
|
39867
40468
|
operations: [{ action: "delete", target: blankSeedIds }],
|
|
@@ -39932,119 +40533,19 @@ async function composeDocument(parser2, markdown, options) {
|
|
|
39932
40533
|
if (/^E_VALIDATION/.test(detail)) throw err;
|
|
39933
40534
|
throw new Error(`E_VALIDATION: line 1: inline formatting failed (${detail}). Remedy: simplify bold, italic, code, or link spans and retry.`);
|
|
39934
40535
|
}
|
|
39935
|
-
return aggregate;
|
|
39936
|
-
}
|
|
39937
|
-
|
|
39938
|
-
// ../engine/src/stats.ts
|
|
39939
|
-
function nearestOwner(el, tagName) {
|
|
39940
|
-
let cur = el.parentNode;
|
|
39941
|
-
while (cur && typeof cur === "object") {
|
|
39942
|
-
const e = cur;
|
|
39943
|
-
if (e.tagName === tagName) return e;
|
|
39944
|
-
cur = e.parentNode;
|
|
39945
|
-
}
|
|
39946
|
-
return null;
|
|
39947
|
-
}
|
|
39948
|
-
function tableCellTextSpaced(node) {
|
|
39949
|
-
try {
|
|
39950
|
-
const xml = node.xml;
|
|
39951
|
-
if (!xml || typeof xml.getElementsByTagName !== "function") return void 0;
|
|
39952
|
-
const tcs = xml.getElementsByTagName("w:tc");
|
|
39953
|
-
if (!tcs || tcs.length === 0) return void 0;
|
|
39954
|
-
const cells = [];
|
|
39955
|
-
for (let i = 0; i < tcs.length; i++) {
|
|
39956
|
-
const tc = tcs[i];
|
|
39957
|
-
const paras = tc.getElementsByTagName("w:p");
|
|
39958
|
-
if (paras.length > 0) {
|
|
39959
|
-
const parts = [];
|
|
39960
|
-
for (let p = 0; p < paras.length; p++) {
|
|
39961
|
-
const para = paras[p];
|
|
39962
|
-
if (nearestOwner(para, "w:tc") !== tc) continue;
|
|
39963
|
-
const runs = para.getElementsByTagName("w:t");
|
|
39964
|
-
let t = "";
|
|
39965
|
-
for (let r = 0; r < runs.length; r++) {
|
|
39966
|
-
const run2 = runs[r];
|
|
39967
|
-
if (nearestOwner(run2, "w:tc") !== tc) continue;
|
|
39968
|
-
if (nearestOwner(run2, "w:p") !== para) continue;
|
|
39969
|
-
t += run2.textContent || "";
|
|
39970
|
-
}
|
|
39971
|
-
if (t.trim().length > 0) parts.push(t.trim());
|
|
39972
|
-
}
|
|
39973
|
-
if (parts.length > 0) cells.push(parts.join(" "));
|
|
39974
|
-
} else {
|
|
39975
|
-
const runs = tc.getElementsByTagName("w:t");
|
|
39976
|
-
let t = "";
|
|
39977
|
-
for (let r = 0; r < runs.length; r++) {
|
|
39978
|
-
const run2 = runs[r];
|
|
39979
|
-
if (nearestOwner(run2, "w:tc") !== tc) continue;
|
|
39980
|
-
t += run2.textContent || "";
|
|
39981
|
-
}
|
|
39982
|
-
if (t.trim().length > 0) cells.push(t.trim());
|
|
39983
|
-
}
|
|
39984
|
-
}
|
|
39985
|
-
if (cells.length === 0) return "";
|
|
39986
|
-
return cells.join(" ");
|
|
39987
|
-
} catch {
|
|
39988
|
-
return void 0;
|
|
39989
|
-
}
|
|
39990
|
-
}
|
|
39991
|
-
function nestedTableCount(node) {
|
|
39992
40536
|
try {
|
|
39993
|
-
const
|
|
39994
|
-
if (
|
|
39995
|
-
return xml.getElementsByTagName("w:tbl").length;
|
|
39996
|
-
} catch {
|
|
39997
|
-
return 0;
|
|
39998
|
-
}
|
|
39999
|
-
}
|
|
40000
|
-
function countExplicitPageBreaks(node) {
|
|
40001
|
-
if (node.type === "pageBreak") return 1;
|
|
40002
|
-
const text2 = node.metadata?.text || "";
|
|
40003
|
-
let count = text2 === "[Page Break]" ? 1 : 0;
|
|
40004
|
-
try {
|
|
40005
|
-
const xml = node.xml;
|
|
40006
|
-
if (xml && typeof xml.getElementsByTagName === "function") {
|
|
40007
|
-
const brs = xml.getElementsByTagName("w:br");
|
|
40008
|
-
let brCount = 0;
|
|
40009
|
-
for (let i = 0; i < brs.length; i++) {
|
|
40010
|
-
if (brs[i].getAttribute("w:type") === "page") brCount++;
|
|
40011
|
-
}
|
|
40012
|
-
if (brCount > count) count = brCount;
|
|
40013
|
-
}
|
|
40537
|
+
const identity = applyDocumentIdentity(ast, parser2);
|
|
40538
|
+
if (identity.titleSetFrom) aggregate.titleSetFrom = identity.titleSetFrom;
|
|
40014
40539
|
} catch {
|
|
40015
40540
|
}
|
|
40016
|
-
return
|
|
40017
|
-
}
|
|
40018
|
-
function computeDocStats(ast) {
|
|
40019
|
-
let words = 0;
|
|
40020
|
-
let paragraphs = 0;
|
|
40021
|
-
let tables = 0;
|
|
40022
|
-
let images = 0;
|
|
40023
|
-
let pageBreaks = 0;
|
|
40024
|
-
for (const sec of ast.sections) {
|
|
40025
|
-
for (const node of sec.nodes) {
|
|
40026
|
-
if (node.type === "paragraph") paragraphs += 1;
|
|
40027
|
-
if (node.type === "table") tables += 1 + nestedTableCount(node);
|
|
40028
|
-
if (node.type === "image") images += 1;
|
|
40029
|
-
pageBreaks += countExplicitPageBreaks(node);
|
|
40030
|
-
let text2 = node.metadata?.text ?? "";
|
|
40031
|
-
if (node.type === "table") {
|
|
40032
|
-
const spaced = tableCellTextSpaced(node);
|
|
40033
|
-
if (spaced !== void 0) text2 = spaced;
|
|
40034
|
-
}
|
|
40035
|
-
const hits = text2.match(/\S+/g);
|
|
40036
|
-
if (hits) words += hits.length;
|
|
40037
|
-
}
|
|
40038
|
-
}
|
|
40039
|
-
const pageBudget = words / 450 + tables * 0.45 + images * 0.5 + pageBreaks;
|
|
40040
|
-
return { words, paragraphs, pages: Math.max(1, Math.ceil(pageBudget)), tables, images };
|
|
40541
|
+
return aggregate;
|
|
40041
40542
|
}
|
|
40042
40543
|
|
|
40043
40544
|
// ../engine/src/style-guide/resolve.ts
|
|
40044
40545
|
var import_fs8 = __toESM(require("fs"));
|
|
40045
40546
|
var import_path7 = __toESM(require("path"));
|
|
40046
40547
|
function hasScheme(ref) {
|
|
40047
|
-
return
|
|
40548
|
+
return hasUriScheme(ref);
|
|
40048
40549
|
}
|
|
40049
40550
|
function failValidation(message, remedy) {
|
|
40050
40551
|
throw toError("E_VALIDATION", message, remedy);
|
|
@@ -40217,7 +40718,7 @@ function summarizeStyleGuide(guide) {
|
|
|
40217
40718
|
// package.json
|
|
40218
40719
|
var package_default = {
|
|
40219
40720
|
name: "@attentionform/astra",
|
|
40220
|
-
version: "0.
|
|
40721
|
+
version: "0.4.1",
|
|
40221
40722
|
description: "Command-line tool for OOXML (.docx) document engineering",
|
|
40222
40723
|
main: "dist/cli.js",
|
|
40223
40724
|
types: "dist/packages/cli/src/cli.d.ts",
|
|
@@ -40260,8 +40761,8 @@ var package_default = {
|
|
|
40260
40761
|
"adm-zip-gate": "node ../../scripts/check-adm-zip-imports.mjs"
|
|
40261
40762
|
},
|
|
40262
40763
|
dependencies: {
|
|
40263
|
-
"@attentionform/astra-engine": "^0.
|
|
40264
|
-
"@attentionform/ooxml-validator": "^0.
|
|
40764
|
+
"@attentionform/astra-engine": "^0.4.0",
|
|
40765
|
+
"@attentionform/ooxml-validator": "^0.4.1",
|
|
40265
40766
|
"@xmldom/xmldom": "0.8.15",
|
|
40266
40767
|
"adm-zip": "0.6.1",
|
|
40267
40768
|
"isomorphic-git": "^1.25.0",
|
|
@@ -40922,9 +41423,9 @@ function commandHelpText(cmd) {
|
|
|
40922
41423
|
" Supported input: #/##/### headings, blank-line paragraphs, flat bullet/ordered lists,",
|
|
40923
41424
|
" header-row tables, --- dividers, fenced code, [^label] footnotes, inline spans (**bold**,",
|
|
40924
41425
|
" *italic*, `code`, [text](url)) applied as runs with real hyperlink targets, `> [!NOTE]`-style",
|
|
40925
|
-
" alert callouts (NOTE|TIP|IMPORTANT|WARNING|CAUTION compile to callout containers), and standalone",
|
|
41426
|
+
" alert callouts (NOTE|TIP|IMPORTANT|WARNING|CAUTION compile to callout containers that keep inline formatting), `> quote` blocks (Quote style, nested up to 3 levels), and standalone",
|
|
40926
41427
|
" block images/charts () resolved from --allowed-dir. Out-of-scope input",
|
|
40927
|
-
" (raw HTML, custom styles,
|
|
41428
|
+
" (raw HTML, custom styles, deeper headings, nested lists/tables,",
|
|
40928
41429
|
" task items) fails closed with E_VALIDATION plus a line number and writes nothing.",
|
|
40929
41430
|
" Subtitle: a line that is only italic (*text*), directly under the opening # title, gets the",
|
|
40930
41431
|
" Subtitle style (the style is already italic, so the emphasis is dropped). For italic body",
|
|
@@ -40956,10 +41457,10 @@ function commandHelpText(cmd) {
|
|
|
40956
41457
|
" Same skinning as create --from-md: tables take headerFill, `> [!TAG]` alerts take the guide",
|
|
40957
41458
|
" defaultTheme, plus chart palette, callout, and code-block defaults where content omits styling.",
|
|
40958
41459
|
"Supported input: #/##/### headings, blank-line paragraphs, flat bullet/ordered lists,",
|
|
40959
|
-
" header-row tables, --- dividers, fenced code, [^label] footnotes, `> [!NOTE]`-style alert callouts, inline spans (**bold**,",
|
|
41460
|
+
" header-row tables, --- dividers, fenced code, [^label] footnotes, `> [!NOTE]`-style alert callouts (inline formatting kept), `> quote` blocks (Quote style, up to 3 levels), inline spans (**bold**,",
|
|
40960
41461
|
" *italic*, `code`, [text](url)) applied as runs with real hyperlink targets, and standalone",
|
|
40961
41462
|
" block images/charts () resolved from --allowed-dir. Out-of-scope input",
|
|
40962
|
-
" (raw HTML, custom styles,
|
|
41463
|
+
" (raw HTML, custom styles, deeper headings, nested lists/tables,",
|
|
40963
41464
|
" task items) fails closed with E_VALIDATION plus a line number and writes nothing.",
|
|
40964
41465
|
" Subtitle: a line that is only italic (*text*), directly under the opening # title, gets the Subtitle style",
|
|
40965
41466
|
" (the style is already italic, so the emphasis is dropped). For italic body text there, put another",
|
|
@@ -40970,6 +41471,7 @@ function commandHelpText(cmd) {
|
|
|
40970
41471
|
`Usage: ${CLI_NAME} stats <docx> [--allow-macros] [--strip-macros]`,
|
|
40971
41472
|
"",
|
|
40972
41473
|
"Print word/paragraph counts plus estimated pages: `STATS <file>: words=N paragraphs=M pages~P (est, 500 wpp)`.",
|
|
41474
|
+
"Also prints `tables=N callouts=N images=N`; a callout (a one-cell filled table, such as a `> [!NOTE]` alert) counts in callouts, not in tables.",
|
|
40973
41475
|
" --allow-macros preserve macro/active-content parts byte-identical (default refuses E_VALIDATION naming the parts)",
|
|
40974
41476
|
" --strip-macros remove macro/active-content parts and continue (never with --allow-macros)",
|
|
40975
41477
|
"Recipe: words count body paragraph text (non-whitespace tokens) plus table cell text (cells joined with spaces).",
|
|
@@ -42373,6 +42875,10 @@ async function runBatchJob(out, ctx) {
|
|
|
42373
42875
|
const backupCode = await ensureBackupCopy(out, file, docx, target, ctx.backupState);
|
|
42374
42876
|
if (backupCode !== void 0) return backupCode;
|
|
42375
42877
|
const gateNormalized = skipValidate ? [] : readParserNormalizations(parser2);
|
|
42878
|
+
try {
|
|
42879
|
+
applyDocumentIdentity(ast, parser2);
|
|
42880
|
+
} catch {
|
|
42881
|
+
}
|
|
42376
42882
|
const finalBytes = parser2.rezip({ reproducible: ctx.reproducible });
|
|
42377
42883
|
const batchNormalized = mergeNormalized(readEngineNormalized(res), gateNormalized, readParserNormalizations(parser2));
|
|
42378
42884
|
if (dupPreState && batchNormalized.some((l) => l.startsWith("Renumbered duplicate"))) {
|
|
@@ -42630,6 +43136,10 @@ async function runStreamSession(out, docx, opts, lines, emit) {
|
|
|
42630
43136
|
remedy: `name the output "${path13.basename(target)}" with a .docm extension, or add --strip-macros to remove the macro parts`
|
|
42631
43137
|
};
|
|
42632
43138
|
}
|
|
43139
|
+
try {
|
|
43140
|
+
applyDocumentIdentity(await parser2.getAST(), parser2);
|
|
43141
|
+
} catch {
|
|
43142
|
+
}
|
|
42633
43143
|
const finalBytes = parser2.rezip({ reproducible });
|
|
42634
43144
|
await writeDocx(target, finalBytes);
|
|
42635
43145
|
lastWritten = finalBytes;
|
|
@@ -44790,8 +45300,9 @@ async function cmdStats(out, args) {
|
|
|
44790
45300
|
const ast = await parser2.getAST();
|
|
44791
45301
|
const stats = computeDocStats(ast);
|
|
44792
45302
|
print(out, `STATS ${file}: words=${stats.words} paragraphs=${stats.paragraphs} pages~${stats.pages} (est, 500 wpp)`);
|
|
45303
|
+
print(out, `tables=${stats.tables} callouts=${stats.callouts} images=${stats.images} (a callout is a one-cell filled table and is not counted in tables)`);
|
|
44793
45304
|
print(out, `recipe: body paragraphs + table cells; excludes headers/footers/footnotes/comments; pages~P rough estimate = max(1, ceil(words/500))`);
|
|
44794
|
-
printJson(out, { ok: true, file, words: stats.words, paragraphs: stats.paragraphs, pages: stats.pages });
|
|
45305
|
+
printJson(out, { ok: true, file, words: stats.words, paragraphs: stats.paragraphs, pages: stats.pages, tables: stats.tables, callouts: stats.callouts, images: stats.images });
|
|
44795
45306
|
return EXIT_OK;
|
|
44796
45307
|
} catch (err) {
|
|
44797
45308
|
return failHuman(out, err, { file, fallbackCode: "E_VALIDATION" });
|
|
@@ -45323,6 +45834,10 @@ async function run(argv) {
|
|
|
45323
45834
|
|
|
45324
45835
|
// src/cli.ts
|
|
45325
45836
|
function isDirectEntry() {
|
|
45837
|
+
try {
|
|
45838
|
+
if (typeof require !== "undefined" && typeof module !== "undefined" && require.main === module) return true;
|
|
45839
|
+
} catch {
|
|
45840
|
+
}
|
|
45326
45841
|
const entry = process.argv?.[1] ?? "";
|
|
45327
45842
|
return entry.endsWith("packages/cli/src/cli.ts") || entry.includes("packages/cli/dist/") || /(^|[/\\])astra$/.test(entry);
|
|
45328
45843
|
}
|