@harbour-enterprises/superdoc 1.5.0 → 1.6.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{PdfViewer-B_zxY1AJ.cjs → PdfViewer-D_YseVgi.cjs} +2 -2
- package/dist/chunks/{PdfViewer-DOsj8296.es.js → PdfViewer-Djd4zd7C.es.js} +2 -2
- package/dist/chunks/{SuperConverter-Dfxp14RO.es.js → SuperConverter-BAUfsE-s.es.js} +1282 -875
- package/dist/chunks/{SuperConverter-DmX1tktl.cjs → SuperConverter-DrhNM5Cd.cjs} +1282 -875
- package/dist/chunks/{index-BqmzYr4Q.cjs → index-CyiEB9wO.cjs} +4 -4
- package/dist/chunks/{index-BFU5qWH7.cjs → index-D-7HfDnq.cjs} +1890 -66
- package/dist/chunks/{index-zeVoYVRM.es.js → index-ajHpG3f-.es.js} +4 -4
- package/dist/chunks/{index-B6UxxE5v.es.js → index-fbohTSaQ.es.js} +1890 -66
- package/dist/style.css +7 -0
- package/dist/super-editor/converter.cjs +1 -1
- package/dist/super-editor/converter.es.js +1 -1
- package/dist/super-editor/docx-zipper.cjs +10 -0
- package/dist/super-editor/docx-zipper.es.js +10 -0
- package/dist/super-editor.cjs +2 -2
- package/dist/super-editor.es.js +3 -3
- package/dist/superdoc/src/dev/components/sidebar/SidebarSearch.vue.d.ts +5 -0
- package/dist/superdoc/src/dev/components/sidebar/SidebarSearch.vue.d.ts.map +1 -0
- package/dist/superdoc.cjs +3 -3
- package/dist/superdoc.es.js +3 -3
- package/dist/superdoc.umd.js +3182 -941
- package/dist/superdoc.umd.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const jszip = require("./jszip-C8_CqJxM.cjs");
|
|
3
3
|
const helpers$1 = require("./helpers-nOdwpmwb.cjs");
|
|
4
|
-
const superEditor_converter = require("./SuperConverter-
|
|
4
|
+
const superEditor_converter = require("./SuperConverter-DrhNM5Cd.cjs");
|
|
5
5
|
const vue = require("./vue-De9wkgLl.cjs");
|
|
6
6
|
require("./jszip.min-BPh2MMAa.cjs");
|
|
7
7
|
const eventemitter3 = require("./eventemitter3-BQuRcMPI.cjs");
|
|
@@ -9387,6 +9387,158 @@ class Schema {
|
|
|
9387
9387
|
return Object.fromEntries(markEntries);
|
|
9388
9388
|
}
|
|
9389
9389
|
}
|
|
9390
|
+
const positionTrackerKey = new superEditor_converter.PluginKey("positionTracker");
|
|
9391
|
+
function createPositionTrackerPlugin() {
|
|
9392
|
+
return new superEditor_converter.Plugin({
|
|
9393
|
+
key: positionTrackerKey,
|
|
9394
|
+
state: {
|
|
9395
|
+
init() {
|
|
9396
|
+
return {
|
|
9397
|
+
decorations: DecorationSet.empty,
|
|
9398
|
+
generation: 0
|
|
9399
|
+
};
|
|
9400
|
+
},
|
|
9401
|
+
apply(tr, state) {
|
|
9402
|
+
let { decorations, generation } = state;
|
|
9403
|
+
const meta = tr.getMeta(positionTrackerKey);
|
|
9404
|
+
if (meta?.action === "add") {
|
|
9405
|
+
decorations = decorations.add(tr.doc, meta.decorations);
|
|
9406
|
+
} else if (meta?.action === "remove") {
|
|
9407
|
+
const toRemove = decorations.find().filter((decoration) => meta.ids.includes(decoration.spec.id));
|
|
9408
|
+
decorations = decorations.remove(toRemove);
|
|
9409
|
+
} else if (meta?.action === "removeByType") {
|
|
9410
|
+
const toRemove = decorations.find().filter((decoration) => decoration.spec.type === meta.type);
|
|
9411
|
+
decorations = decorations.remove(toRemove);
|
|
9412
|
+
}
|
|
9413
|
+
if (tr.docChanged) {
|
|
9414
|
+
decorations = decorations.map(tr.mapping, tr.doc);
|
|
9415
|
+
generation += 1;
|
|
9416
|
+
}
|
|
9417
|
+
return { decorations, generation };
|
|
9418
|
+
}
|
|
9419
|
+
},
|
|
9420
|
+
props: {
|
|
9421
|
+
decorations() {
|
|
9422
|
+
return DecorationSet.empty;
|
|
9423
|
+
}
|
|
9424
|
+
}
|
|
9425
|
+
});
|
|
9426
|
+
}
|
|
9427
|
+
class PositionTracker {
|
|
9428
|
+
#editor;
|
|
9429
|
+
constructor(editor) {
|
|
9430
|
+
this.#editor = editor;
|
|
9431
|
+
}
|
|
9432
|
+
#getState() {
|
|
9433
|
+
if (!this.#editor?.state) return null;
|
|
9434
|
+
return positionTrackerKey.getState(this.#editor.state) ?? null;
|
|
9435
|
+
}
|
|
9436
|
+
track(from3, to, spec) {
|
|
9437
|
+
const id = uuid.v4();
|
|
9438
|
+
if (!this.#editor?.state) return id;
|
|
9439
|
+
const fullSpec = { kind: "range", ...spec, id };
|
|
9440
|
+
const deco = Decoration.inline(from3, to, {}, fullSpec);
|
|
9441
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9442
|
+
action: "add",
|
|
9443
|
+
decorations: [deco]
|
|
9444
|
+
}).setMeta("addToHistory", false);
|
|
9445
|
+
this.#editor.dispatch(tr);
|
|
9446
|
+
return id;
|
|
9447
|
+
}
|
|
9448
|
+
trackMany(ranges) {
|
|
9449
|
+
if (!this.#editor?.state) {
|
|
9450
|
+
return ranges.map(() => uuid.v4());
|
|
9451
|
+
}
|
|
9452
|
+
const ids = [];
|
|
9453
|
+
const decorations = [];
|
|
9454
|
+
for (const { from: from3, to, spec } of ranges) {
|
|
9455
|
+
const id = uuid.v4();
|
|
9456
|
+
ids.push(id);
|
|
9457
|
+
const fullSpec = { kind: "range", ...spec, id };
|
|
9458
|
+
decorations.push(Decoration.inline(from3, to, {}, fullSpec));
|
|
9459
|
+
}
|
|
9460
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9461
|
+
action: "add",
|
|
9462
|
+
decorations
|
|
9463
|
+
}).setMeta("addToHistory", false);
|
|
9464
|
+
this.#editor.dispatch(tr);
|
|
9465
|
+
return ids;
|
|
9466
|
+
}
|
|
9467
|
+
untrack(id) {
|
|
9468
|
+
if (!this.#editor?.state) return;
|
|
9469
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9470
|
+
action: "remove",
|
|
9471
|
+
ids: [id]
|
|
9472
|
+
}).setMeta("addToHistory", false);
|
|
9473
|
+
this.#editor.dispatch(tr);
|
|
9474
|
+
}
|
|
9475
|
+
untrackMany(ids) {
|
|
9476
|
+
if (!this.#editor?.state || ids.length === 0) return;
|
|
9477
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9478
|
+
action: "remove",
|
|
9479
|
+
ids
|
|
9480
|
+
}).setMeta("addToHistory", false);
|
|
9481
|
+
this.#editor.dispatch(tr);
|
|
9482
|
+
}
|
|
9483
|
+
untrackByType(type) {
|
|
9484
|
+
if (!this.#editor?.state) return;
|
|
9485
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9486
|
+
action: "removeByType",
|
|
9487
|
+
type
|
|
9488
|
+
}).setMeta("addToHistory", false);
|
|
9489
|
+
this.#editor.dispatch(tr);
|
|
9490
|
+
}
|
|
9491
|
+
resolve(id) {
|
|
9492
|
+
const state = this.#getState();
|
|
9493
|
+
if (!state) return null;
|
|
9494
|
+
const found = state.decorations.find().find((decoration) => decoration.spec.id === id);
|
|
9495
|
+
if (!found) return null;
|
|
9496
|
+
const spec = found.spec;
|
|
9497
|
+
return {
|
|
9498
|
+
id: spec.id,
|
|
9499
|
+
from: found.from,
|
|
9500
|
+
to: found.to,
|
|
9501
|
+
spec
|
|
9502
|
+
};
|
|
9503
|
+
}
|
|
9504
|
+
resolveMany(ids) {
|
|
9505
|
+
const result = /* @__PURE__ */ new Map();
|
|
9506
|
+
for (const id of ids) {
|
|
9507
|
+
result.set(id, null);
|
|
9508
|
+
}
|
|
9509
|
+
const state = this.#getState();
|
|
9510
|
+
if (!state || ids.length === 0) return result;
|
|
9511
|
+
const idSet = new Set(ids);
|
|
9512
|
+
for (const decoration of state.decorations.find()) {
|
|
9513
|
+
const spec = decoration.spec;
|
|
9514
|
+
if (idSet.has(spec.id)) {
|
|
9515
|
+
result.set(spec.id, {
|
|
9516
|
+
id: spec.id,
|
|
9517
|
+
from: decoration.from,
|
|
9518
|
+
to: decoration.to,
|
|
9519
|
+
spec
|
|
9520
|
+
});
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
return result;
|
|
9524
|
+
}
|
|
9525
|
+
findByType(type) {
|
|
9526
|
+
const state = this.#getState();
|
|
9527
|
+
if (!state) return [];
|
|
9528
|
+
return state.decorations.find().filter((decoration) => decoration.spec.type === type).map((decoration) => {
|
|
9529
|
+
const spec = decoration.spec;
|
|
9530
|
+
return {
|
|
9531
|
+
id: spec.id,
|
|
9532
|
+
from: decoration.from,
|
|
9533
|
+
to: decoration.to,
|
|
9534
|
+
spec
|
|
9535
|
+
};
|
|
9536
|
+
});
|
|
9537
|
+
}
|
|
9538
|
+
get generation() {
|
|
9539
|
+
return this.#getState()?.generation ?? 0;
|
|
9540
|
+
}
|
|
9541
|
+
}
|
|
9390
9542
|
class OxmlNode extends Node$1 {
|
|
9391
9543
|
constructor(config) {
|
|
9392
9544
|
super(config);
|
|
@@ -11747,6 +11899,34 @@ const EditorFocus = Extension.create({
|
|
|
11747
11899
|
return [editorFocusPlugin];
|
|
11748
11900
|
}
|
|
11749
11901
|
});
|
|
11902
|
+
const PositionTrackerExtension = Extension.create({
|
|
11903
|
+
name: "positionTracker",
|
|
11904
|
+
addStorage() {
|
|
11905
|
+
return {
|
|
11906
|
+
tracker: null
|
|
11907
|
+
};
|
|
11908
|
+
},
|
|
11909
|
+
addPmPlugins() {
|
|
11910
|
+
return [createPositionTrackerPlugin()];
|
|
11911
|
+
},
|
|
11912
|
+
onCreate() {
|
|
11913
|
+
const existing = this.editor?.positionTracker ?? this.storage.tracker;
|
|
11914
|
+
if (existing) {
|
|
11915
|
+
this.storage.tracker = existing;
|
|
11916
|
+
this.editor.positionTracker = existing;
|
|
11917
|
+
return;
|
|
11918
|
+
}
|
|
11919
|
+
const tracker = new PositionTracker(this.editor);
|
|
11920
|
+
this.storage.tracker = tracker;
|
|
11921
|
+
this.editor.positionTracker = tracker;
|
|
11922
|
+
},
|
|
11923
|
+
onDestroy() {
|
|
11924
|
+
if (this.editor?.positionTracker === this.storage.tracker) {
|
|
11925
|
+
this.editor.positionTracker = null;
|
|
11926
|
+
}
|
|
11927
|
+
this.storage.tracker = null;
|
|
11928
|
+
}
|
|
11929
|
+
});
|
|
11750
11930
|
class EventEmitter {
|
|
11751
11931
|
#events = /* @__PURE__ */ new Map();
|
|
11752
11932
|
/**
|
|
@@ -15524,7 +15704,7 @@ const canUseDOM = () => {
|
|
|
15524
15704
|
return false;
|
|
15525
15705
|
}
|
|
15526
15706
|
};
|
|
15527
|
-
const summaryVersion = "1.
|
|
15707
|
+
const summaryVersion = "1.6.0-next.2";
|
|
15528
15708
|
const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
|
|
15529
15709
|
const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
|
|
15530
15710
|
function mapAttributes(attrs) {
|
|
@@ -17017,7 +17197,7 @@ class Editor extends EventEmitter {
|
|
|
17017
17197
|
*/
|
|
17018
17198
|
#createExtensionService() {
|
|
17019
17199
|
const allowedExtensions = ["extension", "node", "mark"];
|
|
17020
|
-
const coreExtensions = [Editable, Commands, EditorFocus, Keymap];
|
|
17200
|
+
const coreExtensions = [Editable, Commands, EditorFocus, Keymap, PositionTrackerExtension];
|
|
17021
17201
|
const externalExtensions = this.options.externalExtensions || [];
|
|
17022
17202
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter((extension) => {
|
|
17023
17203
|
const extensionType = typeof extension?.type === "string" ? extension.type : void 0;
|
|
@@ -17781,6 +17961,10 @@ class Editor extends EventEmitter {
|
|
|
17781
17961
|
const hasCustomSettings = !!this.converter.convertedXml["word/settings.xml"]?.elements?.length;
|
|
17782
17962
|
const customSettings = hasCustomSettings ? this.converter.schemaToXml(this.converter.convertedXml["word/settings.xml"]?.elements?.[0]) : null;
|
|
17783
17963
|
const rels = this.converter.schemaToXml(this.converter.convertedXml["word/_rels/document.xml.rels"].elements[0]);
|
|
17964
|
+
const footnotesData = this.converter.convertedXml["word/footnotes.xml"];
|
|
17965
|
+
const footnotesXml = footnotesData?.elements?.[0] ? this.converter.schemaToXml(footnotesData.elements[0]) : null;
|
|
17966
|
+
const footnotesRelsData = this.converter.convertedXml["word/_rels/footnotes.xml.rels"];
|
|
17967
|
+
const footnotesRelsXml = footnotesRelsData?.elements?.[0] ? this.converter.schemaToXml(footnotesRelsData.elements[0]) : null;
|
|
17784
17968
|
const media = this.converter.addedMedia;
|
|
17785
17969
|
const updatedHeadersFooters = {};
|
|
17786
17970
|
Object.entries(this.converter.convertedXml).forEach(([name, json2]) => {
|
|
@@ -17805,6 +17989,12 @@ class Editor extends EventEmitter {
|
|
|
17805
17989
|
if (hasCustomSettings) {
|
|
17806
17990
|
updatedDocs["word/settings.xml"] = String(customSettings);
|
|
17807
17991
|
}
|
|
17992
|
+
if (footnotesXml) {
|
|
17993
|
+
updatedDocs["word/footnotes.xml"] = String(footnotesXml);
|
|
17994
|
+
}
|
|
17995
|
+
if (footnotesRelsXml) {
|
|
17996
|
+
updatedDocs["word/_rels/footnotes.xml.rels"] = String(footnotesRelsXml);
|
|
17997
|
+
}
|
|
17808
17998
|
if (comments.length) {
|
|
17809
17999
|
const commentsXml = this.converter.schemaToXml(this.converter.convertedXml["word/comments.xml"].elements[0]);
|
|
17810
18000
|
const commentsExtendedXml = this.converter.schemaToXml(
|
|
@@ -18181,7 +18371,7 @@ class Editor extends EventEmitter {
|
|
|
18181
18371
|
* Process collaboration migrations
|
|
18182
18372
|
*/
|
|
18183
18373
|
processCollaborationMigrations() {
|
|
18184
|
-
console.debug("[checkVersionMigrations] Current editor version", "1.
|
|
18374
|
+
console.debug("[checkVersionMigrations] Current editor version", "1.6.0-next.2");
|
|
18185
18375
|
if (!this.options.ydoc) return;
|
|
18186
18376
|
const metaMap = this.options.ydoc.getMap("meta");
|
|
18187
18377
|
let docVersion = metaMap.get("version");
|
|
@@ -34062,7 +34252,7 @@ function getParagraphSpacingBefore(block) {
|
|
|
34062
34252
|
const value = spacing?.before ?? spacing?.lineSpaceBefore;
|
|
34063
34253
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
34064
34254
|
}
|
|
34065
|
-
function getParagraphSpacingAfter(block) {
|
|
34255
|
+
function getParagraphSpacingAfter$1(block) {
|
|
34066
34256
|
const spacing = block.attrs?.spacing;
|
|
34067
34257
|
const value = spacing?.after ?? spacing?.lineSpaceAfter;
|
|
34068
34258
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
@@ -34089,7 +34279,7 @@ function getMeasureHeight(block, measure) {
|
|
|
34089
34279
|
}
|
|
34090
34280
|
const DEFAULT_PAGE_SIZE$2 = { w: 612, h: 792 };
|
|
34091
34281
|
const DEFAULT_MARGINS$2 = { top: 72, right: 72, bottom: 72, left: 72 };
|
|
34092
|
-
const COLUMN_EPSILON = 1e-4;
|
|
34282
|
+
const COLUMN_EPSILON$1 = 1e-4;
|
|
34093
34283
|
const layoutDebugEnabled$1 = typeof vue.process$1 !== "undefined" && typeof vue.process$1.env !== "undefined" && Boolean(vue.process$1.env.SD_DEBUG_LAYOUT);
|
|
34094
34284
|
const layoutLog = (...args) => {
|
|
34095
34285
|
if (!layoutDebugEnabled$1) return;
|
|
@@ -34400,7 +34590,13 @@ function layoutDocument(blocks, measures, options = {}) {
|
|
|
34400
34590
|
const paginator = createPaginator({
|
|
34401
34591
|
margins: paginatorMargins,
|
|
34402
34592
|
getActiveTopMargin: () => activeTopMargin,
|
|
34403
|
-
getActiveBottomMargin: () =>
|
|
34593
|
+
getActiveBottomMargin: () => {
|
|
34594
|
+
const reserves = options.footnoteReservedByPageIndex;
|
|
34595
|
+
const pageIndex = Math.max(0, pageCount - 1);
|
|
34596
|
+
const reserve = Array.isArray(reserves) ? reserves[pageIndex] : 0;
|
|
34597
|
+
const reservePx = typeof reserve === "number" && Number.isFinite(reserve) && reserve > 0 ? reserve : 0;
|
|
34598
|
+
return activeBottomMargin + reservePx;
|
|
34599
|
+
},
|
|
34404
34600
|
getActiveHeaderDistance: () => activeHeaderDistance,
|
|
34405
34601
|
getActiveFooterDistance: () => activeFooterDistance,
|
|
34406
34602
|
getActivePageSize: () => activePageSize,
|
|
@@ -34884,7 +35080,7 @@ function layoutDocument(blocks, measures, options = {}) {
|
|
|
34884
35080
|
if (!shouldSkipAnchoredTable) {
|
|
34885
35081
|
let state = paginator.ensurePage();
|
|
34886
35082
|
const availableHeight = state.contentBottom - state.cursorY;
|
|
34887
|
-
const spacingAfter = getParagraphSpacingAfter(paraBlock);
|
|
35083
|
+
const spacingAfter = getParagraphSpacingAfter$1(paraBlock);
|
|
34888
35084
|
const currentHeight = getMeasureHeight(paraBlock, measure);
|
|
34889
35085
|
const nextHeight = getMeasureHeight(nextBlock, nextMeasure);
|
|
34890
35086
|
const nextIsParagraph = nextBlock.kind === "paragraph" && nextMeasure.kind === "paragraph";
|
|
@@ -35197,7 +35393,7 @@ function normalizeColumns(input, contentWidth) {
|
|
|
35197
35393
|
const gap = Math.max(0, input?.gap ?? 0);
|
|
35198
35394
|
const totalGap = gap * (count - 1);
|
|
35199
35395
|
const width = (contentWidth - totalGap) / count;
|
|
35200
|
-
if (width <= COLUMN_EPSILON) {
|
|
35396
|
+
if (width <= COLUMN_EPSILON$1) {
|
|
35201
35397
|
return {
|
|
35202
35398
|
count: 1,
|
|
35203
35399
|
gap: 0,
|
|
@@ -36960,6 +37156,417 @@ const perfLog = (...args) => {
|
|
|
36960
37156
|
if (!layoutDebugEnabled) return;
|
|
36961
37157
|
console.log(...args);
|
|
36962
37158
|
};
|
|
37159
|
+
const isFootnotesLayoutInput = (value) => {
|
|
37160
|
+
if (!value || typeof value !== "object") return false;
|
|
37161
|
+
const v = value;
|
|
37162
|
+
if (!Array.isArray(v.refs)) return false;
|
|
37163
|
+
if (!(v.blocksById instanceof Map)) return false;
|
|
37164
|
+
return true;
|
|
37165
|
+
};
|
|
37166
|
+
const findPageIndexForPos = (layout, pos) => {
|
|
37167
|
+
if (!Number.isFinite(pos)) return null;
|
|
37168
|
+
const fallbackRanges = [];
|
|
37169
|
+
for (let pageIndex = 0; pageIndex < layout.pages.length; pageIndex++) {
|
|
37170
|
+
const page = layout.pages[pageIndex];
|
|
37171
|
+
let minStart = null;
|
|
37172
|
+
let maxEnd = null;
|
|
37173
|
+
for (const fragment of page.fragments) {
|
|
37174
|
+
const pmStart = fragment.pmStart;
|
|
37175
|
+
const pmEnd = fragment.pmEnd;
|
|
37176
|
+
if (pmStart == null || pmEnd == null) continue;
|
|
37177
|
+
if (minStart == null || pmStart < minStart) minStart = pmStart;
|
|
37178
|
+
if (maxEnd == null || pmEnd > maxEnd) maxEnd = pmEnd;
|
|
37179
|
+
if (pos >= pmStart && pos <= pmEnd) {
|
|
37180
|
+
return pageIndex;
|
|
37181
|
+
}
|
|
37182
|
+
}
|
|
37183
|
+
fallbackRanges[pageIndex] = minStart != null && maxEnd != null ? { pageIndex, minStart, maxEnd } : null;
|
|
37184
|
+
}
|
|
37185
|
+
let best = null;
|
|
37186
|
+
for (const entry of fallbackRanges) {
|
|
37187
|
+
if (!entry) continue;
|
|
37188
|
+
const distance = pos < entry.minStart ? entry.minStart - pos : pos > entry.maxEnd ? pos - entry.maxEnd : 0;
|
|
37189
|
+
if (!best || distance < best.distance) {
|
|
37190
|
+
best = { pageIndex: entry.pageIndex, distance };
|
|
37191
|
+
}
|
|
37192
|
+
}
|
|
37193
|
+
if (best) return best.pageIndex;
|
|
37194
|
+
if (layout.pages.length > 0) return layout.pages.length - 1;
|
|
37195
|
+
return null;
|
|
37196
|
+
};
|
|
37197
|
+
const footnoteColumnKey = (pageIndex, columnIndex) => `${pageIndex}:${columnIndex}`;
|
|
37198
|
+
const COLUMN_EPSILON = 0.01;
|
|
37199
|
+
const normalizeColumnsForFootnotes = (input, contentWidth) => {
|
|
37200
|
+
const rawCount = Number.isFinite(input?.count) ? Math.floor(input.count) : 1;
|
|
37201
|
+
const count = Math.max(1, rawCount || 1);
|
|
37202
|
+
const gap = Math.max(0, input?.gap ?? 0);
|
|
37203
|
+
const totalGap = gap * (count - 1);
|
|
37204
|
+
const width = (contentWidth - totalGap) / count;
|
|
37205
|
+
if (!Number.isFinite(width) || width <= COLUMN_EPSILON) {
|
|
37206
|
+
return {
|
|
37207
|
+
count: 1,
|
|
37208
|
+
gap: 0,
|
|
37209
|
+
width: Math.max(0, contentWidth)
|
|
37210
|
+
};
|
|
37211
|
+
}
|
|
37212
|
+
return { count, gap, width };
|
|
37213
|
+
};
|
|
37214
|
+
const resolveSectionColumnsByIndex = (options, blocks) => {
|
|
37215
|
+
const result = /* @__PURE__ */ new Map();
|
|
37216
|
+
let activeColumns = options.columns ?? { count: 1, gap: 0 };
|
|
37217
|
+
if (blocks && blocks.length > 0) {
|
|
37218
|
+
for (const block of blocks) {
|
|
37219
|
+
if (block.kind !== "sectionBreak") continue;
|
|
37220
|
+
const sectionIndexRaw = block.attrs?.sectionIndex;
|
|
37221
|
+
const sectionIndex = typeof sectionIndexRaw === "number" && Number.isFinite(sectionIndexRaw) ? sectionIndexRaw : result.size;
|
|
37222
|
+
if (block.columns) {
|
|
37223
|
+
activeColumns = { count: block.columns.count, gap: block.columns.gap };
|
|
37224
|
+
}
|
|
37225
|
+
result.set(sectionIndex, { ...activeColumns });
|
|
37226
|
+
}
|
|
37227
|
+
}
|
|
37228
|
+
if (result.size === 0) {
|
|
37229
|
+
result.set(0, { ...activeColumns });
|
|
37230
|
+
}
|
|
37231
|
+
return result;
|
|
37232
|
+
};
|
|
37233
|
+
const resolvePageColumns = (layout, options, blocks) => {
|
|
37234
|
+
const sectionColumns = resolveSectionColumnsByIndex(options, blocks);
|
|
37235
|
+
const result = /* @__PURE__ */ new Map();
|
|
37236
|
+
for (let pageIndex = 0; pageIndex < layout.pages.length; pageIndex += 1) {
|
|
37237
|
+
const page = layout.pages[pageIndex];
|
|
37238
|
+
const pageSize = page.size ?? layout.pageSize ?? DEFAULT_PAGE_SIZE$1;
|
|
37239
|
+
const marginLeft = normalizeMargin(
|
|
37240
|
+
page.margins?.left,
|
|
37241
|
+
normalizeMargin(options.margins?.left, DEFAULT_MARGINS$1.left)
|
|
37242
|
+
);
|
|
37243
|
+
const marginRight = normalizeMargin(
|
|
37244
|
+
page.margins?.right,
|
|
37245
|
+
normalizeMargin(options.margins?.right, DEFAULT_MARGINS$1.right)
|
|
37246
|
+
);
|
|
37247
|
+
const contentWidth = pageSize.w - (marginLeft + marginRight);
|
|
37248
|
+
const sectionIndex = page.sectionIndex ?? 0;
|
|
37249
|
+
const columnsConfig = sectionColumns.get(sectionIndex) ?? options.columns ?? { count: 1, gap: 0 };
|
|
37250
|
+
const normalized = normalizeColumnsForFootnotes(columnsConfig, contentWidth);
|
|
37251
|
+
result.set(pageIndex, { ...normalized, left: marginLeft, contentWidth });
|
|
37252
|
+
}
|
|
37253
|
+
return result;
|
|
37254
|
+
};
|
|
37255
|
+
const findFragmentForPos = (page, pos) => {
|
|
37256
|
+
for (const fragment of page.fragments) {
|
|
37257
|
+
const pmStart = fragment.pmStart;
|
|
37258
|
+
const pmEnd = fragment.pmEnd;
|
|
37259
|
+
if (pmStart == null || pmEnd == null) continue;
|
|
37260
|
+
if (pos >= pmStart && pos <= pmEnd) {
|
|
37261
|
+
return fragment;
|
|
37262
|
+
}
|
|
37263
|
+
}
|
|
37264
|
+
return null;
|
|
37265
|
+
};
|
|
37266
|
+
const assignFootnotesToColumns = (layout, refs, pageColumns) => {
|
|
37267
|
+
const result = /* @__PURE__ */ new Map();
|
|
37268
|
+
const seenByColumn = /* @__PURE__ */ new Map();
|
|
37269
|
+
for (const ref of refs) {
|
|
37270
|
+
const pageIndex = findPageIndexForPos(layout, ref.pos);
|
|
37271
|
+
if (pageIndex == null) continue;
|
|
37272
|
+
const columns = pageColumns.get(pageIndex);
|
|
37273
|
+
const page = layout.pages[pageIndex];
|
|
37274
|
+
let columnIndex = 0;
|
|
37275
|
+
if (columns && columns.count > 1 && page) {
|
|
37276
|
+
const fragment = findFragmentForPos(page, ref.pos);
|
|
37277
|
+
if (fragment && typeof fragment.x === "number") {
|
|
37278
|
+
const columnStride = columns.width + columns.gap;
|
|
37279
|
+
const rawIndex = columnStride > 0 ? Math.floor((fragment.x - columns.left) / columnStride) : 0;
|
|
37280
|
+
columnIndex = Math.max(0, Math.min(columns.count - 1, rawIndex));
|
|
37281
|
+
}
|
|
37282
|
+
}
|
|
37283
|
+
const key2 = footnoteColumnKey(pageIndex, columnIndex);
|
|
37284
|
+
let seen = seenByColumn.get(key2);
|
|
37285
|
+
if (!seen) {
|
|
37286
|
+
seen = /* @__PURE__ */ new Set();
|
|
37287
|
+
seenByColumn.set(key2, seen);
|
|
37288
|
+
}
|
|
37289
|
+
if (seen.has(ref.id)) continue;
|
|
37290
|
+
seen.add(ref.id);
|
|
37291
|
+
const pageMap = result.get(pageIndex) ?? /* @__PURE__ */ new Map();
|
|
37292
|
+
const list = pageMap.get(columnIndex) ?? [];
|
|
37293
|
+
list.push(ref.id);
|
|
37294
|
+
pageMap.set(columnIndex, list);
|
|
37295
|
+
result.set(pageIndex, pageMap);
|
|
37296
|
+
}
|
|
37297
|
+
return result;
|
|
37298
|
+
};
|
|
37299
|
+
const resolveFootnoteMeasurementWidth = (options, blocks) => {
|
|
37300
|
+
const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE$1;
|
|
37301
|
+
const margins = {
|
|
37302
|
+
right: normalizeMargin(options.margins?.right, DEFAULT_MARGINS$1.right),
|
|
37303
|
+
left: normalizeMargin(options.margins?.left, DEFAULT_MARGINS$1.left)
|
|
37304
|
+
};
|
|
37305
|
+
let width = pageSize.w - (margins.left + margins.right);
|
|
37306
|
+
let activeColumns = options.columns ?? { count: 1, gap: 0 };
|
|
37307
|
+
let activePageSize = pageSize;
|
|
37308
|
+
let activeMargins = { ...margins };
|
|
37309
|
+
const resolveColumnWidth = () => {
|
|
37310
|
+
const contentWidth = activePageSize.w - (activeMargins.left + activeMargins.right);
|
|
37311
|
+
const normalized = normalizeColumnsForFootnotes(activeColumns, contentWidth);
|
|
37312
|
+
return normalized.width;
|
|
37313
|
+
};
|
|
37314
|
+
width = resolveColumnWidth();
|
|
37315
|
+
if (blocks && blocks.length > 0) {
|
|
37316
|
+
for (const block of blocks) {
|
|
37317
|
+
if (block.kind !== "sectionBreak") continue;
|
|
37318
|
+
activePageSize = block.pageSize ?? activePageSize;
|
|
37319
|
+
activeMargins = {
|
|
37320
|
+
right: normalizeMargin(block.margins?.right, activeMargins.right),
|
|
37321
|
+
left: normalizeMargin(block.margins?.left, activeMargins.left)
|
|
37322
|
+
};
|
|
37323
|
+
if (block.columns) {
|
|
37324
|
+
activeColumns = { count: block.columns.count, gap: block.columns.gap };
|
|
37325
|
+
}
|
|
37326
|
+
const w = resolveColumnWidth();
|
|
37327
|
+
if (w > 0 && w < width) width = w;
|
|
37328
|
+
}
|
|
37329
|
+
}
|
|
37330
|
+
if (!Number.isFinite(width) || width <= 0) return 0;
|
|
37331
|
+
return width;
|
|
37332
|
+
};
|
|
37333
|
+
const MIN_FOOTNOTE_BODY_HEIGHT = 1;
|
|
37334
|
+
const DEFAULT_FOOTNOTE_SEPARATOR_SPACING_BEFORE = 12;
|
|
37335
|
+
const computeMaxFootnoteReserve = (layoutForPages, pageIndex, baseReserve = 0) => {
|
|
37336
|
+
const page = layoutForPages.pages?.[pageIndex];
|
|
37337
|
+
if (!page) return 0;
|
|
37338
|
+
const pageSize = page.size ?? layoutForPages.pageSize ?? DEFAULT_PAGE_SIZE$1;
|
|
37339
|
+
const topMargin = normalizeMargin(page.margins?.top, DEFAULT_MARGINS$1.top);
|
|
37340
|
+
const bottomWithReserve = normalizeMargin(page.margins?.bottom, DEFAULT_MARGINS$1.bottom);
|
|
37341
|
+
const baseReserveSafe = Number.isFinite(baseReserve) ? Math.max(0, baseReserve) : 0;
|
|
37342
|
+
const bottomMargin = Math.max(0, bottomWithReserve - baseReserveSafe);
|
|
37343
|
+
const availableForBody = pageSize.h - topMargin - bottomMargin;
|
|
37344
|
+
if (!Number.isFinite(availableForBody)) return 0;
|
|
37345
|
+
return Math.max(0, availableForBody - MIN_FOOTNOTE_BODY_HEIGHT);
|
|
37346
|
+
};
|
|
37347
|
+
const sumLineHeights$1 = (lines, fromLine, toLine) => {
|
|
37348
|
+
if (!lines || fromLine >= toLine) return 0;
|
|
37349
|
+
let total = 0;
|
|
37350
|
+
for (let i = fromLine; i < toLine; i += 1) {
|
|
37351
|
+
total += lines[i]?.lineHeight ?? 0;
|
|
37352
|
+
}
|
|
37353
|
+
return total;
|
|
37354
|
+
};
|
|
37355
|
+
const getParagraphSpacingAfter = (block) => {
|
|
37356
|
+
const spacing = block.attrs?.spacing;
|
|
37357
|
+
const value = spacing?.after ?? spacing?.lineSpaceAfter;
|
|
37358
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
37359
|
+
};
|
|
37360
|
+
const resolveSeparatorSpacingBefore = (rangesByFootnoteId, measuresById, explicitValue, fallbackValue) => {
|
|
37361
|
+
if (typeof explicitValue === "number" && Number.isFinite(explicitValue)) {
|
|
37362
|
+
return Math.max(0, explicitValue);
|
|
37363
|
+
}
|
|
37364
|
+
for (const ranges of rangesByFootnoteId.values()) {
|
|
37365
|
+
for (const range of ranges) {
|
|
37366
|
+
if (range.kind === "paragraph") {
|
|
37367
|
+
const measure = measuresById.get(range.blockId);
|
|
37368
|
+
if (measure?.kind !== "paragraph") continue;
|
|
37369
|
+
const lineHeight2 = measure.lines?.[range.fromLine]?.lineHeight ?? measure.lines?.[0]?.lineHeight;
|
|
37370
|
+
if (typeof lineHeight2 === "number" && Number.isFinite(lineHeight2) && lineHeight2 > 0) {
|
|
37371
|
+
return lineHeight2;
|
|
37372
|
+
}
|
|
37373
|
+
}
|
|
37374
|
+
if (range.kind === "list-item") {
|
|
37375
|
+
const measure = measuresById.get(range.blockId);
|
|
37376
|
+
if (measure?.kind !== "list") continue;
|
|
37377
|
+
const itemMeasure = measure.items.find((item) => item.itemId === range.itemId);
|
|
37378
|
+
const lineHeight2 = itemMeasure?.paragraph?.lines?.[range.fromLine]?.lineHeight ?? itemMeasure?.paragraph?.lines?.[0]?.lineHeight;
|
|
37379
|
+
if (typeof lineHeight2 === "number" && Number.isFinite(lineHeight2) && lineHeight2 > 0) {
|
|
37380
|
+
return lineHeight2;
|
|
37381
|
+
}
|
|
37382
|
+
}
|
|
37383
|
+
}
|
|
37384
|
+
}
|
|
37385
|
+
return Math.max(0, fallbackValue);
|
|
37386
|
+
};
|
|
37387
|
+
const getRangeRenderHeight = (range) => {
|
|
37388
|
+
if (range.kind === "paragraph" || range.kind === "list-item") {
|
|
37389
|
+
const spacing = range.toLine >= range.totalLines ? range.spacingAfter : 0;
|
|
37390
|
+
return range.height + spacing;
|
|
37391
|
+
}
|
|
37392
|
+
return range.height;
|
|
37393
|
+
};
|
|
37394
|
+
const buildFootnoteRanges = (blocks, measuresById) => {
|
|
37395
|
+
const ranges = [];
|
|
37396
|
+
blocks.forEach((block) => {
|
|
37397
|
+
const measure = measuresById.get(block.id);
|
|
37398
|
+
if (!measure) return;
|
|
37399
|
+
if (block.kind === "paragraph") {
|
|
37400
|
+
if (measure.kind !== "paragraph") return;
|
|
37401
|
+
const lineCount = measure.lines?.length ?? 0;
|
|
37402
|
+
if (lineCount === 0) return;
|
|
37403
|
+
ranges.push({
|
|
37404
|
+
kind: "paragraph",
|
|
37405
|
+
blockId: block.id,
|
|
37406
|
+
fromLine: 0,
|
|
37407
|
+
toLine: lineCount,
|
|
37408
|
+
totalLines: lineCount,
|
|
37409
|
+
height: sumLineHeights$1(measure.lines, 0, lineCount),
|
|
37410
|
+
spacingAfter: getParagraphSpacingAfter(block)
|
|
37411
|
+
});
|
|
37412
|
+
return;
|
|
37413
|
+
}
|
|
37414
|
+
if (block.kind === "list") {
|
|
37415
|
+
if (measure.kind !== "list") return;
|
|
37416
|
+
block.items.forEach((item) => {
|
|
37417
|
+
const itemMeasure = measure.items.find((entry) => entry.itemId === item.id);
|
|
37418
|
+
if (!itemMeasure) return;
|
|
37419
|
+
const lineCount = itemMeasure.paragraph.lines?.length ?? 0;
|
|
37420
|
+
if (lineCount === 0) return;
|
|
37421
|
+
ranges.push({
|
|
37422
|
+
kind: "list-item",
|
|
37423
|
+
blockId: block.id,
|
|
37424
|
+
itemId: item.id,
|
|
37425
|
+
fromLine: 0,
|
|
37426
|
+
toLine: lineCount,
|
|
37427
|
+
totalLines: lineCount,
|
|
37428
|
+
height: sumLineHeights$1(itemMeasure.paragraph.lines, 0, lineCount),
|
|
37429
|
+
spacingAfter: getParagraphSpacingAfter(item.paragraph)
|
|
37430
|
+
});
|
|
37431
|
+
});
|
|
37432
|
+
return;
|
|
37433
|
+
}
|
|
37434
|
+
if (block.kind === "table" && measure.kind === "table") {
|
|
37435
|
+
const height = Math.max(0, measure.totalHeight ?? 0);
|
|
37436
|
+
if (height > 0) {
|
|
37437
|
+
ranges.push({ kind: "table", blockId: block.id, height });
|
|
37438
|
+
}
|
|
37439
|
+
return;
|
|
37440
|
+
}
|
|
37441
|
+
if (block.kind === "image" && measure.kind === "image") {
|
|
37442
|
+
const height = Math.max(0, measure.height ?? 0);
|
|
37443
|
+
if (height > 0) {
|
|
37444
|
+
ranges.push({ kind: "image", blockId: block.id, height });
|
|
37445
|
+
}
|
|
37446
|
+
return;
|
|
37447
|
+
}
|
|
37448
|
+
if (block.kind === "drawing" && measure.kind === "drawing") {
|
|
37449
|
+
const height = Math.max(0, measure.height ?? 0);
|
|
37450
|
+
if (height > 0) {
|
|
37451
|
+
ranges.push({ kind: "drawing", blockId: block.id, height });
|
|
37452
|
+
}
|
|
37453
|
+
}
|
|
37454
|
+
});
|
|
37455
|
+
return ranges;
|
|
37456
|
+
};
|
|
37457
|
+
const splitRangeAtHeight = (range, availableHeight, measuresById) => {
|
|
37458
|
+
if (availableHeight <= 0) return { fitted: null, remaining: range };
|
|
37459
|
+
if (range.kind !== "paragraph") {
|
|
37460
|
+
return getRangeRenderHeight(range) <= availableHeight ? { fitted: range, remaining: null } : { fitted: null, remaining: range };
|
|
37461
|
+
}
|
|
37462
|
+
const measure = measuresById.get(range.blockId);
|
|
37463
|
+
if (!measure || measure.kind !== "paragraph" || !measure.lines) {
|
|
37464
|
+
return getRangeRenderHeight(range) <= availableHeight ? { fitted: range, remaining: null } : { fitted: null, remaining: range };
|
|
37465
|
+
}
|
|
37466
|
+
let accumulatedHeight = 0;
|
|
37467
|
+
let splitLine = range.fromLine;
|
|
37468
|
+
for (let i = range.fromLine; i < range.toLine; i += 1) {
|
|
37469
|
+
const lineHeight2 = measure.lines[i]?.lineHeight ?? 0;
|
|
37470
|
+
if (accumulatedHeight + lineHeight2 > availableHeight) break;
|
|
37471
|
+
accumulatedHeight += lineHeight2;
|
|
37472
|
+
splitLine = i + 1;
|
|
37473
|
+
}
|
|
37474
|
+
if (splitLine === range.fromLine) {
|
|
37475
|
+
return { fitted: null, remaining: range };
|
|
37476
|
+
}
|
|
37477
|
+
const fitted = {
|
|
37478
|
+
...range,
|
|
37479
|
+
toLine: splitLine,
|
|
37480
|
+
height: sumLineHeights$1(measure.lines, range.fromLine, splitLine)
|
|
37481
|
+
};
|
|
37482
|
+
if (splitLine >= range.toLine) {
|
|
37483
|
+
return getRangeRenderHeight(fitted) <= availableHeight ? { fitted, remaining: null } : { fitted: null, remaining: range };
|
|
37484
|
+
}
|
|
37485
|
+
const remaining = {
|
|
37486
|
+
...range,
|
|
37487
|
+
fromLine: splitLine,
|
|
37488
|
+
height: sumLineHeights$1(measure.lines, splitLine, range.toLine)
|
|
37489
|
+
};
|
|
37490
|
+
return { fitted, remaining };
|
|
37491
|
+
};
|
|
37492
|
+
const forceFitFirstRange = (range, measuresById) => {
|
|
37493
|
+
if (range.kind !== "paragraph") {
|
|
37494
|
+
return { fitted: range, remaining: null };
|
|
37495
|
+
}
|
|
37496
|
+
const measure = measuresById.get(range.blockId);
|
|
37497
|
+
if (!measure || measure.kind !== "paragraph" || !measure.lines?.length) {
|
|
37498
|
+
return { fitted: range, remaining: null };
|
|
37499
|
+
}
|
|
37500
|
+
const nextLine = Math.min(range.fromLine + 1, range.toLine);
|
|
37501
|
+
const fitted = {
|
|
37502
|
+
...range,
|
|
37503
|
+
toLine: nextLine,
|
|
37504
|
+
height: sumLineHeights$1(measure.lines, range.fromLine, nextLine)
|
|
37505
|
+
};
|
|
37506
|
+
if (nextLine >= range.toLine) {
|
|
37507
|
+
return { fitted, remaining: null };
|
|
37508
|
+
}
|
|
37509
|
+
const remaining = {
|
|
37510
|
+
...range,
|
|
37511
|
+
fromLine: nextLine,
|
|
37512
|
+
height: sumLineHeights$1(measure.lines, nextLine, range.toLine)
|
|
37513
|
+
};
|
|
37514
|
+
return { fitted, remaining };
|
|
37515
|
+
};
|
|
37516
|
+
const fitFootnoteContent = (id, inputRanges, availableHeight, pageIndex, columnIndex, isContinuation, measuresById, forceFirstRange) => {
|
|
37517
|
+
const fittedRanges = [];
|
|
37518
|
+
let remainingRanges = [];
|
|
37519
|
+
let usedHeight = 0;
|
|
37520
|
+
const maxHeight = Math.max(0, availableHeight);
|
|
37521
|
+
for (let index2 = 0; index2 < inputRanges.length; index2 += 1) {
|
|
37522
|
+
const range = inputRanges[index2];
|
|
37523
|
+
const remainingSpace = maxHeight - usedHeight;
|
|
37524
|
+
const rangeHeight = getRangeRenderHeight(range);
|
|
37525
|
+
if (rangeHeight <= remainingSpace) {
|
|
37526
|
+
fittedRanges.push(range);
|
|
37527
|
+
usedHeight += rangeHeight;
|
|
37528
|
+
continue;
|
|
37529
|
+
}
|
|
37530
|
+
if (range.kind === "paragraph") {
|
|
37531
|
+
const split = splitRangeAtHeight(range, remainingSpace, measuresById);
|
|
37532
|
+
if (split.fitted && getRangeRenderHeight(split.fitted) <= remainingSpace) {
|
|
37533
|
+
fittedRanges.push(split.fitted);
|
|
37534
|
+
usedHeight += getRangeRenderHeight(split.fitted);
|
|
37535
|
+
}
|
|
37536
|
+
if (split.remaining) {
|
|
37537
|
+
remainingRanges = [split.remaining, ...inputRanges.slice(index2 + 1)];
|
|
37538
|
+
} else {
|
|
37539
|
+
remainingRanges = inputRanges.slice(index2 + 1);
|
|
37540
|
+
}
|
|
37541
|
+
break;
|
|
37542
|
+
}
|
|
37543
|
+
remainingRanges = [range, ...inputRanges.slice(index2 + 1)];
|
|
37544
|
+
break;
|
|
37545
|
+
}
|
|
37546
|
+
if (fittedRanges.length === 0 && forceFirstRange && inputRanges.length > 0) {
|
|
37547
|
+
const forced = forceFitFirstRange(inputRanges[0], measuresById);
|
|
37548
|
+
if (forced.fitted) {
|
|
37549
|
+
fittedRanges.push(forced.fitted);
|
|
37550
|
+
usedHeight = getRangeRenderHeight(forced.fitted);
|
|
37551
|
+
remainingRanges = [];
|
|
37552
|
+
if (forced.remaining) {
|
|
37553
|
+
remainingRanges.push(forced.remaining);
|
|
37554
|
+
}
|
|
37555
|
+
remainingRanges.push(...inputRanges.slice(1));
|
|
37556
|
+
}
|
|
37557
|
+
}
|
|
37558
|
+
return {
|
|
37559
|
+
slice: {
|
|
37560
|
+
id,
|
|
37561
|
+
pageIndex,
|
|
37562
|
+
columnIndex,
|
|
37563
|
+
isContinuation,
|
|
37564
|
+
ranges: fittedRanges,
|
|
37565
|
+
totalHeight: usedHeight
|
|
37566
|
+
},
|
|
37567
|
+
remainingRanges
|
|
37568
|
+
};
|
|
37569
|
+
};
|
|
36963
37570
|
async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, options, measureBlock2, headerFooter) {
|
|
36964
37571
|
performance.now();
|
|
36965
37572
|
const dirty = computeDirtyRegions(previousBlocks, nextBlocks);
|
|
@@ -37223,6 +37830,497 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
|
|
|
37223
37830
|
converged
|
|
37224
37831
|
});
|
|
37225
37832
|
}
|
|
37833
|
+
let extraBlocks;
|
|
37834
|
+
let extraMeasures;
|
|
37835
|
+
const footnotesInput = isFootnotesLayoutInput(options.footnotes) ? options.footnotes : null;
|
|
37836
|
+
if (footnotesInput && footnotesInput.refs.length > 0 && footnotesInput.blocksById.size > 0) {
|
|
37837
|
+
const gap = typeof footnotesInput.gap === "number" && Number.isFinite(footnotesInput.gap) ? footnotesInput.gap : 2;
|
|
37838
|
+
const topPadding = typeof footnotesInput.topPadding === "number" && Number.isFinite(footnotesInput.topPadding) ? footnotesInput.topPadding : 6;
|
|
37839
|
+
const dividerHeight = typeof footnotesInput.dividerHeight === "number" && Number.isFinite(footnotesInput.dividerHeight) ? footnotesInput.dividerHeight : 6;
|
|
37840
|
+
const safeGap = Math.max(0, gap);
|
|
37841
|
+
const safeTopPadding = Math.max(0, topPadding);
|
|
37842
|
+
const safeDividerHeight = Math.max(0, dividerHeight);
|
|
37843
|
+
const continuationDividerHeight = safeDividerHeight;
|
|
37844
|
+
const continuationDividerWidthFactor = 0.3;
|
|
37845
|
+
const footnoteWidth = resolveFootnoteMeasurementWidth(options, currentBlocks);
|
|
37846
|
+
if (footnoteWidth > 0) {
|
|
37847
|
+
const footnoteConstraints = { maxWidth: footnoteWidth, maxHeight: measurementHeight };
|
|
37848
|
+
const collectFootnoteIdsByColumn = (idsByColumn2) => {
|
|
37849
|
+
const ids = /* @__PURE__ */ new Set();
|
|
37850
|
+
idsByColumn2.forEach((columns) => {
|
|
37851
|
+
columns.forEach((list) => {
|
|
37852
|
+
list.forEach((id) => ids.add(id));
|
|
37853
|
+
});
|
|
37854
|
+
});
|
|
37855
|
+
return ids;
|
|
37856
|
+
};
|
|
37857
|
+
const measureFootnoteBlocks = async (ids) => {
|
|
37858
|
+
const needed = /* @__PURE__ */ new Map();
|
|
37859
|
+
ids.forEach((id) => {
|
|
37860
|
+
const blocks2 = footnotesInput.blocksById.get(id) ?? [];
|
|
37861
|
+
blocks2.forEach((block) => {
|
|
37862
|
+
if (block?.id && !needed.has(block.id)) {
|
|
37863
|
+
needed.set(block.id, block);
|
|
37864
|
+
}
|
|
37865
|
+
});
|
|
37866
|
+
});
|
|
37867
|
+
const blocks = Array.from(needed.values());
|
|
37868
|
+
const measuresById2 = /* @__PURE__ */ new Map();
|
|
37869
|
+
await Promise.all(
|
|
37870
|
+
blocks.map(async (block) => {
|
|
37871
|
+
const cached = measureCache.get(block, footnoteConstraints.maxWidth, footnoteConstraints.maxHeight);
|
|
37872
|
+
if (cached) {
|
|
37873
|
+
measuresById2.set(block.id, cached);
|
|
37874
|
+
return;
|
|
37875
|
+
}
|
|
37876
|
+
const measurement = await measureBlock2(block, footnoteConstraints);
|
|
37877
|
+
measureCache.set(block, footnoteConstraints.maxWidth, footnoteConstraints.maxHeight, measurement);
|
|
37878
|
+
measuresById2.set(block.id, measurement);
|
|
37879
|
+
})
|
|
37880
|
+
);
|
|
37881
|
+
return { blocks, measuresById: measuresById2 };
|
|
37882
|
+
};
|
|
37883
|
+
const computeFootnoteLayoutPlan = (layoutForPages, idsByColumn2, measuresById2, baseReserves = [], pageColumns2) => {
|
|
37884
|
+
const pageCount = layoutForPages.pages.length;
|
|
37885
|
+
const slicesByPage = /* @__PURE__ */ new Map();
|
|
37886
|
+
const reserves2 = new Array(pageCount).fill(0);
|
|
37887
|
+
const hasContinuationByColumn = /* @__PURE__ */ new Map();
|
|
37888
|
+
const rangesByFootnoteId = /* @__PURE__ */ new Map();
|
|
37889
|
+
const cappedPages = /* @__PURE__ */ new Set();
|
|
37890
|
+
const allIds = collectFootnoteIdsByColumn(idsByColumn2);
|
|
37891
|
+
allIds.forEach((id) => {
|
|
37892
|
+
const blocks = footnotesInput.blocksById.get(id) ?? [];
|
|
37893
|
+
rangesByFootnoteId.set(id, buildFootnoteRanges(blocks, measuresById2));
|
|
37894
|
+
});
|
|
37895
|
+
const separatorSpacingBefore = resolveSeparatorSpacingBefore(
|
|
37896
|
+
rangesByFootnoteId,
|
|
37897
|
+
measuresById2,
|
|
37898
|
+
footnotesInput.separatorSpacingBefore,
|
|
37899
|
+
DEFAULT_FOOTNOTE_SEPARATOR_SPACING_BEFORE
|
|
37900
|
+
);
|
|
37901
|
+
const safeSeparatorSpacingBefore = Math.max(0, separatorSpacingBefore);
|
|
37902
|
+
let pendingByColumn = /* @__PURE__ */ new Map();
|
|
37903
|
+
for (let pageIndex = 0; pageIndex < pageCount; pageIndex += 1) {
|
|
37904
|
+
const baseReserve = Number.isFinite(baseReserves?.[pageIndex]) ? Math.max(0, baseReserves[pageIndex]) : 0;
|
|
37905
|
+
const maxReserve = computeMaxFootnoteReserve(layoutForPages, pageIndex, baseReserve);
|
|
37906
|
+
const columns = pageColumns2.get(pageIndex);
|
|
37907
|
+
const columnCount = Math.max(1, Math.floor(columns?.count ?? 1));
|
|
37908
|
+
const pendingForPage = /* @__PURE__ */ new Map();
|
|
37909
|
+
pendingByColumn.forEach((entries, columnIndex) => {
|
|
37910
|
+
const targetIndex = columnIndex < columnCount ? columnIndex : Math.max(0, columnCount - 1);
|
|
37911
|
+
const list = pendingForPage.get(targetIndex) ?? [];
|
|
37912
|
+
list.push(...entries);
|
|
37913
|
+
pendingForPage.set(targetIndex, list);
|
|
37914
|
+
});
|
|
37915
|
+
pendingByColumn = /* @__PURE__ */ new Map();
|
|
37916
|
+
const pageSlices = [];
|
|
37917
|
+
let pageReserve = 0;
|
|
37918
|
+
for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {
|
|
37919
|
+
let usedHeight = 0;
|
|
37920
|
+
const columnSlices = [];
|
|
37921
|
+
const nextPending = [];
|
|
37922
|
+
let stopPlacement = false;
|
|
37923
|
+
const columnKey = footnoteColumnKey(pageIndex, columnIndex);
|
|
37924
|
+
const placeFootnote = (id, ranges, isContinuation) => {
|
|
37925
|
+
if (!ranges || ranges.length === 0) {
|
|
37926
|
+
return { placed: false, remaining: [] };
|
|
37927
|
+
}
|
|
37928
|
+
const isFirstSlice = columnSlices.length === 0;
|
|
37929
|
+
const separatorBefore = isFirstSlice ? safeSeparatorSpacingBefore : 0;
|
|
37930
|
+
const separatorHeight = isFirstSlice ? isContinuation ? continuationDividerHeight : safeDividerHeight : 0;
|
|
37931
|
+
const overhead = isFirstSlice ? separatorBefore + separatorHeight + safeTopPadding : 0;
|
|
37932
|
+
const gapBefore = !isFirstSlice ? safeGap : 0;
|
|
37933
|
+
const availableHeight = Math.max(0, maxReserve - usedHeight - overhead - gapBefore);
|
|
37934
|
+
const { slice: slice2, remainingRanges } = fitFootnoteContent(
|
|
37935
|
+
id,
|
|
37936
|
+
ranges,
|
|
37937
|
+
availableHeight,
|
|
37938
|
+
pageIndex,
|
|
37939
|
+
columnIndex,
|
|
37940
|
+
isContinuation,
|
|
37941
|
+
measuresById2,
|
|
37942
|
+
isFirstSlice && maxReserve > 0
|
|
37943
|
+
);
|
|
37944
|
+
if (slice2.ranges.length === 0) {
|
|
37945
|
+
return { placed: false, remaining: ranges };
|
|
37946
|
+
}
|
|
37947
|
+
if (isFirstSlice) {
|
|
37948
|
+
usedHeight += overhead;
|
|
37949
|
+
if (isContinuation) {
|
|
37950
|
+
hasContinuationByColumn.set(columnKey, true);
|
|
37951
|
+
}
|
|
37952
|
+
}
|
|
37953
|
+
if (gapBefore > 0) {
|
|
37954
|
+
usedHeight += gapBefore;
|
|
37955
|
+
}
|
|
37956
|
+
usedHeight += slice2.totalHeight;
|
|
37957
|
+
columnSlices.push(slice2);
|
|
37958
|
+
return { placed: true, remaining: remainingRanges };
|
|
37959
|
+
};
|
|
37960
|
+
const pending = pendingForPage.get(columnIndex) ?? [];
|
|
37961
|
+
for (const entry of pending) {
|
|
37962
|
+
if (stopPlacement) {
|
|
37963
|
+
nextPending.push(entry);
|
|
37964
|
+
continue;
|
|
37965
|
+
}
|
|
37966
|
+
if (!entry.ranges || entry.ranges.length === 0) continue;
|
|
37967
|
+
const result = placeFootnote(entry.id, entry.ranges, true);
|
|
37968
|
+
if (!result.placed) {
|
|
37969
|
+
nextPending.push(entry);
|
|
37970
|
+
stopPlacement = true;
|
|
37971
|
+
continue;
|
|
37972
|
+
}
|
|
37973
|
+
if (result.remaining.length > 0) {
|
|
37974
|
+
nextPending.push({ id: entry.id, ranges: result.remaining });
|
|
37975
|
+
}
|
|
37976
|
+
}
|
|
37977
|
+
if (!stopPlacement) {
|
|
37978
|
+
const ids = idsByColumn2.get(pageIndex)?.get(columnIndex) ?? [];
|
|
37979
|
+
for (let idIndex = 0; idIndex < ids.length; idIndex += 1) {
|
|
37980
|
+
const id = ids[idIndex];
|
|
37981
|
+
const ranges = rangesByFootnoteId.get(id) ?? [];
|
|
37982
|
+
if (ranges.length === 0) continue;
|
|
37983
|
+
const result = placeFootnote(id, ranges, false);
|
|
37984
|
+
if (!result.placed) {
|
|
37985
|
+
nextPending.push({ id, ranges });
|
|
37986
|
+
for (let remainingIndex = idIndex + 1; remainingIndex < ids.length; remainingIndex += 1) {
|
|
37987
|
+
const remainingId = ids[remainingIndex];
|
|
37988
|
+
const remainingRanges = rangesByFootnoteId.get(remainingId) ?? [];
|
|
37989
|
+
nextPending.push({ id: remainingId, ranges: remainingRanges });
|
|
37990
|
+
}
|
|
37991
|
+
stopPlacement = true;
|
|
37992
|
+
break;
|
|
37993
|
+
}
|
|
37994
|
+
if (result.remaining.length > 0) {
|
|
37995
|
+
nextPending.push({ id, ranges: result.remaining });
|
|
37996
|
+
}
|
|
37997
|
+
}
|
|
37998
|
+
}
|
|
37999
|
+
if (columnSlices.length > 0) {
|
|
38000
|
+
const rawReserve = Math.max(0, Math.ceil(usedHeight));
|
|
38001
|
+
const cappedReserve = Math.min(rawReserve, maxReserve);
|
|
38002
|
+
if (cappedReserve < rawReserve) {
|
|
38003
|
+
cappedPages.add(pageIndex);
|
|
38004
|
+
}
|
|
38005
|
+
pageReserve = Math.max(pageReserve, cappedReserve);
|
|
38006
|
+
pageSlices.push(...columnSlices);
|
|
38007
|
+
}
|
|
38008
|
+
if (nextPending.length > 0) {
|
|
38009
|
+
pendingByColumn.set(columnIndex, nextPending);
|
|
38010
|
+
}
|
|
38011
|
+
}
|
|
38012
|
+
if (pageSlices.length > 0) {
|
|
38013
|
+
slicesByPage.set(pageIndex, pageSlices);
|
|
38014
|
+
}
|
|
38015
|
+
reserves2[pageIndex] = pageReserve;
|
|
38016
|
+
}
|
|
38017
|
+
if (cappedPages.size > 0) {
|
|
38018
|
+
console.warn("[layout] Footnote reserve capped to preserve body area", {
|
|
38019
|
+
pages: Array.from(cappedPages)
|
|
38020
|
+
});
|
|
38021
|
+
}
|
|
38022
|
+
if (pendingByColumn.size > 0) {
|
|
38023
|
+
const pendingIds = /* @__PURE__ */ new Set();
|
|
38024
|
+
pendingByColumn.forEach((entries) => entries.forEach((entry) => pendingIds.add(entry.id)));
|
|
38025
|
+
console.warn("[layout] Footnote content truncated: extends beyond document pages", {
|
|
38026
|
+
ids: Array.from(pendingIds)
|
|
38027
|
+
});
|
|
38028
|
+
}
|
|
38029
|
+
return { slicesByPage, reserves: reserves2, hasContinuationByColumn, separatorSpacingBefore: safeSeparatorSpacingBefore };
|
|
38030
|
+
};
|
|
38031
|
+
const injectFragments = (layoutForPages, plan2, measuresById2, reservesByPageIndex, blockById, pageColumns2) => {
|
|
38032
|
+
const decorativeBlocks = [];
|
|
38033
|
+
const decorativeMeasures = [];
|
|
38034
|
+
for (let pageIndex = 0; pageIndex < layoutForPages.pages.length; pageIndex++) {
|
|
38035
|
+
const page = layoutForPages.pages[pageIndex];
|
|
38036
|
+
page.footnoteReserved = Math.max(0, reservesByPageIndex[pageIndex] ?? plan2.reserves[pageIndex] ?? 0);
|
|
38037
|
+
const slices = plan2.slicesByPage.get(pageIndex) ?? [];
|
|
38038
|
+
if (slices.length === 0) continue;
|
|
38039
|
+
if (!page.margins) continue;
|
|
38040
|
+
const pageSize = page.size ?? layoutForPages.pageSize;
|
|
38041
|
+
const marginLeft = normalizeMargin(
|
|
38042
|
+
page.margins.left,
|
|
38043
|
+
normalizeMargin(options.margins?.left, DEFAULT_MARGINS$1.left)
|
|
38044
|
+
);
|
|
38045
|
+
const marginRight = normalizeMargin(
|
|
38046
|
+
page.margins.right,
|
|
38047
|
+
normalizeMargin(options.margins?.right, DEFAULT_MARGINS$1.right)
|
|
38048
|
+
);
|
|
38049
|
+
const pageContentWidth = pageSize.w - (marginLeft + marginRight);
|
|
38050
|
+
const fallbackColumns = normalizeColumnsForFootnotes(
|
|
38051
|
+
options.columns ?? { count: 1, gap: 0 },
|
|
38052
|
+
pageContentWidth
|
|
38053
|
+
);
|
|
38054
|
+
const columns = pageColumns2.get(pageIndex) ?? {
|
|
38055
|
+
...fallbackColumns,
|
|
38056
|
+
left: marginLeft
|
|
38057
|
+
};
|
|
38058
|
+
const bandTopY = pageSize.h - (page.margins.bottom ?? 0);
|
|
38059
|
+
const slicesByColumn = /* @__PURE__ */ new Map();
|
|
38060
|
+
slices.forEach((slice2) => {
|
|
38061
|
+
const columnIndex = Number.isFinite(slice2.columnIndex) ? slice2.columnIndex : 0;
|
|
38062
|
+
const list = slicesByColumn.get(columnIndex) ?? [];
|
|
38063
|
+
list.push(slice2);
|
|
38064
|
+
slicesByColumn.set(columnIndex, list);
|
|
38065
|
+
});
|
|
38066
|
+
slicesByColumn.forEach((columnSlices, rawColumnIndex) => {
|
|
38067
|
+
if (columnSlices.length === 0) return;
|
|
38068
|
+
const columnIndex = Math.max(0, Math.min(columns.count - 1, rawColumnIndex));
|
|
38069
|
+
const columnStride = columns.width + columns.gap;
|
|
38070
|
+
const columnX = columns.left + columnIndex * columnStride;
|
|
38071
|
+
const contentWidth = Math.min(columns.width, footnoteWidth);
|
|
38072
|
+
if (!Number.isFinite(contentWidth) || contentWidth <= 0) return;
|
|
38073
|
+
const columnKey = footnoteColumnKey(pageIndex, columnIndex);
|
|
38074
|
+
const isContinuation = plan2.hasContinuationByColumn.get(columnKey) ?? false;
|
|
38075
|
+
let cursorY = bandTopY + Math.max(0, plan2.separatorSpacingBefore);
|
|
38076
|
+
const separatorHeight = isContinuation ? continuationDividerHeight : safeDividerHeight;
|
|
38077
|
+
const separatorWidth = isContinuation ? Math.max(0, contentWidth * continuationDividerWidthFactor) : contentWidth;
|
|
38078
|
+
if (separatorHeight > 0 && separatorWidth > 0) {
|
|
38079
|
+
const separatorId = isContinuation ? `footnote-continuation-separator-page-${page.number}-col-${columnIndex}` : `footnote-separator-page-${page.number}-col-${columnIndex}`;
|
|
38080
|
+
decorativeBlocks.push({
|
|
38081
|
+
kind: "drawing",
|
|
38082
|
+
id: separatorId,
|
|
38083
|
+
drawingKind: "vectorShape",
|
|
38084
|
+
geometry: { width: separatorWidth, height: separatorHeight },
|
|
38085
|
+
shapeKind: "rect",
|
|
38086
|
+
fillColor: "#000000",
|
|
38087
|
+
strokeColor: null,
|
|
38088
|
+
strokeWidth: 0
|
|
38089
|
+
});
|
|
38090
|
+
decorativeMeasures.push({
|
|
38091
|
+
kind: "drawing",
|
|
38092
|
+
drawingKind: "vectorShape",
|
|
38093
|
+
width: separatorWidth,
|
|
38094
|
+
height: separatorHeight,
|
|
38095
|
+
scale: 1,
|
|
38096
|
+
naturalWidth: separatorWidth,
|
|
38097
|
+
naturalHeight: separatorHeight,
|
|
38098
|
+
geometry: { width: separatorWidth, height: separatorHeight }
|
|
38099
|
+
});
|
|
38100
|
+
page.fragments.push({
|
|
38101
|
+
kind: "drawing",
|
|
38102
|
+
blockId: separatorId,
|
|
38103
|
+
drawingKind: "vectorShape",
|
|
38104
|
+
x: columnX,
|
|
38105
|
+
y: cursorY,
|
|
38106
|
+
width: separatorWidth,
|
|
38107
|
+
height: separatorHeight,
|
|
38108
|
+
geometry: { width: separatorWidth, height: separatorHeight },
|
|
38109
|
+
scale: 1
|
|
38110
|
+
});
|
|
38111
|
+
cursorY += separatorHeight;
|
|
38112
|
+
}
|
|
38113
|
+
cursorY += safeTopPadding;
|
|
38114
|
+
columnSlices.forEach((slice2, sliceIndex) => {
|
|
38115
|
+
slice2.ranges.forEach((range) => {
|
|
38116
|
+
if (range.kind === "paragraph") {
|
|
38117
|
+
const measure = measuresById2.get(range.blockId);
|
|
38118
|
+
if (!measure || measure.kind !== "paragraph") return;
|
|
38119
|
+
const marker = measure.marker;
|
|
38120
|
+
page.fragments.push({
|
|
38121
|
+
kind: "para",
|
|
38122
|
+
blockId: range.blockId,
|
|
38123
|
+
fromLine: range.fromLine,
|
|
38124
|
+
toLine: range.toLine,
|
|
38125
|
+
x: columnX,
|
|
38126
|
+
y: cursorY,
|
|
38127
|
+
width: contentWidth,
|
|
38128
|
+
continuesFromPrev: range.fromLine > 0,
|
|
38129
|
+
continuesOnNext: range.toLine < range.totalLines,
|
|
38130
|
+
...marker?.markerWidth != null ? { markerWidth: marker.markerWidth } : {},
|
|
38131
|
+
...marker?.markerTextWidth != null ? { markerTextWidth: marker.markerTextWidth } : {},
|
|
38132
|
+
...marker?.gutterWidth != null ? { markerGutter: marker.gutterWidth } : {}
|
|
38133
|
+
});
|
|
38134
|
+
cursorY += getRangeRenderHeight(range);
|
|
38135
|
+
return;
|
|
38136
|
+
}
|
|
38137
|
+
if (range.kind === "list-item") {
|
|
38138
|
+
const measure = measuresById2.get(range.blockId);
|
|
38139
|
+
const block = blockById.get(range.blockId);
|
|
38140
|
+
if (!measure || measure.kind !== "list") return;
|
|
38141
|
+
if (!block || block.kind !== "list") return;
|
|
38142
|
+
const itemMeasure = measure.items.find((entry) => entry.itemId === range.itemId);
|
|
38143
|
+
if (!itemMeasure) return;
|
|
38144
|
+
const indentLeft = Number.isFinite(itemMeasure.indentLeft) ? itemMeasure.indentLeft : 0;
|
|
38145
|
+
const markerWidth = Number.isFinite(itemMeasure.markerWidth) ? itemMeasure.markerWidth : 0;
|
|
38146
|
+
const itemWidth = Math.max(0, contentWidth - indentLeft - markerWidth);
|
|
38147
|
+
page.fragments.push({
|
|
38148
|
+
kind: "list-item",
|
|
38149
|
+
blockId: range.blockId,
|
|
38150
|
+
itemId: range.itemId,
|
|
38151
|
+
fromLine: range.fromLine,
|
|
38152
|
+
toLine: range.toLine,
|
|
38153
|
+
x: columnX + indentLeft + markerWidth,
|
|
38154
|
+
y: cursorY,
|
|
38155
|
+
width: itemWidth,
|
|
38156
|
+
markerWidth,
|
|
38157
|
+
continuesFromPrev: range.fromLine > 0,
|
|
38158
|
+
continuesOnNext: range.toLine < range.totalLines
|
|
38159
|
+
});
|
|
38160
|
+
cursorY += getRangeRenderHeight(range);
|
|
38161
|
+
return;
|
|
38162
|
+
}
|
|
38163
|
+
if (range.kind === "table") {
|
|
38164
|
+
const measure = measuresById2.get(range.blockId);
|
|
38165
|
+
const block = blockById.get(range.blockId);
|
|
38166
|
+
if (!measure || measure.kind !== "table") return;
|
|
38167
|
+
if (!block || block.kind !== "table") return;
|
|
38168
|
+
const tableWidthRaw = Math.max(0, measure.totalWidth ?? 0);
|
|
38169
|
+
let tableWidth = Math.min(contentWidth, tableWidthRaw);
|
|
38170
|
+
let tableX = columnX;
|
|
38171
|
+
const justification = typeof block.attrs?.justification === "string" ? block.attrs.justification : void 0;
|
|
38172
|
+
if (justification === "center") {
|
|
38173
|
+
tableX = columnX + Math.max(0, (contentWidth - tableWidth) / 2);
|
|
38174
|
+
} else if (justification === "right" || justification === "end") {
|
|
38175
|
+
tableX = columnX + Math.max(0, contentWidth - tableWidth);
|
|
38176
|
+
} else {
|
|
38177
|
+
const indentValue = block.attrs?.tableIndent?.width;
|
|
38178
|
+
const indent = typeof indentValue === "number" && Number.isFinite(indentValue) ? indentValue : 0;
|
|
38179
|
+
tableX += indent;
|
|
38180
|
+
tableWidth = Math.max(0, tableWidth - indent);
|
|
38181
|
+
}
|
|
38182
|
+
page.fragments.push({
|
|
38183
|
+
kind: "table",
|
|
38184
|
+
blockId: range.blockId,
|
|
38185
|
+
fromRow: 0,
|
|
38186
|
+
toRow: block.rows.length,
|
|
38187
|
+
x: tableX,
|
|
38188
|
+
y: cursorY,
|
|
38189
|
+
width: tableWidth,
|
|
38190
|
+
height: Math.max(0, measure.totalHeight ?? 0)
|
|
38191
|
+
});
|
|
38192
|
+
cursorY += getRangeRenderHeight(range);
|
|
38193
|
+
return;
|
|
38194
|
+
}
|
|
38195
|
+
if (range.kind === "image") {
|
|
38196
|
+
const measure = measuresById2.get(range.blockId);
|
|
38197
|
+
if (!measure || measure.kind !== "image") return;
|
|
38198
|
+
page.fragments.push({
|
|
38199
|
+
kind: "image",
|
|
38200
|
+
blockId: range.blockId,
|
|
38201
|
+
x: columnX,
|
|
38202
|
+
y: cursorY,
|
|
38203
|
+
width: Math.min(contentWidth, Math.max(0, measure.width ?? 0)),
|
|
38204
|
+
height: Math.max(0, measure.height ?? 0)
|
|
38205
|
+
});
|
|
38206
|
+
cursorY += getRangeRenderHeight(range);
|
|
38207
|
+
return;
|
|
38208
|
+
}
|
|
38209
|
+
if (range.kind === "drawing") {
|
|
38210
|
+
const measure = measuresById2.get(range.blockId);
|
|
38211
|
+
const block = blockById.get(range.blockId);
|
|
38212
|
+
if (!measure || measure.kind !== "drawing") return;
|
|
38213
|
+
if (!block || block.kind !== "drawing") return;
|
|
38214
|
+
page.fragments.push({
|
|
38215
|
+
kind: "drawing",
|
|
38216
|
+
blockId: range.blockId,
|
|
38217
|
+
drawingKind: block.drawingKind,
|
|
38218
|
+
x: columnX,
|
|
38219
|
+
y: cursorY,
|
|
38220
|
+
width: Math.min(contentWidth, Math.max(0, measure.width ?? 0)),
|
|
38221
|
+
height: Math.max(0, measure.height ?? 0),
|
|
38222
|
+
geometry: measure.geometry,
|
|
38223
|
+
scale: measure.scale
|
|
38224
|
+
});
|
|
38225
|
+
cursorY += getRangeRenderHeight(range);
|
|
38226
|
+
}
|
|
38227
|
+
});
|
|
38228
|
+
if (sliceIndex < columnSlices.length - 1) {
|
|
38229
|
+
cursorY += safeGap;
|
|
38230
|
+
}
|
|
38231
|
+
});
|
|
38232
|
+
});
|
|
38233
|
+
}
|
|
38234
|
+
return { decorativeBlocks, decorativeMeasures };
|
|
38235
|
+
};
|
|
38236
|
+
const resolveFootnoteAssignments = (layoutForPages) => {
|
|
38237
|
+
const columns = resolvePageColumns(layoutForPages, options, currentBlocks);
|
|
38238
|
+
const idsByColumn2 = assignFootnotesToColumns(layoutForPages, footnotesInput.refs, columns);
|
|
38239
|
+
return { columns, idsByColumn: idsByColumn2 };
|
|
38240
|
+
};
|
|
38241
|
+
let { columns: pageColumns, idsByColumn } = resolveFootnoteAssignments(layout);
|
|
38242
|
+
let { measuresById } = await measureFootnoteBlocks(collectFootnoteIdsByColumn(idsByColumn));
|
|
38243
|
+
let plan = computeFootnoteLayoutPlan(layout, idsByColumn, measuresById, [], pageColumns);
|
|
38244
|
+
let reserves = plan.reserves;
|
|
38245
|
+
if (reserves.some((h2) => h2 > 0)) {
|
|
38246
|
+
layout = layoutDocument(currentBlocks, currentMeasures, {
|
|
38247
|
+
...options,
|
|
38248
|
+
footnoteReservedByPageIndex: reserves,
|
|
38249
|
+
headerContentHeights,
|
|
38250
|
+
footerContentHeights,
|
|
38251
|
+
remeasureParagraph: (block, maxWidth, firstLineIndent) => remeasureParagraph(block, maxWidth, firstLineIndent)
|
|
38252
|
+
});
|
|
38253
|
+
({ columns: pageColumns, idsByColumn } = resolveFootnoteAssignments(layout));
|
|
38254
|
+
({ measuresById } = await measureFootnoteBlocks(collectFootnoteIdsByColumn(idsByColumn)));
|
|
38255
|
+
plan = computeFootnoteLayoutPlan(layout, idsByColumn, measuresById, reserves, pageColumns);
|
|
38256
|
+
reserves = plan.reserves;
|
|
38257
|
+
layout = layoutDocument(currentBlocks, currentMeasures, {
|
|
38258
|
+
...options,
|
|
38259
|
+
footnoteReservedByPageIndex: reserves,
|
|
38260
|
+
headerContentHeights,
|
|
38261
|
+
footerContentHeights,
|
|
38262
|
+
remeasureParagraph: (block, maxWidth, firstLineIndent) => remeasureParagraph(block, maxWidth, firstLineIndent)
|
|
38263
|
+
});
|
|
38264
|
+
let { columns: finalPageColumns, idsByColumn: finalIdsByColumn } = resolveFootnoteAssignments(layout);
|
|
38265
|
+
let { blocks: finalBlocks, measuresById: finalMeasuresById } = await measureFootnoteBlocks(
|
|
38266
|
+
collectFootnoteIdsByColumn(finalIdsByColumn)
|
|
38267
|
+
);
|
|
38268
|
+
let finalPlan = computeFootnoteLayoutPlan(
|
|
38269
|
+
layout,
|
|
38270
|
+
finalIdsByColumn,
|
|
38271
|
+
finalMeasuresById,
|
|
38272
|
+
reserves,
|
|
38273
|
+
finalPageColumns
|
|
38274
|
+
);
|
|
38275
|
+
const finalReserves = finalPlan.reserves;
|
|
38276
|
+
let reservesAppliedToLayout = reserves;
|
|
38277
|
+
const reservesDiffer = finalReserves.length !== reserves.length || finalReserves.some((h2, i) => (reserves[i] ?? 0) !== h2) || reserves.some((h2, i) => (finalReserves[i] ?? 0) !== h2);
|
|
38278
|
+
if (reservesDiffer) {
|
|
38279
|
+
layout = layoutDocument(currentBlocks, currentMeasures, {
|
|
38280
|
+
...options,
|
|
38281
|
+
footnoteReservedByPageIndex: finalReserves,
|
|
38282
|
+
headerContentHeights,
|
|
38283
|
+
footerContentHeights,
|
|
38284
|
+
remeasureParagraph: (block, maxWidth, firstLineIndent) => remeasureParagraph(block, maxWidth, firstLineIndent)
|
|
38285
|
+
});
|
|
38286
|
+
reservesAppliedToLayout = finalReserves;
|
|
38287
|
+
({ columns: finalPageColumns, idsByColumn: finalIdsByColumn } = resolveFootnoteAssignments(layout));
|
|
38288
|
+
({ blocks: finalBlocks, measuresById: finalMeasuresById } = await measureFootnoteBlocks(
|
|
38289
|
+
collectFootnoteIdsByColumn(finalIdsByColumn)
|
|
38290
|
+
));
|
|
38291
|
+
finalPlan = computeFootnoteLayoutPlan(
|
|
38292
|
+
layout,
|
|
38293
|
+
finalIdsByColumn,
|
|
38294
|
+
finalMeasuresById,
|
|
38295
|
+
reservesAppliedToLayout,
|
|
38296
|
+
finalPageColumns
|
|
38297
|
+
);
|
|
38298
|
+
}
|
|
38299
|
+
const blockById = /* @__PURE__ */ new Map();
|
|
38300
|
+
finalBlocks.forEach((block) => {
|
|
38301
|
+
blockById.set(block.id, block);
|
|
38302
|
+
});
|
|
38303
|
+
const injected = injectFragments(
|
|
38304
|
+
layout,
|
|
38305
|
+
finalPlan,
|
|
38306
|
+
finalMeasuresById,
|
|
38307
|
+
reservesAppliedToLayout,
|
|
38308
|
+
blockById,
|
|
38309
|
+
finalPageColumns
|
|
38310
|
+
);
|
|
38311
|
+
const alignedBlocks = [];
|
|
38312
|
+
const alignedMeasures = [];
|
|
38313
|
+
finalBlocks.forEach((block) => {
|
|
38314
|
+
const measure = finalMeasuresById.get(block.id);
|
|
38315
|
+
if (!measure) return;
|
|
38316
|
+
alignedBlocks.push(block);
|
|
38317
|
+
alignedMeasures.push(measure);
|
|
38318
|
+
});
|
|
38319
|
+
extraBlocks = injected ? alignedBlocks.concat(injected.decorativeBlocks) : alignedBlocks;
|
|
38320
|
+
extraMeasures = injected ? alignedMeasures.concat(injected.decorativeMeasures) : alignedMeasures;
|
|
38321
|
+
}
|
|
38322
|
+
}
|
|
38323
|
+
}
|
|
37226
38324
|
let headers;
|
|
37227
38325
|
let footers;
|
|
37228
38326
|
if (headerFooter?.constraints && (headerFooter.headerBlocks || headerFooter.footerBlocks)) {
|
|
@@ -37283,7 +38381,9 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
|
|
|
37283
38381
|
measures: currentMeasures,
|
|
37284
38382
|
dirty,
|
|
37285
38383
|
headers,
|
|
37286
|
-
footers
|
|
38384
|
+
footers,
|
|
38385
|
+
extraBlocks,
|
|
38386
|
+
extraMeasures
|
|
37287
38387
|
};
|
|
37288
38388
|
}
|
|
37289
38389
|
const DEFAULT_PAGE_SIZE$1 = { w: 612, h: 792 };
|
|
@@ -41322,6 +42422,7 @@ const ATOMIC_INLINE_TYPES = /* @__PURE__ */ new Set([
|
|
|
41322
42422
|
"lineBreak",
|
|
41323
42423
|
"page-number",
|
|
41324
42424
|
"total-page-number",
|
|
42425
|
+
"footnoteReference",
|
|
41325
42426
|
"passthroughInline",
|
|
41326
42427
|
"bookmarkEnd"
|
|
41327
42428
|
]);
|
|
@@ -46346,6 +47447,28 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
|
|
|
46346
47447
|
let partIndex = 0;
|
|
46347
47448
|
let tabOrdinal = 0;
|
|
46348
47449
|
let suppressedByVanish = false;
|
|
47450
|
+
const toSuperscriptDigits2 = (value) => {
|
|
47451
|
+
const map3 = {
|
|
47452
|
+
"0": "⁰",
|
|
47453
|
+
"1": "¹",
|
|
47454
|
+
"2": "²",
|
|
47455
|
+
"3": "³",
|
|
47456
|
+
"4": "⁴",
|
|
47457
|
+
"5": "⁵",
|
|
47458
|
+
"6": "⁶",
|
|
47459
|
+
"7": "⁷",
|
|
47460
|
+
"8": "⁸",
|
|
47461
|
+
"9": "⁹"
|
|
47462
|
+
};
|
|
47463
|
+
return String(value ?? "").split("").map((ch) => map3[ch] ?? ch).join("");
|
|
47464
|
+
};
|
|
47465
|
+
const resolveFootnoteDisplayNumber2 = (id) => {
|
|
47466
|
+
const key2 = id == null ? null : String(id);
|
|
47467
|
+
if (!key2) return null;
|
|
47468
|
+
const mapping = converterContext?.footnoteNumberById;
|
|
47469
|
+
const mapped = mapping && typeof mapping === "object" ? mapping[key2] : void 0;
|
|
47470
|
+
return typeof mapped === "number" && Number.isFinite(mapped) && mapped > 0 ? mapped : null;
|
|
47471
|
+
};
|
|
46349
47472
|
const nextId = () => partIndex === 0 ? baseBlockId : `${baseBlockId}-${partIndex}`;
|
|
46350
47473
|
const attachAnchorParagraphId = (block, anchorParagraphId) => {
|
|
46351
47474
|
const applicableKinds = /* @__PURE__ */ new Set(["drawing", "image", "table"]);
|
|
@@ -46391,6 +47514,34 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
|
|
|
46391
47514
|
});
|
|
46392
47515
|
};
|
|
46393
47516
|
const visitNode = (node, inheritedMarks = [], activeSdt, activeRunStyleId = null, activeRunProperties, activeHidden = false) => {
|
|
47517
|
+
if (node.type === "footnoteReference") {
|
|
47518
|
+
const mergedMarks = [...node.marks ?? [], ...inheritedMarks ?? []];
|
|
47519
|
+
const refPos = positions.get(node);
|
|
47520
|
+
const id = node.attrs?.id;
|
|
47521
|
+
const displayId = resolveFootnoteDisplayNumber2(id) ?? id ?? "*";
|
|
47522
|
+
const displayText = toSuperscriptDigits2(displayId);
|
|
47523
|
+
const run = textNodeToRun(
|
|
47524
|
+
{ type: "text", text: displayText },
|
|
47525
|
+
positions,
|
|
47526
|
+
defaultFont,
|
|
47527
|
+
defaultSize,
|
|
47528
|
+
[],
|
|
47529
|
+
// marks applied after linked styles/base defaults
|
|
47530
|
+
activeSdt,
|
|
47531
|
+
hyperlinkConfig,
|
|
47532
|
+
themeColors
|
|
47533
|
+
);
|
|
47534
|
+
const inlineStyleId = getInlineStyleId(mergedMarks);
|
|
47535
|
+
applyRunStyles2(run, inlineStyleId, activeRunStyleId);
|
|
47536
|
+
applyBaseRunDefaults(run, baseRunDefaults, defaultFont, defaultSize);
|
|
47537
|
+
applyMarksToRun(run, mergedMarks, hyperlinkConfig, themeColors);
|
|
47538
|
+
if (refPos) {
|
|
47539
|
+
run.pmStart = refPos.start;
|
|
47540
|
+
run.pmEnd = refPos.end;
|
|
47541
|
+
}
|
|
47542
|
+
currentRuns.push(run);
|
|
47543
|
+
return;
|
|
47544
|
+
}
|
|
46394
47545
|
if (activeHidden && node.type !== "run") {
|
|
46395
47546
|
suppressedByVanish = true;
|
|
46396
47547
|
return;
|
|
@@ -54791,12 +55942,36 @@ class PresentationEditor extends EventEmitter {
|
|
|
54791
55942
|
const sectionMetadata = [];
|
|
54792
55943
|
let blocks;
|
|
54793
55944
|
let bookmarks = /* @__PURE__ */ new Map();
|
|
55945
|
+
let converterContext = void 0;
|
|
54794
55946
|
try {
|
|
54795
55947
|
const converter2 = this.#editor.converter;
|
|
54796
|
-
const
|
|
55948
|
+
const footnoteNumberById = {};
|
|
55949
|
+
try {
|
|
55950
|
+
const seen = /* @__PURE__ */ new Set();
|
|
55951
|
+
let counter = 1;
|
|
55952
|
+
this.#editor?.state?.doc?.descendants?.((node) => {
|
|
55953
|
+
if (node?.type?.name !== "footnoteReference") return;
|
|
55954
|
+
const rawId = node?.attrs?.id;
|
|
55955
|
+
if (rawId == null) return;
|
|
55956
|
+
const key2 = String(rawId);
|
|
55957
|
+
if (!key2 || seen.has(key2)) return;
|
|
55958
|
+
seen.add(key2);
|
|
55959
|
+
footnoteNumberById[key2] = counter;
|
|
55960
|
+
counter += 1;
|
|
55961
|
+
});
|
|
55962
|
+
} catch {
|
|
55963
|
+
}
|
|
55964
|
+
try {
|
|
55965
|
+
if (converter2 && typeof converter2 === "object") {
|
|
55966
|
+
converter2["footnoteNumberById"] = footnoteNumberById;
|
|
55967
|
+
}
|
|
55968
|
+
} catch {
|
|
55969
|
+
}
|
|
55970
|
+
converterContext = converter2 ? {
|
|
54797
55971
|
docx: converter2.convertedXml,
|
|
54798
55972
|
numbering: converter2.numbering,
|
|
54799
|
-
linkedStyles: converter2.linkedStyles
|
|
55973
|
+
linkedStyles: converter2.linkedStyles,
|
|
55974
|
+
...Object.keys(footnoteNumberById).length ? { footnoteNumberById } : {}
|
|
54800
55975
|
} : void 0;
|
|
54801
55976
|
const atomNodeTypes = getAtomNodeTypes(this.#editor?.schema ?? null);
|
|
54802
55977
|
const positionMap = this.#editor?.state?.doc && docJson ? buildPositionMapFromPmDoc(this.#editor.state.doc, docJson) : null;
|
|
@@ -54824,13 +55999,20 @@ class PresentationEditor extends EventEmitter {
|
|
|
54824
55999
|
this.#handleLayoutError("render", new Error("toFlowBlocks returned undefined blocks"));
|
|
54825
56000
|
return;
|
|
54826
56001
|
}
|
|
54827
|
-
const
|
|
56002
|
+
const baseLayoutOptions = this.#resolveLayoutOptions(blocks, sectionMetadata);
|
|
56003
|
+
const footnotesLayoutInput = this.#buildFootnotesLayoutInput({
|
|
56004
|
+
converterContext,
|
|
56005
|
+
themeColors: this.#editor?.converter?.themeColors ?? void 0
|
|
56006
|
+
});
|
|
56007
|
+
const layoutOptions = footnotesLayoutInput ? { ...baseLayoutOptions, footnotes: footnotesLayoutInput } : baseLayoutOptions;
|
|
54828
56008
|
const previousBlocks = this.#layoutState.blocks;
|
|
54829
56009
|
const previousLayout = this.#layoutState.layout;
|
|
54830
56010
|
let layout;
|
|
54831
56011
|
let measures;
|
|
54832
56012
|
let headerLayouts;
|
|
54833
56013
|
let footerLayouts;
|
|
56014
|
+
let extraBlocks;
|
|
56015
|
+
let extraMeasures;
|
|
54834
56016
|
const headerFooterInput = this.#buildHeaderFooterInput();
|
|
54835
56017
|
try {
|
|
54836
56018
|
const result = await incrementalLayout(
|
|
@@ -54854,6 +56036,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
54854
56036
|
return;
|
|
54855
56037
|
}
|
|
54856
56038
|
({ layout, measures } = result);
|
|
56039
|
+
extraBlocks = Array.isArray(result.extraBlocks) ? result.extraBlocks : void 0;
|
|
56040
|
+
extraMeasures = Array.isArray(result.extraMeasures) ? result.extraMeasures : void 0;
|
|
54857
56041
|
layout.pageGap = this.#getEffectivePageGap();
|
|
54858
56042
|
layout.layoutEpoch = layoutEpoch;
|
|
54859
56043
|
headerLayouts = result.headers;
|
|
@@ -54913,6 +56097,10 @@ class PresentationEditor extends EventEmitter {
|
|
|
54913
56097
|
footerBlocks.push(...rIdResult.blocks);
|
|
54914
56098
|
footerMeasures.push(...rIdResult.measures);
|
|
54915
56099
|
}
|
|
56100
|
+
if (extraBlocks && extraMeasures && extraBlocks.length === extraMeasures.length && extraBlocks.length > 0) {
|
|
56101
|
+
footerBlocks.push(...extraBlocks);
|
|
56102
|
+
footerMeasures.push(...extraMeasures);
|
|
56103
|
+
}
|
|
54916
56104
|
painter.setData?.(
|
|
54917
56105
|
blocks,
|
|
54918
56106
|
measures,
|
|
@@ -55199,6 +56387,123 @@ class PresentationEditor extends EventEmitter {
|
|
|
55199
56387
|
sectionMetadata
|
|
55200
56388
|
};
|
|
55201
56389
|
}
|
|
56390
|
+
#buildFootnotesLayoutInput({
|
|
56391
|
+
converterContext,
|
|
56392
|
+
themeColors
|
|
56393
|
+
}) {
|
|
56394
|
+
const footnoteNumberById = converterContext?.footnoteNumberById;
|
|
56395
|
+
const toSuperscriptDigits2 = (value) => {
|
|
56396
|
+
const map3 = {
|
|
56397
|
+
"0": "⁰",
|
|
56398
|
+
"1": "¹",
|
|
56399
|
+
"2": "²",
|
|
56400
|
+
"3": "³",
|
|
56401
|
+
"4": "⁴",
|
|
56402
|
+
"5": "⁵",
|
|
56403
|
+
"6": "⁶",
|
|
56404
|
+
"7": "⁷",
|
|
56405
|
+
"8": "⁸",
|
|
56406
|
+
"9": "⁹"
|
|
56407
|
+
};
|
|
56408
|
+
const str = String(value ?? "");
|
|
56409
|
+
return str.split("").map((ch) => map3[ch] ?? ch).join("");
|
|
56410
|
+
};
|
|
56411
|
+
const ensureFootnoteMarker = (blocks, id) => {
|
|
56412
|
+
const displayNumberRaw = footnoteNumberById && typeof footnoteNumberById === "object" ? footnoteNumberById[id] : void 0;
|
|
56413
|
+
const displayNumber = typeof displayNumberRaw === "number" && Number.isFinite(displayNumberRaw) && displayNumberRaw > 0 ? displayNumberRaw : 1;
|
|
56414
|
+
const firstParagraph = blocks.find((b2) => b2?.kind === "paragraph");
|
|
56415
|
+
if (!firstParagraph) return;
|
|
56416
|
+
const runs = Array.isArray(firstParagraph.runs) ? firstParagraph.runs : [];
|
|
56417
|
+
const markerText = toSuperscriptDigits2(displayNumber);
|
|
56418
|
+
const baseRun = runs.find((r2) => {
|
|
56419
|
+
const dataAttrs = r2.dataAttrs;
|
|
56420
|
+
if (dataAttrs?.["data-sd-footnote-number"]) return false;
|
|
56421
|
+
const pmStart = r2.pmStart;
|
|
56422
|
+
const pmEnd = r2.pmEnd;
|
|
56423
|
+
return typeof pmStart === "number" && Number.isFinite(pmStart) && typeof pmEnd === "number" && Number.isFinite(pmEnd);
|
|
56424
|
+
});
|
|
56425
|
+
const markerPmStart = baseRun?.pmStart ?? null;
|
|
56426
|
+
const markerPmEnd = markerPmStart != null ? baseRun?.pmEnd != null ? Math.max(markerPmStart, Math.min(baseRun.pmEnd, markerPmStart + markerText.length)) : markerPmStart + markerText.length : null;
|
|
56427
|
+
const alreadyHasMarker = runs.some((r2) => {
|
|
56428
|
+
const dataAttrs = r2.dataAttrs;
|
|
56429
|
+
return Boolean(dataAttrs?.["data-sd-footnote-number"]);
|
|
56430
|
+
});
|
|
56431
|
+
if (alreadyHasMarker) {
|
|
56432
|
+
if (markerPmStart != null && markerPmEnd != null) {
|
|
56433
|
+
const markerRun2 = runs.find((r2) => {
|
|
56434
|
+
const dataAttrs = r2.dataAttrs;
|
|
56435
|
+
return Boolean(dataAttrs?.["data-sd-footnote-number"]);
|
|
56436
|
+
});
|
|
56437
|
+
if (markerRun2) {
|
|
56438
|
+
if (markerRun2.pmStart == null) markerRun2.pmStart = markerPmStart;
|
|
56439
|
+
if (markerRun2.pmEnd == null) markerRun2.pmEnd = markerPmEnd;
|
|
56440
|
+
}
|
|
56441
|
+
}
|
|
56442
|
+
return;
|
|
56443
|
+
}
|
|
56444
|
+
const firstTextRun = runs.find((r2) => typeof r2.text === "string");
|
|
56445
|
+
const markerRun = {
|
|
56446
|
+
kind: "text",
|
|
56447
|
+
text: markerText,
|
|
56448
|
+
dataAttrs: {
|
|
56449
|
+
"data-sd-footnote-number": "true"
|
|
56450
|
+
},
|
|
56451
|
+
...markerPmStart != null ? { pmStart: markerPmStart } : {},
|
|
56452
|
+
...markerPmEnd != null ? { pmEnd: markerPmEnd } : {}
|
|
56453
|
+
};
|
|
56454
|
+
markerRun.fontFamily = typeof firstTextRun?.fontFamily === "string" ? firstTextRun.fontFamily : "Arial";
|
|
56455
|
+
markerRun.fontSize = typeof firstTextRun?.fontSize === "number" && Number.isFinite(firstTextRun.fontSize) ? firstTextRun.fontSize : 12;
|
|
56456
|
+
if (firstTextRun?.color != null) markerRun.color = firstTextRun.color;
|
|
56457
|
+
runs.unshift(markerRun);
|
|
56458
|
+
firstParagraph.runs = runs;
|
|
56459
|
+
};
|
|
56460
|
+
const state = this.#editor?.state;
|
|
56461
|
+
if (!state) return null;
|
|
56462
|
+
const converter = this.#editor?.converter;
|
|
56463
|
+
const importedFootnotes = Array.isArray(converter?.footnotes) ? converter.footnotes : [];
|
|
56464
|
+
if (importedFootnotes.length === 0) return null;
|
|
56465
|
+
const refs = [];
|
|
56466
|
+
const idsInUse = /* @__PURE__ */ new Set();
|
|
56467
|
+
state.doc.descendants((node, pos) => {
|
|
56468
|
+
if (node.type?.name !== "footnoteReference") return;
|
|
56469
|
+
const id = node.attrs?.id;
|
|
56470
|
+
if (id == null) return;
|
|
56471
|
+
const key2 = String(id);
|
|
56472
|
+
const insidePos = Math.min(pos + 1, state.doc.content.size);
|
|
56473
|
+
refs.push({ id: key2, pos: insidePos });
|
|
56474
|
+
idsInUse.add(key2);
|
|
56475
|
+
});
|
|
56476
|
+
if (refs.length === 0) return null;
|
|
56477
|
+
const blocksById = /* @__PURE__ */ new Map();
|
|
56478
|
+
idsInUse.forEach((id) => {
|
|
56479
|
+
const entry = importedFootnotes.find((f) => String(f?.id) === id);
|
|
56480
|
+
const content = entry?.content;
|
|
56481
|
+
if (!Array.isArray(content) || content.length === 0) return;
|
|
56482
|
+
try {
|
|
56483
|
+
const clonedContent = JSON.parse(JSON.stringify(content));
|
|
56484
|
+
const footnoteDoc = { type: "doc", content: clonedContent };
|
|
56485
|
+
const result = toFlowBlocks(footnoteDoc, {
|
|
56486
|
+
blockIdPrefix: `footnote-${id}-`,
|
|
56487
|
+
enableRichHyperlinks: true,
|
|
56488
|
+
themeColors,
|
|
56489
|
+
converterContext
|
|
56490
|
+
});
|
|
56491
|
+
if (result?.blocks?.length) {
|
|
56492
|
+
ensureFootnoteMarker(result.blocks, id);
|
|
56493
|
+
blocksById.set(id, result.blocks);
|
|
56494
|
+
}
|
|
56495
|
+
} catch {
|
|
56496
|
+
}
|
|
56497
|
+
});
|
|
56498
|
+
if (blocksById.size === 0) return null;
|
|
56499
|
+
return {
|
|
56500
|
+
refs,
|
|
56501
|
+
blocksById,
|
|
56502
|
+
gap: 2,
|
|
56503
|
+
topPadding: 4,
|
|
56504
|
+
dividerHeight: 1
|
|
56505
|
+
};
|
|
56506
|
+
}
|
|
55202
56507
|
#buildHeaderFooterInput() {
|
|
55203
56508
|
if (!this.#headerFooterAdapter) {
|
|
55204
56509
|
return null;
|
|
@@ -55388,7 +56693,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
55388
56693
|
const fragments2 = slotPage2.fragments ?? [];
|
|
55389
56694
|
const pageHeight2 = page?.size?.h ?? layout.pageSize?.h ?? this.#layoutOptions.pageSize?.h ?? DEFAULT_PAGE_SIZE.h;
|
|
55390
56695
|
const margins2 = pageMargins ?? layout.pages[0]?.margins ?? this.#layoutOptions.margins ?? DEFAULT_MARGINS;
|
|
55391
|
-
const
|
|
56696
|
+
const decorationMargins2 = kind === "footer" ? this.#stripFootnoteReserveFromBottomMargin(margins2, page ?? null) : margins2;
|
|
56697
|
+
const box2 = this.#computeDecorationBox(kind, decorationMargins2, pageHeight2);
|
|
55392
56698
|
const rawLayoutHeight2 = rIdLayout.layout.height ?? 0;
|
|
55393
56699
|
const metrics2 = this.#computeHeaderFooterMetrics(
|
|
55394
56700
|
kind,
|
|
@@ -55439,7 +56745,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
55439
56745
|
const fragments = slotPage.fragments ?? [];
|
|
55440
56746
|
const pageHeight = page?.size?.h ?? layout.pageSize?.h ?? this.#layoutOptions.pageSize?.h ?? DEFAULT_PAGE_SIZE.h;
|
|
55441
56747
|
const margins = pageMargins ?? layout.pages[0]?.margins ?? this.#layoutOptions.margins ?? DEFAULT_MARGINS;
|
|
55442
|
-
const
|
|
56748
|
+
const decorationMargins = kind === "footer" ? this.#stripFootnoteReserveFromBottomMargin(margins, page ?? null) : margins;
|
|
56749
|
+
const box = this.#computeDecorationBox(kind, decorationMargins, pageHeight);
|
|
55443
56750
|
const rawLayoutHeight = variant.layout.height ?? 0;
|
|
55444
56751
|
const metrics = this.#computeHeaderFooterMetrics(kind, rawLayoutHeight, box, pageHeight, margins.footer ?? 0);
|
|
55445
56752
|
const fallbackId = this.#headerFooterManager?.getVariantId(kind, headerFooterType);
|
|
@@ -55526,6 +56833,16 @@ class PresentationEditor extends EventEmitter {
|
|
|
55526
56833
|
return { x: left2, width, height, offset: offset2 };
|
|
55527
56834
|
}
|
|
55528
56835
|
}
|
|
56836
|
+
#stripFootnoteReserveFromBottomMargin(pageMargins, page) {
|
|
56837
|
+
const reserveRaw = page?.footnoteReserved;
|
|
56838
|
+
const reserve = typeof reserveRaw === "number" && Number.isFinite(reserveRaw) && reserveRaw > 0 ? reserveRaw : 0;
|
|
56839
|
+
if (!reserve) return pageMargins;
|
|
56840
|
+
const bottomRaw = pageMargins.bottom;
|
|
56841
|
+
const bottom2 = typeof bottomRaw === "number" && Number.isFinite(bottomRaw) ? bottomRaw : 0;
|
|
56842
|
+
const nextBottom = Math.max(0, bottom2 - reserve);
|
|
56843
|
+
if (nextBottom === bottom2) return pageMargins;
|
|
56844
|
+
return { ...pageMargins, bottom: nextBottom };
|
|
56845
|
+
}
|
|
55529
56846
|
/**
|
|
55530
56847
|
* Computes the expected header/footer section type for a page based on document configuration.
|
|
55531
56848
|
*
|
|
@@ -55590,7 +56907,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
55590
56907
|
height: headerPayload?.hitRegion?.height ?? headerBox.height
|
|
55591
56908
|
});
|
|
55592
56909
|
const footerPayload = this.#footerDecorationProvider?.(page.number, margins, page);
|
|
55593
|
-
const
|
|
56910
|
+
const footerBoxMargins = this.#stripFootnoteReserveFromBottomMargin(margins, page);
|
|
56911
|
+
const footerBox = this.#computeDecorationBox("footer", footerBoxMargins, actualPageHeight);
|
|
55594
56912
|
this.#footerRegions.set(pageIndex, {
|
|
55595
56913
|
kind: "footer",
|
|
55596
56914
|
headerId: footerPayload?.headerId,
|
|
@@ -61491,6 +62809,109 @@ const CommentsMark = Mark.create({
|
|
|
61491
62809
|
return [CommentMarkName$1, Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
|
|
61492
62810
|
}
|
|
61493
62811
|
});
|
|
62812
|
+
const toSuperscriptDigits = (value) => {
|
|
62813
|
+
const map3 = {
|
|
62814
|
+
0: "⁰",
|
|
62815
|
+
1: "¹",
|
|
62816
|
+
2: "²",
|
|
62817
|
+
3: "³",
|
|
62818
|
+
4: "⁴",
|
|
62819
|
+
5: "⁵",
|
|
62820
|
+
6: "⁶",
|
|
62821
|
+
7: "⁷",
|
|
62822
|
+
8: "⁸",
|
|
62823
|
+
9: "⁹"
|
|
62824
|
+
};
|
|
62825
|
+
return String(value ?? "").split("").map((ch) => map3[ch] ?? ch).join("");
|
|
62826
|
+
};
|
|
62827
|
+
const resolveFootnoteDisplayNumber = (editor, id) => {
|
|
62828
|
+
const key2 = id == null ? null : String(id);
|
|
62829
|
+
if (!key2) return null;
|
|
62830
|
+
const map3 = editor?.converter?.footnoteNumberById;
|
|
62831
|
+
const mapped = map3 && typeof map3 === "object" ? map3[key2] : void 0;
|
|
62832
|
+
return typeof mapped === "number" && Number.isFinite(mapped) && mapped > 0 ? mapped : null;
|
|
62833
|
+
};
|
|
62834
|
+
class FootnoteReferenceNodeView {
|
|
62835
|
+
constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
|
|
62836
|
+
this.node = node;
|
|
62837
|
+
this.getPos = getPos;
|
|
62838
|
+
this.editor = editor;
|
|
62839
|
+
this.dom = this.#renderDom(node, htmlAttributes);
|
|
62840
|
+
}
|
|
62841
|
+
#renderDom(node, htmlAttributes) {
|
|
62842
|
+
const el = document.createElement("sup");
|
|
62843
|
+
el.className = "sd-footnote-ref";
|
|
62844
|
+
el.setAttribute("contenteditable", "false");
|
|
62845
|
+
el.setAttribute("aria-label", "Footnote reference");
|
|
62846
|
+
Object.entries(htmlAttributes).forEach(([key2, value]) => {
|
|
62847
|
+
if (value != null && value !== false) {
|
|
62848
|
+
el.setAttribute(key2, String(value));
|
|
62849
|
+
}
|
|
62850
|
+
});
|
|
62851
|
+
const id = node?.attrs?.id;
|
|
62852
|
+
if (id != null) {
|
|
62853
|
+
el.setAttribute("data-footnote-id", String(id));
|
|
62854
|
+
const display = resolveFootnoteDisplayNumber(this.editor, id) ?? id;
|
|
62855
|
+
el.textContent = toSuperscriptDigits(display);
|
|
62856
|
+
} else {
|
|
62857
|
+
el.textContent = "*";
|
|
62858
|
+
}
|
|
62859
|
+
return el;
|
|
62860
|
+
}
|
|
62861
|
+
update(node) {
|
|
62862
|
+
const incomingType = node?.type?.name;
|
|
62863
|
+
const currentType = this.node?.type?.name;
|
|
62864
|
+
if (!incomingType || incomingType !== currentType) return false;
|
|
62865
|
+
this.node = node;
|
|
62866
|
+
const id = node?.attrs?.id;
|
|
62867
|
+
if (id != null) {
|
|
62868
|
+
this.dom.setAttribute("data-footnote-id", String(id));
|
|
62869
|
+
const display = resolveFootnoteDisplayNumber(this.editor, id) ?? id;
|
|
62870
|
+
this.dom.textContent = toSuperscriptDigits(display);
|
|
62871
|
+
} else {
|
|
62872
|
+
this.dom.removeAttribute("data-footnote-id");
|
|
62873
|
+
this.dom.textContent = "*";
|
|
62874
|
+
}
|
|
62875
|
+
return true;
|
|
62876
|
+
}
|
|
62877
|
+
}
|
|
62878
|
+
const FootnoteReference = Node$1.create({
|
|
62879
|
+
name: "footnoteReference",
|
|
62880
|
+
group: "inline",
|
|
62881
|
+
inline: true,
|
|
62882
|
+
atom: true,
|
|
62883
|
+
selectable: false,
|
|
62884
|
+
draggable: false,
|
|
62885
|
+
addOptions() {
|
|
62886
|
+
return {
|
|
62887
|
+
htmlAttributes: {
|
|
62888
|
+
"data-footnote-ref": "true"
|
|
62889
|
+
}
|
|
62890
|
+
};
|
|
62891
|
+
},
|
|
62892
|
+
addAttributes() {
|
|
62893
|
+
return {
|
|
62894
|
+
id: {
|
|
62895
|
+
default: null
|
|
62896
|
+
},
|
|
62897
|
+
customMarkFollows: {
|
|
62898
|
+
default: null
|
|
62899
|
+
}
|
|
62900
|
+
};
|
|
62901
|
+
},
|
|
62902
|
+
addNodeView() {
|
|
62903
|
+
return ({ node, editor, getPos, decorations }) => {
|
|
62904
|
+
const htmlAttributes = this.options.htmlAttributes;
|
|
62905
|
+
return new FootnoteReferenceNodeView(node, getPos, decorations, editor, htmlAttributes);
|
|
62906
|
+
};
|
|
62907
|
+
},
|
|
62908
|
+
parseDOM() {
|
|
62909
|
+
return [{ tag: "sup[data-footnote-id]" }];
|
|
62910
|
+
},
|
|
62911
|
+
renderDOM({ htmlAttributes }) {
|
|
62912
|
+
return ["sup", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
|
|
62913
|
+
}
|
|
62914
|
+
});
|
|
61494
62915
|
let cache$1 = /* @__PURE__ */ new WeakMap();
|
|
61495
62916
|
function getParagraphContext(paragraph, startPos, helpers2, revision, compute) {
|
|
61496
62917
|
const cached = cache$1.get(paragraph);
|
|
@@ -74303,14 +75724,317 @@ function getMatchHighlights(state) {
|
|
|
74303
75724
|
let search2 = searchKey.getState(state);
|
|
74304
75725
|
return search2 ? search2.deco : DecorationSet.empty;
|
|
74305
75726
|
}
|
|
74306
|
-
|
|
74307
|
-
|
|
74308
|
-
|
|
75727
|
+
const BLOCK_SEPARATOR = "\n";
|
|
75728
|
+
const ATOM_PLACEHOLDER = "";
|
|
75729
|
+
class SearchIndex {
|
|
75730
|
+
/** @type {string} */
|
|
75731
|
+
text = "";
|
|
75732
|
+
/** @type {Segment[]} */
|
|
75733
|
+
segments = [];
|
|
75734
|
+
/** @type {boolean} */
|
|
75735
|
+
valid = false;
|
|
75736
|
+
/** @type {number} */
|
|
75737
|
+
docSize = 0;
|
|
75738
|
+
/**
|
|
75739
|
+
* Build the search index from a ProseMirror document.
|
|
75740
|
+
* Uses doc.textBetween for the flattened string and walks
|
|
75741
|
+
* the document to build the segment offset map.
|
|
75742
|
+
*
|
|
75743
|
+
* @param {import('prosemirror-model').Node} doc - The ProseMirror document
|
|
75744
|
+
*/
|
|
75745
|
+
build(doc2) {
|
|
75746
|
+
this.text = doc2.textBetween(0, doc2.content.size, BLOCK_SEPARATOR, ATOM_PLACEHOLDER);
|
|
75747
|
+
this.segments = [];
|
|
75748
|
+
this.docSize = doc2.content.size;
|
|
75749
|
+
let offset2 = 0;
|
|
75750
|
+
this.#walkNodeContent(doc2, 0, offset2, (segment) => {
|
|
75751
|
+
this.segments.push(segment);
|
|
75752
|
+
offset2 = segment.offsetEnd;
|
|
75753
|
+
});
|
|
75754
|
+
this.valid = true;
|
|
75755
|
+
}
|
|
75756
|
+
/**
|
|
75757
|
+
* Walk the content of a node to build segments.
|
|
75758
|
+
* This method processes the children of a node, given the position
|
|
75759
|
+
* where the node's content starts.
|
|
75760
|
+
*
|
|
75761
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
75762
|
+
* @param {number} contentStart - Document position where this node's content starts
|
|
75763
|
+
* @param {number} offset - Current offset in flattened string
|
|
75764
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
75765
|
+
* @returns {number} The new offset after processing this node's content
|
|
75766
|
+
*/
|
|
75767
|
+
#walkNodeContent(node, contentStart, offset2, addSegment) {
|
|
75768
|
+
let currentOffset = offset2;
|
|
75769
|
+
let isFirstChild = true;
|
|
75770
|
+
node.forEach((child, childContentOffset) => {
|
|
75771
|
+
const childDocPos = contentStart + childContentOffset;
|
|
75772
|
+
if (child.isBlock && !isFirstChild) {
|
|
75773
|
+
addSegment({
|
|
75774
|
+
offsetStart: currentOffset,
|
|
75775
|
+
offsetEnd: currentOffset + 1,
|
|
75776
|
+
docFrom: childDocPos,
|
|
75777
|
+
docTo: childDocPos,
|
|
75778
|
+
kind: "blockSep"
|
|
75779
|
+
});
|
|
75780
|
+
currentOffset += 1;
|
|
75781
|
+
}
|
|
75782
|
+
currentOffset = this.#walkNode(child, childDocPos, currentOffset, addSegment);
|
|
75783
|
+
isFirstChild = false;
|
|
75784
|
+
});
|
|
75785
|
+
return currentOffset;
|
|
75786
|
+
}
|
|
75787
|
+
/**
|
|
75788
|
+
* Recursively walk a node and its descendants to build segments.
|
|
75789
|
+
*
|
|
75790
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
75791
|
+
* @param {number} docPos - Document position at start of this node
|
|
75792
|
+
* @param {number} offset - Current offset in flattened string
|
|
75793
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
75794
|
+
* @returns {number} The new offset after processing this node
|
|
75795
|
+
*/
|
|
75796
|
+
#walkNode(node, docPos, offset2, addSegment) {
|
|
75797
|
+
if (node.isText) {
|
|
75798
|
+
const text = node.text || "";
|
|
75799
|
+
if (text.length > 0) {
|
|
75800
|
+
addSegment({
|
|
75801
|
+
offsetStart: offset2,
|
|
75802
|
+
offsetEnd: offset2 + text.length,
|
|
75803
|
+
docFrom: docPos,
|
|
75804
|
+
docTo: docPos + text.length,
|
|
75805
|
+
kind: "text"
|
|
75806
|
+
});
|
|
75807
|
+
return offset2 + text.length;
|
|
75808
|
+
}
|
|
75809
|
+
return offset2;
|
|
75810
|
+
}
|
|
75811
|
+
if (node.isLeaf) {
|
|
75812
|
+
if (node.type.name === "hard_break") {
|
|
75813
|
+
addSegment({
|
|
75814
|
+
offsetStart: offset2,
|
|
75815
|
+
offsetEnd: offset2 + 1,
|
|
75816
|
+
docFrom: docPos,
|
|
75817
|
+
docTo: docPos + node.nodeSize,
|
|
75818
|
+
kind: "hardBreak"
|
|
75819
|
+
});
|
|
75820
|
+
return offset2 + 1;
|
|
75821
|
+
}
|
|
75822
|
+
addSegment({
|
|
75823
|
+
offsetStart: offset2,
|
|
75824
|
+
offsetEnd: offset2 + 1,
|
|
75825
|
+
docFrom: docPos,
|
|
75826
|
+
docTo: docPos + node.nodeSize,
|
|
75827
|
+
kind: "atom"
|
|
75828
|
+
});
|
|
75829
|
+
return offset2 + 1;
|
|
75830
|
+
}
|
|
75831
|
+
return this.#walkNodeContent(node, docPos + 1, offset2, addSegment);
|
|
75832
|
+
}
|
|
75833
|
+
/**
|
|
75834
|
+
* Mark the index as stale. It will be rebuilt on next search.
|
|
75835
|
+
*/
|
|
75836
|
+
invalidate() {
|
|
75837
|
+
this.valid = false;
|
|
75838
|
+
}
|
|
75839
|
+
/**
|
|
75840
|
+
* Check if the index needs rebuilding for the given document.
|
|
75841
|
+
*
|
|
75842
|
+
* @param {import('prosemirror-model').Node} doc - The document to check against
|
|
75843
|
+
* @returns {boolean} True if index is stale and needs rebuilding
|
|
75844
|
+
*/
|
|
75845
|
+
isStale(doc2) {
|
|
75846
|
+
return !this.valid || doc2.content.size !== this.docSize;
|
|
75847
|
+
}
|
|
75848
|
+
/**
|
|
75849
|
+
* Ensure the index is valid for the given document.
|
|
75850
|
+
* Rebuilds if stale.
|
|
75851
|
+
*
|
|
75852
|
+
* @param {import('prosemirror-model').Node} doc - The document
|
|
75853
|
+
*/
|
|
75854
|
+
ensureValid(doc2) {
|
|
75855
|
+
if (this.isStale(doc2)) {
|
|
75856
|
+
this.build(doc2);
|
|
75857
|
+
}
|
|
75858
|
+
}
|
|
75859
|
+
/**
|
|
75860
|
+
* Convert an offset range in the flattened string to document ranges.
|
|
75861
|
+
* Skips separator/atom segments and returns only text ranges.
|
|
75862
|
+
*
|
|
75863
|
+
* @param {number} start - Start offset in flattened string
|
|
75864
|
+
* @param {number} end - End offset in flattened string
|
|
75865
|
+
* @returns {DocRange[]} Array of document ranges (text segments only)
|
|
75866
|
+
*/
|
|
75867
|
+
offsetRangeToDocRanges(start2, end2) {
|
|
75868
|
+
const ranges = [];
|
|
75869
|
+
for (const segment of this.segments) {
|
|
75870
|
+
if (segment.offsetEnd <= start2) continue;
|
|
75871
|
+
if (segment.offsetStart >= end2) break;
|
|
75872
|
+
if (segment.kind !== "text") continue;
|
|
75873
|
+
const overlapStart = Math.max(start2, segment.offsetStart);
|
|
75874
|
+
const overlapEnd = Math.min(end2, segment.offsetEnd);
|
|
75875
|
+
if (overlapStart < overlapEnd) {
|
|
75876
|
+
const startInSegment = overlapStart - segment.offsetStart;
|
|
75877
|
+
const endInSegment = overlapEnd - segment.offsetStart;
|
|
75878
|
+
ranges.push({
|
|
75879
|
+
from: segment.docFrom + startInSegment,
|
|
75880
|
+
to: segment.docFrom + endInSegment
|
|
75881
|
+
});
|
|
75882
|
+
}
|
|
75883
|
+
}
|
|
75884
|
+
return ranges;
|
|
75885
|
+
}
|
|
75886
|
+
/**
|
|
75887
|
+
* Find the document position for a given offset in the flattened string.
|
|
75888
|
+
*
|
|
75889
|
+
* @param {number} offset - Offset in flattened string
|
|
75890
|
+
* @returns {number|null} Document position, or null if not found
|
|
75891
|
+
*/
|
|
75892
|
+
offsetToDocPos(offset2) {
|
|
75893
|
+
for (const segment of this.segments) {
|
|
75894
|
+
if (offset2 >= segment.offsetStart && offset2 < segment.offsetEnd) {
|
|
75895
|
+
if (segment.kind === "text") {
|
|
75896
|
+
return segment.docFrom + (offset2 - segment.offsetStart);
|
|
75897
|
+
}
|
|
75898
|
+
return segment.docFrom;
|
|
75899
|
+
}
|
|
75900
|
+
}
|
|
75901
|
+
if (this.segments.length > 0 && offset2 === this.segments[this.segments.length - 1].offsetEnd) {
|
|
75902
|
+
const lastSeg = this.segments[this.segments.length - 1];
|
|
75903
|
+
return lastSeg.docTo;
|
|
75904
|
+
}
|
|
75905
|
+
return null;
|
|
75906
|
+
}
|
|
75907
|
+
/**
|
|
75908
|
+
* Escape special regex characters in a string.
|
|
75909
|
+
*
|
|
75910
|
+
* @param {string} str - String to escape
|
|
75911
|
+
* @returns {string} Escaped string safe for use in RegExp
|
|
75912
|
+
*/
|
|
75913
|
+
static escapeRegex(str) {
|
|
75914
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75915
|
+
}
|
|
75916
|
+
/**
|
|
75917
|
+
* Convert a plain search string to a whitespace-flexible regex pattern.
|
|
75918
|
+
* This allows matching across paragraph boundaries.
|
|
75919
|
+
*
|
|
75920
|
+
* @param {string} searchString - The search string
|
|
75921
|
+
* @returns {string} Regex pattern string
|
|
75922
|
+
*/
|
|
75923
|
+
static toFlexiblePattern(searchString) {
|
|
75924
|
+
const parts = searchString.split(/\s+/).filter((part) => part.length > 0);
|
|
75925
|
+
if (parts.length === 0) return "";
|
|
75926
|
+
return parts.map((part) => SearchIndex.escapeRegex(part)).join("\\s+");
|
|
75927
|
+
}
|
|
75928
|
+
/**
|
|
75929
|
+
* Search the index for matches.
|
|
75930
|
+
*
|
|
75931
|
+
* @param {string | RegExp} pattern - Search pattern (string or regex)
|
|
75932
|
+
* @param {Object} options - Search options
|
|
75933
|
+
* @param {boolean} [options.caseSensitive=false] - Case sensitive search
|
|
75934
|
+
* @param {number} [options.maxMatches=1000] - Maximum number of matches to return
|
|
75935
|
+
* @returns {Array<{start: number, end: number, text: string}>} Array of matches with offsets
|
|
75936
|
+
*/
|
|
75937
|
+
search(pattern, options = {}) {
|
|
75938
|
+
const { caseSensitive = false, maxMatches = 1e3 } = options;
|
|
75939
|
+
const matches = [];
|
|
75940
|
+
let regex;
|
|
75941
|
+
if (pattern instanceof RegExp) {
|
|
75942
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
|
|
75943
|
+
regex = new RegExp(pattern.source, flags);
|
|
75944
|
+
} else if (typeof pattern === "string") {
|
|
75945
|
+
if (pattern.length === 0) return matches;
|
|
75946
|
+
const flexiblePattern = SearchIndex.toFlexiblePattern(pattern);
|
|
75947
|
+
if (flexiblePattern.length === 0) return matches;
|
|
75948
|
+
const flags = caseSensitive ? "g" : "gi";
|
|
75949
|
+
regex = new RegExp(flexiblePattern, flags);
|
|
75950
|
+
} else {
|
|
75951
|
+
return matches;
|
|
75952
|
+
}
|
|
75953
|
+
let match;
|
|
75954
|
+
while ((match = regex.exec(this.text)) !== null && matches.length < maxMatches) {
|
|
75955
|
+
matches.push({
|
|
75956
|
+
start: match.index,
|
|
75957
|
+
end: match.index + match[0].length,
|
|
75958
|
+
text: match[0]
|
|
75959
|
+
});
|
|
75960
|
+
if (match[0].length === 0) {
|
|
75961
|
+
regex.lastIndex++;
|
|
75962
|
+
}
|
|
75963
|
+
}
|
|
75964
|
+
return matches;
|
|
74309
75965
|
}
|
|
74310
|
-
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
74311
|
-
return tr.setMeta(searchKey, { query, range, highlight });
|
|
74312
75966
|
}
|
|
75967
|
+
const customSearchHighlightsKey = new superEditor_converter.PluginKey("customSearchHighlights");
|
|
74313
75968
|
const isRegExp = (value) => Object.prototype.toString.call(value) === "[object RegExp]";
|
|
75969
|
+
const resolveInlineTextPosition = (doc2, position, direction) => {
|
|
75970
|
+
const docSize = doc2.content.size;
|
|
75971
|
+
if (!Number.isFinite(position) || position < 0 || position > docSize) {
|
|
75972
|
+
return position;
|
|
75973
|
+
}
|
|
75974
|
+
const step = direction === "forward" ? 1 : -1;
|
|
75975
|
+
let current = position;
|
|
75976
|
+
let iterations = 0;
|
|
75977
|
+
while (iterations < 8) {
|
|
75978
|
+
iterations += 1;
|
|
75979
|
+
const resolved = doc2.resolve(current);
|
|
75980
|
+
const boundaryNode = direction === "forward" ? resolved.nodeAfter : resolved.nodeBefore;
|
|
75981
|
+
if (!boundaryNode) break;
|
|
75982
|
+
if (boundaryNode.isText) break;
|
|
75983
|
+
if (!boundaryNode.isInline || boundaryNode.isAtom || boundaryNode.content.size === 0) break;
|
|
75984
|
+
const next = current + step;
|
|
75985
|
+
if (next < 0 || next > docSize) break;
|
|
75986
|
+
current = next;
|
|
75987
|
+
const adjacent = doc2.resolve(current);
|
|
75988
|
+
const checkNode = direction === "forward" ? adjacent.nodeAfter : adjacent.nodeBefore;
|
|
75989
|
+
if (checkNode && checkNode.isText) break;
|
|
75990
|
+
}
|
|
75991
|
+
return current;
|
|
75992
|
+
};
|
|
75993
|
+
const resolveSearchRange = ({ doc: doc2, from: from3, to, expectedText, highlights }) => {
|
|
75994
|
+
const docSize = doc2.content.size;
|
|
75995
|
+
let resolvedFrom = Math.max(0, Math.min(from3, docSize));
|
|
75996
|
+
let resolvedTo = Math.max(0, Math.min(to, docSize));
|
|
75997
|
+
if (highlights) {
|
|
75998
|
+
const windowStart = Math.max(0, resolvedFrom - 4);
|
|
75999
|
+
const windowEnd = Math.min(docSize, resolvedTo + 4);
|
|
76000
|
+
const candidates = highlights.find(windowStart, windowEnd);
|
|
76001
|
+
if (candidates.length > 0) {
|
|
76002
|
+
let chosen = candidates[0];
|
|
76003
|
+
if (expectedText) {
|
|
76004
|
+
const matching = candidates.filter(
|
|
76005
|
+
(decoration) => doc2.textBetween(decoration.from, decoration.to) === expectedText
|
|
76006
|
+
);
|
|
76007
|
+
if (matching.length > 0) {
|
|
76008
|
+
chosen = matching[0];
|
|
76009
|
+
}
|
|
76010
|
+
}
|
|
76011
|
+
resolvedFrom = chosen.from;
|
|
76012
|
+
resolvedTo = chosen.to;
|
|
76013
|
+
}
|
|
76014
|
+
}
|
|
76015
|
+
const normalizedFrom = resolveInlineTextPosition(doc2, resolvedFrom, "forward");
|
|
76016
|
+
const normalizedTo = resolveInlineTextPosition(doc2, resolvedTo, "backward");
|
|
76017
|
+
if (Number.isFinite(normalizedFrom) && Number.isFinite(normalizedTo) && normalizedFrom <= normalizedTo) {
|
|
76018
|
+
resolvedFrom = normalizedFrom;
|
|
76019
|
+
resolvedTo = normalizedTo;
|
|
76020
|
+
}
|
|
76021
|
+
return { from: resolvedFrom, to: resolvedTo };
|
|
76022
|
+
};
|
|
76023
|
+
const getPositionTracker = (editor) => {
|
|
76024
|
+
if (!editor) return null;
|
|
76025
|
+
if (editor.positionTracker) return editor.positionTracker;
|
|
76026
|
+
const storageTracker = editor.storage?.positionTracker?.tracker;
|
|
76027
|
+
if (storageTracker) {
|
|
76028
|
+
editor.positionTracker = storageTracker;
|
|
76029
|
+
return storageTracker;
|
|
76030
|
+
}
|
|
76031
|
+
const tracker = new PositionTracker(editor);
|
|
76032
|
+
if (editor.storage?.positionTracker) {
|
|
76033
|
+
editor.storage.positionTracker.tracker = tracker;
|
|
76034
|
+
}
|
|
76035
|
+
editor.positionTracker = tracker;
|
|
76036
|
+
return tracker;
|
|
76037
|
+
};
|
|
74314
76038
|
const Search = Extension.create({
|
|
74315
76039
|
// @ts-expect-error - Storage type mismatch will be fixed in TS migration
|
|
74316
76040
|
addStorage() {
|
|
@@ -74319,29 +76043,58 @@ const Search = Extension.create({
|
|
|
74319
76043
|
* @private
|
|
74320
76044
|
* @type {SearchMatch[]|null}
|
|
74321
76045
|
*/
|
|
74322
|
-
searchResults: []
|
|
76046
|
+
searchResults: [],
|
|
76047
|
+
/**
|
|
76048
|
+
* @private
|
|
76049
|
+
* @type {boolean}
|
|
76050
|
+
* Whether to apply CSS highlight classes to matches
|
|
76051
|
+
*/
|
|
76052
|
+
highlightEnabled: true,
|
|
76053
|
+
/**
|
|
76054
|
+
* @private
|
|
76055
|
+
* @type {SearchIndex}
|
|
76056
|
+
* Lazily-built search index for cross-paragraph matching
|
|
76057
|
+
*/
|
|
76058
|
+
searchIndex: new SearchIndex()
|
|
74323
76059
|
};
|
|
74324
76060
|
},
|
|
74325
76061
|
addPmPlugins() {
|
|
74326
76062
|
const editor = this.editor;
|
|
74327
76063
|
const storage = this.storage;
|
|
76064
|
+
const searchIndexInvalidatorPlugin = new superEditor_converter.Plugin({
|
|
76065
|
+
key: new superEditor_converter.PluginKey("searchIndexInvalidator"),
|
|
76066
|
+
appendTransaction(transactions, oldState, newState) {
|
|
76067
|
+
const docChanged = transactions.some((tr) => tr.docChanged);
|
|
76068
|
+
if (docChanged && storage?.searchIndex) {
|
|
76069
|
+
storage.searchIndex.invalidate();
|
|
76070
|
+
}
|
|
76071
|
+
return null;
|
|
76072
|
+
}
|
|
76073
|
+
});
|
|
74328
76074
|
const searchHighlightWithIdPlugin = new superEditor_converter.Plugin({
|
|
74329
|
-
key:
|
|
76075
|
+
key: customSearchHighlightsKey,
|
|
74330
76076
|
props: {
|
|
74331
76077
|
decorations(state) {
|
|
74332
76078
|
if (!editor) return null;
|
|
74333
76079
|
const matches = storage?.searchResults;
|
|
74334
76080
|
if (!matches?.length) return null;
|
|
74335
|
-
const
|
|
74336
|
-
|
|
74337
|
-
|
|
74338
|
-
}
|
|
74339
|
-
|
|
76081
|
+
const highlightEnabled = storage?.highlightEnabled !== false;
|
|
76082
|
+
const decorations = [];
|
|
76083
|
+
for (const match of matches) {
|
|
76084
|
+
const attrs = highlightEnabled ? { id: `search-match-${match.id}`, class: "ProseMirror-search-match" } : { id: `search-match-${match.id}` };
|
|
76085
|
+
if (match.ranges && match.ranges.length > 0) {
|
|
76086
|
+
for (const range of match.ranges) {
|
|
76087
|
+
decorations.push(Decoration.inline(range.from, range.to, attrs));
|
|
76088
|
+
}
|
|
76089
|
+
} else {
|
|
76090
|
+
decorations.push(Decoration.inline(match.from, match.to, attrs));
|
|
76091
|
+
}
|
|
76092
|
+
}
|
|
74340
76093
|
return DecorationSet.create(state.doc, decorations);
|
|
74341
76094
|
}
|
|
74342
76095
|
}
|
|
74343
76096
|
});
|
|
74344
|
-
return [search(), searchHighlightWithIdPlugin];
|
|
76097
|
+
return [search(), searchIndexInvalidatorPlugin, searchHighlightWithIdPlugin];
|
|
74345
76098
|
},
|
|
74346
76099
|
addCommands() {
|
|
74347
76100
|
return {
|
|
@@ -74355,21 +76108,51 @@ const Search = Extension.create({
|
|
|
74355
76108
|
goToFirstMatch: () => (
|
|
74356
76109
|
/** @returns {boolean} */
|
|
74357
76110
|
({ state, editor, dispatch }) => {
|
|
76111
|
+
const searchResults = this.storage?.searchResults;
|
|
76112
|
+
if (Array.isArray(searchResults) && searchResults.length > 0) {
|
|
76113
|
+
const firstMatch = searchResults[0];
|
|
76114
|
+
const from3 = firstMatch.ranges?.[0]?.from ?? firstMatch.from;
|
|
76115
|
+
const to = firstMatch.ranges?.[0]?.to ?? firstMatch.to;
|
|
76116
|
+
if (typeof from3 !== "number" || typeof to !== "number") {
|
|
76117
|
+
return false;
|
|
76118
|
+
}
|
|
76119
|
+
editor.view.focus();
|
|
76120
|
+
const tr2 = state.tr.setSelection(superEditor_converter.TextSelection.create(state.doc, from3, to)).scrollIntoView();
|
|
76121
|
+
if (dispatch) dispatch(tr2);
|
|
76122
|
+
const presentationEditor2 = editor.presentationEditor;
|
|
76123
|
+
if (presentationEditor2 && typeof presentationEditor2.scrollToPosition === "function") {
|
|
76124
|
+
const didScroll = presentationEditor2.scrollToPosition(from3, { block: "center" });
|
|
76125
|
+
if (didScroll) return true;
|
|
76126
|
+
}
|
|
76127
|
+
try {
|
|
76128
|
+
const domPos = editor.view.domAtPos(from3);
|
|
76129
|
+
if (domPos?.node?.scrollIntoView) {
|
|
76130
|
+
domPos.node.scrollIntoView(true);
|
|
76131
|
+
}
|
|
76132
|
+
} catch {
|
|
76133
|
+
}
|
|
76134
|
+
return true;
|
|
76135
|
+
}
|
|
74358
76136
|
const highlights = getMatchHighlights(state);
|
|
74359
76137
|
if (!highlights) return false;
|
|
74360
76138
|
const decorations = highlights.find();
|
|
74361
76139
|
if (!decorations?.length) return false;
|
|
74362
|
-
const
|
|
76140
|
+
const firstDeco = decorations[0];
|
|
74363
76141
|
editor.view.focus();
|
|
74364
|
-
const tr = state.tr.setSelection(superEditor_converter.TextSelection.create(state.doc,
|
|
76142
|
+
const tr = state.tr.setSelection(superEditor_converter.TextSelection.create(state.doc, firstDeco.from, firstDeco.to)).scrollIntoView();
|
|
74365
76143
|
if (dispatch) dispatch(tr);
|
|
74366
76144
|
const presentationEditor = editor.presentationEditor;
|
|
74367
76145
|
if (presentationEditor && typeof presentationEditor.scrollToPosition === "function") {
|
|
74368
|
-
const didScroll = presentationEditor.scrollToPosition(
|
|
76146
|
+
const didScroll = presentationEditor.scrollToPosition(firstDeco.from, { block: "center" });
|
|
74369
76147
|
if (didScroll) return true;
|
|
74370
76148
|
}
|
|
74371
|
-
|
|
74372
|
-
|
|
76149
|
+
try {
|
|
76150
|
+
const domPos = editor.view.domAtPos(firstDeco.from);
|
|
76151
|
+
if (domPos?.node?.scrollIntoView) {
|
|
76152
|
+
domPos.node.scrollIntoView(true);
|
|
76153
|
+
}
|
|
76154
|
+
} catch {
|
|
76155
|
+
}
|
|
74373
76156
|
return true;
|
|
74374
76157
|
}
|
|
74375
76158
|
),
|
|
@@ -74387,53 +76170,57 @@ const Search = Extension.create({
|
|
|
74387
76170
|
*
|
|
74388
76171
|
* // Search without visual highlighting
|
|
74389
76172
|
* const silentMatches = editor.commands.search('test', { highlight: false })
|
|
74390
|
-
*
|
|
76173
|
+
*
|
|
76174
|
+
* // Cross-paragraph search (works by default for plain strings)
|
|
76175
|
+
* const crossParagraphMatches = editor.commands.search('end of paragraph start of next')
|
|
76176
|
+
* @note Returns array of SearchMatch objects with positions and IDs.
|
|
76177
|
+
* Plain string searches are whitespace-flexible and match across paragraphs.
|
|
76178
|
+
* Regex searches match exactly as specified.
|
|
74391
76179
|
*/
|
|
74392
76180
|
search: (patternInput, options = {}) => (
|
|
74393
76181
|
/** @returns {SearchMatch[]} */
|
|
74394
|
-
({ state, dispatch }) => {
|
|
76182
|
+
({ state, dispatch, editor }) => {
|
|
74395
76183
|
if (options != null && (typeof options !== "object" || Array.isArray(options))) {
|
|
74396
76184
|
throw new TypeError("Search options must be an object");
|
|
74397
76185
|
}
|
|
74398
76186
|
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
74399
|
-
|
|
76187
|
+
const maxMatches = typeof options?.maxMatches === "number" ? options.maxMatches : 1e3;
|
|
74400
76188
|
let caseSensitive = false;
|
|
74401
|
-
let
|
|
74402
|
-
const wholeWord = false;
|
|
76189
|
+
let searchPattern = patternInput;
|
|
74403
76190
|
if (isRegExp(patternInput)) {
|
|
74404
|
-
|
|
74405
|
-
|
|
74406
|
-
patternInput
|
|
74407
|
-
);
|
|
74408
|
-
regexp = true;
|
|
74409
|
-
pattern = regexPattern.source;
|
|
74410
|
-
caseSensitive = !regexPattern.flags.includes("i");
|
|
76191
|
+
caseSensitive = !patternInput.flags.includes("i");
|
|
76192
|
+
searchPattern = patternInput;
|
|
74411
76193
|
} else if (typeof patternInput === "string" && /^\/(.+)\/([gimsuy]*)$/.test(patternInput)) {
|
|
74412
76194
|
const [, body, flags] = patternInput.match(/^\/(.+)\/([gimsuy]*)$/);
|
|
74413
|
-
regexp = true;
|
|
74414
|
-
pattern = body;
|
|
74415
76195
|
caseSensitive = !flags.includes("i");
|
|
76196
|
+
searchPattern = new RegExp(body, flags.includes("g") ? flags : flags + "g");
|
|
74416
76197
|
} else {
|
|
74417
|
-
|
|
76198
|
+
searchPattern = String(patternInput);
|
|
74418
76199
|
}
|
|
74419
|
-
const
|
|
74420
|
-
|
|
76200
|
+
const searchIndex = this.storage.searchIndex;
|
|
76201
|
+
searchIndex.ensureValid(state.doc);
|
|
76202
|
+
const indexMatches = searchIndex.search(searchPattern, {
|
|
74421
76203
|
caseSensitive,
|
|
74422
|
-
|
|
74423
|
-
wholeWord
|
|
76204
|
+
maxMatches
|
|
74424
76205
|
});
|
|
74425
|
-
const
|
|
74426
|
-
|
|
74427
|
-
|
|
74428
|
-
|
|
74429
|
-
|
|
74430
|
-
|
|
74431
|
-
|
|
74432
|
-
|
|
74433
|
-
|
|
74434
|
-
|
|
74435
|
-
|
|
76206
|
+
const resultMatches = [];
|
|
76207
|
+
for (const indexMatch of indexMatches) {
|
|
76208
|
+
const ranges = searchIndex.offsetRangeToDocRanges(indexMatch.start, indexMatch.end);
|
|
76209
|
+
if (ranges.length === 0) continue;
|
|
76210
|
+
const matchTexts = ranges.map((r2) => state.doc.textBetween(r2.from, r2.to));
|
|
76211
|
+
const combinedText = matchTexts.join("");
|
|
76212
|
+
const match = {
|
|
76213
|
+
from: ranges[0].from,
|
|
76214
|
+
to: ranges[ranges.length - 1].to,
|
|
76215
|
+
text: combinedText,
|
|
76216
|
+
id: uuid.v4(),
|
|
76217
|
+
ranges,
|
|
76218
|
+
trackerIds: []
|
|
76219
|
+
};
|
|
76220
|
+
resultMatches.push(match);
|
|
76221
|
+
}
|
|
74436
76222
|
this.storage.searchResults = resultMatches;
|
|
76223
|
+
this.storage.highlightEnabled = highlight;
|
|
74437
76224
|
return resultMatches;
|
|
74438
76225
|
}
|
|
74439
76226
|
),
|
|
@@ -74444,12 +76231,48 @@ const Search = Extension.create({
|
|
|
74444
76231
|
* @example
|
|
74445
76232
|
* const searchResults = editor.commands.search('test string')
|
|
74446
76233
|
* editor.commands.goToSearchResult(searchResults[3])
|
|
74447
|
-
* @note Scrolls to match and selects it
|
|
76234
|
+
* @note Scrolls to match and selects it. For multi-range matches (cross-paragraph),
|
|
76235
|
+
* selects the first range and scrolls to it.
|
|
74448
76236
|
*/
|
|
74449
76237
|
goToSearchResult: (match) => (
|
|
74450
76238
|
/** @returns {boolean} */
|
|
74451
76239
|
({ state, dispatch, editor }) => {
|
|
74452
|
-
const
|
|
76240
|
+
const positionTracker = getPositionTracker(editor);
|
|
76241
|
+
const doc2 = state.doc;
|
|
76242
|
+
const highlights = getMatchHighlights(state);
|
|
76243
|
+
let from3, to;
|
|
76244
|
+
if (match?.ranges && match.ranges.length > 0 && match?.trackerIds && match.trackerIds.length > 0) {
|
|
76245
|
+
if (positionTracker?.resolve && match.trackerIds[0]) {
|
|
76246
|
+
const resolved = positionTracker.resolve(match.trackerIds[0]);
|
|
76247
|
+
if (resolved) {
|
|
76248
|
+
from3 = resolved.from;
|
|
76249
|
+
to = resolved.to;
|
|
76250
|
+
}
|
|
76251
|
+
}
|
|
76252
|
+
if (from3 === void 0) {
|
|
76253
|
+
from3 = match.ranges[0].from;
|
|
76254
|
+
to = match.ranges[0].to;
|
|
76255
|
+
}
|
|
76256
|
+
} else {
|
|
76257
|
+
from3 = match.from;
|
|
76258
|
+
to = match.to;
|
|
76259
|
+
if (positionTracker?.resolve && match?.id) {
|
|
76260
|
+
const resolved = positionTracker.resolve(match.id);
|
|
76261
|
+
if (resolved) {
|
|
76262
|
+
from3 = resolved.from;
|
|
76263
|
+
to = resolved.to;
|
|
76264
|
+
}
|
|
76265
|
+
}
|
|
76266
|
+
}
|
|
76267
|
+
const normalized = resolveSearchRange({
|
|
76268
|
+
doc: doc2,
|
|
76269
|
+
from: from3,
|
|
76270
|
+
to,
|
|
76271
|
+
expectedText: match?.text ?? null,
|
|
76272
|
+
highlights
|
|
76273
|
+
});
|
|
76274
|
+
from3 = normalized.from;
|
|
76275
|
+
to = normalized.to;
|
|
74453
76276
|
editor.view.focus();
|
|
74454
76277
|
const tr = state.tr.setSelection(superEditor_converter.TextSelection.create(state.doc, from3, to)).scrollIntoView();
|
|
74455
76278
|
if (dispatch) dispatch(tr);
|
|
@@ -75102,6 +76925,7 @@ const getStarterExtensions = () => {
|
|
|
75102
76925
|
CommentRangeStart,
|
|
75103
76926
|
CommentRangeEnd,
|
|
75104
76927
|
CommentReference,
|
|
76928
|
+
FootnoteReference,
|
|
75105
76929
|
Document,
|
|
75106
76930
|
FontFamily,
|
|
75107
76931
|
FontSize,
|