@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,6 +1,6 @@
|
|
|
1
1
|
import { B as Buffer$2 } from "./jszip-B1fkPkPJ.es.js";
|
|
2
2
|
import { t as twipsToInches, i as inchesToTwips, p as ptToTwips, l as linesToTwips, a as twipsToLines, b as pixelsToTwips, h as halfPointToPoints, c as twipsToPixels$2, d as convertSizeToCSS, e as inchesToPixels } from "./helpers-C8e9wR5l.es.js";
|
|
3
|
-
import { g as generateDocxRandomId, T as TextSelection$1, o as objectIncludes, w as wrapTextsInRuns, D as DOMParser$1, c as createDocFromMarkdown, a as createDocFromHTML, b as chainableEditorState, d as convertMarkdownToHTML, f as findParentNode, e as findParentNodeClosestToPos, h as generateRandom32BitHex, i as generateRandomSigned32BitIntStrId, P as PluginKey, j as Plugin, M as Mapping, N as NodeSelection, k as Selection, l as Slice, m as DOMSerializer, F as Fragment, n as Mark$1, p as dropPoint, A as AllSelection, q as Schema$1, s as canSplit, t as resolveRunProperties, u as encodeMarksFromRPr, v as liftTarget, x as canJoin, y as joinPoint, z as replaceStep$1, R as ReplaceAroundStep$1, B as htmlHandler, C as ReplaceStep, E as getResolvedParagraphProperties, G as changeListLevel, H as isList$1, I as updateNumberingProperties, L as ListHelpers, J as inputRulesPlugin, K as TrackDeleteMarkName$1, O as TrackInsertMarkName$1, Q as TrackFormatMarkName$1, U as AddMarkStep, V as RemoveMarkStep, W as CommandService, S as SuperConverter, X as EditorState, Y as unflattenListsInHtml, Z as SelectionRange, _ as Transform, $ as createOoxmlResolver, a0 as translator, a1 as translator$1, a2 as resolveDocxFontFamily, a3 as combineIndentProperties, a4 as _getReferencedTableStyles, a5 as decodeRPrFromMarks, a6 as calculateResolvedParagraphProperties, a7 as encodeCSSFromPPr, a8 as encodeCSSFromRPr, a9 as generateOrderedListIndex, aa as docxNumberingHelpers, ab as InputRule, ac as insertNewRelationship, ad as kebabCase$1, ae as getUnderlineCssString } from "./SuperConverter-
|
|
3
|
+
import { g as generateDocxRandomId, T as TextSelection$1, o as objectIncludes, w as wrapTextsInRuns, D as DOMParser$1, c as createDocFromMarkdown, a as createDocFromHTML, b as chainableEditorState, d as convertMarkdownToHTML, f as findParentNode, e as findParentNodeClosestToPos, h as generateRandom32BitHex, i as generateRandomSigned32BitIntStrId, P as PluginKey, j as Plugin, M as Mapping, N as NodeSelection, k as Selection, l as Slice, m as DOMSerializer, F as Fragment, n as Mark$1, p as dropPoint, A as AllSelection, q as Schema$1, s as canSplit, t as resolveRunProperties, u as encodeMarksFromRPr, v as liftTarget, x as canJoin, y as joinPoint, z as replaceStep$1, R as ReplaceAroundStep$1, B as htmlHandler, C as ReplaceStep, E as getResolvedParagraphProperties, G as changeListLevel, H as isList$1, I as updateNumberingProperties, L as ListHelpers, J as inputRulesPlugin, K as TrackDeleteMarkName$1, O as TrackInsertMarkName$1, Q as TrackFormatMarkName$1, U as AddMarkStep, V as RemoveMarkStep, W as CommandService, S as SuperConverter, X as EditorState, Y as unflattenListsInHtml, Z as SelectionRange, _ as Transform, $ as createOoxmlResolver, a0 as translator, a1 as translator$1, a2 as resolveDocxFontFamily, a3 as combineIndentProperties, a4 as _getReferencedTableStyles, a5 as decodeRPrFromMarks, a6 as calculateResolvedParagraphProperties, a7 as encodeCSSFromPPr, a8 as encodeCSSFromRPr, a9 as generateOrderedListIndex, aa as docxNumberingHelpers, ab as InputRule, ac as insertNewRelationship, ad as kebabCase$1, ae as getUnderlineCssString } from "./SuperConverter-BAUfsE-s.es.js";
|
|
4
4
|
import { p as process$1, r as ref, C as global$1, c as computed, E as createElementBlock, F as Fragment$1, S as renderList, O as withModifiers, G as openBlock, P as normalizeClass, M as createCommentVNode, H as toDisplayString, K as createBaseVNode, U as createApp, f as onMounted, X as onUnmounted, R as withDirectives, v as unref, Y as vModelText, y as nextTick, L as normalizeStyle, u as watch, Z as withKeys, _ as createTextVNode, I as createVNode, h as h$1, $ as readonly, s as getCurrentInstance, o as onBeforeUnmount, j as reactive, b as onBeforeMount, i as inject, a0 as onActivated, a1 as onDeactivated, a2 as Comment, d as defineComponent, a as provide, g as Teleport, t as toRef, a3 as renderSlot, a4 as isVNode, D as shallowRef, w as watchEffect, T as Transition, a5 as mergeProps, a6 as vShow, a7 as cloneVNode, a8 as Text$2, m as markRaw, N as createBlock, J as withCtx, a9 as useCssVars, V as resolveDynamicComponent, aa as normalizeProps, ab as guardReactiveProps } from "./vue-BnBKJwCW.es.js";
|
|
5
5
|
import "./jszip.min-DCl8qkFO.es.js";
|
|
6
6
|
import { E as EventEmitter$1 } from "./eventemitter3-CwrdEv8r.es.js";
|
|
@@ -9370,6 +9370,158 @@ class Schema {
|
|
|
9370
9370
|
return Object.fromEntries(markEntries);
|
|
9371
9371
|
}
|
|
9372
9372
|
}
|
|
9373
|
+
const positionTrackerKey = new PluginKey("positionTracker");
|
|
9374
|
+
function createPositionTrackerPlugin() {
|
|
9375
|
+
return new Plugin({
|
|
9376
|
+
key: positionTrackerKey,
|
|
9377
|
+
state: {
|
|
9378
|
+
init() {
|
|
9379
|
+
return {
|
|
9380
|
+
decorations: DecorationSet.empty,
|
|
9381
|
+
generation: 0
|
|
9382
|
+
};
|
|
9383
|
+
},
|
|
9384
|
+
apply(tr, state) {
|
|
9385
|
+
let { decorations, generation } = state;
|
|
9386
|
+
const meta = tr.getMeta(positionTrackerKey);
|
|
9387
|
+
if (meta?.action === "add") {
|
|
9388
|
+
decorations = decorations.add(tr.doc, meta.decorations);
|
|
9389
|
+
} else if (meta?.action === "remove") {
|
|
9390
|
+
const toRemove = decorations.find().filter((decoration) => meta.ids.includes(decoration.spec.id));
|
|
9391
|
+
decorations = decorations.remove(toRemove);
|
|
9392
|
+
} else if (meta?.action === "removeByType") {
|
|
9393
|
+
const toRemove = decorations.find().filter((decoration) => decoration.spec.type === meta.type);
|
|
9394
|
+
decorations = decorations.remove(toRemove);
|
|
9395
|
+
}
|
|
9396
|
+
if (tr.docChanged) {
|
|
9397
|
+
decorations = decorations.map(tr.mapping, tr.doc);
|
|
9398
|
+
generation += 1;
|
|
9399
|
+
}
|
|
9400
|
+
return { decorations, generation };
|
|
9401
|
+
}
|
|
9402
|
+
},
|
|
9403
|
+
props: {
|
|
9404
|
+
decorations() {
|
|
9405
|
+
return DecorationSet.empty;
|
|
9406
|
+
}
|
|
9407
|
+
}
|
|
9408
|
+
});
|
|
9409
|
+
}
|
|
9410
|
+
class PositionTracker {
|
|
9411
|
+
#editor;
|
|
9412
|
+
constructor(editor) {
|
|
9413
|
+
this.#editor = editor;
|
|
9414
|
+
}
|
|
9415
|
+
#getState() {
|
|
9416
|
+
if (!this.#editor?.state) return null;
|
|
9417
|
+
return positionTrackerKey.getState(this.#editor.state) ?? null;
|
|
9418
|
+
}
|
|
9419
|
+
track(from3, to, spec) {
|
|
9420
|
+
const id = v4();
|
|
9421
|
+
if (!this.#editor?.state) return id;
|
|
9422
|
+
const fullSpec = { kind: "range", ...spec, id };
|
|
9423
|
+
const deco = Decoration.inline(from3, to, {}, fullSpec);
|
|
9424
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9425
|
+
action: "add",
|
|
9426
|
+
decorations: [deco]
|
|
9427
|
+
}).setMeta("addToHistory", false);
|
|
9428
|
+
this.#editor.dispatch(tr);
|
|
9429
|
+
return id;
|
|
9430
|
+
}
|
|
9431
|
+
trackMany(ranges) {
|
|
9432
|
+
if (!this.#editor?.state) {
|
|
9433
|
+
return ranges.map(() => v4());
|
|
9434
|
+
}
|
|
9435
|
+
const ids = [];
|
|
9436
|
+
const decorations = [];
|
|
9437
|
+
for (const { from: from3, to, spec } of ranges) {
|
|
9438
|
+
const id = v4();
|
|
9439
|
+
ids.push(id);
|
|
9440
|
+
const fullSpec = { kind: "range", ...spec, id };
|
|
9441
|
+
decorations.push(Decoration.inline(from3, to, {}, fullSpec));
|
|
9442
|
+
}
|
|
9443
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9444
|
+
action: "add",
|
|
9445
|
+
decorations
|
|
9446
|
+
}).setMeta("addToHistory", false);
|
|
9447
|
+
this.#editor.dispatch(tr);
|
|
9448
|
+
return ids;
|
|
9449
|
+
}
|
|
9450
|
+
untrack(id) {
|
|
9451
|
+
if (!this.#editor?.state) return;
|
|
9452
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9453
|
+
action: "remove",
|
|
9454
|
+
ids: [id]
|
|
9455
|
+
}).setMeta("addToHistory", false);
|
|
9456
|
+
this.#editor.dispatch(tr);
|
|
9457
|
+
}
|
|
9458
|
+
untrackMany(ids) {
|
|
9459
|
+
if (!this.#editor?.state || ids.length === 0) return;
|
|
9460
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9461
|
+
action: "remove",
|
|
9462
|
+
ids
|
|
9463
|
+
}).setMeta("addToHistory", false);
|
|
9464
|
+
this.#editor.dispatch(tr);
|
|
9465
|
+
}
|
|
9466
|
+
untrackByType(type) {
|
|
9467
|
+
if (!this.#editor?.state) return;
|
|
9468
|
+
const tr = this.#editor.state.tr.setMeta(positionTrackerKey, {
|
|
9469
|
+
action: "removeByType",
|
|
9470
|
+
type
|
|
9471
|
+
}).setMeta("addToHistory", false);
|
|
9472
|
+
this.#editor.dispatch(tr);
|
|
9473
|
+
}
|
|
9474
|
+
resolve(id) {
|
|
9475
|
+
const state = this.#getState();
|
|
9476
|
+
if (!state) return null;
|
|
9477
|
+
const found = state.decorations.find().find((decoration) => decoration.spec.id === id);
|
|
9478
|
+
if (!found) return null;
|
|
9479
|
+
const spec = found.spec;
|
|
9480
|
+
return {
|
|
9481
|
+
id: spec.id,
|
|
9482
|
+
from: found.from,
|
|
9483
|
+
to: found.to,
|
|
9484
|
+
spec
|
|
9485
|
+
};
|
|
9486
|
+
}
|
|
9487
|
+
resolveMany(ids) {
|
|
9488
|
+
const result = /* @__PURE__ */ new Map();
|
|
9489
|
+
for (const id of ids) {
|
|
9490
|
+
result.set(id, null);
|
|
9491
|
+
}
|
|
9492
|
+
const state = this.#getState();
|
|
9493
|
+
if (!state || ids.length === 0) return result;
|
|
9494
|
+
const idSet = new Set(ids);
|
|
9495
|
+
for (const decoration of state.decorations.find()) {
|
|
9496
|
+
const spec = decoration.spec;
|
|
9497
|
+
if (idSet.has(spec.id)) {
|
|
9498
|
+
result.set(spec.id, {
|
|
9499
|
+
id: spec.id,
|
|
9500
|
+
from: decoration.from,
|
|
9501
|
+
to: decoration.to,
|
|
9502
|
+
spec
|
|
9503
|
+
});
|
|
9504
|
+
}
|
|
9505
|
+
}
|
|
9506
|
+
return result;
|
|
9507
|
+
}
|
|
9508
|
+
findByType(type) {
|
|
9509
|
+
const state = this.#getState();
|
|
9510
|
+
if (!state) return [];
|
|
9511
|
+
return state.decorations.find().filter((decoration) => decoration.spec.type === type).map((decoration) => {
|
|
9512
|
+
const spec = decoration.spec;
|
|
9513
|
+
return {
|
|
9514
|
+
id: spec.id,
|
|
9515
|
+
from: decoration.from,
|
|
9516
|
+
to: decoration.to,
|
|
9517
|
+
spec
|
|
9518
|
+
};
|
|
9519
|
+
});
|
|
9520
|
+
}
|
|
9521
|
+
get generation() {
|
|
9522
|
+
return this.#getState()?.generation ?? 0;
|
|
9523
|
+
}
|
|
9524
|
+
}
|
|
9373
9525
|
class OxmlNode extends Node$1 {
|
|
9374
9526
|
constructor(config) {
|
|
9375
9527
|
super(config);
|
|
@@ -11730,6 +11882,34 @@ const EditorFocus = Extension.create({
|
|
|
11730
11882
|
return [editorFocusPlugin];
|
|
11731
11883
|
}
|
|
11732
11884
|
});
|
|
11885
|
+
const PositionTrackerExtension = Extension.create({
|
|
11886
|
+
name: "positionTracker",
|
|
11887
|
+
addStorage() {
|
|
11888
|
+
return {
|
|
11889
|
+
tracker: null
|
|
11890
|
+
};
|
|
11891
|
+
},
|
|
11892
|
+
addPmPlugins() {
|
|
11893
|
+
return [createPositionTrackerPlugin()];
|
|
11894
|
+
},
|
|
11895
|
+
onCreate() {
|
|
11896
|
+
const existing = this.editor?.positionTracker ?? this.storage.tracker;
|
|
11897
|
+
if (existing) {
|
|
11898
|
+
this.storage.tracker = existing;
|
|
11899
|
+
this.editor.positionTracker = existing;
|
|
11900
|
+
return;
|
|
11901
|
+
}
|
|
11902
|
+
const tracker = new PositionTracker(this.editor);
|
|
11903
|
+
this.storage.tracker = tracker;
|
|
11904
|
+
this.editor.positionTracker = tracker;
|
|
11905
|
+
},
|
|
11906
|
+
onDestroy() {
|
|
11907
|
+
if (this.editor?.positionTracker === this.storage.tracker) {
|
|
11908
|
+
this.editor.positionTracker = null;
|
|
11909
|
+
}
|
|
11910
|
+
this.storage.tracker = null;
|
|
11911
|
+
}
|
|
11912
|
+
});
|
|
11733
11913
|
class EventEmitter {
|
|
11734
11914
|
#events = /* @__PURE__ */ new Map();
|
|
11735
11915
|
/**
|
|
@@ -15507,7 +15687,7 @@ const canUseDOM = () => {
|
|
|
15507
15687
|
return false;
|
|
15508
15688
|
}
|
|
15509
15689
|
};
|
|
15510
|
-
const summaryVersion = "1.
|
|
15690
|
+
const summaryVersion = "1.6.0-next.2";
|
|
15511
15691
|
const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
|
|
15512
15692
|
const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
|
|
15513
15693
|
function mapAttributes(attrs) {
|
|
@@ -17000,7 +17180,7 @@ class Editor extends EventEmitter {
|
|
|
17000
17180
|
*/
|
|
17001
17181
|
#createExtensionService() {
|
|
17002
17182
|
const allowedExtensions = ["extension", "node", "mark"];
|
|
17003
|
-
const coreExtensions = [Editable, Commands, EditorFocus, Keymap];
|
|
17183
|
+
const coreExtensions = [Editable, Commands, EditorFocus, Keymap, PositionTrackerExtension];
|
|
17004
17184
|
const externalExtensions = this.options.externalExtensions || [];
|
|
17005
17185
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter((extension) => {
|
|
17006
17186
|
const extensionType = typeof extension?.type === "string" ? extension.type : void 0;
|
|
@@ -17764,6 +17944,10 @@ class Editor extends EventEmitter {
|
|
|
17764
17944
|
const hasCustomSettings = !!this.converter.convertedXml["word/settings.xml"]?.elements?.length;
|
|
17765
17945
|
const customSettings = hasCustomSettings ? this.converter.schemaToXml(this.converter.convertedXml["word/settings.xml"]?.elements?.[0]) : null;
|
|
17766
17946
|
const rels = this.converter.schemaToXml(this.converter.convertedXml["word/_rels/document.xml.rels"].elements[0]);
|
|
17947
|
+
const footnotesData = this.converter.convertedXml["word/footnotes.xml"];
|
|
17948
|
+
const footnotesXml = footnotesData?.elements?.[0] ? this.converter.schemaToXml(footnotesData.elements[0]) : null;
|
|
17949
|
+
const footnotesRelsData = this.converter.convertedXml["word/_rels/footnotes.xml.rels"];
|
|
17950
|
+
const footnotesRelsXml = footnotesRelsData?.elements?.[0] ? this.converter.schemaToXml(footnotesRelsData.elements[0]) : null;
|
|
17767
17951
|
const media = this.converter.addedMedia;
|
|
17768
17952
|
const updatedHeadersFooters = {};
|
|
17769
17953
|
Object.entries(this.converter.convertedXml).forEach(([name, json2]) => {
|
|
@@ -17788,6 +17972,12 @@ class Editor extends EventEmitter {
|
|
|
17788
17972
|
if (hasCustomSettings) {
|
|
17789
17973
|
updatedDocs["word/settings.xml"] = String(customSettings);
|
|
17790
17974
|
}
|
|
17975
|
+
if (footnotesXml) {
|
|
17976
|
+
updatedDocs["word/footnotes.xml"] = String(footnotesXml);
|
|
17977
|
+
}
|
|
17978
|
+
if (footnotesRelsXml) {
|
|
17979
|
+
updatedDocs["word/_rels/footnotes.xml.rels"] = String(footnotesRelsXml);
|
|
17980
|
+
}
|
|
17791
17981
|
if (comments.length) {
|
|
17792
17982
|
const commentsXml = this.converter.schemaToXml(this.converter.convertedXml["word/comments.xml"].elements[0]);
|
|
17793
17983
|
const commentsExtendedXml = this.converter.schemaToXml(
|
|
@@ -18164,7 +18354,7 @@ class Editor extends EventEmitter {
|
|
|
18164
18354
|
* Process collaboration migrations
|
|
18165
18355
|
*/
|
|
18166
18356
|
processCollaborationMigrations() {
|
|
18167
|
-
console.debug("[checkVersionMigrations] Current editor version", "1.
|
|
18357
|
+
console.debug("[checkVersionMigrations] Current editor version", "1.6.0-next.2");
|
|
18168
18358
|
if (!this.options.ydoc) return;
|
|
18169
18359
|
const metaMap = this.options.ydoc.getMap("meta");
|
|
18170
18360
|
let docVersion = metaMap.get("version");
|
|
@@ -34045,7 +34235,7 @@ function getParagraphSpacingBefore(block) {
|
|
|
34045
34235
|
const value = spacing?.before ?? spacing?.lineSpaceBefore;
|
|
34046
34236
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
34047
34237
|
}
|
|
34048
|
-
function getParagraphSpacingAfter(block) {
|
|
34238
|
+
function getParagraphSpacingAfter$1(block) {
|
|
34049
34239
|
const spacing = block.attrs?.spacing;
|
|
34050
34240
|
const value = spacing?.after ?? spacing?.lineSpaceAfter;
|
|
34051
34241
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
@@ -34072,7 +34262,7 @@ function getMeasureHeight(block, measure) {
|
|
|
34072
34262
|
}
|
|
34073
34263
|
const DEFAULT_PAGE_SIZE$2 = { w: 612, h: 792 };
|
|
34074
34264
|
const DEFAULT_MARGINS$2 = { top: 72, right: 72, bottom: 72, left: 72 };
|
|
34075
|
-
const COLUMN_EPSILON = 1e-4;
|
|
34265
|
+
const COLUMN_EPSILON$1 = 1e-4;
|
|
34076
34266
|
const layoutDebugEnabled$1 = typeof process$1 !== "undefined" && typeof process$1.env !== "undefined" && Boolean(process$1.env.SD_DEBUG_LAYOUT);
|
|
34077
34267
|
const layoutLog = (...args) => {
|
|
34078
34268
|
if (!layoutDebugEnabled$1) return;
|
|
@@ -34383,7 +34573,13 @@ function layoutDocument(blocks, measures, options = {}) {
|
|
|
34383
34573
|
const paginator = createPaginator({
|
|
34384
34574
|
margins: paginatorMargins,
|
|
34385
34575
|
getActiveTopMargin: () => activeTopMargin,
|
|
34386
|
-
getActiveBottomMargin: () =>
|
|
34576
|
+
getActiveBottomMargin: () => {
|
|
34577
|
+
const reserves = options.footnoteReservedByPageIndex;
|
|
34578
|
+
const pageIndex = Math.max(0, pageCount - 1);
|
|
34579
|
+
const reserve = Array.isArray(reserves) ? reserves[pageIndex] : 0;
|
|
34580
|
+
const reservePx = typeof reserve === "number" && Number.isFinite(reserve) && reserve > 0 ? reserve : 0;
|
|
34581
|
+
return activeBottomMargin + reservePx;
|
|
34582
|
+
},
|
|
34387
34583
|
getActiveHeaderDistance: () => activeHeaderDistance,
|
|
34388
34584
|
getActiveFooterDistance: () => activeFooterDistance,
|
|
34389
34585
|
getActivePageSize: () => activePageSize,
|
|
@@ -34867,7 +35063,7 @@ function layoutDocument(blocks, measures, options = {}) {
|
|
|
34867
35063
|
if (!shouldSkipAnchoredTable) {
|
|
34868
35064
|
let state = paginator.ensurePage();
|
|
34869
35065
|
const availableHeight = state.contentBottom - state.cursorY;
|
|
34870
|
-
const spacingAfter = getParagraphSpacingAfter(paraBlock);
|
|
35066
|
+
const spacingAfter = getParagraphSpacingAfter$1(paraBlock);
|
|
34871
35067
|
const currentHeight = getMeasureHeight(paraBlock, measure);
|
|
34872
35068
|
const nextHeight = getMeasureHeight(nextBlock, nextMeasure);
|
|
34873
35069
|
const nextIsParagraph = nextBlock.kind === "paragraph" && nextMeasure.kind === "paragraph";
|
|
@@ -35180,7 +35376,7 @@ function normalizeColumns(input, contentWidth) {
|
|
|
35180
35376
|
const gap = Math.max(0, input?.gap ?? 0);
|
|
35181
35377
|
const totalGap = gap * (count - 1);
|
|
35182
35378
|
const width = (contentWidth - totalGap) / count;
|
|
35183
|
-
if (width <= COLUMN_EPSILON) {
|
|
35379
|
+
if (width <= COLUMN_EPSILON$1) {
|
|
35184
35380
|
return {
|
|
35185
35381
|
count: 1,
|
|
35186
35382
|
gap: 0,
|
|
@@ -36943,6 +37139,417 @@ const perfLog = (...args) => {
|
|
|
36943
37139
|
if (!layoutDebugEnabled) return;
|
|
36944
37140
|
console.log(...args);
|
|
36945
37141
|
};
|
|
37142
|
+
const isFootnotesLayoutInput = (value) => {
|
|
37143
|
+
if (!value || typeof value !== "object") return false;
|
|
37144
|
+
const v = value;
|
|
37145
|
+
if (!Array.isArray(v.refs)) return false;
|
|
37146
|
+
if (!(v.blocksById instanceof Map)) return false;
|
|
37147
|
+
return true;
|
|
37148
|
+
};
|
|
37149
|
+
const findPageIndexForPos = (layout, pos) => {
|
|
37150
|
+
if (!Number.isFinite(pos)) return null;
|
|
37151
|
+
const fallbackRanges = [];
|
|
37152
|
+
for (let pageIndex = 0; pageIndex < layout.pages.length; pageIndex++) {
|
|
37153
|
+
const page = layout.pages[pageIndex];
|
|
37154
|
+
let minStart = null;
|
|
37155
|
+
let maxEnd = null;
|
|
37156
|
+
for (const fragment of page.fragments) {
|
|
37157
|
+
const pmStart = fragment.pmStart;
|
|
37158
|
+
const pmEnd = fragment.pmEnd;
|
|
37159
|
+
if (pmStart == null || pmEnd == null) continue;
|
|
37160
|
+
if (minStart == null || pmStart < minStart) minStart = pmStart;
|
|
37161
|
+
if (maxEnd == null || pmEnd > maxEnd) maxEnd = pmEnd;
|
|
37162
|
+
if (pos >= pmStart && pos <= pmEnd) {
|
|
37163
|
+
return pageIndex;
|
|
37164
|
+
}
|
|
37165
|
+
}
|
|
37166
|
+
fallbackRanges[pageIndex] = minStart != null && maxEnd != null ? { pageIndex, minStart, maxEnd } : null;
|
|
37167
|
+
}
|
|
37168
|
+
let best = null;
|
|
37169
|
+
for (const entry of fallbackRanges) {
|
|
37170
|
+
if (!entry) continue;
|
|
37171
|
+
const distance = pos < entry.minStart ? entry.minStart - pos : pos > entry.maxEnd ? pos - entry.maxEnd : 0;
|
|
37172
|
+
if (!best || distance < best.distance) {
|
|
37173
|
+
best = { pageIndex: entry.pageIndex, distance };
|
|
37174
|
+
}
|
|
37175
|
+
}
|
|
37176
|
+
if (best) return best.pageIndex;
|
|
37177
|
+
if (layout.pages.length > 0) return layout.pages.length - 1;
|
|
37178
|
+
return null;
|
|
37179
|
+
};
|
|
37180
|
+
const footnoteColumnKey = (pageIndex, columnIndex) => `${pageIndex}:${columnIndex}`;
|
|
37181
|
+
const COLUMN_EPSILON = 0.01;
|
|
37182
|
+
const normalizeColumnsForFootnotes = (input, contentWidth) => {
|
|
37183
|
+
const rawCount = Number.isFinite(input?.count) ? Math.floor(input.count) : 1;
|
|
37184
|
+
const count = Math.max(1, rawCount || 1);
|
|
37185
|
+
const gap = Math.max(0, input?.gap ?? 0);
|
|
37186
|
+
const totalGap = gap * (count - 1);
|
|
37187
|
+
const width = (contentWidth - totalGap) / count;
|
|
37188
|
+
if (!Number.isFinite(width) || width <= COLUMN_EPSILON) {
|
|
37189
|
+
return {
|
|
37190
|
+
count: 1,
|
|
37191
|
+
gap: 0,
|
|
37192
|
+
width: Math.max(0, contentWidth)
|
|
37193
|
+
};
|
|
37194
|
+
}
|
|
37195
|
+
return { count, gap, width };
|
|
37196
|
+
};
|
|
37197
|
+
const resolveSectionColumnsByIndex = (options, blocks) => {
|
|
37198
|
+
const result = /* @__PURE__ */ new Map();
|
|
37199
|
+
let activeColumns = options.columns ?? { count: 1, gap: 0 };
|
|
37200
|
+
if (blocks && blocks.length > 0) {
|
|
37201
|
+
for (const block of blocks) {
|
|
37202
|
+
if (block.kind !== "sectionBreak") continue;
|
|
37203
|
+
const sectionIndexRaw = block.attrs?.sectionIndex;
|
|
37204
|
+
const sectionIndex = typeof sectionIndexRaw === "number" && Number.isFinite(sectionIndexRaw) ? sectionIndexRaw : result.size;
|
|
37205
|
+
if (block.columns) {
|
|
37206
|
+
activeColumns = { count: block.columns.count, gap: block.columns.gap };
|
|
37207
|
+
}
|
|
37208
|
+
result.set(sectionIndex, { ...activeColumns });
|
|
37209
|
+
}
|
|
37210
|
+
}
|
|
37211
|
+
if (result.size === 0) {
|
|
37212
|
+
result.set(0, { ...activeColumns });
|
|
37213
|
+
}
|
|
37214
|
+
return result;
|
|
37215
|
+
};
|
|
37216
|
+
const resolvePageColumns = (layout, options, blocks) => {
|
|
37217
|
+
const sectionColumns = resolveSectionColumnsByIndex(options, blocks);
|
|
37218
|
+
const result = /* @__PURE__ */ new Map();
|
|
37219
|
+
for (let pageIndex = 0; pageIndex < layout.pages.length; pageIndex += 1) {
|
|
37220
|
+
const page = layout.pages[pageIndex];
|
|
37221
|
+
const pageSize = page.size ?? layout.pageSize ?? DEFAULT_PAGE_SIZE$1;
|
|
37222
|
+
const marginLeft = normalizeMargin(
|
|
37223
|
+
page.margins?.left,
|
|
37224
|
+
normalizeMargin(options.margins?.left, DEFAULT_MARGINS$1.left)
|
|
37225
|
+
);
|
|
37226
|
+
const marginRight = normalizeMargin(
|
|
37227
|
+
page.margins?.right,
|
|
37228
|
+
normalizeMargin(options.margins?.right, DEFAULT_MARGINS$1.right)
|
|
37229
|
+
);
|
|
37230
|
+
const contentWidth = pageSize.w - (marginLeft + marginRight);
|
|
37231
|
+
const sectionIndex = page.sectionIndex ?? 0;
|
|
37232
|
+
const columnsConfig = sectionColumns.get(sectionIndex) ?? options.columns ?? { count: 1, gap: 0 };
|
|
37233
|
+
const normalized = normalizeColumnsForFootnotes(columnsConfig, contentWidth);
|
|
37234
|
+
result.set(pageIndex, { ...normalized, left: marginLeft, contentWidth });
|
|
37235
|
+
}
|
|
37236
|
+
return result;
|
|
37237
|
+
};
|
|
37238
|
+
const findFragmentForPos = (page, pos) => {
|
|
37239
|
+
for (const fragment of page.fragments) {
|
|
37240
|
+
const pmStart = fragment.pmStart;
|
|
37241
|
+
const pmEnd = fragment.pmEnd;
|
|
37242
|
+
if (pmStart == null || pmEnd == null) continue;
|
|
37243
|
+
if (pos >= pmStart && pos <= pmEnd) {
|
|
37244
|
+
return fragment;
|
|
37245
|
+
}
|
|
37246
|
+
}
|
|
37247
|
+
return null;
|
|
37248
|
+
};
|
|
37249
|
+
const assignFootnotesToColumns = (layout, refs, pageColumns) => {
|
|
37250
|
+
const result = /* @__PURE__ */ new Map();
|
|
37251
|
+
const seenByColumn = /* @__PURE__ */ new Map();
|
|
37252
|
+
for (const ref2 of refs) {
|
|
37253
|
+
const pageIndex = findPageIndexForPos(layout, ref2.pos);
|
|
37254
|
+
if (pageIndex == null) continue;
|
|
37255
|
+
const columns = pageColumns.get(pageIndex);
|
|
37256
|
+
const page = layout.pages[pageIndex];
|
|
37257
|
+
let columnIndex = 0;
|
|
37258
|
+
if (columns && columns.count > 1 && page) {
|
|
37259
|
+
const fragment = findFragmentForPos(page, ref2.pos);
|
|
37260
|
+
if (fragment && typeof fragment.x === "number") {
|
|
37261
|
+
const columnStride = columns.width + columns.gap;
|
|
37262
|
+
const rawIndex = columnStride > 0 ? Math.floor((fragment.x - columns.left) / columnStride) : 0;
|
|
37263
|
+
columnIndex = Math.max(0, Math.min(columns.count - 1, rawIndex));
|
|
37264
|
+
}
|
|
37265
|
+
}
|
|
37266
|
+
const key2 = footnoteColumnKey(pageIndex, columnIndex);
|
|
37267
|
+
let seen = seenByColumn.get(key2);
|
|
37268
|
+
if (!seen) {
|
|
37269
|
+
seen = /* @__PURE__ */ new Set();
|
|
37270
|
+
seenByColumn.set(key2, seen);
|
|
37271
|
+
}
|
|
37272
|
+
if (seen.has(ref2.id)) continue;
|
|
37273
|
+
seen.add(ref2.id);
|
|
37274
|
+
const pageMap = result.get(pageIndex) ?? /* @__PURE__ */ new Map();
|
|
37275
|
+
const list = pageMap.get(columnIndex) ?? [];
|
|
37276
|
+
list.push(ref2.id);
|
|
37277
|
+
pageMap.set(columnIndex, list);
|
|
37278
|
+
result.set(pageIndex, pageMap);
|
|
37279
|
+
}
|
|
37280
|
+
return result;
|
|
37281
|
+
};
|
|
37282
|
+
const resolveFootnoteMeasurementWidth = (options, blocks) => {
|
|
37283
|
+
const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE$1;
|
|
37284
|
+
const margins = {
|
|
37285
|
+
right: normalizeMargin(options.margins?.right, DEFAULT_MARGINS$1.right),
|
|
37286
|
+
left: normalizeMargin(options.margins?.left, DEFAULT_MARGINS$1.left)
|
|
37287
|
+
};
|
|
37288
|
+
let width = pageSize.w - (margins.left + margins.right);
|
|
37289
|
+
let activeColumns = options.columns ?? { count: 1, gap: 0 };
|
|
37290
|
+
let activePageSize = pageSize;
|
|
37291
|
+
let activeMargins = { ...margins };
|
|
37292
|
+
const resolveColumnWidth = () => {
|
|
37293
|
+
const contentWidth = activePageSize.w - (activeMargins.left + activeMargins.right);
|
|
37294
|
+
const normalized = normalizeColumnsForFootnotes(activeColumns, contentWidth);
|
|
37295
|
+
return normalized.width;
|
|
37296
|
+
};
|
|
37297
|
+
width = resolveColumnWidth();
|
|
37298
|
+
if (blocks && blocks.length > 0) {
|
|
37299
|
+
for (const block of blocks) {
|
|
37300
|
+
if (block.kind !== "sectionBreak") continue;
|
|
37301
|
+
activePageSize = block.pageSize ?? activePageSize;
|
|
37302
|
+
activeMargins = {
|
|
37303
|
+
right: normalizeMargin(block.margins?.right, activeMargins.right),
|
|
37304
|
+
left: normalizeMargin(block.margins?.left, activeMargins.left)
|
|
37305
|
+
};
|
|
37306
|
+
if (block.columns) {
|
|
37307
|
+
activeColumns = { count: block.columns.count, gap: block.columns.gap };
|
|
37308
|
+
}
|
|
37309
|
+
const w = resolveColumnWidth();
|
|
37310
|
+
if (w > 0 && w < width) width = w;
|
|
37311
|
+
}
|
|
37312
|
+
}
|
|
37313
|
+
if (!Number.isFinite(width) || width <= 0) return 0;
|
|
37314
|
+
return width;
|
|
37315
|
+
};
|
|
37316
|
+
const MIN_FOOTNOTE_BODY_HEIGHT = 1;
|
|
37317
|
+
const DEFAULT_FOOTNOTE_SEPARATOR_SPACING_BEFORE = 12;
|
|
37318
|
+
const computeMaxFootnoteReserve = (layoutForPages, pageIndex, baseReserve = 0) => {
|
|
37319
|
+
const page = layoutForPages.pages?.[pageIndex];
|
|
37320
|
+
if (!page) return 0;
|
|
37321
|
+
const pageSize = page.size ?? layoutForPages.pageSize ?? DEFAULT_PAGE_SIZE$1;
|
|
37322
|
+
const topMargin = normalizeMargin(page.margins?.top, DEFAULT_MARGINS$1.top);
|
|
37323
|
+
const bottomWithReserve = normalizeMargin(page.margins?.bottom, DEFAULT_MARGINS$1.bottom);
|
|
37324
|
+
const baseReserveSafe = Number.isFinite(baseReserve) ? Math.max(0, baseReserve) : 0;
|
|
37325
|
+
const bottomMargin = Math.max(0, bottomWithReserve - baseReserveSafe);
|
|
37326
|
+
const availableForBody = pageSize.h - topMargin - bottomMargin;
|
|
37327
|
+
if (!Number.isFinite(availableForBody)) return 0;
|
|
37328
|
+
return Math.max(0, availableForBody - MIN_FOOTNOTE_BODY_HEIGHT);
|
|
37329
|
+
};
|
|
37330
|
+
const sumLineHeights$1 = (lines, fromLine, toLine) => {
|
|
37331
|
+
if (!lines || fromLine >= toLine) return 0;
|
|
37332
|
+
let total = 0;
|
|
37333
|
+
for (let i = fromLine; i < toLine; i += 1) {
|
|
37334
|
+
total += lines[i]?.lineHeight ?? 0;
|
|
37335
|
+
}
|
|
37336
|
+
return total;
|
|
37337
|
+
};
|
|
37338
|
+
const getParagraphSpacingAfter = (block) => {
|
|
37339
|
+
const spacing = block.attrs?.spacing;
|
|
37340
|
+
const value = spacing?.after ?? spacing?.lineSpaceAfter;
|
|
37341
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
37342
|
+
};
|
|
37343
|
+
const resolveSeparatorSpacingBefore = (rangesByFootnoteId, measuresById, explicitValue, fallbackValue) => {
|
|
37344
|
+
if (typeof explicitValue === "number" && Number.isFinite(explicitValue)) {
|
|
37345
|
+
return Math.max(0, explicitValue);
|
|
37346
|
+
}
|
|
37347
|
+
for (const ranges of rangesByFootnoteId.values()) {
|
|
37348
|
+
for (const range of ranges) {
|
|
37349
|
+
if (range.kind === "paragraph") {
|
|
37350
|
+
const measure = measuresById.get(range.blockId);
|
|
37351
|
+
if (measure?.kind !== "paragraph") continue;
|
|
37352
|
+
const lineHeight2 = measure.lines?.[range.fromLine]?.lineHeight ?? measure.lines?.[0]?.lineHeight;
|
|
37353
|
+
if (typeof lineHeight2 === "number" && Number.isFinite(lineHeight2) && lineHeight2 > 0) {
|
|
37354
|
+
return lineHeight2;
|
|
37355
|
+
}
|
|
37356
|
+
}
|
|
37357
|
+
if (range.kind === "list-item") {
|
|
37358
|
+
const measure = measuresById.get(range.blockId);
|
|
37359
|
+
if (measure?.kind !== "list") continue;
|
|
37360
|
+
const itemMeasure = measure.items.find((item) => item.itemId === range.itemId);
|
|
37361
|
+
const lineHeight2 = itemMeasure?.paragraph?.lines?.[range.fromLine]?.lineHeight ?? itemMeasure?.paragraph?.lines?.[0]?.lineHeight;
|
|
37362
|
+
if (typeof lineHeight2 === "number" && Number.isFinite(lineHeight2) && lineHeight2 > 0) {
|
|
37363
|
+
return lineHeight2;
|
|
37364
|
+
}
|
|
37365
|
+
}
|
|
37366
|
+
}
|
|
37367
|
+
}
|
|
37368
|
+
return Math.max(0, fallbackValue);
|
|
37369
|
+
};
|
|
37370
|
+
const getRangeRenderHeight = (range) => {
|
|
37371
|
+
if (range.kind === "paragraph" || range.kind === "list-item") {
|
|
37372
|
+
const spacing = range.toLine >= range.totalLines ? range.spacingAfter : 0;
|
|
37373
|
+
return range.height + spacing;
|
|
37374
|
+
}
|
|
37375
|
+
return range.height;
|
|
37376
|
+
};
|
|
37377
|
+
const buildFootnoteRanges = (blocks, measuresById) => {
|
|
37378
|
+
const ranges = [];
|
|
37379
|
+
blocks.forEach((block) => {
|
|
37380
|
+
const measure = measuresById.get(block.id);
|
|
37381
|
+
if (!measure) return;
|
|
37382
|
+
if (block.kind === "paragraph") {
|
|
37383
|
+
if (measure.kind !== "paragraph") return;
|
|
37384
|
+
const lineCount = measure.lines?.length ?? 0;
|
|
37385
|
+
if (lineCount === 0) return;
|
|
37386
|
+
ranges.push({
|
|
37387
|
+
kind: "paragraph",
|
|
37388
|
+
blockId: block.id,
|
|
37389
|
+
fromLine: 0,
|
|
37390
|
+
toLine: lineCount,
|
|
37391
|
+
totalLines: lineCount,
|
|
37392
|
+
height: sumLineHeights$1(measure.lines, 0, lineCount),
|
|
37393
|
+
spacingAfter: getParagraphSpacingAfter(block)
|
|
37394
|
+
});
|
|
37395
|
+
return;
|
|
37396
|
+
}
|
|
37397
|
+
if (block.kind === "list") {
|
|
37398
|
+
if (measure.kind !== "list") return;
|
|
37399
|
+
block.items.forEach((item) => {
|
|
37400
|
+
const itemMeasure = measure.items.find((entry) => entry.itemId === item.id);
|
|
37401
|
+
if (!itemMeasure) return;
|
|
37402
|
+
const lineCount = itemMeasure.paragraph.lines?.length ?? 0;
|
|
37403
|
+
if (lineCount === 0) return;
|
|
37404
|
+
ranges.push({
|
|
37405
|
+
kind: "list-item",
|
|
37406
|
+
blockId: block.id,
|
|
37407
|
+
itemId: item.id,
|
|
37408
|
+
fromLine: 0,
|
|
37409
|
+
toLine: lineCount,
|
|
37410
|
+
totalLines: lineCount,
|
|
37411
|
+
height: sumLineHeights$1(itemMeasure.paragraph.lines, 0, lineCount),
|
|
37412
|
+
spacingAfter: getParagraphSpacingAfter(item.paragraph)
|
|
37413
|
+
});
|
|
37414
|
+
});
|
|
37415
|
+
return;
|
|
37416
|
+
}
|
|
37417
|
+
if (block.kind === "table" && measure.kind === "table") {
|
|
37418
|
+
const height = Math.max(0, measure.totalHeight ?? 0);
|
|
37419
|
+
if (height > 0) {
|
|
37420
|
+
ranges.push({ kind: "table", blockId: block.id, height });
|
|
37421
|
+
}
|
|
37422
|
+
return;
|
|
37423
|
+
}
|
|
37424
|
+
if (block.kind === "image" && measure.kind === "image") {
|
|
37425
|
+
const height = Math.max(0, measure.height ?? 0);
|
|
37426
|
+
if (height > 0) {
|
|
37427
|
+
ranges.push({ kind: "image", blockId: block.id, height });
|
|
37428
|
+
}
|
|
37429
|
+
return;
|
|
37430
|
+
}
|
|
37431
|
+
if (block.kind === "drawing" && measure.kind === "drawing") {
|
|
37432
|
+
const height = Math.max(0, measure.height ?? 0);
|
|
37433
|
+
if (height > 0) {
|
|
37434
|
+
ranges.push({ kind: "drawing", blockId: block.id, height });
|
|
37435
|
+
}
|
|
37436
|
+
}
|
|
37437
|
+
});
|
|
37438
|
+
return ranges;
|
|
37439
|
+
};
|
|
37440
|
+
const splitRangeAtHeight = (range, availableHeight, measuresById) => {
|
|
37441
|
+
if (availableHeight <= 0) return { fitted: null, remaining: range };
|
|
37442
|
+
if (range.kind !== "paragraph") {
|
|
37443
|
+
return getRangeRenderHeight(range) <= availableHeight ? { fitted: range, remaining: null } : { fitted: null, remaining: range };
|
|
37444
|
+
}
|
|
37445
|
+
const measure = measuresById.get(range.blockId);
|
|
37446
|
+
if (!measure || measure.kind !== "paragraph" || !measure.lines) {
|
|
37447
|
+
return getRangeRenderHeight(range) <= availableHeight ? { fitted: range, remaining: null } : { fitted: null, remaining: range };
|
|
37448
|
+
}
|
|
37449
|
+
let accumulatedHeight = 0;
|
|
37450
|
+
let splitLine = range.fromLine;
|
|
37451
|
+
for (let i = range.fromLine; i < range.toLine; i += 1) {
|
|
37452
|
+
const lineHeight2 = measure.lines[i]?.lineHeight ?? 0;
|
|
37453
|
+
if (accumulatedHeight + lineHeight2 > availableHeight) break;
|
|
37454
|
+
accumulatedHeight += lineHeight2;
|
|
37455
|
+
splitLine = i + 1;
|
|
37456
|
+
}
|
|
37457
|
+
if (splitLine === range.fromLine) {
|
|
37458
|
+
return { fitted: null, remaining: range };
|
|
37459
|
+
}
|
|
37460
|
+
const fitted = {
|
|
37461
|
+
...range,
|
|
37462
|
+
toLine: splitLine,
|
|
37463
|
+
height: sumLineHeights$1(measure.lines, range.fromLine, splitLine)
|
|
37464
|
+
};
|
|
37465
|
+
if (splitLine >= range.toLine) {
|
|
37466
|
+
return getRangeRenderHeight(fitted) <= availableHeight ? { fitted, remaining: null } : { fitted: null, remaining: range };
|
|
37467
|
+
}
|
|
37468
|
+
const remaining = {
|
|
37469
|
+
...range,
|
|
37470
|
+
fromLine: splitLine,
|
|
37471
|
+
height: sumLineHeights$1(measure.lines, splitLine, range.toLine)
|
|
37472
|
+
};
|
|
37473
|
+
return { fitted, remaining };
|
|
37474
|
+
};
|
|
37475
|
+
const forceFitFirstRange = (range, measuresById) => {
|
|
37476
|
+
if (range.kind !== "paragraph") {
|
|
37477
|
+
return { fitted: range, remaining: null };
|
|
37478
|
+
}
|
|
37479
|
+
const measure = measuresById.get(range.blockId);
|
|
37480
|
+
if (!measure || measure.kind !== "paragraph" || !measure.lines?.length) {
|
|
37481
|
+
return { fitted: range, remaining: null };
|
|
37482
|
+
}
|
|
37483
|
+
const nextLine = Math.min(range.fromLine + 1, range.toLine);
|
|
37484
|
+
const fitted = {
|
|
37485
|
+
...range,
|
|
37486
|
+
toLine: nextLine,
|
|
37487
|
+
height: sumLineHeights$1(measure.lines, range.fromLine, nextLine)
|
|
37488
|
+
};
|
|
37489
|
+
if (nextLine >= range.toLine) {
|
|
37490
|
+
return { fitted, remaining: null };
|
|
37491
|
+
}
|
|
37492
|
+
const remaining = {
|
|
37493
|
+
...range,
|
|
37494
|
+
fromLine: nextLine,
|
|
37495
|
+
height: sumLineHeights$1(measure.lines, nextLine, range.toLine)
|
|
37496
|
+
};
|
|
37497
|
+
return { fitted, remaining };
|
|
37498
|
+
};
|
|
37499
|
+
const fitFootnoteContent = (id, inputRanges, availableHeight, pageIndex, columnIndex, isContinuation, measuresById, forceFirstRange) => {
|
|
37500
|
+
const fittedRanges = [];
|
|
37501
|
+
let remainingRanges = [];
|
|
37502
|
+
let usedHeight = 0;
|
|
37503
|
+
const maxHeight = Math.max(0, availableHeight);
|
|
37504
|
+
for (let index2 = 0; index2 < inputRanges.length; index2 += 1) {
|
|
37505
|
+
const range = inputRanges[index2];
|
|
37506
|
+
const remainingSpace = maxHeight - usedHeight;
|
|
37507
|
+
const rangeHeight = getRangeRenderHeight(range);
|
|
37508
|
+
if (rangeHeight <= remainingSpace) {
|
|
37509
|
+
fittedRanges.push(range);
|
|
37510
|
+
usedHeight += rangeHeight;
|
|
37511
|
+
continue;
|
|
37512
|
+
}
|
|
37513
|
+
if (range.kind === "paragraph") {
|
|
37514
|
+
const split = splitRangeAtHeight(range, remainingSpace, measuresById);
|
|
37515
|
+
if (split.fitted && getRangeRenderHeight(split.fitted) <= remainingSpace) {
|
|
37516
|
+
fittedRanges.push(split.fitted);
|
|
37517
|
+
usedHeight += getRangeRenderHeight(split.fitted);
|
|
37518
|
+
}
|
|
37519
|
+
if (split.remaining) {
|
|
37520
|
+
remainingRanges = [split.remaining, ...inputRanges.slice(index2 + 1)];
|
|
37521
|
+
} else {
|
|
37522
|
+
remainingRanges = inputRanges.slice(index2 + 1);
|
|
37523
|
+
}
|
|
37524
|
+
break;
|
|
37525
|
+
}
|
|
37526
|
+
remainingRanges = [range, ...inputRanges.slice(index2 + 1)];
|
|
37527
|
+
break;
|
|
37528
|
+
}
|
|
37529
|
+
if (fittedRanges.length === 0 && forceFirstRange && inputRanges.length > 0) {
|
|
37530
|
+
const forced = forceFitFirstRange(inputRanges[0], measuresById);
|
|
37531
|
+
if (forced.fitted) {
|
|
37532
|
+
fittedRanges.push(forced.fitted);
|
|
37533
|
+
usedHeight = getRangeRenderHeight(forced.fitted);
|
|
37534
|
+
remainingRanges = [];
|
|
37535
|
+
if (forced.remaining) {
|
|
37536
|
+
remainingRanges.push(forced.remaining);
|
|
37537
|
+
}
|
|
37538
|
+
remainingRanges.push(...inputRanges.slice(1));
|
|
37539
|
+
}
|
|
37540
|
+
}
|
|
37541
|
+
return {
|
|
37542
|
+
slice: {
|
|
37543
|
+
id,
|
|
37544
|
+
pageIndex,
|
|
37545
|
+
columnIndex,
|
|
37546
|
+
isContinuation,
|
|
37547
|
+
ranges: fittedRanges,
|
|
37548
|
+
totalHeight: usedHeight
|
|
37549
|
+
},
|
|
37550
|
+
remainingRanges
|
|
37551
|
+
};
|
|
37552
|
+
};
|
|
36946
37553
|
async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, options, measureBlock2, headerFooter) {
|
|
36947
37554
|
performance.now();
|
|
36948
37555
|
const dirty = computeDirtyRegions(previousBlocks, nextBlocks);
|
|
@@ -37206,6 +37813,497 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
|
|
|
37206
37813
|
converged
|
|
37207
37814
|
});
|
|
37208
37815
|
}
|
|
37816
|
+
let extraBlocks;
|
|
37817
|
+
let extraMeasures;
|
|
37818
|
+
const footnotesInput = isFootnotesLayoutInput(options.footnotes) ? options.footnotes : null;
|
|
37819
|
+
if (footnotesInput && footnotesInput.refs.length > 0 && footnotesInput.blocksById.size > 0) {
|
|
37820
|
+
const gap = typeof footnotesInput.gap === "number" && Number.isFinite(footnotesInput.gap) ? footnotesInput.gap : 2;
|
|
37821
|
+
const topPadding = typeof footnotesInput.topPadding === "number" && Number.isFinite(footnotesInput.topPadding) ? footnotesInput.topPadding : 6;
|
|
37822
|
+
const dividerHeight = typeof footnotesInput.dividerHeight === "number" && Number.isFinite(footnotesInput.dividerHeight) ? footnotesInput.dividerHeight : 6;
|
|
37823
|
+
const safeGap = Math.max(0, gap);
|
|
37824
|
+
const safeTopPadding = Math.max(0, topPadding);
|
|
37825
|
+
const safeDividerHeight = Math.max(0, dividerHeight);
|
|
37826
|
+
const continuationDividerHeight = safeDividerHeight;
|
|
37827
|
+
const continuationDividerWidthFactor = 0.3;
|
|
37828
|
+
const footnoteWidth = resolveFootnoteMeasurementWidth(options, currentBlocks);
|
|
37829
|
+
if (footnoteWidth > 0) {
|
|
37830
|
+
const footnoteConstraints = { maxWidth: footnoteWidth, maxHeight: measurementHeight };
|
|
37831
|
+
const collectFootnoteIdsByColumn = (idsByColumn2) => {
|
|
37832
|
+
const ids = /* @__PURE__ */ new Set();
|
|
37833
|
+
idsByColumn2.forEach((columns) => {
|
|
37834
|
+
columns.forEach((list) => {
|
|
37835
|
+
list.forEach((id) => ids.add(id));
|
|
37836
|
+
});
|
|
37837
|
+
});
|
|
37838
|
+
return ids;
|
|
37839
|
+
};
|
|
37840
|
+
const measureFootnoteBlocks = async (ids) => {
|
|
37841
|
+
const needed = /* @__PURE__ */ new Map();
|
|
37842
|
+
ids.forEach((id) => {
|
|
37843
|
+
const blocks2 = footnotesInput.blocksById.get(id) ?? [];
|
|
37844
|
+
blocks2.forEach((block) => {
|
|
37845
|
+
if (block?.id && !needed.has(block.id)) {
|
|
37846
|
+
needed.set(block.id, block);
|
|
37847
|
+
}
|
|
37848
|
+
});
|
|
37849
|
+
});
|
|
37850
|
+
const blocks = Array.from(needed.values());
|
|
37851
|
+
const measuresById2 = /* @__PURE__ */ new Map();
|
|
37852
|
+
await Promise.all(
|
|
37853
|
+
blocks.map(async (block) => {
|
|
37854
|
+
const cached = measureCache.get(block, footnoteConstraints.maxWidth, footnoteConstraints.maxHeight);
|
|
37855
|
+
if (cached) {
|
|
37856
|
+
measuresById2.set(block.id, cached);
|
|
37857
|
+
return;
|
|
37858
|
+
}
|
|
37859
|
+
const measurement = await measureBlock2(block, footnoteConstraints);
|
|
37860
|
+
measureCache.set(block, footnoteConstraints.maxWidth, footnoteConstraints.maxHeight, measurement);
|
|
37861
|
+
measuresById2.set(block.id, measurement);
|
|
37862
|
+
})
|
|
37863
|
+
);
|
|
37864
|
+
return { blocks, measuresById: measuresById2 };
|
|
37865
|
+
};
|
|
37866
|
+
const computeFootnoteLayoutPlan = (layoutForPages, idsByColumn2, measuresById2, baseReserves = [], pageColumns2) => {
|
|
37867
|
+
const pageCount = layoutForPages.pages.length;
|
|
37868
|
+
const slicesByPage = /* @__PURE__ */ new Map();
|
|
37869
|
+
const reserves2 = new Array(pageCount).fill(0);
|
|
37870
|
+
const hasContinuationByColumn = /* @__PURE__ */ new Map();
|
|
37871
|
+
const rangesByFootnoteId = /* @__PURE__ */ new Map();
|
|
37872
|
+
const cappedPages = /* @__PURE__ */ new Set();
|
|
37873
|
+
const allIds = collectFootnoteIdsByColumn(idsByColumn2);
|
|
37874
|
+
allIds.forEach((id) => {
|
|
37875
|
+
const blocks = footnotesInput.blocksById.get(id) ?? [];
|
|
37876
|
+
rangesByFootnoteId.set(id, buildFootnoteRanges(blocks, measuresById2));
|
|
37877
|
+
});
|
|
37878
|
+
const separatorSpacingBefore = resolveSeparatorSpacingBefore(
|
|
37879
|
+
rangesByFootnoteId,
|
|
37880
|
+
measuresById2,
|
|
37881
|
+
footnotesInput.separatorSpacingBefore,
|
|
37882
|
+
DEFAULT_FOOTNOTE_SEPARATOR_SPACING_BEFORE
|
|
37883
|
+
);
|
|
37884
|
+
const safeSeparatorSpacingBefore = Math.max(0, separatorSpacingBefore);
|
|
37885
|
+
let pendingByColumn = /* @__PURE__ */ new Map();
|
|
37886
|
+
for (let pageIndex = 0; pageIndex < pageCount; pageIndex += 1) {
|
|
37887
|
+
const baseReserve = Number.isFinite(baseReserves?.[pageIndex]) ? Math.max(0, baseReserves[pageIndex]) : 0;
|
|
37888
|
+
const maxReserve = computeMaxFootnoteReserve(layoutForPages, pageIndex, baseReserve);
|
|
37889
|
+
const columns = pageColumns2.get(pageIndex);
|
|
37890
|
+
const columnCount = Math.max(1, Math.floor(columns?.count ?? 1));
|
|
37891
|
+
const pendingForPage = /* @__PURE__ */ new Map();
|
|
37892
|
+
pendingByColumn.forEach((entries, columnIndex) => {
|
|
37893
|
+
const targetIndex = columnIndex < columnCount ? columnIndex : Math.max(0, columnCount - 1);
|
|
37894
|
+
const list = pendingForPage.get(targetIndex) ?? [];
|
|
37895
|
+
list.push(...entries);
|
|
37896
|
+
pendingForPage.set(targetIndex, list);
|
|
37897
|
+
});
|
|
37898
|
+
pendingByColumn = /* @__PURE__ */ new Map();
|
|
37899
|
+
const pageSlices = [];
|
|
37900
|
+
let pageReserve = 0;
|
|
37901
|
+
for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {
|
|
37902
|
+
let usedHeight = 0;
|
|
37903
|
+
const columnSlices = [];
|
|
37904
|
+
const nextPending = [];
|
|
37905
|
+
let stopPlacement = false;
|
|
37906
|
+
const columnKey = footnoteColumnKey(pageIndex, columnIndex);
|
|
37907
|
+
const placeFootnote = (id, ranges, isContinuation) => {
|
|
37908
|
+
if (!ranges || ranges.length === 0) {
|
|
37909
|
+
return { placed: false, remaining: [] };
|
|
37910
|
+
}
|
|
37911
|
+
const isFirstSlice = columnSlices.length === 0;
|
|
37912
|
+
const separatorBefore = isFirstSlice ? safeSeparatorSpacingBefore : 0;
|
|
37913
|
+
const separatorHeight = isFirstSlice ? isContinuation ? continuationDividerHeight : safeDividerHeight : 0;
|
|
37914
|
+
const overhead = isFirstSlice ? separatorBefore + separatorHeight + safeTopPadding : 0;
|
|
37915
|
+
const gapBefore = !isFirstSlice ? safeGap : 0;
|
|
37916
|
+
const availableHeight = Math.max(0, maxReserve - usedHeight - overhead - gapBefore);
|
|
37917
|
+
const { slice: slice2, remainingRanges } = fitFootnoteContent(
|
|
37918
|
+
id,
|
|
37919
|
+
ranges,
|
|
37920
|
+
availableHeight,
|
|
37921
|
+
pageIndex,
|
|
37922
|
+
columnIndex,
|
|
37923
|
+
isContinuation,
|
|
37924
|
+
measuresById2,
|
|
37925
|
+
isFirstSlice && maxReserve > 0
|
|
37926
|
+
);
|
|
37927
|
+
if (slice2.ranges.length === 0) {
|
|
37928
|
+
return { placed: false, remaining: ranges };
|
|
37929
|
+
}
|
|
37930
|
+
if (isFirstSlice) {
|
|
37931
|
+
usedHeight += overhead;
|
|
37932
|
+
if (isContinuation) {
|
|
37933
|
+
hasContinuationByColumn.set(columnKey, true);
|
|
37934
|
+
}
|
|
37935
|
+
}
|
|
37936
|
+
if (gapBefore > 0) {
|
|
37937
|
+
usedHeight += gapBefore;
|
|
37938
|
+
}
|
|
37939
|
+
usedHeight += slice2.totalHeight;
|
|
37940
|
+
columnSlices.push(slice2);
|
|
37941
|
+
return { placed: true, remaining: remainingRanges };
|
|
37942
|
+
};
|
|
37943
|
+
const pending = pendingForPage.get(columnIndex) ?? [];
|
|
37944
|
+
for (const entry of pending) {
|
|
37945
|
+
if (stopPlacement) {
|
|
37946
|
+
nextPending.push(entry);
|
|
37947
|
+
continue;
|
|
37948
|
+
}
|
|
37949
|
+
if (!entry.ranges || entry.ranges.length === 0) continue;
|
|
37950
|
+
const result = placeFootnote(entry.id, entry.ranges, true);
|
|
37951
|
+
if (!result.placed) {
|
|
37952
|
+
nextPending.push(entry);
|
|
37953
|
+
stopPlacement = true;
|
|
37954
|
+
continue;
|
|
37955
|
+
}
|
|
37956
|
+
if (result.remaining.length > 0) {
|
|
37957
|
+
nextPending.push({ id: entry.id, ranges: result.remaining });
|
|
37958
|
+
}
|
|
37959
|
+
}
|
|
37960
|
+
if (!stopPlacement) {
|
|
37961
|
+
const ids = idsByColumn2.get(pageIndex)?.get(columnIndex) ?? [];
|
|
37962
|
+
for (let idIndex = 0; idIndex < ids.length; idIndex += 1) {
|
|
37963
|
+
const id = ids[idIndex];
|
|
37964
|
+
const ranges = rangesByFootnoteId.get(id) ?? [];
|
|
37965
|
+
if (ranges.length === 0) continue;
|
|
37966
|
+
const result = placeFootnote(id, ranges, false);
|
|
37967
|
+
if (!result.placed) {
|
|
37968
|
+
nextPending.push({ id, ranges });
|
|
37969
|
+
for (let remainingIndex = idIndex + 1; remainingIndex < ids.length; remainingIndex += 1) {
|
|
37970
|
+
const remainingId = ids[remainingIndex];
|
|
37971
|
+
const remainingRanges = rangesByFootnoteId.get(remainingId) ?? [];
|
|
37972
|
+
nextPending.push({ id: remainingId, ranges: remainingRanges });
|
|
37973
|
+
}
|
|
37974
|
+
stopPlacement = true;
|
|
37975
|
+
break;
|
|
37976
|
+
}
|
|
37977
|
+
if (result.remaining.length > 0) {
|
|
37978
|
+
nextPending.push({ id, ranges: result.remaining });
|
|
37979
|
+
}
|
|
37980
|
+
}
|
|
37981
|
+
}
|
|
37982
|
+
if (columnSlices.length > 0) {
|
|
37983
|
+
const rawReserve = Math.max(0, Math.ceil(usedHeight));
|
|
37984
|
+
const cappedReserve = Math.min(rawReserve, maxReserve);
|
|
37985
|
+
if (cappedReserve < rawReserve) {
|
|
37986
|
+
cappedPages.add(pageIndex);
|
|
37987
|
+
}
|
|
37988
|
+
pageReserve = Math.max(pageReserve, cappedReserve);
|
|
37989
|
+
pageSlices.push(...columnSlices);
|
|
37990
|
+
}
|
|
37991
|
+
if (nextPending.length > 0) {
|
|
37992
|
+
pendingByColumn.set(columnIndex, nextPending);
|
|
37993
|
+
}
|
|
37994
|
+
}
|
|
37995
|
+
if (pageSlices.length > 0) {
|
|
37996
|
+
slicesByPage.set(pageIndex, pageSlices);
|
|
37997
|
+
}
|
|
37998
|
+
reserves2[pageIndex] = pageReserve;
|
|
37999
|
+
}
|
|
38000
|
+
if (cappedPages.size > 0) {
|
|
38001
|
+
console.warn("[layout] Footnote reserve capped to preserve body area", {
|
|
38002
|
+
pages: Array.from(cappedPages)
|
|
38003
|
+
});
|
|
38004
|
+
}
|
|
38005
|
+
if (pendingByColumn.size > 0) {
|
|
38006
|
+
const pendingIds = /* @__PURE__ */ new Set();
|
|
38007
|
+
pendingByColumn.forEach((entries) => entries.forEach((entry) => pendingIds.add(entry.id)));
|
|
38008
|
+
console.warn("[layout] Footnote content truncated: extends beyond document pages", {
|
|
38009
|
+
ids: Array.from(pendingIds)
|
|
38010
|
+
});
|
|
38011
|
+
}
|
|
38012
|
+
return { slicesByPage, reserves: reserves2, hasContinuationByColumn, separatorSpacingBefore: safeSeparatorSpacingBefore };
|
|
38013
|
+
};
|
|
38014
|
+
const injectFragments = (layoutForPages, plan2, measuresById2, reservesByPageIndex, blockById, pageColumns2) => {
|
|
38015
|
+
const decorativeBlocks = [];
|
|
38016
|
+
const decorativeMeasures = [];
|
|
38017
|
+
for (let pageIndex = 0; pageIndex < layoutForPages.pages.length; pageIndex++) {
|
|
38018
|
+
const page = layoutForPages.pages[pageIndex];
|
|
38019
|
+
page.footnoteReserved = Math.max(0, reservesByPageIndex[pageIndex] ?? plan2.reserves[pageIndex] ?? 0);
|
|
38020
|
+
const slices = plan2.slicesByPage.get(pageIndex) ?? [];
|
|
38021
|
+
if (slices.length === 0) continue;
|
|
38022
|
+
if (!page.margins) continue;
|
|
38023
|
+
const pageSize = page.size ?? layoutForPages.pageSize;
|
|
38024
|
+
const marginLeft = normalizeMargin(
|
|
38025
|
+
page.margins.left,
|
|
38026
|
+
normalizeMargin(options.margins?.left, DEFAULT_MARGINS$1.left)
|
|
38027
|
+
);
|
|
38028
|
+
const marginRight = normalizeMargin(
|
|
38029
|
+
page.margins.right,
|
|
38030
|
+
normalizeMargin(options.margins?.right, DEFAULT_MARGINS$1.right)
|
|
38031
|
+
);
|
|
38032
|
+
const pageContentWidth = pageSize.w - (marginLeft + marginRight);
|
|
38033
|
+
const fallbackColumns = normalizeColumnsForFootnotes(
|
|
38034
|
+
options.columns ?? { count: 1, gap: 0 },
|
|
38035
|
+
pageContentWidth
|
|
38036
|
+
);
|
|
38037
|
+
const columns = pageColumns2.get(pageIndex) ?? {
|
|
38038
|
+
...fallbackColumns,
|
|
38039
|
+
left: marginLeft
|
|
38040
|
+
};
|
|
38041
|
+
const bandTopY = pageSize.h - (page.margins.bottom ?? 0);
|
|
38042
|
+
const slicesByColumn = /* @__PURE__ */ new Map();
|
|
38043
|
+
slices.forEach((slice2) => {
|
|
38044
|
+
const columnIndex = Number.isFinite(slice2.columnIndex) ? slice2.columnIndex : 0;
|
|
38045
|
+
const list = slicesByColumn.get(columnIndex) ?? [];
|
|
38046
|
+
list.push(slice2);
|
|
38047
|
+
slicesByColumn.set(columnIndex, list);
|
|
38048
|
+
});
|
|
38049
|
+
slicesByColumn.forEach((columnSlices, rawColumnIndex) => {
|
|
38050
|
+
if (columnSlices.length === 0) return;
|
|
38051
|
+
const columnIndex = Math.max(0, Math.min(columns.count - 1, rawColumnIndex));
|
|
38052
|
+
const columnStride = columns.width + columns.gap;
|
|
38053
|
+
const columnX = columns.left + columnIndex * columnStride;
|
|
38054
|
+
const contentWidth = Math.min(columns.width, footnoteWidth);
|
|
38055
|
+
if (!Number.isFinite(contentWidth) || contentWidth <= 0) return;
|
|
38056
|
+
const columnKey = footnoteColumnKey(pageIndex, columnIndex);
|
|
38057
|
+
const isContinuation = plan2.hasContinuationByColumn.get(columnKey) ?? false;
|
|
38058
|
+
let cursorY = bandTopY + Math.max(0, plan2.separatorSpacingBefore);
|
|
38059
|
+
const separatorHeight = isContinuation ? continuationDividerHeight : safeDividerHeight;
|
|
38060
|
+
const separatorWidth = isContinuation ? Math.max(0, contentWidth * continuationDividerWidthFactor) : contentWidth;
|
|
38061
|
+
if (separatorHeight > 0 && separatorWidth > 0) {
|
|
38062
|
+
const separatorId = isContinuation ? `footnote-continuation-separator-page-${page.number}-col-${columnIndex}` : `footnote-separator-page-${page.number}-col-${columnIndex}`;
|
|
38063
|
+
decorativeBlocks.push({
|
|
38064
|
+
kind: "drawing",
|
|
38065
|
+
id: separatorId,
|
|
38066
|
+
drawingKind: "vectorShape",
|
|
38067
|
+
geometry: { width: separatorWidth, height: separatorHeight },
|
|
38068
|
+
shapeKind: "rect",
|
|
38069
|
+
fillColor: "#000000",
|
|
38070
|
+
strokeColor: null,
|
|
38071
|
+
strokeWidth: 0
|
|
38072
|
+
});
|
|
38073
|
+
decorativeMeasures.push({
|
|
38074
|
+
kind: "drawing",
|
|
38075
|
+
drawingKind: "vectorShape",
|
|
38076
|
+
width: separatorWidth,
|
|
38077
|
+
height: separatorHeight,
|
|
38078
|
+
scale: 1,
|
|
38079
|
+
naturalWidth: separatorWidth,
|
|
38080
|
+
naturalHeight: separatorHeight,
|
|
38081
|
+
geometry: { width: separatorWidth, height: separatorHeight }
|
|
38082
|
+
});
|
|
38083
|
+
page.fragments.push({
|
|
38084
|
+
kind: "drawing",
|
|
38085
|
+
blockId: separatorId,
|
|
38086
|
+
drawingKind: "vectorShape",
|
|
38087
|
+
x: columnX,
|
|
38088
|
+
y: cursorY,
|
|
38089
|
+
width: separatorWidth,
|
|
38090
|
+
height: separatorHeight,
|
|
38091
|
+
geometry: { width: separatorWidth, height: separatorHeight },
|
|
38092
|
+
scale: 1
|
|
38093
|
+
});
|
|
38094
|
+
cursorY += separatorHeight;
|
|
38095
|
+
}
|
|
38096
|
+
cursorY += safeTopPadding;
|
|
38097
|
+
columnSlices.forEach((slice2, sliceIndex) => {
|
|
38098
|
+
slice2.ranges.forEach((range) => {
|
|
38099
|
+
if (range.kind === "paragraph") {
|
|
38100
|
+
const measure = measuresById2.get(range.blockId);
|
|
38101
|
+
if (!measure || measure.kind !== "paragraph") return;
|
|
38102
|
+
const marker = measure.marker;
|
|
38103
|
+
page.fragments.push({
|
|
38104
|
+
kind: "para",
|
|
38105
|
+
blockId: range.blockId,
|
|
38106
|
+
fromLine: range.fromLine,
|
|
38107
|
+
toLine: range.toLine,
|
|
38108
|
+
x: columnX,
|
|
38109
|
+
y: cursorY,
|
|
38110
|
+
width: contentWidth,
|
|
38111
|
+
continuesFromPrev: range.fromLine > 0,
|
|
38112
|
+
continuesOnNext: range.toLine < range.totalLines,
|
|
38113
|
+
...marker?.markerWidth != null ? { markerWidth: marker.markerWidth } : {},
|
|
38114
|
+
...marker?.markerTextWidth != null ? { markerTextWidth: marker.markerTextWidth } : {},
|
|
38115
|
+
...marker?.gutterWidth != null ? { markerGutter: marker.gutterWidth } : {}
|
|
38116
|
+
});
|
|
38117
|
+
cursorY += getRangeRenderHeight(range);
|
|
38118
|
+
return;
|
|
38119
|
+
}
|
|
38120
|
+
if (range.kind === "list-item") {
|
|
38121
|
+
const measure = measuresById2.get(range.blockId);
|
|
38122
|
+
const block = blockById.get(range.blockId);
|
|
38123
|
+
if (!measure || measure.kind !== "list") return;
|
|
38124
|
+
if (!block || block.kind !== "list") return;
|
|
38125
|
+
const itemMeasure = measure.items.find((entry) => entry.itemId === range.itemId);
|
|
38126
|
+
if (!itemMeasure) return;
|
|
38127
|
+
const indentLeft = Number.isFinite(itemMeasure.indentLeft) ? itemMeasure.indentLeft : 0;
|
|
38128
|
+
const markerWidth = Number.isFinite(itemMeasure.markerWidth) ? itemMeasure.markerWidth : 0;
|
|
38129
|
+
const itemWidth = Math.max(0, contentWidth - indentLeft - markerWidth);
|
|
38130
|
+
page.fragments.push({
|
|
38131
|
+
kind: "list-item",
|
|
38132
|
+
blockId: range.blockId,
|
|
38133
|
+
itemId: range.itemId,
|
|
38134
|
+
fromLine: range.fromLine,
|
|
38135
|
+
toLine: range.toLine,
|
|
38136
|
+
x: columnX + indentLeft + markerWidth,
|
|
38137
|
+
y: cursorY,
|
|
38138
|
+
width: itemWidth,
|
|
38139
|
+
markerWidth,
|
|
38140
|
+
continuesFromPrev: range.fromLine > 0,
|
|
38141
|
+
continuesOnNext: range.toLine < range.totalLines
|
|
38142
|
+
});
|
|
38143
|
+
cursorY += getRangeRenderHeight(range);
|
|
38144
|
+
return;
|
|
38145
|
+
}
|
|
38146
|
+
if (range.kind === "table") {
|
|
38147
|
+
const measure = measuresById2.get(range.blockId);
|
|
38148
|
+
const block = blockById.get(range.blockId);
|
|
38149
|
+
if (!measure || measure.kind !== "table") return;
|
|
38150
|
+
if (!block || block.kind !== "table") return;
|
|
38151
|
+
const tableWidthRaw = Math.max(0, measure.totalWidth ?? 0);
|
|
38152
|
+
let tableWidth = Math.min(contentWidth, tableWidthRaw);
|
|
38153
|
+
let tableX = columnX;
|
|
38154
|
+
const justification = typeof block.attrs?.justification === "string" ? block.attrs.justification : void 0;
|
|
38155
|
+
if (justification === "center") {
|
|
38156
|
+
tableX = columnX + Math.max(0, (contentWidth - tableWidth) / 2);
|
|
38157
|
+
} else if (justification === "right" || justification === "end") {
|
|
38158
|
+
tableX = columnX + Math.max(0, contentWidth - tableWidth);
|
|
38159
|
+
} else {
|
|
38160
|
+
const indentValue = block.attrs?.tableIndent?.width;
|
|
38161
|
+
const indent = typeof indentValue === "number" && Number.isFinite(indentValue) ? indentValue : 0;
|
|
38162
|
+
tableX += indent;
|
|
38163
|
+
tableWidth = Math.max(0, tableWidth - indent);
|
|
38164
|
+
}
|
|
38165
|
+
page.fragments.push({
|
|
38166
|
+
kind: "table",
|
|
38167
|
+
blockId: range.blockId,
|
|
38168
|
+
fromRow: 0,
|
|
38169
|
+
toRow: block.rows.length,
|
|
38170
|
+
x: tableX,
|
|
38171
|
+
y: cursorY,
|
|
38172
|
+
width: tableWidth,
|
|
38173
|
+
height: Math.max(0, measure.totalHeight ?? 0)
|
|
38174
|
+
});
|
|
38175
|
+
cursorY += getRangeRenderHeight(range);
|
|
38176
|
+
return;
|
|
38177
|
+
}
|
|
38178
|
+
if (range.kind === "image") {
|
|
38179
|
+
const measure = measuresById2.get(range.blockId);
|
|
38180
|
+
if (!measure || measure.kind !== "image") return;
|
|
38181
|
+
page.fragments.push({
|
|
38182
|
+
kind: "image",
|
|
38183
|
+
blockId: range.blockId,
|
|
38184
|
+
x: columnX,
|
|
38185
|
+
y: cursorY,
|
|
38186
|
+
width: Math.min(contentWidth, Math.max(0, measure.width ?? 0)),
|
|
38187
|
+
height: Math.max(0, measure.height ?? 0)
|
|
38188
|
+
});
|
|
38189
|
+
cursorY += getRangeRenderHeight(range);
|
|
38190
|
+
return;
|
|
38191
|
+
}
|
|
38192
|
+
if (range.kind === "drawing") {
|
|
38193
|
+
const measure = measuresById2.get(range.blockId);
|
|
38194
|
+
const block = blockById.get(range.blockId);
|
|
38195
|
+
if (!measure || measure.kind !== "drawing") return;
|
|
38196
|
+
if (!block || block.kind !== "drawing") return;
|
|
38197
|
+
page.fragments.push({
|
|
38198
|
+
kind: "drawing",
|
|
38199
|
+
blockId: range.blockId,
|
|
38200
|
+
drawingKind: block.drawingKind,
|
|
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
|
+
geometry: measure.geometry,
|
|
38206
|
+
scale: measure.scale
|
|
38207
|
+
});
|
|
38208
|
+
cursorY += getRangeRenderHeight(range);
|
|
38209
|
+
}
|
|
38210
|
+
});
|
|
38211
|
+
if (sliceIndex < columnSlices.length - 1) {
|
|
38212
|
+
cursorY += safeGap;
|
|
38213
|
+
}
|
|
38214
|
+
});
|
|
38215
|
+
});
|
|
38216
|
+
}
|
|
38217
|
+
return { decorativeBlocks, decorativeMeasures };
|
|
38218
|
+
};
|
|
38219
|
+
const resolveFootnoteAssignments = (layoutForPages) => {
|
|
38220
|
+
const columns = resolvePageColumns(layoutForPages, options, currentBlocks);
|
|
38221
|
+
const idsByColumn2 = assignFootnotesToColumns(layoutForPages, footnotesInput.refs, columns);
|
|
38222
|
+
return { columns, idsByColumn: idsByColumn2 };
|
|
38223
|
+
};
|
|
38224
|
+
let { columns: pageColumns, idsByColumn } = resolveFootnoteAssignments(layout);
|
|
38225
|
+
let { measuresById } = await measureFootnoteBlocks(collectFootnoteIdsByColumn(idsByColumn));
|
|
38226
|
+
let plan = computeFootnoteLayoutPlan(layout, idsByColumn, measuresById, [], pageColumns);
|
|
38227
|
+
let reserves = plan.reserves;
|
|
38228
|
+
if (reserves.some((h2) => h2 > 0)) {
|
|
38229
|
+
layout = layoutDocument(currentBlocks, currentMeasures, {
|
|
38230
|
+
...options,
|
|
38231
|
+
footnoteReservedByPageIndex: reserves,
|
|
38232
|
+
headerContentHeights,
|
|
38233
|
+
footerContentHeights,
|
|
38234
|
+
remeasureParagraph: (block, maxWidth, firstLineIndent) => remeasureParagraph(block, maxWidth, firstLineIndent)
|
|
38235
|
+
});
|
|
38236
|
+
({ columns: pageColumns, idsByColumn } = resolveFootnoteAssignments(layout));
|
|
38237
|
+
({ measuresById } = await measureFootnoteBlocks(collectFootnoteIdsByColumn(idsByColumn)));
|
|
38238
|
+
plan = computeFootnoteLayoutPlan(layout, idsByColumn, measuresById, reserves, pageColumns);
|
|
38239
|
+
reserves = plan.reserves;
|
|
38240
|
+
layout = layoutDocument(currentBlocks, currentMeasures, {
|
|
38241
|
+
...options,
|
|
38242
|
+
footnoteReservedByPageIndex: reserves,
|
|
38243
|
+
headerContentHeights,
|
|
38244
|
+
footerContentHeights,
|
|
38245
|
+
remeasureParagraph: (block, maxWidth, firstLineIndent) => remeasureParagraph(block, maxWidth, firstLineIndent)
|
|
38246
|
+
});
|
|
38247
|
+
let { columns: finalPageColumns, idsByColumn: finalIdsByColumn } = resolveFootnoteAssignments(layout);
|
|
38248
|
+
let { blocks: finalBlocks, measuresById: finalMeasuresById } = await measureFootnoteBlocks(
|
|
38249
|
+
collectFootnoteIdsByColumn(finalIdsByColumn)
|
|
38250
|
+
);
|
|
38251
|
+
let finalPlan = computeFootnoteLayoutPlan(
|
|
38252
|
+
layout,
|
|
38253
|
+
finalIdsByColumn,
|
|
38254
|
+
finalMeasuresById,
|
|
38255
|
+
reserves,
|
|
38256
|
+
finalPageColumns
|
|
38257
|
+
);
|
|
38258
|
+
const finalReserves = finalPlan.reserves;
|
|
38259
|
+
let reservesAppliedToLayout = reserves;
|
|
38260
|
+
const reservesDiffer = finalReserves.length !== reserves.length || finalReserves.some((h2, i) => (reserves[i] ?? 0) !== h2) || reserves.some((h2, i) => (finalReserves[i] ?? 0) !== h2);
|
|
38261
|
+
if (reservesDiffer) {
|
|
38262
|
+
layout = layoutDocument(currentBlocks, currentMeasures, {
|
|
38263
|
+
...options,
|
|
38264
|
+
footnoteReservedByPageIndex: finalReserves,
|
|
38265
|
+
headerContentHeights,
|
|
38266
|
+
footerContentHeights,
|
|
38267
|
+
remeasureParagraph: (block, maxWidth, firstLineIndent) => remeasureParagraph(block, maxWidth, firstLineIndent)
|
|
38268
|
+
});
|
|
38269
|
+
reservesAppliedToLayout = finalReserves;
|
|
38270
|
+
({ columns: finalPageColumns, idsByColumn: finalIdsByColumn } = resolveFootnoteAssignments(layout));
|
|
38271
|
+
({ blocks: finalBlocks, measuresById: finalMeasuresById } = await measureFootnoteBlocks(
|
|
38272
|
+
collectFootnoteIdsByColumn(finalIdsByColumn)
|
|
38273
|
+
));
|
|
38274
|
+
finalPlan = computeFootnoteLayoutPlan(
|
|
38275
|
+
layout,
|
|
38276
|
+
finalIdsByColumn,
|
|
38277
|
+
finalMeasuresById,
|
|
38278
|
+
reservesAppliedToLayout,
|
|
38279
|
+
finalPageColumns
|
|
38280
|
+
);
|
|
38281
|
+
}
|
|
38282
|
+
const blockById = /* @__PURE__ */ new Map();
|
|
38283
|
+
finalBlocks.forEach((block) => {
|
|
38284
|
+
blockById.set(block.id, block);
|
|
38285
|
+
});
|
|
38286
|
+
const injected = injectFragments(
|
|
38287
|
+
layout,
|
|
38288
|
+
finalPlan,
|
|
38289
|
+
finalMeasuresById,
|
|
38290
|
+
reservesAppliedToLayout,
|
|
38291
|
+
blockById,
|
|
38292
|
+
finalPageColumns
|
|
38293
|
+
);
|
|
38294
|
+
const alignedBlocks = [];
|
|
38295
|
+
const alignedMeasures = [];
|
|
38296
|
+
finalBlocks.forEach((block) => {
|
|
38297
|
+
const measure = finalMeasuresById.get(block.id);
|
|
38298
|
+
if (!measure) return;
|
|
38299
|
+
alignedBlocks.push(block);
|
|
38300
|
+
alignedMeasures.push(measure);
|
|
38301
|
+
});
|
|
38302
|
+
extraBlocks = injected ? alignedBlocks.concat(injected.decorativeBlocks) : alignedBlocks;
|
|
38303
|
+
extraMeasures = injected ? alignedMeasures.concat(injected.decorativeMeasures) : alignedMeasures;
|
|
38304
|
+
}
|
|
38305
|
+
}
|
|
38306
|
+
}
|
|
37209
38307
|
let headers;
|
|
37210
38308
|
let footers;
|
|
37211
38309
|
if (headerFooter?.constraints && (headerFooter.headerBlocks || headerFooter.footerBlocks)) {
|
|
@@ -37266,7 +38364,9 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
|
|
|
37266
38364
|
measures: currentMeasures,
|
|
37267
38365
|
dirty,
|
|
37268
38366
|
headers,
|
|
37269
|
-
footers
|
|
38367
|
+
footers,
|
|
38368
|
+
extraBlocks,
|
|
38369
|
+
extraMeasures
|
|
37270
38370
|
};
|
|
37271
38371
|
}
|
|
37272
38372
|
const DEFAULT_PAGE_SIZE$1 = { w: 612, h: 792 };
|
|
@@ -41305,6 +42405,7 @@ const ATOMIC_INLINE_TYPES = /* @__PURE__ */ new Set([
|
|
|
41305
42405
|
"lineBreak",
|
|
41306
42406
|
"page-number",
|
|
41307
42407
|
"total-page-number",
|
|
42408
|
+
"footnoteReference",
|
|
41308
42409
|
"passthroughInline",
|
|
41309
42410
|
"bookmarkEnd"
|
|
41310
42411
|
]);
|
|
@@ -46329,6 +47430,28 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
|
|
|
46329
47430
|
let partIndex = 0;
|
|
46330
47431
|
let tabOrdinal = 0;
|
|
46331
47432
|
let suppressedByVanish = false;
|
|
47433
|
+
const toSuperscriptDigits2 = (value) => {
|
|
47434
|
+
const map3 = {
|
|
47435
|
+
"0": "⁰",
|
|
47436
|
+
"1": "¹",
|
|
47437
|
+
"2": "²",
|
|
47438
|
+
"3": "³",
|
|
47439
|
+
"4": "⁴",
|
|
47440
|
+
"5": "⁵",
|
|
47441
|
+
"6": "⁶",
|
|
47442
|
+
"7": "⁷",
|
|
47443
|
+
"8": "⁸",
|
|
47444
|
+
"9": "⁹"
|
|
47445
|
+
};
|
|
47446
|
+
return String(value ?? "").split("").map((ch) => map3[ch] ?? ch).join("");
|
|
47447
|
+
};
|
|
47448
|
+
const resolveFootnoteDisplayNumber2 = (id) => {
|
|
47449
|
+
const key2 = id == null ? null : String(id);
|
|
47450
|
+
if (!key2) return null;
|
|
47451
|
+
const mapping = converterContext?.footnoteNumberById;
|
|
47452
|
+
const mapped = mapping && typeof mapping === "object" ? mapping[key2] : void 0;
|
|
47453
|
+
return typeof mapped === "number" && Number.isFinite(mapped) && mapped > 0 ? mapped : null;
|
|
47454
|
+
};
|
|
46332
47455
|
const nextId = () => partIndex === 0 ? baseBlockId : `${baseBlockId}-${partIndex}`;
|
|
46333
47456
|
const attachAnchorParagraphId = (block, anchorParagraphId) => {
|
|
46334
47457
|
const applicableKinds = /* @__PURE__ */ new Set(["drawing", "image", "table"]);
|
|
@@ -46374,6 +47497,34 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
|
|
|
46374
47497
|
});
|
|
46375
47498
|
};
|
|
46376
47499
|
const visitNode = (node, inheritedMarks = [], activeSdt, activeRunStyleId = null, activeRunProperties, activeHidden = false) => {
|
|
47500
|
+
if (node.type === "footnoteReference") {
|
|
47501
|
+
const mergedMarks = [...node.marks ?? [], ...inheritedMarks ?? []];
|
|
47502
|
+
const refPos = positions.get(node);
|
|
47503
|
+
const id = node.attrs?.id;
|
|
47504
|
+
const displayId = resolveFootnoteDisplayNumber2(id) ?? id ?? "*";
|
|
47505
|
+
const displayText = toSuperscriptDigits2(displayId);
|
|
47506
|
+
const run = textNodeToRun(
|
|
47507
|
+
{ type: "text", text: displayText },
|
|
47508
|
+
positions,
|
|
47509
|
+
defaultFont,
|
|
47510
|
+
defaultSize,
|
|
47511
|
+
[],
|
|
47512
|
+
// marks applied after linked styles/base defaults
|
|
47513
|
+
activeSdt,
|
|
47514
|
+
hyperlinkConfig,
|
|
47515
|
+
themeColors
|
|
47516
|
+
);
|
|
47517
|
+
const inlineStyleId = getInlineStyleId(mergedMarks);
|
|
47518
|
+
applyRunStyles2(run, inlineStyleId, activeRunStyleId);
|
|
47519
|
+
applyBaseRunDefaults(run, baseRunDefaults, defaultFont, defaultSize);
|
|
47520
|
+
applyMarksToRun(run, mergedMarks, hyperlinkConfig, themeColors);
|
|
47521
|
+
if (refPos) {
|
|
47522
|
+
run.pmStart = refPos.start;
|
|
47523
|
+
run.pmEnd = refPos.end;
|
|
47524
|
+
}
|
|
47525
|
+
currentRuns.push(run);
|
|
47526
|
+
return;
|
|
47527
|
+
}
|
|
46377
47528
|
if (activeHidden && node.type !== "run") {
|
|
46378
47529
|
suppressedByVanish = true;
|
|
46379
47530
|
return;
|
|
@@ -54774,12 +55925,36 @@ class PresentationEditor extends EventEmitter {
|
|
|
54774
55925
|
const sectionMetadata = [];
|
|
54775
55926
|
let blocks;
|
|
54776
55927
|
let bookmarks = /* @__PURE__ */ new Map();
|
|
55928
|
+
let converterContext = void 0;
|
|
54777
55929
|
try {
|
|
54778
55930
|
const converter2 = this.#editor.converter;
|
|
54779
|
-
const
|
|
55931
|
+
const footnoteNumberById = {};
|
|
55932
|
+
try {
|
|
55933
|
+
const seen = /* @__PURE__ */ new Set();
|
|
55934
|
+
let counter = 1;
|
|
55935
|
+
this.#editor?.state?.doc?.descendants?.((node) => {
|
|
55936
|
+
if (node?.type?.name !== "footnoteReference") return;
|
|
55937
|
+
const rawId = node?.attrs?.id;
|
|
55938
|
+
if (rawId == null) return;
|
|
55939
|
+
const key2 = String(rawId);
|
|
55940
|
+
if (!key2 || seen.has(key2)) return;
|
|
55941
|
+
seen.add(key2);
|
|
55942
|
+
footnoteNumberById[key2] = counter;
|
|
55943
|
+
counter += 1;
|
|
55944
|
+
});
|
|
55945
|
+
} catch {
|
|
55946
|
+
}
|
|
55947
|
+
try {
|
|
55948
|
+
if (converter2 && typeof converter2 === "object") {
|
|
55949
|
+
converter2["footnoteNumberById"] = footnoteNumberById;
|
|
55950
|
+
}
|
|
55951
|
+
} catch {
|
|
55952
|
+
}
|
|
55953
|
+
converterContext = converter2 ? {
|
|
54780
55954
|
docx: converter2.convertedXml,
|
|
54781
55955
|
numbering: converter2.numbering,
|
|
54782
|
-
linkedStyles: converter2.linkedStyles
|
|
55956
|
+
linkedStyles: converter2.linkedStyles,
|
|
55957
|
+
...Object.keys(footnoteNumberById).length ? { footnoteNumberById } : {}
|
|
54783
55958
|
} : void 0;
|
|
54784
55959
|
const atomNodeTypes = getAtomNodeTypes(this.#editor?.schema ?? null);
|
|
54785
55960
|
const positionMap = this.#editor?.state?.doc && docJson ? buildPositionMapFromPmDoc(this.#editor.state.doc, docJson) : null;
|
|
@@ -54807,13 +55982,20 @@ class PresentationEditor extends EventEmitter {
|
|
|
54807
55982
|
this.#handleLayoutError("render", new Error("toFlowBlocks returned undefined blocks"));
|
|
54808
55983
|
return;
|
|
54809
55984
|
}
|
|
54810
|
-
const
|
|
55985
|
+
const baseLayoutOptions = this.#resolveLayoutOptions(blocks, sectionMetadata);
|
|
55986
|
+
const footnotesLayoutInput = this.#buildFootnotesLayoutInput({
|
|
55987
|
+
converterContext,
|
|
55988
|
+
themeColors: this.#editor?.converter?.themeColors ?? void 0
|
|
55989
|
+
});
|
|
55990
|
+
const layoutOptions = footnotesLayoutInput ? { ...baseLayoutOptions, footnotes: footnotesLayoutInput } : baseLayoutOptions;
|
|
54811
55991
|
const previousBlocks = this.#layoutState.blocks;
|
|
54812
55992
|
const previousLayout = this.#layoutState.layout;
|
|
54813
55993
|
let layout;
|
|
54814
55994
|
let measures;
|
|
54815
55995
|
let headerLayouts;
|
|
54816
55996
|
let footerLayouts;
|
|
55997
|
+
let extraBlocks;
|
|
55998
|
+
let extraMeasures;
|
|
54817
55999
|
const headerFooterInput = this.#buildHeaderFooterInput();
|
|
54818
56000
|
try {
|
|
54819
56001
|
const result = await incrementalLayout(
|
|
@@ -54837,6 +56019,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
54837
56019
|
return;
|
|
54838
56020
|
}
|
|
54839
56021
|
({ layout, measures } = result);
|
|
56022
|
+
extraBlocks = Array.isArray(result.extraBlocks) ? result.extraBlocks : void 0;
|
|
56023
|
+
extraMeasures = Array.isArray(result.extraMeasures) ? result.extraMeasures : void 0;
|
|
54840
56024
|
layout.pageGap = this.#getEffectivePageGap();
|
|
54841
56025
|
layout.layoutEpoch = layoutEpoch;
|
|
54842
56026
|
headerLayouts = result.headers;
|
|
@@ -54896,6 +56080,10 @@ class PresentationEditor extends EventEmitter {
|
|
|
54896
56080
|
footerBlocks.push(...rIdResult.blocks);
|
|
54897
56081
|
footerMeasures.push(...rIdResult.measures);
|
|
54898
56082
|
}
|
|
56083
|
+
if (extraBlocks && extraMeasures && extraBlocks.length === extraMeasures.length && extraBlocks.length > 0) {
|
|
56084
|
+
footerBlocks.push(...extraBlocks);
|
|
56085
|
+
footerMeasures.push(...extraMeasures);
|
|
56086
|
+
}
|
|
54899
56087
|
painter.setData?.(
|
|
54900
56088
|
blocks,
|
|
54901
56089
|
measures,
|
|
@@ -55182,6 +56370,123 @@ class PresentationEditor extends EventEmitter {
|
|
|
55182
56370
|
sectionMetadata
|
|
55183
56371
|
};
|
|
55184
56372
|
}
|
|
56373
|
+
#buildFootnotesLayoutInput({
|
|
56374
|
+
converterContext,
|
|
56375
|
+
themeColors
|
|
56376
|
+
}) {
|
|
56377
|
+
const footnoteNumberById = converterContext?.footnoteNumberById;
|
|
56378
|
+
const toSuperscriptDigits2 = (value) => {
|
|
56379
|
+
const map3 = {
|
|
56380
|
+
"0": "⁰",
|
|
56381
|
+
"1": "¹",
|
|
56382
|
+
"2": "²",
|
|
56383
|
+
"3": "³",
|
|
56384
|
+
"4": "⁴",
|
|
56385
|
+
"5": "⁵",
|
|
56386
|
+
"6": "⁶",
|
|
56387
|
+
"7": "⁷",
|
|
56388
|
+
"8": "⁸",
|
|
56389
|
+
"9": "⁹"
|
|
56390
|
+
};
|
|
56391
|
+
const str = String(value ?? "");
|
|
56392
|
+
return str.split("").map((ch) => map3[ch] ?? ch).join("");
|
|
56393
|
+
};
|
|
56394
|
+
const ensureFootnoteMarker = (blocks, id) => {
|
|
56395
|
+
const displayNumberRaw = footnoteNumberById && typeof footnoteNumberById === "object" ? footnoteNumberById[id] : void 0;
|
|
56396
|
+
const displayNumber = typeof displayNumberRaw === "number" && Number.isFinite(displayNumberRaw) && displayNumberRaw > 0 ? displayNumberRaw : 1;
|
|
56397
|
+
const firstParagraph = blocks.find((b2) => b2?.kind === "paragraph");
|
|
56398
|
+
if (!firstParagraph) return;
|
|
56399
|
+
const runs = Array.isArray(firstParagraph.runs) ? firstParagraph.runs : [];
|
|
56400
|
+
const markerText = toSuperscriptDigits2(displayNumber);
|
|
56401
|
+
const baseRun = runs.find((r2) => {
|
|
56402
|
+
const dataAttrs = r2.dataAttrs;
|
|
56403
|
+
if (dataAttrs?.["data-sd-footnote-number"]) return false;
|
|
56404
|
+
const pmStart = r2.pmStart;
|
|
56405
|
+
const pmEnd = r2.pmEnd;
|
|
56406
|
+
return typeof pmStart === "number" && Number.isFinite(pmStart) && typeof pmEnd === "number" && Number.isFinite(pmEnd);
|
|
56407
|
+
});
|
|
56408
|
+
const markerPmStart = baseRun?.pmStart ?? null;
|
|
56409
|
+
const markerPmEnd = markerPmStart != null ? baseRun?.pmEnd != null ? Math.max(markerPmStart, Math.min(baseRun.pmEnd, markerPmStart + markerText.length)) : markerPmStart + markerText.length : null;
|
|
56410
|
+
const alreadyHasMarker = runs.some((r2) => {
|
|
56411
|
+
const dataAttrs = r2.dataAttrs;
|
|
56412
|
+
return Boolean(dataAttrs?.["data-sd-footnote-number"]);
|
|
56413
|
+
});
|
|
56414
|
+
if (alreadyHasMarker) {
|
|
56415
|
+
if (markerPmStart != null && markerPmEnd != null) {
|
|
56416
|
+
const markerRun2 = runs.find((r2) => {
|
|
56417
|
+
const dataAttrs = r2.dataAttrs;
|
|
56418
|
+
return Boolean(dataAttrs?.["data-sd-footnote-number"]);
|
|
56419
|
+
});
|
|
56420
|
+
if (markerRun2) {
|
|
56421
|
+
if (markerRun2.pmStart == null) markerRun2.pmStart = markerPmStart;
|
|
56422
|
+
if (markerRun2.pmEnd == null) markerRun2.pmEnd = markerPmEnd;
|
|
56423
|
+
}
|
|
56424
|
+
}
|
|
56425
|
+
return;
|
|
56426
|
+
}
|
|
56427
|
+
const firstTextRun = runs.find((r2) => typeof r2.text === "string");
|
|
56428
|
+
const markerRun = {
|
|
56429
|
+
kind: "text",
|
|
56430
|
+
text: markerText,
|
|
56431
|
+
dataAttrs: {
|
|
56432
|
+
"data-sd-footnote-number": "true"
|
|
56433
|
+
},
|
|
56434
|
+
...markerPmStart != null ? { pmStart: markerPmStart } : {},
|
|
56435
|
+
...markerPmEnd != null ? { pmEnd: markerPmEnd } : {}
|
|
56436
|
+
};
|
|
56437
|
+
markerRun.fontFamily = typeof firstTextRun?.fontFamily === "string" ? firstTextRun.fontFamily : "Arial";
|
|
56438
|
+
markerRun.fontSize = typeof firstTextRun?.fontSize === "number" && Number.isFinite(firstTextRun.fontSize) ? firstTextRun.fontSize : 12;
|
|
56439
|
+
if (firstTextRun?.color != null) markerRun.color = firstTextRun.color;
|
|
56440
|
+
runs.unshift(markerRun);
|
|
56441
|
+
firstParagraph.runs = runs;
|
|
56442
|
+
};
|
|
56443
|
+
const state = this.#editor?.state;
|
|
56444
|
+
if (!state) return null;
|
|
56445
|
+
const converter = this.#editor?.converter;
|
|
56446
|
+
const importedFootnotes = Array.isArray(converter?.footnotes) ? converter.footnotes : [];
|
|
56447
|
+
if (importedFootnotes.length === 0) return null;
|
|
56448
|
+
const refs = [];
|
|
56449
|
+
const idsInUse = /* @__PURE__ */ new Set();
|
|
56450
|
+
state.doc.descendants((node, pos) => {
|
|
56451
|
+
if (node.type?.name !== "footnoteReference") return;
|
|
56452
|
+
const id = node.attrs?.id;
|
|
56453
|
+
if (id == null) return;
|
|
56454
|
+
const key2 = String(id);
|
|
56455
|
+
const insidePos = Math.min(pos + 1, state.doc.content.size);
|
|
56456
|
+
refs.push({ id: key2, pos: insidePos });
|
|
56457
|
+
idsInUse.add(key2);
|
|
56458
|
+
});
|
|
56459
|
+
if (refs.length === 0) return null;
|
|
56460
|
+
const blocksById = /* @__PURE__ */ new Map();
|
|
56461
|
+
idsInUse.forEach((id) => {
|
|
56462
|
+
const entry = importedFootnotes.find((f) => String(f?.id) === id);
|
|
56463
|
+
const content = entry?.content;
|
|
56464
|
+
if (!Array.isArray(content) || content.length === 0) return;
|
|
56465
|
+
try {
|
|
56466
|
+
const clonedContent = JSON.parse(JSON.stringify(content));
|
|
56467
|
+
const footnoteDoc = { type: "doc", content: clonedContent };
|
|
56468
|
+
const result = toFlowBlocks(footnoteDoc, {
|
|
56469
|
+
blockIdPrefix: `footnote-${id}-`,
|
|
56470
|
+
enableRichHyperlinks: true,
|
|
56471
|
+
themeColors,
|
|
56472
|
+
converterContext
|
|
56473
|
+
});
|
|
56474
|
+
if (result?.blocks?.length) {
|
|
56475
|
+
ensureFootnoteMarker(result.blocks, id);
|
|
56476
|
+
blocksById.set(id, result.blocks);
|
|
56477
|
+
}
|
|
56478
|
+
} catch {
|
|
56479
|
+
}
|
|
56480
|
+
});
|
|
56481
|
+
if (blocksById.size === 0) return null;
|
|
56482
|
+
return {
|
|
56483
|
+
refs,
|
|
56484
|
+
blocksById,
|
|
56485
|
+
gap: 2,
|
|
56486
|
+
topPadding: 4,
|
|
56487
|
+
dividerHeight: 1
|
|
56488
|
+
};
|
|
56489
|
+
}
|
|
55185
56490
|
#buildHeaderFooterInput() {
|
|
55186
56491
|
if (!this.#headerFooterAdapter) {
|
|
55187
56492
|
return null;
|
|
@@ -55371,7 +56676,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
55371
56676
|
const fragments2 = slotPage2.fragments ?? [];
|
|
55372
56677
|
const pageHeight2 = page?.size?.h ?? layout.pageSize?.h ?? this.#layoutOptions.pageSize?.h ?? DEFAULT_PAGE_SIZE.h;
|
|
55373
56678
|
const margins2 = pageMargins ?? layout.pages[0]?.margins ?? this.#layoutOptions.margins ?? DEFAULT_MARGINS;
|
|
55374
|
-
const
|
|
56679
|
+
const decorationMargins2 = kind === "footer" ? this.#stripFootnoteReserveFromBottomMargin(margins2, page ?? null) : margins2;
|
|
56680
|
+
const box2 = this.#computeDecorationBox(kind, decorationMargins2, pageHeight2);
|
|
55375
56681
|
const rawLayoutHeight2 = rIdLayout.layout.height ?? 0;
|
|
55376
56682
|
const metrics2 = this.#computeHeaderFooterMetrics(
|
|
55377
56683
|
kind,
|
|
@@ -55422,7 +56728,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
55422
56728
|
const fragments = slotPage.fragments ?? [];
|
|
55423
56729
|
const pageHeight = page?.size?.h ?? layout.pageSize?.h ?? this.#layoutOptions.pageSize?.h ?? DEFAULT_PAGE_SIZE.h;
|
|
55424
56730
|
const margins = pageMargins ?? layout.pages[0]?.margins ?? this.#layoutOptions.margins ?? DEFAULT_MARGINS;
|
|
55425
|
-
const
|
|
56731
|
+
const decorationMargins = kind === "footer" ? this.#stripFootnoteReserveFromBottomMargin(margins, page ?? null) : margins;
|
|
56732
|
+
const box = this.#computeDecorationBox(kind, decorationMargins, pageHeight);
|
|
55426
56733
|
const rawLayoutHeight = variant.layout.height ?? 0;
|
|
55427
56734
|
const metrics = this.#computeHeaderFooterMetrics(kind, rawLayoutHeight, box, pageHeight, margins.footer ?? 0);
|
|
55428
56735
|
const fallbackId = this.#headerFooterManager?.getVariantId(kind, headerFooterType);
|
|
@@ -55509,6 +56816,16 @@ class PresentationEditor extends EventEmitter {
|
|
|
55509
56816
|
return { x: left2, width, height, offset: offset2 };
|
|
55510
56817
|
}
|
|
55511
56818
|
}
|
|
56819
|
+
#stripFootnoteReserveFromBottomMargin(pageMargins, page) {
|
|
56820
|
+
const reserveRaw = page?.footnoteReserved;
|
|
56821
|
+
const reserve = typeof reserveRaw === "number" && Number.isFinite(reserveRaw) && reserveRaw > 0 ? reserveRaw : 0;
|
|
56822
|
+
if (!reserve) return pageMargins;
|
|
56823
|
+
const bottomRaw = pageMargins.bottom;
|
|
56824
|
+
const bottom2 = typeof bottomRaw === "number" && Number.isFinite(bottomRaw) ? bottomRaw : 0;
|
|
56825
|
+
const nextBottom = Math.max(0, bottom2 - reserve);
|
|
56826
|
+
if (nextBottom === bottom2) return pageMargins;
|
|
56827
|
+
return { ...pageMargins, bottom: nextBottom };
|
|
56828
|
+
}
|
|
55512
56829
|
/**
|
|
55513
56830
|
* Computes the expected header/footer section type for a page based on document configuration.
|
|
55514
56831
|
*
|
|
@@ -55573,7 +56890,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
55573
56890
|
height: headerPayload?.hitRegion?.height ?? headerBox.height
|
|
55574
56891
|
});
|
|
55575
56892
|
const footerPayload = this.#footerDecorationProvider?.(page.number, margins, page);
|
|
55576
|
-
const
|
|
56893
|
+
const footerBoxMargins = this.#stripFootnoteReserveFromBottomMargin(margins, page);
|
|
56894
|
+
const footerBox = this.#computeDecorationBox("footer", footerBoxMargins, actualPageHeight);
|
|
55577
56895
|
this.#footerRegions.set(pageIndex, {
|
|
55578
56896
|
kind: "footer",
|
|
55579
56897
|
headerId: footerPayload?.headerId,
|
|
@@ -61474,6 +62792,109 @@ const CommentsMark = Mark.create({
|
|
|
61474
62792
|
return [CommentMarkName$1, Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
|
|
61475
62793
|
}
|
|
61476
62794
|
});
|
|
62795
|
+
const toSuperscriptDigits = (value) => {
|
|
62796
|
+
const map3 = {
|
|
62797
|
+
0: "⁰",
|
|
62798
|
+
1: "¹",
|
|
62799
|
+
2: "²",
|
|
62800
|
+
3: "³",
|
|
62801
|
+
4: "⁴",
|
|
62802
|
+
5: "⁵",
|
|
62803
|
+
6: "⁶",
|
|
62804
|
+
7: "⁷",
|
|
62805
|
+
8: "⁸",
|
|
62806
|
+
9: "⁹"
|
|
62807
|
+
};
|
|
62808
|
+
return String(value ?? "").split("").map((ch) => map3[ch] ?? ch).join("");
|
|
62809
|
+
};
|
|
62810
|
+
const resolveFootnoteDisplayNumber = (editor, id) => {
|
|
62811
|
+
const key2 = id == null ? null : String(id);
|
|
62812
|
+
if (!key2) return null;
|
|
62813
|
+
const map3 = editor?.converter?.footnoteNumberById;
|
|
62814
|
+
const mapped = map3 && typeof map3 === "object" ? map3[key2] : void 0;
|
|
62815
|
+
return typeof mapped === "number" && Number.isFinite(mapped) && mapped > 0 ? mapped : null;
|
|
62816
|
+
};
|
|
62817
|
+
class FootnoteReferenceNodeView {
|
|
62818
|
+
constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
|
|
62819
|
+
this.node = node;
|
|
62820
|
+
this.getPos = getPos;
|
|
62821
|
+
this.editor = editor;
|
|
62822
|
+
this.dom = this.#renderDom(node, htmlAttributes);
|
|
62823
|
+
}
|
|
62824
|
+
#renderDom(node, htmlAttributes) {
|
|
62825
|
+
const el = document.createElement("sup");
|
|
62826
|
+
el.className = "sd-footnote-ref";
|
|
62827
|
+
el.setAttribute("contenteditable", "false");
|
|
62828
|
+
el.setAttribute("aria-label", "Footnote reference");
|
|
62829
|
+
Object.entries(htmlAttributes).forEach(([key2, value]) => {
|
|
62830
|
+
if (value != null && value !== false) {
|
|
62831
|
+
el.setAttribute(key2, String(value));
|
|
62832
|
+
}
|
|
62833
|
+
});
|
|
62834
|
+
const id = node?.attrs?.id;
|
|
62835
|
+
if (id != null) {
|
|
62836
|
+
el.setAttribute("data-footnote-id", String(id));
|
|
62837
|
+
const display = resolveFootnoteDisplayNumber(this.editor, id) ?? id;
|
|
62838
|
+
el.textContent = toSuperscriptDigits(display);
|
|
62839
|
+
} else {
|
|
62840
|
+
el.textContent = "*";
|
|
62841
|
+
}
|
|
62842
|
+
return el;
|
|
62843
|
+
}
|
|
62844
|
+
update(node) {
|
|
62845
|
+
const incomingType = node?.type?.name;
|
|
62846
|
+
const currentType = this.node?.type?.name;
|
|
62847
|
+
if (!incomingType || incomingType !== currentType) return false;
|
|
62848
|
+
this.node = node;
|
|
62849
|
+
const id = node?.attrs?.id;
|
|
62850
|
+
if (id != null) {
|
|
62851
|
+
this.dom.setAttribute("data-footnote-id", String(id));
|
|
62852
|
+
const display = resolveFootnoteDisplayNumber(this.editor, id) ?? id;
|
|
62853
|
+
this.dom.textContent = toSuperscriptDigits(display);
|
|
62854
|
+
} else {
|
|
62855
|
+
this.dom.removeAttribute("data-footnote-id");
|
|
62856
|
+
this.dom.textContent = "*";
|
|
62857
|
+
}
|
|
62858
|
+
return true;
|
|
62859
|
+
}
|
|
62860
|
+
}
|
|
62861
|
+
const FootnoteReference = Node$1.create({
|
|
62862
|
+
name: "footnoteReference",
|
|
62863
|
+
group: "inline",
|
|
62864
|
+
inline: true,
|
|
62865
|
+
atom: true,
|
|
62866
|
+
selectable: false,
|
|
62867
|
+
draggable: false,
|
|
62868
|
+
addOptions() {
|
|
62869
|
+
return {
|
|
62870
|
+
htmlAttributes: {
|
|
62871
|
+
"data-footnote-ref": "true"
|
|
62872
|
+
}
|
|
62873
|
+
};
|
|
62874
|
+
},
|
|
62875
|
+
addAttributes() {
|
|
62876
|
+
return {
|
|
62877
|
+
id: {
|
|
62878
|
+
default: null
|
|
62879
|
+
},
|
|
62880
|
+
customMarkFollows: {
|
|
62881
|
+
default: null
|
|
62882
|
+
}
|
|
62883
|
+
};
|
|
62884
|
+
},
|
|
62885
|
+
addNodeView() {
|
|
62886
|
+
return ({ node, editor, getPos, decorations }) => {
|
|
62887
|
+
const htmlAttributes = this.options.htmlAttributes;
|
|
62888
|
+
return new FootnoteReferenceNodeView(node, getPos, decorations, editor, htmlAttributes);
|
|
62889
|
+
};
|
|
62890
|
+
},
|
|
62891
|
+
parseDOM() {
|
|
62892
|
+
return [{ tag: "sup[data-footnote-id]" }];
|
|
62893
|
+
},
|
|
62894
|
+
renderDOM({ htmlAttributes }) {
|
|
62895
|
+
return ["sup", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
|
|
62896
|
+
}
|
|
62897
|
+
});
|
|
61477
62898
|
let cache$1 = /* @__PURE__ */ new WeakMap();
|
|
61478
62899
|
function getParagraphContext(paragraph, startPos, helpers2, revision, compute) {
|
|
61479
62900
|
const cached = cache$1.get(paragraph);
|
|
@@ -74286,14 +75707,317 @@ function getMatchHighlights(state) {
|
|
|
74286
75707
|
let search2 = searchKey.getState(state);
|
|
74287
75708
|
return search2 ? search2.deco : DecorationSet.empty;
|
|
74288
75709
|
}
|
|
74289
|
-
|
|
74290
|
-
|
|
74291
|
-
|
|
75710
|
+
const BLOCK_SEPARATOR = "\n";
|
|
75711
|
+
const ATOM_PLACEHOLDER = "";
|
|
75712
|
+
class SearchIndex {
|
|
75713
|
+
/** @type {string} */
|
|
75714
|
+
text = "";
|
|
75715
|
+
/** @type {Segment[]} */
|
|
75716
|
+
segments = [];
|
|
75717
|
+
/** @type {boolean} */
|
|
75718
|
+
valid = false;
|
|
75719
|
+
/** @type {number} */
|
|
75720
|
+
docSize = 0;
|
|
75721
|
+
/**
|
|
75722
|
+
* Build the search index from a ProseMirror document.
|
|
75723
|
+
* Uses doc.textBetween for the flattened string and walks
|
|
75724
|
+
* the document to build the segment offset map.
|
|
75725
|
+
*
|
|
75726
|
+
* @param {import('prosemirror-model').Node} doc - The ProseMirror document
|
|
75727
|
+
*/
|
|
75728
|
+
build(doc2) {
|
|
75729
|
+
this.text = doc2.textBetween(0, doc2.content.size, BLOCK_SEPARATOR, ATOM_PLACEHOLDER);
|
|
75730
|
+
this.segments = [];
|
|
75731
|
+
this.docSize = doc2.content.size;
|
|
75732
|
+
let offset2 = 0;
|
|
75733
|
+
this.#walkNodeContent(doc2, 0, offset2, (segment) => {
|
|
75734
|
+
this.segments.push(segment);
|
|
75735
|
+
offset2 = segment.offsetEnd;
|
|
75736
|
+
});
|
|
75737
|
+
this.valid = true;
|
|
75738
|
+
}
|
|
75739
|
+
/**
|
|
75740
|
+
* Walk the content of a node to build segments.
|
|
75741
|
+
* This method processes the children of a node, given the position
|
|
75742
|
+
* where the node's content starts.
|
|
75743
|
+
*
|
|
75744
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
75745
|
+
* @param {number} contentStart - Document position where this node's content starts
|
|
75746
|
+
* @param {number} offset - Current offset in flattened string
|
|
75747
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
75748
|
+
* @returns {number} The new offset after processing this node's content
|
|
75749
|
+
*/
|
|
75750
|
+
#walkNodeContent(node, contentStart, offset2, addSegment) {
|
|
75751
|
+
let currentOffset = offset2;
|
|
75752
|
+
let isFirstChild = true;
|
|
75753
|
+
node.forEach((child, childContentOffset) => {
|
|
75754
|
+
const childDocPos = contentStart + childContentOffset;
|
|
75755
|
+
if (child.isBlock && !isFirstChild) {
|
|
75756
|
+
addSegment({
|
|
75757
|
+
offsetStart: currentOffset,
|
|
75758
|
+
offsetEnd: currentOffset + 1,
|
|
75759
|
+
docFrom: childDocPos,
|
|
75760
|
+
docTo: childDocPos,
|
|
75761
|
+
kind: "blockSep"
|
|
75762
|
+
});
|
|
75763
|
+
currentOffset += 1;
|
|
75764
|
+
}
|
|
75765
|
+
currentOffset = this.#walkNode(child, childDocPos, currentOffset, addSegment);
|
|
75766
|
+
isFirstChild = false;
|
|
75767
|
+
});
|
|
75768
|
+
return currentOffset;
|
|
75769
|
+
}
|
|
75770
|
+
/**
|
|
75771
|
+
* Recursively walk a node and its descendants to build segments.
|
|
75772
|
+
*
|
|
75773
|
+
* @param {import('prosemirror-model').Node} node - Current node
|
|
75774
|
+
* @param {number} docPos - Document position at start of this node
|
|
75775
|
+
* @param {number} offset - Current offset in flattened string
|
|
75776
|
+
* @param {(segment: Segment) => void} addSegment - Callback to add a segment
|
|
75777
|
+
* @returns {number} The new offset after processing this node
|
|
75778
|
+
*/
|
|
75779
|
+
#walkNode(node, docPos, offset2, addSegment) {
|
|
75780
|
+
if (node.isText) {
|
|
75781
|
+
const text = node.text || "";
|
|
75782
|
+
if (text.length > 0) {
|
|
75783
|
+
addSegment({
|
|
75784
|
+
offsetStart: offset2,
|
|
75785
|
+
offsetEnd: offset2 + text.length,
|
|
75786
|
+
docFrom: docPos,
|
|
75787
|
+
docTo: docPos + text.length,
|
|
75788
|
+
kind: "text"
|
|
75789
|
+
});
|
|
75790
|
+
return offset2 + text.length;
|
|
75791
|
+
}
|
|
75792
|
+
return offset2;
|
|
75793
|
+
}
|
|
75794
|
+
if (node.isLeaf) {
|
|
75795
|
+
if (node.type.name === "hard_break") {
|
|
75796
|
+
addSegment({
|
|
75797
|
+
offsetStart: offset2,
|
|
75798
|
+
offsetEnd: offset2 + 1,
|
|
75799
|
+
docFrom: docPos,
|
|
75800
|
+
docTo: docPos + node.nodeSize,
|
|
75801
|
+
kind: "hardBreak"
|
|
75802
|
+
});
|
|
75803
|
+
return offset2 + 1;
|
|
75804
|
+
}
|
|
75805
|
+
addSegment({
|
|
75806
|
+
offsetStart: offset2,
|
|
75807
|
+
offsetEnd: offset2 + 1,
|
|
75808
|
+
docFrom: docPos,
|
|
75809
|
+
docTo: docPos + node.nodeSize,
|
|
75810
|
+
kind: "atom"
|
|
75811
|
+
});
|
|
75812
|
+
return offset2 + 1;
|
|
75813
|
+
}
|
|
75814
|
+
return this.#walkNodeContent(node, docPos + 1, offset2, addSegment);
|
|
75815
|
+
}
|
|
75816
|
+
/**
|
|
75817
|
+
* Mark the index as stale. It will be rebuilt on next search.
|
|
75818
|
+
*/
|
|
75819
|
+
invalidate() {
|
|
75820
|
+
this.valid = false;
|
|
75821
|
+
}
|
|
75822
|
+
/**
|
|
75823
|
+
* Check if the index needs rebuilding for the given document.
|
|
75824
|
+
*
|
|
75825
|
+
* @param {import('prosemirror-model').Node} doc - The document to check against
|
|
75826
|
+
* @returns {boolean} True if index is stale and needs rebuilding
|
|
75827
|
+
*/
|
|
75828
|
+
isStale(doc2) {
|
|
75829
|
+
return !this.valid || doc2.content.size !== this.docSize;
|
|
75830
|
+
}
|
|
75831
|
+
/**
|
|
75832
|
+
* Ensure the index is valid for the given document.
|
|
75833
|
+
* Rebuilds if stale.
|
|
75834
|
+
*
|
|
75835
|
+
* @param {import('prosemirror-model').Node} doc - The document
|
|
75836
|
+
*/
|
|
75837
|
+
ensureValid(doc2) {
|
|
75838
|
+
if (this.isStale(doc2)) {
|
|
75839
|
+
this.build(doc2);
|
|
75840
|
+
}
|
|
75841
|
+
}
|
|
75842
|
+
/**
|
|
75843
|
+
* Convert an offset range in the flattened string to document ranges.
|
|
75844
|
+
* Skips separator/atom segments and returns only text ranges.
|
|
75845
|
+
*
|
|
75846
|
+
* @param {number} start - Start offset in flattened string
|
|
75847
|
+
* @param {number} end - End offset in flattened string
|
|
75848
|
+
* @returns {DocRange[]} Array of document ranges (text segments only)
|
|
75849
|
+
*/
|
|
75850
|
+
offsetRangeToDocRanges(start2, end2) {
|
|
75851
|
+
const ranges = [];
|
|
75852
|
+
for (const segment of this.segments) {
|
|
75853
|
+
if (segment.offsetEnd <= start2) continue;
|
|
75854
|
+
if (segment.offsetStart >= end2) break;
|
|
75855
|
+
if (segment.kind !== "text") continue;
|
|
75856
|
+
const overlapStart = Math.max(start2, segment.offsetStart);
|
|
75857
|
+
const overlapEnd = Math.min(end2, segment.offsetEnd);
|
|
75858
|
+
if (overlapStart < overlapEnd) {
|
|
75859
|
+
const startInSegment = overlapStart - segment.offsetStart;
|
|
75860
|
+
const endInSegment = overlapEnd - segment.offsetStart;
|
|
75861
|
+
ranges.push({
|
|
75862
|
+
from: segment.docFrom + startInSegment,
|
|
75863
|
+
to: segment.docFrom + endInSegment
|
|
75864
|
+
});
|
|
75865
|
+
}
|
|
75866
|
+
}
|
|
75867
|
+
return ranges;
|
|
75868
|
+
}
|
|
75869
|
+
/**
|
|
75870
|
+
* Find the document position for a given offset in the flattened string.
|
|
75871
|
+
*
|
|
75872
|
+
* @param {number} offset - Offset in flattened string
|
|
75873
|
+
* @returns {number|null} Document position, or null if not found
|
|
75874
|
+
*/
|
|
75875
|
+
offsetToDocPos(offset2) {
|
|
75876
|
+
for (const segment of this.segments) {
|
|
75877
|
+
if (offset2 >= segment.offsetStart && offset2 < segment.offsetEnd) {
|
|
75878
|
+
if (segment.kind === "text") {
|
|
75879
|
+
return segment.docFrom + (offset2 - segment.offsetStart);
|
|
75880
|
+
}
|
|
75881
|
+
return segment.docFrom;
|
|
75882
|
+
}
|
|
75883
|
+
}
|
|
75884
|
+
if (this.segments.length > 0 && offset2 === this.segments[this.segments.length - 1].offsetEnd) {
|
|
75885
|
+
const lastSeg = this.segments[this.segments.length - 1];
|
|
75886
|
+
return lastSeg.docTo;
|
|
75887
|
+
}
|
|
75888
|
+
return null;
|
|
75889
|
+
}
|
|
75890
|
+
/**
|
|
75891
|
+
* Escape special regex characters in a string.
|
|
75892
|
+
*
|
|
75893
|
+
* @param {string} str - String to escape
|
|
75894
|
+
* @returns {string} Escaped string safe for use in RegExp
|
|
75895
|
+
*/
|
|
75896
|
+
static escapeRegex(str) {
|
|
75897
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75898
|
+
}
|
|
75899
|
+
/**
|
|
75900
|
+
* Convert a plain search string to a whitespace-flexible regex pattern.
|
|
75901
|
+
* This allows matching across paragraph boundaries.
|
|
75902
|
+
*
|
|
75903
|
+
* @param {string} searchString - The search string
|
|
75904
|
+
* @returns {string} Regex pattern string
|
|
75905
|
+
*/
|
|
75906
|
+
static toFlexiblePattern(searchString) {
|
|
75907
|
+
const parts = searchString.split(/\s+/).filter((part) => part.length > 0);
|
|
75908
|
+
if (parts.length === 0) return "";
|
|
75909
|
+
return parts.map((part) => SearchIndex.escapeRegex(part)).join("\\s+");
|
|
75910
|
+
}
|
|
75911
|
+
/**
|
|
75912
|
+
* Search the index for matches.
|
|
75913
|
+
*
|
|
75914
|
+
* @param {string | RegExp} pattern - Search pattern (string or regex)
|
|
75915
|
+
* @param {Object} options - Search options
|
|
75916
|
+
* @param {boolean} [options.caseSensitive=false] - Case sensitive search
|
|
75917
|
+
* @param {number} [options.maxMatches=1000] - Maximum number of matches to return
|
|
75918
|
+
* @returns {Array<{start: number, end: number, text: string}>} Array of matches with offsets
|
|
75919
|
+
*/
|
|
75920
|
+
search(pattern, options = {}) {
|
|
75921
|
+
const { caseSensitive = false, maxMatches = 1e3 } = options;
|
|
75922
|
+
const matches = [];
|
|
75923
|
+
let regex;
|
|
75924
|
+
if (pattern instanceof RegExp) {
|
|
75925
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
|
|
75926
|
+
regex = new RegExp(pattern.source, flags);
|
|
75927
|
+
} else if (typeof pattern === "string") {
|
|
75928
|
+
if (pattern.length === 0) return matches;
|
|
75929
|
+
const flexiblePattern = SearchIndex.toFlexiblePattern(pattern);
|
|
75930
|
+
if (flexiblePattern.length === 0) return matches;
|
|
75931
|
+
const flags = caseSensitive ? "g" : "gi";
|
|
75932
|
+
regex = new RegExp(flexiblePattern, flags);
|
|
75933
|
+
} else {
|
|
75934
|
+
return matches;
|
|
75935
|
+
}
|
|
75936
|
+
let match;
|
|
75937
|
+
while ((match = regex.exec(this.text)) !== null && matches.length < maxMatches) {
|
|
75938
|
+
matches.push({
|
|
75939
|
+
start: match.index,
|
|
75940
|
+
end: match.index + match[0].length,
|
|
75941
|
+
text: match[0]
|
|
75942
|
+
});
|
|
75943
|
+
if (match[0].length === 0) {
|
|
75944
|
+
regex.lastIndex++;
|
|
75945
|
+
}
|
|
75946
|
+
}
|
|
75947
|
+
return matches;
|
|
74292
75948
|
}
|
|
74293
|
-
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
74294
|
-
return tr.setMeta(searchKey, { query, range, highlight });
|
|
74295
75949
|
}
|
|
75950
|
+
const customSearchHighlightsKey = new PluginKey("customSearchHighlights");
|
|
74296
75951
|
const isRegExp = (value) => Object.prototype.toString.call(value) === "[object RegExp]";
|
|
75952
|
+
const resolveInlineTextPosition = (doc2, position, direction) => {
|
|
75953
|
+
const docSize = doc2.content.size;
|
|
75954
|
+
if (!Number.isFinite(position) || position < 0 || position > docSize) {
|
|
75955
|
+
return position;
|
|
75956
|
+
}
|
|
75957
|
+
const step = direction === "forward" ? 1 : -1;
|
|
75958
|
+
let current = position;
|
|
75959
|
+
let iterations = 0;
|
|
75960
|
+
while (iterations < 8) {
|
|
75961
|
+
iterations += 1;
|
|
75962
|
+
const resolved = doc2.resolve(current);
|
|
75963
|
+
const boundaryNode = direction === "forward" ? resolved.nodeAfter : resolved.nodeBefore;
|
|
75964
|
+
if (!boundaryNode) break;
|
|
75965
|
+
if (boundaryNode.isText) break;
|
|
75966
|
+
if (!boundaryNode.isInline || boundaryNode.isAtom || boundaryNode.content.size === 0) break;
|
|
75967
|
+
const next = current + step;
|
|
75968
|
+
if (next < 0 || next > docSize) break;
|
|
75969
|
+
current = next;
|
|
75970
|
+
const adjacent = doc2.resolve(current);
|
|
75971
|
+
const checkNode = direction === "forward" ? adjacent.nodeAfter : adjacent.nodeBefore;
|
|
75972
|
+
if (checkNode && checkNode.isText) break;
|
|
75973
|
+
}
|
|
75974
|
+
return current;
|
|
75975
|
+
};
|
|
75976
|
+
const resolveSearchRange = ({ doc: doc2, from: from3, to, expectedText, highlights }) => {
|
|
75977
|
+
const docSize = doc2.content.size;
|
|
75978
|
+
let resolvedFrom = Math.max(0, Math.min(from3, docSize));
|
|
75979
|
+
let resolvedTo = Math.max(0, Math.min(to, docSize));
|
|
75980
|
+
if (highlights) {
|
|
75981
|
+
const windowStart = Math.max(0, resolvedFrom - 4);
|
|
75982
|
+
const windowEnd = Math.min(docSize, resolvedTo + 4);
|
|
75983
|
+
const candidates = highlights.find(windowStart, windowEnd);
|
|
75984
|
+
if (candidates.length > 0) {
|
|
75985
|
+
let chosen = candidates[0];
|
|
75986
|
+
if (expectedText) {
|
|
75987
|
+
const matching = candidates.filter(
|
|
75988
|
+
(decoration) => doc2.textBetween(decoration.from, decoration.to) === expectedText
|
|
75989
|
+
);
|
|
75990
|
+
if (matching.length > 0) {
|
|
75991
|
+
chosen = matching[0];
|
|
75992
|
+
}
|
|
75993
|
+
}
|
|
75994
|
+
resolvedFrom = chosen.from;
|
|
75995
|
+
resolvedTo = chosen.to;
|
|
75996
|
+
}
|
|
75997
|
+
}
|
|
75998
|
+
const normalizedFrom = resolveInlineTextPosition(doc2, resolvedFrom, "forward");
|
|
75999
|
+
const normalizedTo = resolveInlineTextPosition(doc2, resolvedTo, "backward");
|
|
76000
|
+
if (Number.isFinite(normalizedFrom) && Number.isFinite(normalizedTo) && normalizedFrom <= normalizedTo) {
|
|
76001
|
+
resolvedFrom = normalizedFrom;
|
|
76002
|
+
resolvedTo = normalizedTo;
|
|
76003
|
+
}
|
|
76004
|
+
return { from: resolvedFrom, to: resolvedTo };
|
|
76005
|
+
};
|
|
76006
|
+
const getPositionTracker = (editor) => {
|
|
76007
|
+
if (!editor) return null;
|
|
76008
|
+
if (editor.positionTracker) return editor.positionTracker;
|
|
76009
|
+
const storageTracker = editor.storage?.positionTracker?.tracker;
|
|
76010
|
+
if (storageTracker) {
|
|
76011
|
+
editor.positionTracker = storageTracker;
|
|
76012
|
+
return storageTracker;
|
|
76013
|
+
}
|
|
76014
|
+
const tracker = new PositionTracker(editor);
|
|
76015
|
+
if (editor.storage?.positionTracker) {
|
|
76016
|
+
editor.storage.positionTracker.tracker = tracker;
|
|
76017
|
+
}
|
|
76018
|
+
editor.positionTracker = tracker;
|
|
76019
|
+
return tracker;
|
|
76020
|
+
};
|
|
74297
76021
|
const Search = Extension.create({
|
|
74298
76022
|
// @ts-expect-error - Storage type mismatch will be fixed in TS migration
|
|
74299
76023
|
addStorage() {
|
|
@@ -74302,29 +76026,58 @@ const Search = Extension.create({
|
|
|
74302
76026
|
* @private
|
|
74303
76027
|
* @type {SearchMatch[]|null}
|
|
74304
76028
|
*/
|
|
74305
|
-
searchResults: []
|
|
76029
|
+
searchResults: [],
|
|
76030
|
+
/**
|
|
76031
|
+
* @private
|
|
76032
|
+
* @type {boolean}
|
|
76033
|
+
* Whether to apply CSS highlight classes to matches
|
|
76034
|
+
*/
|
|
76035
|
+
highlightEnabled: true,
|
|
76036
|
+
/**
|
|
76037
|
+
* @private
|
|
76038
|
+
* @type {SearchIndex}
|
|
76039
|
+
* Lazily-built search index for cross-paragraph matching
|
|
76040
|
+
*/
|
|
76041
|
+
searchIndex: new SearchIndex()
|
|
74306
76042
|
};
|
|
74307
76043
|
},
|
|
74308
76044
|
addPmPlugins() {
|
|
74309
76045
|
const editor = this.editor;
|
|
74310
76046
|
const storage = this.storage;
|
|
76047
|
+
const searchIndexInvalidatorPlugin = new Plugin({
|
|
76048
|
+
key: new PluginKey("searchIndexInvalidator"),
|
|
76049
|
+
appendTransaction(transactions, oldState, newState) {
|
|
76050
|
+
const docChanged = transactions.some((tr) => tr.docChanged);
|
|
76051
|
+
if (docChanged && storage?.searchIndex) {
|
|
76052
|
+
storage.searchIndex.invalidate();
|
|
76053
|
+
}
|
|
76054
|
+
return null;
|
|
76055
|
+
}
|
|
76056
|
+
});
|
|
74311
76057
|
const searchHighlightWithIdPlugin = new Plugin({
|
|
74312
|
-
key:
|
|
76058
|
+
key: customSearchHighlightsKey,
|
|
74313
76059
|
props: {
|
|
74314
76060
|
decorations(state) {
|
|
74315
76061
|
if (!editor) return null;
|
|
74316
76062
|
const matches = storage?.searchResults;
|
|
74317
76063
|
if (!matches?.length) return null;
|
|
74318
|
-
const
|
|
74319
|
-
|
|
74320
|
-
|
|
74321
|
-
}
|
|
74322
|
-
|
|
76064
|
+
const highlightEnabled = storage?.highlightEnabled !== false;
|
|
76065
|
+
const decorations = [];
|
|
76066
|
+
for (const match of matches) {
|
|
76067
|
+
const attrs = highlightEnabled ? { id: `search-match-${match.id}`, class: "ProseMirror-search-match" } : { id: `search-match-${match.id}` };
|
|
76068
|
+
if (match.ranges && match.ranges.length > 0) {
|
|
76069
|
+
for (const range of match.ranges) {
|
|
76070
|
+
decorations.push(Decoration.inline(range.from, range.to, attrs));
|
|
76071
|
+
}
|
|
76072
|
+
} else {
|
|
76073
|
+
decorations.push(Decoration.inline(match.from, match.to, attrs));
|
|
76074
|
+
}
|
|
76075
|
+
}
|
|
74323
76076
|
return DecorationSet.create(state.doc, decorations);
|
|
74324
76077
|
}
|
|
74325
76078
|
}
|
|
74326
76079
|
});
|
|
74327
|
-
return [search(), searchHighlightWithIdPlugin];
|
|
76080
|
+
return [search(), searchIndexInvalidatorPlugin, searchHighlightWithIdPlugin];
|
|
74328
76081
|
},
|
|
74329
76082
|
addCommands() {
|
|
74330
76083
|
return {
|
|
@@ -74338,21 +76091,51 @@ const Search = Extension.create({
|
|
|
74338
76091
|
goToFirstMatch: () => (
|
|
74339
76092
|
/** @returns {boolean} */
|
|
74340
76093
|
({ state, editor, dispatch }) => {
|
|
76094
|
+
const searchResults = this.storage?.searchResults;
|
|
76095
|
+
if (Array.isArray(searchResults) && searchResults.length > 0) {
|
|
76096
|
+
const firstMatch = searchResults[0];
|
|
76097
|
+
const from3 = firstMatch.ranges?.[0]?.from ?? firstMatch.from;
|
|
76098
|
+
const to = firstMatch.ranges?.[0]?.to ?? firstMatch.to;
|
|
76099
|
+
if (typeof from3 !== "number" || typeof to !== "number") {
|
|
76100
|
+
return false;
|
|
76101
|
+
}
|
|
76102
|
+
editor.view.focus();
|
|
76103
|
+
const tr2 = state.tr.setSelection(TextSelection$1.create(state.doc, from3, to)).scrollIntoView();
|
|
76104
|
+
if (dispatch) dispatch(tr2);
|
|
76105
|
+
const presentationEditor2 = editor.presentationEditor;
|
|
76106
|
+
if (presentationEditor2 && typeof presentationEditor2.scrollToPosition === "function") {
|
|
76107
|
+
const didScroll = presentationEditor2.scrollToPosition(from3, { block: "center" });
|
|
76108
|
+
if (didScroll) return true;
|
|
76109
|
+
}
|
|
76110
|
+
try {
|
|
76111
|
+
const domPos = editor.view.domAtPos(from3);
|
|
76112
|
+
if (domPos?.node?.scrollIntoView) {
|
|
76113
|
+
domPos.node.scrollIntoView(true);
|
|
76114
|
+
}
|
|
76115
|
+
} catch {
|
|
76116
|
+
}
|
|
76117
|
+
return true;
|
|
76118
|
+
}
|
|
74341
76119
|
const highlights = getMatchHighlights(state);
|
|
74342
76120
|
if (!highlights) return false;
|
|
74343
76121
|
const decorations = highlights.find();
|
|
74344
76122
|
if (!decorations?.length) return false;
|
|
74345
|
-
const
|
|
76123
|
+
const firstDeco = decorations[0];
|
|
74346
76124
|
editor.view.focus();
|
|
74347
|
-
const tr = state.tr.setSelection(TextSelection$1.create(state.doc,
|
|
76125
|
+
const tr = state.tr.setSelection(TextSelection$1.create(state.doc, firstDeco.from, firstDeco.to)).scrollIntoView();
|
|
74348
76126
|
if (dispatch) dispatch(tr);
|
|
74349
76127
|
const presentationEditor = editor.presentationEditor;
|
|
74350
76128
|
if (presentationEditor && typeof presentationEditor.scrollToPosition === "function") {
|
|
74351
|
-
const didScroll = presentationEditor.scrollToPosition(
|
|
76129
|
+
const didScroll = presentationEditor.scrollToPosition(firstDeco.from, { block: "center" });
|
|
74352
76130
|
if (didScroll) return true;
|
|
74353
76131
|
}
|
|
74354
|
-
|
|
74355
|
-
|
|
76132
|
+
try {
|
|
76133
|
+
const domPos = editor.view.domAtPos(firstDeco.from);
|
|
76134
|
+
if (domPos?.node?.scrollIntoView) {
|
|
76135
|
+
domPos.node.scrollIntoView(true);
|
|
76136
|
+
}
|
|
76137
|
+
} catch {
|
|
76138
|
+
}
|
|
74356
76139
|
return true;
|
|
74357
76140
|
}
|
|
74358
76141
|
),
|
|
@@ -74370,53 +76153,57 @@ const Search = Extension.create({
|
|
|
74370
76153
|
*
|
|
74371
76154
|
* // Search without visual highlighting
|
|
74372
76155
|
* const silentMatches = editor.commands.search('test', { highlight: false })
|
|
74373
|
-
*
|
|
76156
|
+
*
|
|
76157
|
+
* // Cross-paragraph search (works by default for plain strings)
|
|
76158
|
+
* const crossParagraphMatches = editor.commands.search('end of paragraph start of next')
|
|
76159
|
+
* @note Returns array of SearchMatch objects with positions and IDs.
|
|
76160
|
+
* Plain string searches are whitespace-flexible and match across paragraphs.
|
|
76161
|
+
* Regex searches match exactly as specified.
|
|
74374
76162
|
*/
|
|
74375
76163
|
search: (patternInput, options = {}) => (
|
|
74376
76164
|
/** @returns {SearchMatch[]} */
|
|
74377
|
-
({ state, dispatch }) => {
|
|
76165
|
+
({ state, dispatch, editor }) => {
|
|
74378
76166
|
if (options != null && (typeof options !== "object" || Array.isArray(options))) {
|
|
74379
76167
|
throw new TypeError("Search options must be an object");
|
|
74380
76168
|
}
|
|
74381
76169
|
const highlight = typeof options?.highlight === "boolean" ? options.highlight : true;
|
|
74382
|
-
|
|
76170
|
+
const maxMatches = typeof options?.maxMatches === "number" ? options.maxMatches : 1e3;
|
|
74383
76171
|
let caseSensitive = false;
|
|
74384
|
-
let
|
|
74385
|
-
const wholeWord = false;
|
|
76172
|
+
let searchPattern = patternInput;
|
|
74386
76173
|
if (isRegExp(patternInput)) {
|
|
74387
|
-
|
|
74388
|
-
|
|
74389
|
-
patternInput
|
|
74390
|
-
);
|
|
74391
|
-
regexp = true;
|
|
74392
|
-
pattern = regexPattern.source;
|
|
74393
|
-
caseSensitive = !regexPattern.flags.includes("i");
|
|
76174
|
+
caseSensitive = !patternInput.flags.includes("i");
|
|
76175
|
+
searchPattern = patternInput;
|
|
74394
76176
|
} else if (typeof patternInput === "string" && /^\/(.+)\/([gimsuy]*)$/.test(patternInput)) {
|
|
74395
76177
|
const [, body, flags] = patternInput.match(/^\/(.+)\/([gimsuy]*)$/);
|
|
74396
|
-
regexp = true;
|
|
74397
|
-
pattern = body;
|
|
74398
76178
|
caseSensitive = !flags.includes("i");
|
|
76179
|
+
searchPattern = new RegExp(body, flags.includes("g") ? flags : flags + "g");
|
|
74399
76180
|
} else {
|
|
74400
|
-
|
|
76181
|
+
searchPattern = String(patternInput);
|
|
74401
76182
|
}
|
|
74402
|
-
const
|
|
74403
|
-
|
|
76183
|
+
const searchIndex = this.storage.searchIndex;
|
|
76184
|
+
searchIndex.ensureValid(state.doc);
|
|
76185
|
+
const indexMatches = searchIndex.search(searchPattern, {
|
|
74404
76186
|
caseSensitive,
|
|
74405
|
-
|
|
74406
|
-
wholeWord
|
|
76187
|
+
maxMatches
|
|
74407
76188
|
});
|
|
74408
|
-
const
|
|
74409
|
-
|
|
74410
|
-
|
|
74411
|
-
|
|
74412
|
-
|
|
74413
|
-
|
|
74414
|
-
|
|
74415
|
-
|
|
74416
|
-
|
|
74417
|
-
|
|
74418
|
-
|
|
76189
|
+
const resultMatches = [];
|
|
76190
|
+
for (const indexMatch of indexMatches) {
|
|
76191
|
+
const ranges = searchIndex.offsetRangeToDocRanges(indexMatch.start, indexMatch.end);
|
|
76192
|
+
if (ranges.length === 0) continue;
|
|
76193
|
+
const matchTexts = ranges.map((r2) => state.doc.textBetween(r2.from, r2.to));
|
|
76194
|
+
const combinedText = matchTexts.join("");
|
|
76195
|
+
const match = {
|
|
76196
|
+
from: ranges[0].from,
|
|
76197
|
+
to: ranges[ranges.length - 1].to,
|
|
76198
|
+
text: combinedText,
|
|
76199
|
+
id: v4(),
|
|
76200
|
+
ranges,
|
|
76201
|
+
trackerIds: []
|
|
76202
|
+
};
|
|
76203
|
+
resultMatches.push(match);
|
|
76204
|
+
}
|
|
74419
76205
|
this.storage.searchResults = resultMatches;
|
|
76206
|
+
this.storage.highlightEnabled = highlight;
|
|
74420
76207
|
return resultMatches;
|
|
74421
76208
|
}
|
|
74422
76209
|
),
|
|
@@ -74427,12 +76214,48 @@ const Search = Extension.create({
|
|
|
74427
76214
|
* @example
|
|
74428
76215
|
* const searchResults = editor.commands.search('test string')
|
|
74429
76216
|
* editor.commands.goToSearchResult(searchResults[3])
|
|
74430
|
-
* @note Scrolls to match and selects it
|
|
76217
|
+
* @note Scrolls to match and selects it. For multi-range matches (cross-paragraph),
|
|
76218
|
+
* selects the first range and scrolls to it.
|
|
74431
76219
|
*/
|
|
74432
76220
|
goToSearchResult: (match) => (
|
|
74433
76221
|
/** @returns {boolean} */
|
|
74434
76222
|
({ state, dispatch, editor }) => {
|
|
74435
|
-
const
|
|
76223
|
+
const positionTracker = getPositionTracker(editor);
|
|
76224
|
+
const doc2 = state.doc;
|
|
76225
|
+
const highlights = getMatchHighlights(state);
|
|
76226
|
+
let from3, to;
|
|
76227
|
+
if (match?.ranges && match.ranges.length > 0 && match?.trackerIds && match.trackerIds.length > 0) {
|
|
76228
|
+
if (positionTracker?.resolve && match.trackerIds[0]) {
|
|
76229
|
+
const resolved = positionTracker.resolve(match.trackerIds[0]);
|
|
76230
|
+
if (resolved) {
|
|
76231
|
+
from3 = resolved.from;
|
|
76232
|
+
to = resolved.to;
|
|
76233
|
+
}
|
|
76234
|
+
}
|
|
76235
|
+
if (from3 === void 0) {
|
|
76236
|
+
from3 = match.ranges[0].from;
|
|
76237
|
+
to = match.ranges[0].to;
|
|
76238
|
+
}
|
|
76239
|
+
} else {
|
|
76240
|
+
from3 = match.from;
|
|
76241
|
+
to = match.to;
|
|
76242
|
+
if (positionTracker?.resolve && match?.id) {
|
|
76243
|
+
const resolved = positionTracker.resolve(match.id);
|
|
76244
|
+
if (resolved) {
|
|
76245
|
+
from3 = resolved.from;
|
|
76246
|
+
to = resolved.to;
|
|
76247
|
+
}
|
|
76248
|
+
}
|
|
76249
|
+
}
|
|
76250
|
+
const normalized = resolveSearchRange({
|
|
76251
|
+
doc: doc2,
|
|
76252
|
+
from: from3,
|
|
76253
|
+
to,
|
|
76254
|
+
expectedText: match?.text ?? null,
|
|
76255
|
+
highlights
|
|
76256
|
+
});
|
|
76257
|
+
from3 = normalized.from;
|
|
76258
|
+
to = normalized.to;
|
|
74436
76259
|
editor.view.focus();
|
|
74437
76260
|
const tr = state.tr.setSelection(TextSelection$1.create(state.doc, from3, to)).scrollIntoView();
|
|
74438
76261
|
if (dispatch) dispatch(tr);
|
|
@@ -75085,6 +76908,7 @@ const getStarterExtensions = () => {
|
|
|
75085
76908
|
CommentRangeStart,
|
|
75086
76909
|
CommentRangeEnd,
|
|
75087
76910
|
CommentReference,
|
|
76911
|
+
FootnoteReference,
|
|
75088
76912
|
Document,
|
|
75089
76913
|
FontFamily,
|
|
75090
76914
|
FontSize,
|