@harbour-enterprises/superdoc 1.8.0-next.3 → 1.8.0-next.4
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-D2QJOg3-.es.js → PdfViewer-BWztCpkc.es.js} +2 -2
- package/dist/chunks/{PdfViewer-NDFZ2Rp7.cjs → PdfViewer-D4C9rwVJ.cjs} +2 -2
- package/dist/chunks/{SuperConverter-BOsF-mqZ.es.js → SuperConverter-DR0eIRL-.es.js} +1 -1
- package/dist/chunks/{SuperConverter-0rmmECRS.cjs → SuperConverter-z513kcRv.cjs} +1 -1
- package/dist/chunks/{index-DmCBAgUe.es.js → index-ChmTjXj0.es.js} +1752 -1889
- package/dist/chunks/{index-BiiF4VRu.es.js → index-DjT2v3D4.es.js} +4 -4
- package/dist/chunks/{index-C2RSw98q.cjs → index-Q4P7PHq8.cjs} +4 -4
- package/dist/chunks/{index-DJtl6sUM.cjs → index-zX9xjVrM.cjs} +1752 -1889
- package/dist/super-editor/converter.cjs +1 -1
- package/dist/super-editor/converter.es.js +1 -1
- package/dist/super-editor.cjs +2 -2
- package/dist/super-editor.es.js +3 -3
- package/dist/superdoc.cjs +3 -3
- package/dist/superdoc.es.js +3 -3
- package/dist/superdoc.umd.js +1754 -1874
- package/dist/superdoc.umd.js.map +1 -1
- package/package.json +3 -3
package/dist/superdoc.umd.js
CHANGED
|
@@ -38710,7 +38710,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
38710
38710
|
static getStoredSuperdocVersion(docx) {
|
|
38711
38711
|
return SuperConverter.getStoredCustomProperty(docx, "SuperdocVersion");
|
|
38712
38712
|
}
|
|
38713
|
-
static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.8.0-next.
|
|
38713
|
+
static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.8.0-next.4") {
|
|
38714
38714
|
return SuperConverter.setStoredCustomProperty(docx, "SuperdocVersion", version2, false);
|
|
38715
38715
|
}
|
|
38716
38716
|
/**
|
|
@@ -65052,7 +65052,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
65052
65052
|
return false;
|
|
65053
65053
|
}
|
|
65054
65054
|
};
|
|
65055
|
-
const summaryVersion = "1.8.0-next.
|
|
65055
|
+
const summaryVersion = "1.8.0-next.4";
|
|
65056
65056
|
const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
|
|
65057
65057
|
const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
|
|
65058
65058
|
function mapAttributes(attrs) {
|
|
@@ -67710,7 +67710,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
67710
67710
|
* Process collaboration migrations
|
|
67711
67711
|
*/
|
|
67712
67712
|
processCollaborationMigrations() {
|
|
67713
|
-
console.debug("[checkVersionMigrations] Current editor version", "1.8.0-next.
|
|
67713
|
+
console.debug("[checkVersionMigrations] Current editor version", "1.8.0-next.4");
|
|
67714
67714
|
if (!this.options.ydoc) return;
|
|
67715
67715
|
const metaMap = this.options.ydoc.getMap("meta");
|
|
67716
67716
|
let docVersion = metaMap.get("version");
|
|
@@ -77754,24 +77754,6 @@ ${o}
|
|
|
77754
77754
|
}
|
|
77755
77755
|
elem.setAttribute("role", "link");
|
|
77756
77756
|
elem.setAttribute("tabindex", "0");
|
|
77757
|
-
elem.addEventListener("click", (event) => {
|
|
77758
|
-
event.preventDefault();
|
|
77759
|
-
event.stopPropagation();
|
|
77760
|
-
const linkClickEvent = new CustomEvent("superdoc-link-click", {
|
|
77761
|
-
bubbles: true,
|
|
77762
|
-
composed: true,
|
|
77763
|
-
detail: {
|
|
77764
|
-
href: linkData.href,
|
|
77765
|
-
target: linkData.target,
|
|
77766
|
-
rel: linkData.rel,
|
|
77767
|
-
tooltip: linkData.tooltip,
|
|
77768
|
-
element: elem,
|
|
77769
|
-
clientX: event.clientX,
|
|
77770
|
-
clientY: event.clientY
|
|
77771
|
-
}
|
|
77772
|
-
});
|
|
77773
|
-
elem.dispatchEvent(linkClickEvent);
|
|
77774
|
-
});
|
|
77775
77757
|
}
|
|
77776
77758
|
/**
|
|
77777
77759
|
* Render a single run as an HTML element (span or anchor).
|
|
@@ -89110,255 +89092,6 @@ ${o}
|
|
|
89110
89092
|
/** P3: Heavy debounce for full document layout */
|
|
89111
89093
|
[Priority.P3]: 150
|
|
89112
89094
|
});
|
|
89113
|
-
const DEFAULT_MIME_TYPE$1 = "application/x-field-annotation";
|
|
89114
|
-
const LEGACY_MIME_TYPE = "fieldAnnotation";
|
|
89115
|
-
function parseIntSafe$1(value) {
|
|
89116
|
-
if (!value) return void 0;
|
|
89117
|
-
const parsed = parseInt(value, 10);
|
|
89118
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
89119
|
-
}
|
|
89120
|
-
function extractFieldAnnotationData(element2) {
|
|
89121
|
-
const dataset = element2.dataset;
|
|
89122
|
-
const attributes = {};
|
|
89123
|
-
for (const key2 in dataset) {
|
|
89124
|
-
const value = dataset[key2];
|
|
89125
|
-
if (value !== void 0) {
|
|
89126
|
-
attributes[key2] = value;
|
|
89127
|
-
}
|
|
89128
|
-
}
|
|
89129
|
-
return {
|
|
89130
|
-
fieldId: dataset.fieldId,
|
|
89131
|
-
fieldType: dataset.fieldType,
|
|
89132
|
-
variant: dataset.variant ?? dataset.type,
|
|
89133
|
-
displayLabel: dataset.displayLabel,
|
|
89134
|
-
pmStart: parseIntSafe$1(dataset.pmStart),
|
|
89135
|
-
pmEnd: parseIntSafe$1(dataset.pmEnd),
|
|
89136
|
-
attributes
|
|
89137
|
-
};
|
|
89138
|
-
}
|
|
89139
|
-
class DragHandler {
|
|
89140
|
-
/**
|
|
89141
|
-
* Creates a new DragHandler instance.
|
|
89142
|
-
*
|
|
89143
|
-
* @param container - The DOM container element (typically .superdoc-layout)
|
|
89144
|
-
* @param config - Configuration options and callbacks
|
|
89145
|
-
*/
|
|
89146
|
-
constructor(container, config2 = {}) {
|
|
89147
|
-
this.container = container;
|
|
89148
|
-
this.config = config2;
|
|
89149
|
-
this.mimeType = config2.mimeType ?? DEFAULT_MIME_TYPE$1;
|
|
89150
|
-
this.boundHandlers = {
|
|
89151
|
-
dragstart: this.handleDragStart.bind(this),
|
|
89152
|
-
dragover: this.handleDragOver.bind(this),
|
|
89153
|
-
drop: this.handleDrop.bind(this),
|
|
89154
|
-
dragend: this.handleDragEnd.bind(this),
|
|
89155
|
-
dragleave: this.handleDragLeave.bind(this)
|
|
89156
|
-
};
|
|
89157
|
-
this.windowDragoverHandler = this.handleWindowDragOver.bind(this);
|
|
89158
|
-
this.windowDropHandler = this.handleWindowDrop.bind(this);
|
|
89159
|
-
this.attachListeners();
|
|
89160
|
-
}
|
|
89161
|
-
/**
|
|
89162
|
-
* Attaches event listeners to the container and window.
|
|
89163
|
-
*/
|
|
89164
|
-
attachListeners() {
|
|
89165
|
-
this.container.addEventListener("dragstart", this.boundHandlers.dragstart);
|
|
89166
|
-
this.container.addEventListener("dragover", this.boundHandlers.dragover);
|
|
89167
|
-
this.container.addEventListener("drop", this.boundHandlers.drop);
|
|
89168
|
-
this.container.addEventListener("dragend", this.boundHandlers.dragend);
|
|
89169
|
-
this.container.addEventListener("dragleave", this.boundHandlers.dragleave);
|
|
89170
|
-
window.addEventListener("dragover", this.windowDragoverHandler, false);
|
|
89171
|
-
window.addEventListener("drop", this.windowDropHandler, false);
|
|
89172
|
-
}
|
|
89173
|
-
/**
|
|
89174
|
-
* Removes event listeners from the container and window.
|
|
89175
|
-
*/
|
|
89176
|
-
removeListeners() {
|
|
89177
|
-
this.container.removeEventListener("dragstart", this.boundHandlers.dragstart);
|
|
89178
|
-
this.container.removeEventListener("dragover", this.boundHandlers.dragover);
|
|
89179
|
-
this.container.removeEventListener("drop", this.boundHandlers.drop);
|
|
89180
|
-
this.container.removeEventListener("dragend", this.boundHandlers.dragend);
|
|
89181
|
-
this.container.removeEventListener("dragleave", this.boundHandlers.dragleave);
|
|
89182
|
-
window.removeEventListener("dragover", this.windowDragoverHandler, false);
|
|
89183
|
-
window.removeEventListener("drop", this.windowDropHandler, false);
|
|
89184
|
-
}
|
|
89185
|
-
/**
|
|
89186
|
-
* Handles dragover at window level to allow drops on overlay elements.
|
|
89187
|
-
* This ensures preventDefault is called even when dragging over selection
|
|
89188
|
-
* highlights or other UI elements that sit on top of the layout content.
|
|
89189
|
-
*/
|
|
89190
|
-
handleWindowDragOver(event) {
|
|
89191
|
-
if (this.hasFieldAnnotationData(event)) {
|
|
89192
|
-
event.preventDefault();
|
|
89193
|
-
if (event.dataTransfer) {
|
|
89194
|
-
event.dataTransfer.dropEffect = "move";
|
|
89195
|
-
}
|
|
89196
|
-
const target = event.target;
|
|
89197
|
-
if (!this.container.contains(target)) {
|
|
89198
|
-
this.config.onDragOver?.({
|
|
89199
|
-
event,
|
|
89200
|
-
clientX: event.clientX,
|
|
89201
|
-
clientY: event.clientY,
|
|
89202
|
-
hasFieldAnnotation: true
|
|
89203
|
-
});
|
|
89204
|
-
}
|
|
89205
|
-
}
|
|
89206
|
-
}
|
|
89207
|
-
/**
|
|
89208
|
-
* Handles drop at window level to catch drops on overlay elements.
|
|
89209
|
-
* If the drop target is outside the container, we process it here.
|
|
89210
|
-
*/
|
|
89211
|
-
handleWindowDrop(event) {
|
|
89212
|
-
if (this.hasFieldAnnotationData(event)) {
|
|
89213
|
-
const target = event.target;
|
|
89214
|
-
if (!this.container.contains(target)) {
|
|
89215
|
-
this.handleDrop(event);
|
|
89216
|
-
}
|
|
89217
|
-
}
|
|
89218
|
-
}
|
|
89219
|
-
/**
|
|
89220
|
-
* Handles the dragstart event.
|
|
89221
|
-
* Sets up dataTransfer with field annotation data and drag image.
|
|
89222
|
-
*/
|
|
89223
|
-
handleDragStart(event) {
|
|
89224
|
-
const target = event.target;
|
|
89225
|
-
if (!target?.dataset?.draggable || target.dataset.draggable !== "true") {
|
|
89226
|
-
return;
|
|
89227
|
-
}
|
|
89228
|
-
const data = extractFieldAnnotationData(target);
|
|
89229
|
-
if (event.dataTransfer) {
|
|
89230
|
-
const jsonData = JSON.stringify({
|
|
89231
|
-
attributes: data.attributes,
|
|
89232
|
-
sourceField: data
|
|
89233
|
-
});
|
|
89234
|
-
event.dataTransfer.setData(this.mimeType, jsonData);
|
|
89235
|
-
event.dataTransfer.setData(LEGACY_MIME_TYPE, jsonData);
|
|
89236
|
-
event.dataTransfer.setData("text/plain", data.displayLabel ?? "Field Annotation");
|
|
89237
|
-
event.dataTransfer.setDragImage(target, 0, 0);
|
|
89238
|
-
event.dataTransfer.effectAllowed = "move";
|
|
89239
|
-
}
|
|
89240
|
-
this.config.onDragStart?.({
|
|
89241
|
-
event,
|
|
89242
|
-
element: target,
|
|
89243
|
-
data
|
|
89244
|
-
});
|
|
89245
|
-
}
|
|
89246
|
-
/**
|
|
89247
|
-
* Handles the dragover event.
|
|
89248
|
-
* Provides visual feedback and determines if drop is allowed.
|
|
89249
|
-
*/
|
|
89250
|
-
handleDragOver(event) {
|
|
89251
|
-
const hasFieldAnnotation = this.hasFieldAnnotationData(event);
|
|
89252
|
-
if (hasFieldAnnotation) {
|
|
89253
|
-
event.preventDefault();
|
|
89254
|
-
if (event.dataTransfer) {
|
|
89255
|
-
event.dataTransfer.dropEffect = "move";
|
|
89256
|
-
}
|
|
89257
|
-
this.container.classList.add("drag-over");
|
|
89258
|
-
}
|
|
89259
|
-
this.config.onDragOver?.({
|
|
89260
|
-
event,
|
|
89261
|
-
clientX: event.clientX,
|
|
89262
|
-
clientY: event.clientY,
|
|
89263
|
-
hasFieldAnnotation
|
|
89264
|
-
});
|
|
89265
|
-
}
|
|
89266
|
-
/**
|
|
89267
|
-
* Handles the dragleave event.
|
|
89268
|
-
* Removes visual feedback when drag leaves the container.
|
|
89269
|
-
*/
|
|
89270
|
-
handleDragLeave(event) {
|
|
89271
|
-
const relatedTarget = event.relatedTarget;
|
|
89272
|
-
if (!relatedTarget || !this.container.contains(relatedTarget)) {
|
|
89273
|
-
this.container.classList.remove("drag-over");
|
|
89274
|
-
}
|
|
89275
|
-
}
|
|
89276
|
-
/**
|
|
89277
|
-
* Handles the drop event.
|
|
89278
|
-
* Maps drop coordinates to ProseMirror position and emits drop event.
|
|
89279
|
-
*/
|
|
89280
|
-
handleDrop(event) {
|
|
89281
|
-
this.container.classList.remove("drag-over");
|
|
89282
|
-
if (!this.hasFieldAnnotationData(event)) {
|
|
89283
|
-
return;
|
|
89284
|
-
}
|
|
89285
|
-
event.preventDefault();
|
|
89286
|
-
const data = this.extractDragData(event);
|
|
89287
|
-
if (!data) {
|
|
89288
|
-
return;
|
|
89289
|
-
}
|
|
89290
|
-
const pmPosition = clickToPositionDom(this.container, event.clientX, event.clientY);
|
|
89291
|
-
this.config.onDrop?.({
|
|
89292
|
-
event,
|
|
89293
|
-
data,
|
|
89294
|
-
pmPosition,
|
|
89295
|
-
clientX: event.clientX,
|
|
89296
|
-
clientY: event.clientY
|
|
89297
|
-
});
|
|
89298
|
-
}
|
|
89299
|
-
/**
|
|
89300
|
-
* Handles the dragend event.
|
|
89301
|
-
* Cleans up drag state.
|
|
89302
|
-
*/
|
|
89303
|
-
handleDragEnd(event) {
|
|
89304
|
-
this.container.classList.remove("drag-over");
|
|
89305
|
-
this.config.onDragEnd?.(event);
|
|
89306
|
-
}
|
|
89307
|
-
/**
|
|
89308
|
-
* Checks if a drag event contains field annotation data.
|
|
89309
|
-
*/
|
|
89310
|
-
hasFieldAnnotationData(event) {
|
|
89311
|
-
if (!event.dataTransfer) {
|
|
89312
|
-
return false;
|
|
89313
|
-
}
|
|
89314
|
-
const types2 = event.dataTransfer.types;
|
|
89315
|
-
return types2.includes(this.mimeType) || types2.includes(LEGACY_MIME_TYPE);
|
|
89316
|
-
}
|
|
89317
|
-
/**
|
|
89318
|
-
* Extracts field annotation data from a drag event's dataTransfer.
|
|
89319
|
-
*/
|
|
89320
|
-
extractDragData(event) {
|
|
89321
|
-
if (!event.dataTransfer) {
|
|
89322
|
-
return null;
|
|
89323
|
-
}
|
|
89324
|
-
let jsonData = event.dataTransfer.getData(this.mimeType);
|
|
89325
|
-
if (!jsonData) {
|
|
89326
|
-
jsonData = event.dataTransfer.getData(LEGACY_MIME_TYPE);
|
|
89327
|
-
}
|
|
89328
|
-
if (!jsonData) {
|
|
89329
|
-
return null;
|
|
89330
|
-
}
|
|
89331
|
-
try {
|
|
89332
|
-
const parsed = JSON.parse(jsonData);
|
|
89333
|
-
return parsed.sourceField ?? parsed.attributes ?? parsed;
|
|
89334
|
-
} catch {
|
|
89335
|
-
return null;
|
|
89336
|
-
}
|
|
89337
|
-
}
|
|
89338
|
-
/**
|
|
89339
|
-
* Updates the configuration options.
|
|
89340
|
-
*
|
|
89341
|
-
* @param config - New configuration options to merge
|
|
89342
|
-
*/
|
|
89343
|
-
updateConfig(config2) {
|
|
89344
|
-
this.config = { ...this.config, ...config2 };
|
|
89345
|
-
if (config2.mimeType) {
|
|
89346
|
-
this.mimeType = config2.mimeType;
|
|
89347
|
-
}
|
|
89348
|
-
}
|
|
89349
|
-
/**
|
|
89350
|
-
* Destroys the drag handler and removes all event listeners.
|
|
89351
|
-
* Call this when the layout engine is unmounted or the container is removed.
|
|
89352
|
-
*/
|
|
89353
|
-
destroy() {
|
|
89354
|
-
this.removeListeners();
|
|
89355
|
-
this.container.classList.remove("drag-over");
|
|
89356
|
-
}
|
|
89357
|
-
}
|
|
89358
|
-
function createDragHandler(container, config2 = {}) {
|
|
89359
|
-
const handler2 = new DragHandler(container, config2);
|
|
89360
|
-
return () => handler2.destroy();
|
|
89361
|
-
}
|
|
89362
89095
|
const isAtomicFragment = (fragment) => {
|
|
89363
89096
|
return fragment.kind === "drawing" || fragment.kind === "image";
|
|
89364
89097
|
};
|
|
@@ -90922,229 +90655,1425 @@ ${o}
|
|
|
90922
90655
|
this.#isSetup = false;
|
|
90923
90656
|
}
|
|
90924
90657
|
}
|
|
90925
|
-
const
|
|
90926
|
-
|
|
90927
|
-
|
|
90928
|
-
|
|
90929
|
-
cancelAnimationFrame: (handle2) => window.cancelAnimationFrame(handle2)
|
|
90930
|
-
};
|
|
90658
|
+
const WORD_CHARACTER_REGEX = /[\p{L}\p{N}'\u2018\u2019_~-]/u;
|
|
90659
|
+
function isWordCharacter(char) {
|
|
90660
|
+
if (!char) {
|
|
90661
|
+
return false;
|
|
90931
90662
|
}
|
|
90932
|
-
|
|
90933
|
-
|
|
90934
|
-
|
|
90935
|
-
|
|
90936
|
-
|
|
90937
|
-
|
|
90663
|
+
return WORD_CHARACTER_REGEX.test(char);
|
|
90664
|
+
}
|
|
90665
|
+
function calculateExtendedSelection(blocks2, anchor, head, mode) {
|
|
90666
|
+
if (mode === "word") {
|
|
90667
|
+
const anchorBounds = findWordBoundaries(blocks2, anchor);
|
|
90668
|
+
const headBounds = findWordBoundaries(blocks2, head);
|
|
90669
|
+
if (anchorBounds && headBounds) {
|
|
90670
|
+
if (head >= anchor) {
|
|
90671
|
+
return { selAnchor: anchorBounds.from, selHead: headBounds.to };
|
|
90672
|
+
} else {
|
|
90673
|
+
return { selAnchor: anchorBounds.to, selHead: headBounds.from };
|
|
90674
|
+
}
|
|
90675
|
+
}
|
|
90676
|
+
} else if (mode === "para") {
|
|
90677
|
+
const anchorBounds = findParagraphBoundaries(blocks2, anchor);
|
|
90678
|
+
const headBounds = findParagraphBoundaries(blocks2, head);
|
|
90679
|
+
if (anchorBounds && headBounds) {
|
|
90680
|
+
if (head >= anchor) {
|
|
90681
|
+
return { selAnchor: anchorBounds.from, selHead: headBounds.to };
|
|
90682
|
+
} else {
|
|
90683
|
+
return { selAnchor: anchorBounds.to, selHead: headBounds.from };
|
|
90684
|
+
}
|
|
90685
|
+
}
|
|
90938
90686
|
}
|
|
90687
|
+
return { selAnchor: anchor, selHead: head };
|
|
90688
|
+
}
|
|
90689
|
+
function registerPointerClick(event, previous2, options) {
|
|
90690
|
+
const time2 = event.timeStamp ?? performance.now();
|
|
90691
|
+
const timeDelta = time2 - previous2.lastClickTime;
|
|
90692
|
+
const withinTime = timeDelta <= options.timeThresholdMs;
|
|
90693
|
+
const distanceX = Math.abs(event.clientX - previous2.lastClickPosition.x);
|
|
90694
|
+
const distanceY = Math.abs(event.clientY - previous2.lastClickPosition.y);
|
|
90695
|
+
const withinDistance = distanceX <= options.distanceThresholdPx && distanceY <= options.distanceThresholdPx;
|
|
90696
|
+
const clickCount = withinTime && withinDistance ? Math.min(previous2.clickCount + 1, options.maxClickCount) : 1;
|
|
90939
90697
|
return {
|
|
90940
|
-
|
|
90941
|
-
|
|
90942
|
-
|
|
90943
|
-
},
|
|
90944
|
-
cancelAnimationFrame: (handle2) => {
|
|
90945
|
-
anyGlobal.clearTimeout?.(handle2);
|
|
90946
|
-
}
|
|
90698
|
+
clickCount,
|
|
90699
|
+
lastClickTime: time2,
|
|
90700
|
+
lastClickPosition: { x: event.clientX, y: event.clientY }
|
|
90947
90701
|
};
|
|
90948
|
-
}
|
|
90949
|
-
|
|
90950
|
-
|
|
90951
|
-
|
|
90952
|
-
#layoutUpdating = false;
|
|
90953
|
-
#pending = false;
|
|
90954
|
-
#scheduled = false;
|
|
90955
|
-
#rafHandle = null;
|
|
90956
|
-
#scheduler;
|
|
90957
|
-
/**
|
|
90958
|
-
* Creates a new SelectionSyncCoordinator.
|
|
90959
|
-
*
|
|
90960
|
-
* @param options - Configuration options
|
|
90961
|
-
* @param options.scheduler - Custom scheduler for animation frames (useful for testing), defaults to platform scheduler
|
|
90962
|
-
*/
|
|
90963
|
-
constructor(options) {
|
|
90964
|
-
super();
|
|
90965
|
-
this.#scheduler = options?.scheduler ?? createDefaultScheduler();
|
|
90702
|
+
}
|
|
90703
|
+
function getFirstTextPosition(doc2) {
|
|
90704
|
+
if (!doc2 || !doc2.content) {
|
|
90705
|
+
return 1;
|
|
90966
90706
|
}
|
|
90967
|
-
|
|
90968
|
-
|
|
90969
|
-
|
|
90970
|
-
|
|
90971
|
-
|
|
90972
|
-
|
|
90973
|
-
return
|
|
90707
|
+
let validPos = 1;
|
|
90708
|
+
doc2.nodesBetween(0, doc2.content.size, (node2, pos) => {
|
|
90709
|
+
if (node2.isTextblock) {
|
|
90710
|
+
validPos = pos + 1;
|
|
90711
|
+
return false;
|
|
90712
|
+
}
|
|
90713
|
+
return true;
|
|
90714
|
+
});
|
|
90715
|
+
return validPos;
|
|
90716
|
+
}
|
|
90717
|
+
function computeWordSelectionRangeAt(state, pos) {
|
|
90718
|
+
if (!state?.doc) {
|
|
90719
|
+
return null;
|
|
90974
90720
|
}
|
|
90975
|
-
|
|
90976
|
-
|
|
90977
|
-
*
|
|
90978
|
-
* @returns The epoch of the document version currently painted in the DOM
|
|
90979
|
-
*/
|
|
90980
|
-
getLayoutEpoch() {
|
|
90981
|
-
return this.#layoutEpoch;
|
|
90721
|
+
if (pos < 0 || pos > state.doc.content.size) {
|
|
90722
|
+
return null;
|
|
90982
90723
|
}
|
|
90983
|
-
|
|
90984
|
-
|
|
90985
|
-
|
|
90986
|
-
* @returns True if between onLayoutStart() and onLayoutComplete(), false otherwise
|
|
90987
|
-
*/
|
|
90988
|
-
isLayoutUpdating() {
|
|
90989
|
-
return this.#layoutUpdating;
|
|
90724
|
+
const textblockPos = findNearestTextblockResolvedPos(state.doc, pos);
|
|
90725
|
+
if (!textblockPos) {
|
|
90726
|
+
return null;
|
|
90990
90727
|
}
|
|
90991
|
-
|
|
90992
|
-
|
|
90993
|
-
|
|
90994
|
-
|
|
90995
|
-
|
|
90996
|
-
|
|
90997
|
-
* When the document epoch changes:
|
|
90998
|
-
* 1. Any scheduled render is cancelled (layout will be out of sync)
|
|
90999
|
-
* 2. If layout has already caught up, rendering is rescheduled
|
|
91000
|
-
*
|
|
91001
|
-
* Calling with the same epoch as the current value is a no-op.
|
|
91002
|
-
* Invalid epoch values are silently ignored.
|
|
91003
|
-
*/
|
|
91004
|
-
setDocEpoch(epoch) {
|
|
91005
|
-
if (!Number.isFinite(epoch) || epoch < 0) return;
|
|
91006
|
-
if (epoch === this.#docEpoch) return;
|
|
91007
|
-
this.#docEpoch = epoch;
|
|
91008
|
-
this.#cancelScheduledRender();
|
|
91009
|
-
this.#maybeSchedule();
|
|
90728
|
+
const parentStart = textblockPos.start();
|
|
90729
|
+
const parentEnd = textblockPos.end();
|
|
90730
|
+
const sampleEnd = Math.min(pos + 1, parentEnd);
|
|
90731
|
+
const charAtPos = state.doc.textBetween(pos, sampleEnd, "\0", "\0");
|
|
90732
|
+
if (!isWordCharacter(charAtPos)) {
|
|
90733
|
+
return null;
|
|
91010
90734
|
}
|
|
91011
|
-
|
|
91012
|
-
|
|
91013
|
-
|
|
91014
|
-
|
|
91015
|
-
|
|
91016
|
-
|
|
91017
|
-
|
|
91018
|
-
* Safe to call multiple times (e.g., if layouts overlap) - subsequent calls are ignored
|
|
91019
|
-
* until onLayoutComplete() is called.
|
|
91020
|
-
*/
|
|
91021
|
-
onLayoutStart() {
|
|
91022
|
-
if (this.#layoutUpdating) return;
|
|
91023
|
-
this.#layoutUpdating = true;
|
|
91024
|
-
this.#cancelScheduledRender();
|
|
90735
|
+
let startPos = pos;
|
|
90736
|
+
while (startPos > parentStart) {
|
|
90737
|
+
const prevChar = state.doc.textBetween(startPos - 1, startPos, "\0", "\0");
|
|
90738
|
+
if (!isWordCharacter(prevChar)) {
|
|
90739
|
+
break;
|
|
90740
|
+
}
|
|
90741
|
+
startPos -= 1;
|
|
91025
90742
|
}
|
|
91026
|
-
|
|
91027
|
-
|
|
91028
|
-
|
|
91029
|
-
|
|
91030
|
-
|
|
91031
|
-
* @remarks
|
|
91032
|
-
* Marks the layout as no longer updating, records the new layout epoch, and attempts
|
|
91033
|
-
* to schedule rendering if conditions are now safe.
|
|
91034
|
-
*
|
|
91035
|
-
* If the layoutEpoch is invalid (not a finite non-negative number), it is ignored and
|
|
91036
|
-
* the previous layoutEpoch value is retained.
|
|
91037
|
-
*
|
|
91038
|
-
* This method is the primary trigger for selection rendering - if there's a pending
|
|
91039
|
-
* render request and layoutEpoch >= docEpoch, a render event will be scheduled.
|
|
91040
|
-
*/
|
|
91041
|
-
onLayoutComplete(layoutEpoch) {
|
|
91042
|
-
this.#layoutUpdating = false;
|
|
91043
|
-
if (Number.isFinite(layoutEpoch) && layoutEpoch >= 0) {
|
|
91044
|
-
this.#layoutEpoch = layoutEpoch;
|
|
90743
|
+
let endPos = pos;
|
|
90744
|
+
while (endPos < parentEnd) {
|
|
90745
|
+
const nextChar = state.doc.textBetween(endPos, endPos + 1, "\0", "\0");
|
|
90746
|
+
if (!isWordCharacter(nextChar)) {
|
|
90747
|
+
break;
|
|
91045
90748
|
}
|
|
91046
|
-
|
|
90749
|
+
endPos += 1;
|
|
91047
90750
|
}
|
|
91048
|
-
|
|
91049
|
-
|
|
91050
|
-
*
|
|
91051
|
-
* @remarks
|
|
91052
|
-
* Marks the layout as no longer updating (without updating layoutEpoch) and attempts
|
|
91053
|
-
* to schedule rendering if conditions are safe.
|
|
91054
|
-
*
|
|
91055
|
-
* Use this when layout computation is cancelled or fails partway through.
|
|
91056
|
-
*/
|
|
91057
|
-
onLayoutAbort() {
|
|
91058
|
-
this.#layoutUpdating = false;
|
|
91059
|
-
this.#maybeSchedule();
|
|
90751
|
+
if (startPos === endPos) {
|
|
90752
|
+
return null;
|
|
91060
90753
|
}
|
|
91061
|
-
|
|
91062
|
-
|
|
91063
|
-
|
|
91064
|
-
|
|
91065
|
-
|
|
91066
|
-
|
|
91067
|
-
|
|
91068
|
-
|
|
91069
|
-
|
|
91070
|
-
|
|
91071
|
-
|
|
91072
|
-
|
|
91073
|
-
|
|
91074
|
-
|
|
91075
|
-
|
|
91076
|
-
|
|
91077
|
-
|
|
91078
|
-
|
|
91079
|
-
this.flushNow();
|
|
90754
|
+
return { from: startPos, to: endPos };
|
|
90755
|
+
}
|
|
90756
|
+
function computeParagraphSelectionRangeAt(state, pos) {
|
|
90757
|
+
if (!state?.doc) {
|
|
90758
|
+
return null;
|
|
90759
|
+
}
|
|
90760
|
+
const textblockPos = findNearestTextblockResolvedPos(state.doc, pos);
|
|
90761
|
+
if (!textblockPos) {
|
|
90762
|
+
return null;
|
|
90763
|
+
}
|
|
90764
|
+
return { from: textblockPos.start(), to: textblockPos.end() };
|
|
90765
|
+
}
|
|
90766
|
+
function findNearestTextblockResolvedPos(doc2, pos) {
|
|
90767
|
+
const $pos = doc2.resolve(pos);
|
|
90768
|
+
let textblockPos = $pos;
|
|
90769
|
+
while (textblockPos.depth > 0) {
|
|
90770
|
+
if (textblockPos.parent?.isTextblock) {
|
|
90771
|
+
break;
|
|
91080
90772
|
}
|
|
91081
|
-
|
|
90773
|
+
if (!textblockPos.parent || textblockPos.depth === 0) {
|
|
90774
|
+
break;
|
|
90775
|
+
}
|
|
90776
|
+
const beforePos = textblockPos.before();
|
|
90777
|
+
if (beforePos < 0 || beforePos > doc2.content.size) {
|
|
90778
|
+
return null;
|
|
90779
|
+
}
|
|
90780
|
+
textblockPos = doc2.resolve(beforePos);
|
|
91082
90781
|
}
|
|
91083
|
-
|
|
91084
|
-
|
|
91085
|
-
*
|
|
91086
|
-
* @remarks
|
|
91087
|
-
* If there's a pending render request and conditions are safe (layout not updating,
|
|
91088
|
-
* layoutEpoch >= docEpoch), this method:
|
|
91089
|
-
* 1. Cancels any scheduled asynchronous render
|
|
91090
|
-
* 2. Clears the pending flag
|
|
91091
|
-
* 3. Emits the 'render' event synchronously
|
|
91092
|
-
*
|
|
91093
|
-
* If no render is pending or conditions are not safe, this is a no-op.
|
|
91094
|
-
*
|
|
91095
|
-
* Use this for immediate selection updates in response to user actions (e.g., click
|
|
91096
|
-
* handlers) where waiting for the next animation frame would cause noticeable lag.
|
|
91097
|
-
*/
|
|
91098
|
-
flushNow() {
|
|
91099
|
-
if (!this.#pending) return;
|
|
91100
|
-
if (!this.#isSafeToRender()) return;
|
|
91101
|
-
this.#cancelScheduledRender();
|
|
91102
|
-
this.#pending = false;
|
|
91103
|
-
this.emit("render", { docEpoch: this.#docEpoch, layoutEpoch: this.#layoutEpoch });
|
|
90782
|
+
if (!textblockPos.parent?.isTextblock) {
|
|
90783
|
+
return null;
|
|
91104
90784
|
}
|
|
91105
|
-
|
|
91106
|
-
|
|
91107
|
-
|
|
91108
|
-
|
|
91109
|
-
|
|
91110
|
-
|
|
91111
|
-
*
|
|
91112
|
-
* Safe to call multiple times.
|
|
91113
|
-
*/
|
|
91114
|
-
destroy() {
|
|
91115
|
-
this.#cancelScheduledRender();
|
|
91116
|
-
this.removeAllListeners();
|
|
90785
|
+
return textblockPos;
|
|
90786
|
+
}
|
|
90787
|
+
function getCellPosFromTableHit(tableHit, doc2, blocks2) {
|
|
90788
|
+
if (!tableHit || !tableHit.block || typeof tableHit.block.id !== "string") {
|
|
90789
|
+
console.warn("[getCellPosFromTableHit] Invalid tableHit input:", tableHit);
|
|
90790
|
+
return null;
|
|
91117
90791
|
}
|
|
91118
|
-
|
|
91119
|
-
|
|
90792
|
+
if (typeof tableHit.cellRowIndex !== "number" || typeof tableHit.cellColIndex !== "number" || tableHit.cellRowIndex < 0 || tableHit.cellColIndex < 0) {
|
|
90793
|
+
console.warn("[getCellPosFromTableHit] Invalid cell indices:", {
|
|
90794
|
+
row: tableHit.cellRowIndex,
|
|
90795
|
+
col: tableHit.cellColIndex
|
|
90796
|
+
});
|
|
90797
|
+
return null;
|
|
91120
90798
|
}
|
|
91121
|
-
|
|
91122
|
-
|
|
91123
|
-
|
|
91124
|
-
|
|
91125
|
-
|
|
91126
|
-
|
|
91127
|
-
|
|
91128
|
-
|
|
91129
|
-
if (
|
|
91130
|
-
|
|
91131
|
-
|
|
91132
|
-
|
|
90799
|
+
if (!doc2) return null;
|
|
90800
|
+
const tableBlocks = blocks2.filter((b2) => b2.kind === "table");
|
|
90801
|
+
const targetTableIndex = tableBlocks.findIndex((b2) => b2.id === tableHit.block.id);
|
|
90802
|
+
if (targetTableIndex === -1) return null;
|
|
90803
|
+
let tablePos = null;
|
|
90804
|
+
let currentTableIndex = 0;
|
|
90805
|
+
try {
|
|
90806
|
+
doc2.descendants((node2, pos) => {
|
|
90807
|
+
if (node2.type.name === "table") {
|
|
90808
|
+
if (currentTableIndex === targetTableIndex) {
|
|
90809
|
+
tablePos = pos;
|
|
90810
|
+
return false;
|
|
90811
|
+
}
|
|
90812
|
+
currentTableIndex++;
|
|
90813
|
+
}
|
|
90814
|
+
return true;
|
|
91133
90815
|
});
|
|
90816
|
+
} catch (error) {
|
|
90817
|
+
console.error("[getCellPosFromTableHit] Error during document traversal:", error);
|
|
90818
|
+
return null;
|
|
91134
90819
|
}
|
|
91135
|
-
|
|
91136
|
-
|
|
91137
|
-
|
|
91138
|
-
|
|
91139
|
-
|
|
91140
|
-
|
|
90820
|
+
if (tablePos === null) return null;
|
|
90821
|
+
const tableNode = doc2.nodeAt(tablePos);
|
|
90822
|
+
if (!tableNode || tableNode.type.name !== "table") return null;
|
|
90823
|
+
const targetRowIndex = tableHit.cellRowIndex;
|
|
90824
|
+
const targetColIndex = tableHit.cellColIndex;
|
|
90825
|
+
if (targetRowIndex >= tableNode.childCount) {
|
|
90826
|
+
console.warn("[getCellPosFromTableHit] Target row index out of bounds:", {
|
|
90827
|
+
targetRowIndex,
|
|
90828
|
+
tableChildCount: tableNode.childCount
|
|
90829
|
+
});
|
|
90830
|
+
return null;
|
|
90831
|
+
}
|
|
90832
|
+
let currentPos = tablePos + 1;
|
|
90833
|
+
for (let r2 = 0; r2 < tableNode.childCount && r2 <= targetRowIndex; r2++) {
|
|
90834
|
+
const row2 = tableNode.child(r2);
|
|
90835
|
+
if (r2 === targetRowIndex) {
|
|
90836
|
+
currentPos += 1;
|
|
90837
|
+
let logicalCol = 0;
|
|
90838
|
+
for (let cellIndex = 0; cellIndex < row2.childCount; cellIndex++) {
|
|
90839
|
+
const cell2 = row2.child(cellIndex);
|
|
90840
|
+
const rawColspan = cell2.attrs?.colspan;
|
|
90841
|
+
const colspan = typeof rawColspan === "number" && Number.isFinite(rawColspan) && rawColspan > 0 ? rawColspan : 1;
|
|
90842
|
+
if (targetColIndex >= logicalCol && targetColIndex < logicalCol + colspan) {
|
|
90843
|
+
return currentPos;
|
|
90844
|
+
}
|
|
90845
|
+
currentPos += cell2.nodeSize;
|
|
90846
|
+
logicalCol += colspan;
|
|
91141
90847
|
}
|
|
90848
|
+
console.warn("[getCellPosFromTableHit] Target column not found in row:", {
|
|
90849
|
+
targetColIndex,
|
|
90850
|
+
logicalColReached: logicalCol,
|
|
90851
|
+
rowCellCount: row2.childCount
|
|
90852
|
+
});
|
|
90853
|
+
return null;
|
|
90854
|
+
} else {
|
|
90855
|
+
currentPos += row2.nodeSize;
|
|
91142
90856
|
}
|
|
91143
|
-
this.#scheduled = false;
|
|
91144
90857
|
}
|
|
90858
|
+
return null;
|
|
91145
90859
|
}
|
|
91146
|
-
|
|
91147
|
-
|
|
90860
|
+
function getTablePosFromHit(tableHit, doc2, blocks2) {
|
|
90861
|
+
if (!doc2) return null;
|
|
90862
|
+
const tableBlocks = blocks2.filter((b2) => b2.kind === "table");
|
|
90863
|
+
const targetTableIndex = tableBlocks.findIndex((b2) => b2.id === tableHit.block.id);
|
|
90864
|
+
if (targetTableIndex === -1) return null;
|
|
90865
|
+
let tablePos = null;
|
|
90866
|
+
let currentTableIndex = 0;
|
|
90867
|
+
doc2.descendants((node2, pos) => {
|
|
90868
|
+
if (node2.type.name === "table") {
|
|
90869
|
+
if (currentTableIndex === targetTableIndex) {
|
|
90870
|
+
tablePos = pos;
|
|
90871
|
+
return false;
|
|
90872
|
+
}
|
|
90873
|
+
currentTableIndex++;
|
|
90874
|
+
}
|
|
90875
|
+
return true;
|
|
90876
|
+
});
|
|
90877
|
+
return tablePos;
|
|
90878
|
+
}
|
|
90879
|
+
function shouldUseCellSelection(currentTableHit, cellAnchor, cellDragMode) {
|
|
90880
|
+
if (!cellAnchor) return false;
|
|
90881
|
+
if (!currentTableHit) return cellDragMode === "active";
|
|
90882
|
+
if (currentTableHit.block.id !== cellAnchor.tableBlockId) {
|
|
90883
|
+
return cellDragMode === "active";
|
|
90884
|
+
}
|
|
90885
|
+
const sameCell = currentTableHit.cellRowIndex === cellAnchor.cellRowIndex && currentTableHit.cellColIndex === cellAnchor.cellColIndex;
|
|
90886
|
+
if (!sameCell) {
|
|
90887
|
+
return true;
|
|
90888
|
+
}
|
|
90889
|
+
return cellDragMode === "active";
|
|
90890
|
+
}
|
|
90891
|
+
function hitTestTable(layout, blocks2, measures, normalizedX, normalizedY, configuredPageHeight, pageGapFallback, geometryHelper) {
|
|
90892
|
+
if (!layout) {
|
|
90893
|
+
return null;
|
|
90894
|
+
}
|
|
90895
|
+
let pageY = 0;
|
|
90896
|
+
let pageHit = null;
|
|
90897
|
+
if (geometryHelper) {
|
|
90898
|
+
const idx = geometryHelper.getPageIndexAtY(normalizedY) ?? geometryHelper.getNearestPageIndex(normalizedY);
|
|
90899
|
+
if (idx != null && layout.pages[idx]) {
|
|
90900
|
+
pageHit = { pageIndex: idx, page: layout.pages[idx] };
|
|
90901
|
+
pageY = geometryHelper.getPageTop(idx);
|
|
90902
|
+
}
|
|
90903
|
+
}
|
|
90904
|
+
if (!pageHit) {
|
|
90905
|
+
const gap = layout.pageGap ?? pageGapFallback;
|
|
90906
|
+
for (let i2 = 0; i2 < layout.pages.length; i2++) {
|
|
90907
|
+
const page = layout.pages[i2];
|
|
90908
|
+
const pageHeight = page.size?.h ?? configuredPageHeight;
|
|
90909
|
+
if (normalizedY >= pageY && normalizedY < pageY + pageHeight) {
|
|
90910
|
+
pageHit = { pageIndex: i2, page };
|
|
90911
|
+
break;
|
|
90912
|
+
}
|
|
90913
|
+
pageY += pageHeight + gap;
|
|
90914
|
+
}
|
|
90915
|
+
}
|
|
90916
|
+
if (!pageHit) {
|
|
90917
|
+
return null;
|
|
90918
|
+
}
|
|
90919
|
+
const pageRelativeY = normalizedY - pageY;
|
|
90920
|
+
const point2 = { x: normalizedX, y: pageRelativeY };
|
|
90921
|
+
return hitTestTableFragment(pageHit, blocks2, measures, point2);
|
|
90922
|
+
}
|
|
90923
|
+
const MULTI_CLICK_TIME_THRESHOLD_MS = 400;
|
|
90924
|
+
const MULTI_CLICK_DISTANCE_THRESHOLD_PX = 5;
|
|
90925
|
+
class EditorInputManager {
|
|
90926
|
+
// Dependencies
|
|
90927
|
+
#deps = null;
|
|
90928
|
+
#callbacks = {};
|
|
90929
|
+
// Drag selection state
|
|
90930
|
+
#isDragging = false;
|
|
90931
|
+
#dragAnchor = null;
|
|
90932
|
+
#dragAnchorPageIndex = null;
|
|
90933
|
+
#dragExtensionMode = "char";
|
|
90934
|
+
#dragLastPointer = null;
|
|
90935
|
+
#dragLastRawHit = null;
|
|
90936
|
+
#dragUsedPageNotMountedFallback = false;
|
|
90937
|
+
// Click tracking for multi-click detection
|
|
90938
|
+
#clickCount = 0;
|
|
90939
|
+
#lastClickTime = 0;
|
|
90940
|
+
#lastClickPosition = null;
|
|
90941
|
+
// Cell selection state
|
|
90942
|
+
#cellAnchor = null;
|
|
90943
|
+
#cellDragMode = "none";
|
|
90944
|
+
// Margin click state
|
|
90945
|
+
#pendingMarginClick = null;
|
|
90946
|
+
// Image selection state
|
|
90947
|
+
#lastSelectedImageBlockId = null;
|
|
90948
|
+
// Focus suppression (for draggable annotations)
|
|
90949
|
+
#suppressFocusInFromDraggable = false;
|
|
90950
|
+
// Debug state
|
|
90951
|
+
#debugLastPointer = null;
|
|
90952
|
+
#debugLastHit = null;
|
|
90953
|
+
// Bound handlers for event listener cleanup
|
|
90954
|
+
#boundHandlePointerDown = null;
|
|
90955
|
+
#boundHandlePointerMove = null;
|
|
90956
|
+
#boundHandlePointerUp = null;
|
|
90957
|
+
#boundHandlePointerLeave = null;
|
|
90958
|
+
#boundHandleDoubleClick = null;
|
|
90959
|
+
#boundHandleClick = null;
|
|
90960
|
+
#boundHandleKeyDown = null;
|
|
90961
|
+
#boundHandleFocusIn = null;
|
|
90962
|
+
// ==========================================================================
|
|
90963
|
+
// Constructor
|
|
90964
|
+
// ==========================================================================
|
|
90965
|
+
constructor() {
|
|
90966
|
+
}
|
|
90967
|
+
// ==========================================================================
|
|
90968
|
+
// Setup Methods
|
|
90969
|
+
// ==========================================================================
|
|
90970
|
+
/**
|
|
90971
|
+
* Set dependencies from PresentationEditor.
|
|
90972
|
+
*/
|
|
90973
|
+
setDependencies(deps) {
|
|
90974
|
+
this.#deps = deps;
|
|
90975
|
+
}
|
|
90976
|
+
/**
|
|
90977
|
+
* Set callbacks for events.
|
|
90978
|
+
*/
|
|
90979
|
+
setCallbacks(callbacks2) {
|
|
90980
|
+
this.#callbacks = callbacks2;
|
|
90981
|
+
}
|
|
90982
|
+
/**
|
|
90983
|
+
* Bind event listeners to DOM elements.
|
|
90984
|
+
*/
|
|
90985
|
+
bind() {
|
|
90986
|
+
if (!this.#deps) return;
|
|
90987
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
90988
|
+
const visibleHost = this.#deps.getVisibleHost();
|
|
90989
|
+
viewportHost.ownerDocument ?? document;
|
|
90990
|
+
this.#boundHandlePointerDown = this.#handlePointerDown.bind(this);
|
|
90991
|
+
this.#boundHandlePointerMove = this.#handlePointerMove.bind(this);
|
|
90992
|
+
this.#boundHandlePointerUp = this.#handlePointerUp.bind(this);
|
|
90993
|
+
this.#boundHandlePointerLeave = this.#handlePointerLeave.bind(this);
|
|
90994
|
+
this.#boundHandleDoubleClick = this.#handleDoubleClick.bind(this);
|
|
90995
|
+
this.#boundHandleClick = this.#handleClick.bind(this);
|
|
90996
|
+
this.#boundHandleKeyDown = this.#handleKeyDown.bind(this);
|
|
90997
|
+
this.#boundHandleFocusIn = this.#handleFocusIn.bind(this);
|
|
90998
|
+
viewportHost.addEventListener("pointerdown", this.#boundHandlePointerDown);
|
|
90999
|
+
viewportHost.addEventListener("pointermove", this.#boundHandlePointerMove);
|
|
91000
|
+
viewportHost.addEventListener("pointerup", this.#boundHandlePointerUp);
|
|
91001
|
+
viewportHost.addEventListener("pointerleave", this.#boundHandlePointerLeave);
|
|
91002
|
+
viewportHost.addEventListener("dblclick", this.#boundHandleDoubleClick);
|
|
91003
|
+
viewportHost.addEventListener("click", this.#boundHandleClick);
|
|
91004
|
+
const container = viewportHost.closest(".presentation-editor");
|
|
91005
|
+
if (container) {
|
|
91006
|
+
container.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
91007
|
+
}
|
|
91008
|
+
visibleHost.addEventListener("focusin", this.#boundHandleFocusIn);
|
|
91009
|
+
}
|
|
91010
|
+
/**
|
|
91011
|
+
* Unbind event listeners.
|
|
91012
|
+
*/
|
|
91013
|
+
unbind() {
|
|
91014
|
+
if (!this.#deps) return;
|
|
91015
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
91016
|
+
const visibleHost = this.#deps.getVisibleHost();
|
|
91017
|
+
if (this.#boundHandlePointerDown) {
|
|
91018
|
+
viewportHost.removeEventListener("pointerdown", this.#boundHandlePointerDown);
|
|
91019
|
+
}
|
|
91020
|
+
if (this.#boundHandlePointerMove) {
|
|
91021
|
+
viewportHost.removeEventListener("pointermove", this.#boundHandlePointerMove);
|
|
91022
|
+
}
|
|
91023
|
+
if (this.#boundHandlePointerUp) {
|
|
91024
|
+
viewportHost.removeEventListener("pointerup", this.#boundHandlePointerUp);
|
|
91025
|
+
}
|
|
91026
|
+
if (this.#boundHandlePointerLeave) {
|
|
91027
|
+
viewportHost.removeEventListener("pointerleave", this.#boundHandlePointerLeave);
|
|
91028
|
+
}
|
|
91029
|
+
if (this.#boundHandleDoubleClick) {
|
|
91030
|
+
viewportHost.removeEventListener("dblclick", this.#boundHandleDoubleClick);
|
|
91031
|
+
}
|
|
91032
|
+
if (this.#boundHandleClick) {
|
|
91033
|
+
viewportHost.removeEventListener("click", this.#boundHandleClick);
|
|
91034
|
+
}
|
|
91035
|
+
if (this.#boundHandleKeyDown) {
|
|
91036
|
+
const container = viewportHost.closest(".presentation-editor");
|
|
91037
|
+
if (container) {
|
|
91038
|
+
container.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
91039
|
+
}
|
|
91040
|
+
}
|
|
91041
|
+
if (this.#boundHandleFocusIn) {
|
|
91042
|
+
visibleHost.removeEventListener("focusin", this.#boundHandleFocusIn);
|
|
91043
|
+
}
|
|
91044
|
+
this.#boundHandlePointerDown = null;
|
|
91045
|
+
this.#boundHandlePointerMove = null;
|
|
91046
|
+
this.#boundHandlePointerUp = null;
|
|
91047
|
+
this.#boundHandlePointerLeave = null;
|
|
91048
|
+
this.#boundHandleDoubleClick = null;
|
|
91049
|
+
this.#boundHandleClick = null;
|
|
91050
|
+
this.#boundHandleKeyDown = null;
|
|
91051
|
+
this.#boundHandleFocusIn = null;
|
|
91052
|
+
}
|
|
91053
|
+
/**
|
|
91054
|
+
* Destroy the manager and clean up.
|
|
91055
|
+
*/
|
|
91056
|
+
destroy() {
|
|
91057
|
+
this.unbind();
|
|
91058
|
+
this.#deps = null;
|
|
91059
|
+
this.#callbacks = {};
|
|
91060
|
+
this.#clearDragState();
|
|
91061
|
+
this.#clearCellAnchor();
|
|
91062
|
+
}
|
|
91063
|
+
// ==========================================================================
|
|
91064
|
+
// Public Getters
|
|
91065
|
+
// ==========================================================================
|
|
91066
|
+
/** Whether currently dragging */
|
|
91067
|
+
get isDragging() {
|
|
91068
|
+
return this.#isDragging;
|
|
91069
|
+
}
|
|
91070
|
+
/** Current drag anchor position */
|
|
91071
|
+
get dragAnchor() {
|
|
91072
|
+
return this.#dragAnchor;
|
|
91073
|
+
}
|
|
91074
|
+
/** Cell anchor state for table selection */
|
|
91075
|
+
get cellAnchor() {
|
|
91076
|
+
return this.#cellAnchor;
|
|
91077
|
+
}
|
|
91078
|
+
/** Debug last pointer position */
|
|
91079
|
+
get debugLastPointer() {
|
|
91080
|
+
return this.#debugLastPointer;
|
|
91081
|
+
}
|
|
91082
|
+
/** Debug last hit */
|
|
91083
|
+
get debugLastHit() {
|
|
91084
|
+
return this.#debugLastHit;
|
|
91085
|
+
}
|
|
91086
|
+
/** Last selected image block ID */
|
|
91087
|
+
get lastSelectedImageBlockId() {
|
|
91088
|
+
return this.#lastSelectedImageBlockId;
|
|
91089
|
+
}
|
|
91090
|
+
/** Drag anchor page index */
|
|
91091
|
+
get dragAnchorPageIndex() {
|
|
91092
|
+
return this.#dragAnchorPageIndex;
|
|
91093
|
+
}
|
|
91094
|
+
/** Get the page index from the last raw hit during drag */
|
|
91095
|
+
get dragLastHitPageIndex() {
|
|
91096
|
+
return this.#dragLastRawHit?.pageIndex ?? null;
|
|
91097
|
+
}
|
|
91098
|
+
/** Get the last raw hit during drag (for finalization) */
|
|
91099
|
+
get dragLastRawHit() {
|
|
91100
|
+
return this.#dragLastRawHit;
|
|
91101
|
+
}
|
|
91102
|
+
// ==========================================================================
|
|
91103
|
+
// Public Methods
|
|
91104
|
+
// ==========================================================================
|
|
91105
|
+
/**
|
|
91106
|
+
* Clear cell anchor (used when document changes).
|
|
91107
|
+
*/
|
|
91108
|
+
clearCellAnchor() {
|
|
91109
|
+
this.#clearCellAnchor();
|
|
91110
|
+
}
|
|
91111
|
+
/**
|
|
91112
|
+
* Set suppress focus in flag (for draggable annotations).
|
|
91113
|
+
*/
|
|
91114
|
+
setSuppressFocusInFromDraggable(value) {
|
|
91115
|
+
this.#suppressFocusInFromDraggable = value;
|
|
91116
|
+
}
|
|
91117
|
+
// ==========================================================================
|
|
91118
|
+
// Private Helper Methods
|
|
91119
|
+
// ==========================================================================
|
|
91120
|
+
#clearDragState() {
|
|
91121
|
+
this.#isDragging = false;
|
|
91122
|
+
this.#dragAnchor = null;
|
|
91123
|
+
this.#dragAnchorPageIndex = null;
|
|
91124
|
+
this.#dragExtensionMode = "char";
|
|
91125
|
+
this.#dragLastPointer = null;
|
|
91126
|
+
this.#dragLastRawHit = null;
|
|
91127
|
+
this.#dragUsedPageNotMountedFallback = false;
|
|
91128
|
+
}
|
|
91129
|
+
#clearCellAnchor() {
|
|
91130
|
+
this.#cellAnchor = null;
|
|
91131
|
+
this.#cellDragMode = "none";
|
|
91132
|
+
}
|
|
91133
|
+
#registerPointerClick(event) {
|
|
91134
|
+
const nextState = registerPointerClick(
|
|
91135
|
+
event,
|
|
91136
|
+
{
|
|
91137
|
+
clickCount: this.#clickCount,
|
|
91138
|
+
lastClickTime: this.#lastClickTime,
|
|
91139
|
+
lastClickPosition: this.#lastClickPosition ?? { x: 0, y: 0 }
|
|
91140
|
+
},
|
|
91141
|
+
{
|
|
91142
|
+
timeThresholdMs: MULTI_CLICK_TIME_THRESHOLD_MS,
|
|
91143
|
+
distanceThresholdPx: MULTI_CLICK_DISTANCE_THRESHOLD_PX,
|
|
91144
|
+
maxClickCount: 3
|
|
91145
|
+
}
|
|
91146
|
+
);
|
|
91147
|
+
this.#clickCount = nextState.clickCount;
|
|
91148
|
+
this.#lastClickTime = nextState.lastClickTime;
|
|
91149
|
+
this.#lastClickPosition = nextState.lastClickPosition;
|
|
91150
|
+
return nextState.clickCount;
|
|
91151
|
+
}
|
|
91152
|
+
#getFirstTextPosition() {
|
|
91153
|
+
const editor = this.#deps?.getEditor();
|
|
91154
|
+
return getFirstTextPosition(editor?.state?.doc ?? null);
|
|
91155
|
+
}
|
|
91156
|
+
#calculateExtendedSelection(anchor, head, mode) {
|
|
91157
|
+
const layoutState = this.#deps?.getLayoutState();
|
|
91158
|
+
return calculateExtendedSelection(layoutState?.blocks ?? [], anchor, head, mode);
|
|
91159
|
+
}
|
|
91160
|
+
#shouldUseCellSelection(currentTableHit) {
|
|
91161
|
+
return shouldUseCellSelection(currentTableHit, this.#cellAnchor, this.#cellDragMode);
|
|
91162
|
+
}
|
|
91163
|
+
#getCellPosFromTableHit(tableHit) {
|
|
91164
|
+
const editor = this.#deps?.getEditor();
|
|
91165
|
+
const layoutState = this.#deps?.getLayoutState();
|
|
91166
|
+
return getCellPosFromTableHit(tableHit, editor?.state?.doc ?? null, layoutState?.blocks ?? []);
|
|
91167
|
+
}
|
|
91168
|
+
#getTablePosFromHit(tableHit) {
|
|
91169
|
+
const editor = this.#deps?.getEditor();
|
|
91170
|
+
const layoutState = this.#deps?.getLayoutState();
|
|
91171
|
+
return getTablePosFromHit(tableHit, editor?.state?.doc ?? null, layoutState?.blocks ?? []);
|
|
91172
|
+
}
|
|
91173
|
+
#setCellAnchor(tableHit, tablePos) {
|
|
91174
|
+
const cellPos = this.#getCellPosFromTableHit(tableHit);
|
|
91175
|
+
if (cellPos === null) return;
|
|
91176
|
+
this.#cellAnchor = {
|
|
91177
|
+
tablePos,
|
|
91178
|
+
cellPos,
|
|
91179
|
+
cellRowIndex: tableHit.cellRowIndex,
|
|
91180
|
+
cellColIndex: tableHit.cellColIndex,
|
|
91181
|
+
tableBlockId: tableHit.block.id
|
|
91182
|
+
};
|
|
91183
|
+
this.#cellDragMode = "pending";
|
|
91184
|
+
}
|
|
91185
|
+
#hitTestTable(x2, y2) {
|
|
91186
|
+
return this.#callbacks.hitTestTable?.(x2, y2) ?? null;
|
|
91187
|
+
}
|
|
91188
|
+
// ==========================================================================
|
|
91189
|
+
// Event Handlers
|
|
91190
|
+
// ==========================================================================
|
|
91191
|
+
/**
|
|
91192
|
+
* Handle click events - specifically for link navigation prevention.
|
|
91193
|
+
*
|
|
91194
|
+
* Link handling is split between pointerdown and click:
|
|
91195
|
+
* - pointerdown: dispatches superdoc-link-click event (for popover/UI response)
|
|
91196
|
+
* - click: prevents default navigation (preventDefault only works on click, not pointerdown)
|
|
91197
|
+
*
|
|
91198
|
+
* This also handles keyboard activation (Enter/Space) which triggers click but not pointerdown.
|
|
91199
|
+
*/
|
|
91200
|
+
#handleClick(event) {
|
|
91201
|
+
const target = event.target;
|
|
91202
|
+
const linkEl = target?.closest?.("a.superdoc-link");
|
|
91203
|
+
if (linkEl) {
|
|
91204
|
+
event.preventDefault();
|
|
91205
|
+
if (!event.pointerId && event.detail === 0) {
|
|
91206
|
+
this.#handleLinkClick(event, linkEl);
|
|
91207
|
+
}
|
|
91208
|
+
}
|
|
91209
|
+
}
|
|
91210
|
+
#handlePointerDown(event) {
|
|
91211
|
+
if (!this.#deps) return;
|
|
91212
|
+
if (event.button !== 0) return;
|
|
91213
|
+
if (event.ctrlKey && navigator.platform.includes("Mac")) return;
|
|
91214
|
+
this.#pendingMarginClick = null;
|
|
91215
|
+
const target = event.target;
|
|
91216
|
+
if (target?.closest?.(".superdoc-ruler-handle") != null) return;
|
|
91217
|
+
const linkEl = target?.closest?.("a.superdoc-link");
|
|
91218
|
+
if (linkEl) {
|
|
91219
|
+
this.#handleLinkClick(event, linkEl);
|
|
91220
|
+
return;
|
|
91221
|
+
}
|
|
91222
|
+
const annotationEl = target?.closest?.(".annotation[data-pm-start]");
|
|
91223
|
+
const isDraggableAnnotation = target?.closest?.('[data-draggable="true"]') != null;
|
|
91224
|
+
this.#suppressFocusInFromDraggable = isDraggableAnnotation;
|
|
91225
|
+
if (annotationEl) {
|
|
91226
|
+
this.#handleAnnotationClick(event, annotationEl);
|
|
91227
|
+
return;
|
|
91228
|
+
}
|
|
91229
|
+
const layoutState = this.#deps.getLayoutState();
|
|
91230
|
+
if (!layoutState.layout) {
|
|
91231
|
+
this.#handleClickWithoutLayout(event, isDraggableAnnotation);
|
|
91232
|
+
return;
|
|
91233
|
+
}
|
|
91234
|
+
const normalizedPoint = this.#callbacks.normalizeClientPoint?.(event.clientX, event.clientY);
|
|
91235
|
+
if (!normalizedPoint) return;
|
|
91236
|
+
const { x: x2, y: y2 } = normalizedPoint;
|
|
91237
|
+
this.#debugLastPointer = { clientX: event.clientX, clientY: event.clientY, x: x2, y: y2 };
|
|
91238
|
+
const sessionMode = this.#deps.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
91239
|
+
if (sessionMode !== "body") {
|
|
91240
|
+
if (this.#handleClickInHeaderFooterMode(event, x2, y2)) return;
|
|
91241
|
+
}
|
|
91242
|
+
const headerFooterRegion = this.#callbacks.hitTestHeaderFooterRegion?.(x2, y2);
|
|
91243
|
+
if (headerFooterRegion) return;
|
|
91244
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
91245
|
+
const pageGeometryHelper = this.#deps.getPageGeometryHelper();
|
|
91246
|
+
const rawHit = clickToPosition(
|
|
91247
|
+
layoutState.layout,
|
|
91248
|
+
layoutState.blocks,
|
|
91249
|
+
layoutState.measures,
|
|
91250
|
+
{ x: x2, y: y2 },
|
|
91251
|
+
viewportHost,
|
|
91252
|
+
event.clientX,
|
|
91253
|
+
event.clientY,
|
|
91254
|
+
pageGeometryHelper ?? void 0
|
|
91255
|
+
);
|
|
91256
|
+
const editor = this.#deps.getEditor();
|
|
91257
|
+
const doc2 = editor.state?.doc;
|
|
91258
|
+
const epochMapper = this.#deps.getEpochMapper();
|
|
91259
|
+
const mapped = rawHit && doc2 ? epochMapper.mapPosFromLayoutToCurrentDetailed(rawHit.pos, rawHit.layoutEpoch, 1) : null;
|
|
91260
|
+
if (mapped && !mapped.ok) {
|
|
91261
|
+
debugLog("warn", "pointerdown mapping failed", mapped);
|
|
91262
|
+
}
|
|
91263
|
+
const hit = rawHit && doc2 && mapped?.ok ? { ...rawHit, pos: Math.max(0, Math.min(mapped.pos, doc2.content.size)), layoutEpoch: mapped.toEpoch } : null;
|
|
91264
|
+
this.#debugLastHit = hit ? { source: "dom", pos: rawHit?.pos ?? null, layoutEpoch: rawHit?.layoutEpoch ?? null, mappedPos: hit.pos } : { source: "none", pos: rawHit?.pos ?? null, layoutEpoch: rawHit?.layoutEpoch ?? null, mappedPos: null };
|
|
91265
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
91266
|
+
if (!isDraggableAnnotation) {
|
|
91267
|
+
event.preventDefault();
|
|
91268
|
+
}
|
|
91269
|
+
if (!rawHit) {
|
|
91270
|
+
this.#focusEditorAtFirstPosition();
|
|
91271
|
+
return;
|
|
91272
|
+
}
|
|
91273
|
+
if (!hit || !doc2) {
|
|
91274
|
+
this.#callbacks.setPendingDocChange?.();
|
|
91275
|
+
this.#callbacks.scheduleRerender?.();
|
|
91276
|
+
return;
|
|
91277
|
+
}
|
|
91278
|
+
const fragmentHit = getFragmentAtPosition(layoutState.layout, layoutState.blocks, layoutState.measures, rawHit.pos);
|
|
91279
|
+
const targetImg = event.target?.closest?.("img");
|
|
91280
|
+
if (this.#handleInlineImageClick(event, targetImg, rawHit, doc2, epochMapper)) return;
|
|
91281
|
+
if (this.#handleFragmentClick(event, fragmentHit, hit, doc2)) return;
|
|
91282
|
+
if (this.#lastSelectedImageBlockId) {
|
|
91283
|
+
this.#callbacks.emit?.("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
91284
|
+
this.#lastSelectedImageBlockId = null;
|
|
91285
|
+
}
|
|
91286
|
+
if (event.shiftKey && editor.state.selection.$anchor) {
|
|
91287
|
+
this.#handleShiftClick(event, hit.pos);
|
|
91288
|
+
return;
|
|
91289
|
+
}
|
|
91290
|
+
const clickDepth = this.#registerPointerClick(event);
|
|
91291
|
+
if (clickDepth === 1) {
|
|
91292
|
+
this.#dragAnchor = hit.pos;
|
|
91293
|
+
this.#dragAnchorPageIndex = hit.pageIndex;
|
|
91294
|
+
this.#pendingMarginClick = this.#callbacks.computePendingMarginClick?.(event.pointerId, x2, y2) ?? null;
|
|
91295
|
+
const tableHit = this.#hitTestTable(x2, y2);
|
|
91296
|
+
if (tableHit) {
|
|
91297
|
+
const tablePos = this.#getTablePosFromHit(tableHit);
|
|
91298
|
+
if (tablePos !== null) {
|
|
91299
|
+
this.#setCellAnchor(tableHit, tablePos);
|
|
91300
|
+
}
|
|
91301
|
+
} else {
|
|
91302
|
+
this.#clearCellAnchor();
|
|
91303
|
+
}
|
|
91304
|
+
} else {
|
|
91305
|
+
this.#pendingMarginClick = null;
|
|
91306
|
+
}
|
|
91307
|
+
this.#dragLastPointer = { clientX: event.clientX, clientY: event.clientY, x: x2, y: y2 };
|
|
91308
|
+
this.#dragLastRawHit = hit;
|
|
91309
|
+
this.#dragUsedPageNotMountedFallback = false;
|
|
91310
|
+
this.#isDragging = true;
|
|
91311
|
+
if (clickDepth >= 3) {
|
|
91312
|
+
this.#dragExtensionMode = "para";
|
|
91313
|
+
} else if (clickDepth === 2) {
|
|
91314
|
+
this.#dragExtensionMode = "word";
|
|
91315
|
+
} else {
|
|
91316
|
+
this.#dragExtensionMode = "char";
|
|
91317
|
+
}
|
|
91318
|
+
if (typeof viewportHost.setPointerCapture === "function") {
|
|
91319
|
+
viewportHost.setPointerCapture(event.pointerId);
|
|
91320
|
+
}
|
|
91321
|
+
let handledByDepth = false;
|
|
91322
|
+
const sessionModeForDepth = this.#deps.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
91323
|
+
if (sessionModeForDepth === "body") {
|
|
91324
|
+
const selectionPos = clickDepth >= 2 && this.#dragAnchor !== null ? this.#dragAnchor : hit.pos;
|
|
91325
|
+
if (clickDepth >= 3) {
|
|
91326
|
+
handledByDepth = this.#callbacks.selectParagraphAt?.(selectionPos) ?? false;
|
|
91327
|
+
} else if (clickDepth === 2) {
|
|
91328
|
+
handledByDepth = this.#callbacks.selectWordAt?.(selectionPos) ?? false;
|
|
91329
|
+
}
|
|
91330
|
+
}
|
|
91331
|
+
if (!handledByDepth) {
|
|
91332
|
+
try {
|
|
91333
|
+
let nextSelection = TextSelection$1.create(doc2, hit.pos);
|
|
91334
|
+
if (!nextSelection.$from.parent.inlineContent) {
|
|
91335
|
+
nextSelection = Selection.near(doc2.resolve(hit.pos), 1);
|
|
91336
|
+
}
|
|
91337
|
+
const tr = editor.state.tr.setSelection(nextSelection);
|
|
91338
|
+
editor.view?.dispatch(tr);
|
|
91339
|
+
} catch {
|
|
91340
|
+
}
|
|
91341
|
+
}
|
|
91342
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
91343
|
+
this.#focusEditor();
|
|
91344
|
+
}
|
|
91345
|
+
#handlePointerMove(event) {
|
|
91346
|
+
if (!this.#deps) return;
|
|
91347
|
+
const layoutState = this.#deps.getLayoutState();
|
|
91348
|
+
if (!layoutState.layout) return;
|
|
91349
|
+
const normalized = this.#callbacks.normalizeClientPoint?.(event.clientX, event.clientY);
|
|
91350
|
+
if (!normalized) return;
|
|
91351
|
+
if (this.#isDragging && this.#dragAnchor !== null && event.buttons & 1) {
|
|
91352
|
+
this.#handleDragSelection(event, normalized);
|
|
91353
|
+
return;
|
|
91354
|
+
}
|
|
91355
|
+
this.#handleHover(normalized);
|
|
91356
|
+
}
|
|
91357
|
+
#handlePointerUp(event) {
|
|
91358
|
+
if (!this.#deps) return;
|
|
91359
|
+
this.#suppressFocusInFromDraggable = false;
|
|
91360
|
+
if (!this.#isDragging) return;
|
|
91361
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
91362
|
+
if (typeof viewportHost.hasPointerCapture === "function" && typeof viewportHost.releasePointerCapture === "function" && viewportHost.hasPointerCapture(event.pointerId)) {
|
|
91363
|
+
viewportHost.releasePointerCapture(event.pointerId);
|
|
91364
|
+
}
|
|
91365
|
+
const pendingMarginClick = this.#pendingMarginClick;
|
|
91366
|
+
this.#pendingMarginClick = null;
|
|
91367
|
+
const dragAnchor = this.#dragAnchor;
|
|
91368
|
+
const dragMode = this.#dragExtensionMode;
|
|
91369
|
+
const dragUsedFallback = this.#dragUsedPageNotMountedFallback;
|
|
91370
|
+
const dragPointer = this.#dragLastPointer;
|
|
91371
|
+
this.#isDragging = false;
|
|
91372
|
+
if (this.#cellDragMode !== "none") {
|
|
91373
|
+
this.#cellDragMode = "none";
|
|
91374
|
+
}
|
|
91375
|
+
if (!pendingMarginClick || pendingMarginClick.pointerId !== event.pointerId) {
|
|
91376
|
+
this.#callbacks.updateSelectionVirtualizationPins?.({ includeDragBuffer: false });
|
|
91377
|
+
if (dragUsedFallback && dragAnchor != null) {
|
|
91378
|
+
const pointer = dragPointer ?? { clientX: event.clientX, clientY: event.clientY };
|
|
91379
|
+
this.#callbacks.finalizeDragSelectionWithDom?.(pointer, dragAnchor, dragMode);
|
|
91380
|
+
}
|
|
91381
|
+
this.#callbacks.scheduleA11ySelectionAnnouncement?.({ immediate: true });
|
|
91382
|
+
this.#dragLastPointer = null;
|
|
91383
|
+
this.#dragLastRawHit = null;
|
|
91384
|
+
this.#dragUsedPageNotMountedFallback = false;
|
|
91385
|
+
return;
|
|
91386
|
+
}
|
|
91387
|
+
this.#handleMarginClickEnd(event, pendingMarginClick);
|
|
91388
|
+
}
|
|
91389
|
+
#handlePointerLeave() {
|
|
91390
|
+
this.#callbacks.clearHoverRegion?.();
|
|
91391
|
+
}
|
|
91392
|
+
#handleDoubleClick(event) {
|
|
91393
|
+
if (!this.#deps) return;
|
|
91394
|
+
if (event.button !== 0) return;
|
|
91395
|
+
const layoutState = this.#deps.getLayoutState();
|
|
91396
|
+
if (!layoutState.layout) return;
|
|
91397
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
91398
|
+
const visibleHost = this.#deps.getVisibleHost();
|
|
91399
|
+
const zoom = this.#deps.getZoom();
|
|
91400
|
+
const rect = viewportHost.getBoundingClientRect();
|
|
91401
|
+
const scrollLeft = visibleHost.scrollLeft ?? 0;
|
|
91402
|
+
const scrollTop = visibleHost.scrollTop ?? 0;
|
|
91403
|
+
const x2 = (event.clientX - rect.left + scrollLeft) / zoom;
|
|
91404
|
+
const y2 = (event.clientY - rect.top + scrollTop) / zoom;
|
|
91405
|
+
const region = this.#callbacks.hitTestHeaderFooterRegion?.(x2, y2);
|
|
91406
|
+
if (region) {
|
|
91407
|
+
event.preventDefault();
|
|
91408
|
+
event.stopPropagation();
|
|
91409
|
+
const descriptor = this.#callbacks.resolveDescriptorForRegion?.(region);
|
|
91410
|
+
const hfManager = this.#deps.getHeaderFooterSession()?.manager;
|
|
91411
|
+
if (!descriptor && hfManager) {
|
|
91412
|
+
this.#callbacks.createDefaultHeaderFooter?.(region);
|
|
91413
|
+
hfManager.refresh();
|
|
91414
|
+
}
|
|
91415
|
+
this.#callbacks.activateHeaderFooterRegion?.(region);
|
|
91416
|
+
} else if ((this.#deps.getHeaderFooterSession()?.session?.mode ?? "body") !== "body") {
|
|
91417
|
+
this.#callbacks.exitHeaderFooterMode?.();
|
|
91418
|
+
}
|
|
91419
|
+
}
|
|
91420
|
+
#handleKeyDown(event) {
|
|
91421
|
+
if (!this.#deps) return;
|
|
91422
|
+
const sessionMode = this.#deps.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
91423
|
+
if (event.key === "Escape" && sessionMode !== "body") {
|
|
91424
|
+
event.preventDefault();
|
|
91425
|
+
this.#callbacks.exitHeaderFooterMode?.();
|
|
91426
|
+
return;
|
|
91427
|
+
}
|
|
91428
|
+
if (event.ctrlKey && event.altKey && !event.shiftKey) {
|
|
91429
|
+
if (event.code === "KeyH") {
|
|
91430
|
+
event.preventDefault();
|
|
91431
|
+
this.#focusHeaderFooterShortcut("header");
|
|
91432
|
+
} else if (event.code === "KeyF") {
|
|
91433
|
+
event.preventDefault();
|
|
91434
|
+
this.#focusHeaderFooterShortcut("footer");
|
|
91435
|
+
}
|
|
91436
|
+
}
|
|
91437
|
+
}
|
|
91438
|
+
#handleFocusIn(event) {
|
|
91439
|
+
if (!this.#deps) return;
|
|
91440
|
+
if (this.#suppressFocusInFromDraggable) {
|
|
91441
|
+
this.#suppressFocusInFromDraggable = false;
|
|
91442
|
+
return;
|
|
91443
|
+
}
|
|
91444
|
+
try {
|
|
91445
|
+
this.#deps.getActiveEditor().view?.focus();
|
|
91446
|
+
} catch {
|
|
91447
|
+
}
|
|
91448
|
+
}
|
|
91449
|
+
// ==========================================================================
|
|
91450
|
+
// Handler Helpers
|
|
91451
|
+
// ==========================================================================
|
|
91452
|
+
#handleLinkClick(event, linkEl) {
|
|
91453
|
+
const href = linkEl.getAttribute("href") ?? "";
|
|
91454
|
+
const isAnchorLink = href.startsWith("#") && href.length > 1;
|
|
91455
|
+
const isTocLink = linkEl.closest(".superdoc-toc-entry") !== null;
|
|
91456
|
+
if (isAnchorLink && isTocLink) {
|
|
91457
|
+
event.preventDefault();
|
|
91458
|
+
event.stopPropagation();
|
|
91459
|
+
this.#callbacks.goToAnchor?.(href);
|
|
91460
|
+
return;
|
|
91461
|
+
}
|
|
91462
|
+
event.preventDefault();
|
|
91463
|
+
event.stopPropagation();
|
|
91464
|
+
const linkClickEvent = new CustomEvent("superdoc-link-click", {
|
|
91465
|
+
bubbles: true,
|
|
91466
|
+
composed: true,
|
|
91467
|
+
detail: {
|
|
91468
|
+
href,
|
|
91469
|
+
target: linkEl.getAttribute("target"),
|
|
91470
|
+
rel: linkEl.getAttribute("rel"),
|
|
91471
|
+
tooltip: linkEl.getAttribute("title"),
|
|
91472
|
+
element: linkEl,
|
|
91473
|
+
clientX: event.clientX,
|
|
91474
|
+
clientY: event.clientY
|
|
91475
|
+
}
|
|
91476
|
+
});
|
|
91477
|
+
linkEl.dispatchEvent(linkClickEvent);
|
|
91478
|
+
}
|
|
91479
|
+
#handleAnnotationClick(event, annotationEl) {
|
|
91480
|
+
const editor = this.#deps?.getEditor();
|
|
91481
|
+
if (!editor?.isEditable) return;
|
|
91482
|
+
const resolved = this.#callbacks.resolveFieldAnnotationSelectionFromElement?.(annotationEl);
|
|
91483
|
+
if (resolved) {
|
|
91484
|
+
try {
|
|
91485
|
+
const tr = editor.state.tr.setSelection(NodeSelection.create(editor.state.doc, resolved.pos));
|
|
91486
|
+
editor.view?.dispatch(tr);
|
|
91487
|
+
} catch {
|
|
91488
|
+
}
|
|
91489
|
+
editor.emit("fieldAnnotationClicked", {
|
|
91490
|
+
editor,
|
|
91491
|
+
node: resolved.node,
|
|
91492
|
+
nodePos: resolved.pos,
|
|
91493
|
+
event,
|
|
91494
|
+
currentTarget: annotationEl
|
|
91495
|
+
});
|
|
91496
|
+
}
|
|
91497
|
+
}
|
|
91498
|
+
#handleClickWithoutLayout(event, isDraggableAnnotation) {
|
|
91499
|
+
if (!isDraggableAnnotation) {
|
|
91500
|
+
event.preventDefault();
|
|
91501
|
+
}
|
|
91502
|
+
if (document.activeElement instanceof HTMLElement) {
|
|
91503
|
+
document.activeElement.blur();
|
|
91504
|
+
}
|
|
91505
|
+
this.#focusEditorAtFirstPosition();
|
|
91506
|
+
}
|
|
91507
|
+
#handleClickInHeaderFooterMode(event, x2, y2) {
|
|
91508
|
+
const session = this.#deps?.getHeaderFooterSession();
|
|
91509
|
+
const activeEditorHost = session?.overlayManager?.getActiveEditorHost?.();
|
|
91510
|
+
const clickedInsideEditorHost = activeEditorHost && (activeEditorHost.contains(event.target) || activeEditorHost === event.target);
|
|
91511
|
+
if (clickedInsideEditorHost) {
|
|
91512
|
+
return true;
|
|
91513
|
+
}
|
|
91514
|
+
const headerFooterRegion = this.#callbacks.hitTestHeaderFooterRegion?.(x2, y2);
|
|
91515
|
+
if (!headerFooterRegion) {
|
|
91516
|
+
this.#callbacks.exitHeaderFooterMode?.();
|
|
91517
|
+
return false;
|
|
91518
|
+
}
|
|
91519
|
+
return true;
|
|
91520
|
+
}
|
|
91521
|
+
#handleInlineImageClick(event, targetImg, rawHit, doc2, epochMapper) {
|
|
91522
|
+
if (!targetImg) return false;
|
|
91523
|
+
const imgPmStart = targetImg.dataset?.pmStart ? Number(targetImg.dataset.pmStart) : null;
|
|
91524
|
+
if (Number.isNaN(imgPmStart) || imgPmStart == null) return false;
|
|
91525
|
+
const imgLayoutEpochRaw = targetImg.dataset?.layoutEpoch;
|
|
91526
|
+
const imgLayoutEpoch = imgLayoutEpochRaw != null ? Number(imgLayoutEpochRaw) : NaN;
|
|
91527
|
+
const rawLayoutEpoch = Number.isFinite(rawHit.layoutEpoch) ? rawHit.layoutEpoch : NaN;
|
|
91528
|
+
const effectiveEpoch = Number.isFinite(imgLayoutEpoch) && Number.isFinite(rawLayoutEpoch) ? Math.max(imgLayoutEpoch, rawLayoutEpoch) : Number.isFinite(imgLayoutEpoch) ? imgLayoutEpoch : rawHit.layoutEpoch;
|
|
91529
|
+
const mappedImg = epochMapper.mapPosFromLayoutToCurrentDetailed(imgPmStart, effectiveEpoch, 1);
|
|
91530
|
+
if (!mappedImg.ok) {
|
|
91531
|
+
debugLog("warn", "inline image mapping failed", mappedImg);
|
|
91532
|
+
this.#callbacks.setPendingDocChange?.();
|
|
91533
|
+
this.#callbacks.scheduleRerender?.();
|
|
91534
|
+
return true;
|
|
91535
|
+
}
|
|
91536
|
+
const clampedImgPos = Math.max(0, Math.min(mappedImg.pos, doc2.content.size));
|
|
91537
|
+
if (clampedImgPos < 0 || clampedImgPos >= doc2.content.size) return true;
|
|
91538
|
+
const newSelectionId = `inline-${clampedImgPos}`;
|
|
91539
|
+
if (this.#lastSelectedImageBlockId && this.#lastSelectedImageBlockId !== newSelectionId) {
|
|
91540
|
+
this.#callbacks.emit?.("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
91541
|
+
}
|
|
91542
|
+
const editor = this.#deps?.getEditor();
|
|
91543
|
+
try {
|
|
91544
|
+
const tr = editor.state.tr.setSelection(NodeSelection.create(doc2, clampedImgPos));
|
|
91545
|
+
editor.view?.dispatch(tr);
|
|
91546
|
+
const selector = `.superdoc-inline-image[data-pm-start="${imgPmStart}"]`;
|
|
91547
|
+
const viewportHost = this.#deps?.getViewportHost();
|
|
91548
|
+
const targetElement = viewportHost?.querySelector(selector);
|
|
91549
|
+
this.#callbacks.emit?.("imageSelected", {
|
|
91550
|
+
element: targetElement ?? targetImg,
|
|
91551
|
+
blockId: null,
|
|
91552
|
+
pmStart: clampedImgPos
|
|
91553
|
+
});
|
|
91554
|
+
this.#lastSelectedImageBlockId = newSelectionId;
|
|
91555
|
+
} catch (error) {
|
|
91556
|
+
}
|
|
91557
|
+
this.#callbacks.focusEditorAfterImageSelection?.();
|
|
91558
|
+
return true;
|
|
91559
|
+
}
|
|
91560
|
+
#handleFragmentClick(event, fragmentHit, hit, doc2) {
|
|
91561
|
+
if (!fragmentHit) return false;
|
|
91562
|
+
if (fragmentHit.fragment.kind !== "image" && fragmentHit.fragment.kind !== "drawing") return false;
|
|
91563
|
+
const editor = this.#deps?.getEditor();
|
|
91564
|
+
try {
|
|
91565
|
+
const tr = editor.state.tr.setSelection(NodeSelection.create(doc2, hit.pos));
|
|
91566
|
+
editor.view?.dispatch(tr);
|
|
91567
|
+
if (this.#lastSelectedImageBlockId && this.#lastSelectedImageBlockId !== fragmentHit.fragment.blockId) {
|
|
91568
|
+
this.#callbacks.emit?.("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
91569
|
+
}
|
|
91570
|
+
if (fragmentHit.fragment.kind === "image") {
|
|
91571
|
+
const viewportHost = this.#deps?.getViewportHost();
|
|
91572
|
+
const targetElement = viewportHost?.querySelector(
|
|
91573
|
+
`.superdoc-image-fragment[data-pm-start="${fragmentHit.fragment.pmStart}"]`
|
|
91574
|
+
);
|
|
91575
|
+
if (targetElement) {
|
|
91576
|
+
this.#callbacks.emit?.("imageSelected", {
|
|
91577
|
+
element: targetElement,
|
|
91578
|
+
blockId: fragmentHit.fragment.blockId,
|
|
91579
|
+
pmStart: fragmentHit.fragment.pmStart
|
|
91580
|
+
});
|
|
91581
|
+
this.#lastSelectedImageBlockId = fragmentHit.fragment.blockId;
|
|
91582
|
+
}
|
|
91583
|
+
}
|
|
91584
|
+
} catch (error) {
|
|
91585
|
+
}
|
|
91586
|
+
this.#callbacks.focusEditorAfterImageSelection?.();
|
|
91587
|
+
return true;
|
|
91588
|
+
}
|
|
91589
|
+
#handleShiftClick(event, headPos) {
|
|
91590
|
+
const editor = this.#deps?.getEditor();
|
|
91591
|
+
if (!editor) return;
|
|
91592
|
+
const anchor = editor.state.selection.anchor;
|
|
91593
|
+
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, headPos, this.#dragExtensionMode);
|
|
91594
|
+
try {
|
|
91595
|
+
const tr = editor.state.tr.setSelection(TextSelection$1.create(editor.state.doc, selAnchor, selHead));
|
|
91596
|
+
editor.view?.dispatch(tr);
|
|
91597
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
91598
|
+
} catch (error) {
|
|
91599
|
+
console.warn("[SELECTION] Failed to extend selection on shift+click:", error);
|
|
91600
|
+
}
|
|
91601
|
+
this.#focusEditor();
|
|
91602
|
+
}
|
|
91603
|
+
#handleDragSelection(event, normalized) {
|
|
91604
|
+
if (!this.#deps) return;
|
|
91605
|
+
this.#pendingMarginClick = null;
|
|
91606
|
+
this.#dragLastPointer;
|
|
91607
|
+
this.#dragLastPointer = { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y };
|
|
91608
|
+
const layoutState = this.#deps.getLayoutState();
|
|
91609
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
91610
|
+
const pageGeometryHelper = this.#deps.getPageGeometryHelper();
|
|
91611
|
+
const rawHit = clickToPosition(
|
|
91612
|
+
layoutState.layout,
|
|
91613
|
+
layoutState.blocks,
|
|
91614
|
+
layoutState.measures,
|
|
91615
|
+
{ x: normalized.x, y: normalized.y },
|
|
91616
|
+
viewportHost,
|
|
91617
|
+
event.clientX,
|
|
91618
|
+
event.clientY,
|
|
91619
|
+
pageGeometryHelper ?? void 0
|
|
91620
|
+
);
|
|
91621
|
+
if (!rawHit) return;
|
|
91622
|
+
const editor = this.#deps.getEditor();
|
|
91623
|
+
const doc2 = editor.state?.doc;
|
|
91624
|
+
if (!doc2) return;
|
|
91625
|
+
this.#dragLastRawHit = rawHit;
|
|
91626
|
+
const pageMounted = this.#deps.getPageElement(rawHit.pageIndex) != null;
|
|
91627
|
+
if (!pageMounted && this.#deps.isSelectionAwareVirtualizationEnabled()) {
|
|
91628
|
+
this.#dragUsedPageNotMountedFallback = true;
|
|
91629
|
+
}
|
|
91630
|
+
this.#callbacks.updateSelectionVirtualizationPins?.({ includeDragBuffer: true, extraPages: [rawHit.pageIndex] });
|
|
91631
|
+
const epochMapper = this.#deps.getEpochMapper();
|
|
91632
|
+
const mappedHead = epochMapper.mapPosFromLayoutToCurrentDetailed(rawHit.pos, rawHit.layoutEpoch, 1);
|
|
91633
|
+
if (!mappedHead.ok) {
|
|
91634
|
+
debugLog("warn", "drag mapping failed", mappedHead);
|
|
91635
|
+
return;
|
|
91636
|
+
}
|
|
91637
|
+
const hit = {
|
|
91638
|
+
...rawHit,
|
|
91639
|
+
pos: Math.max(0, Math.min(mappedHead.pos, doc2.content.size)),
|
|
91640
|
+
layoutEpoch: mappedHead.toEpoch
|
|
91641
|
+
};
|
|
91642
|
+
this.#debugLastHit = {
|
|
91643
|
+
source: pageMounted ? "dom" : "geometry",
|
|
91644
|
+
pos: rawHit.pos,
|
|
91645
|
+
layoutEpoch: rawHit.layoutEpoch,
|
|
91646
|
+
mappedPos: hit.pos
|
|
91647
|
+
};
|
|
91648
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
91649
|
+
const currentTableHit = this.#hitTestTable(normalized.x, normalized.y);
|
|
91650
|
+
const shouldUseCellSel = this.#shouldUseCellSelection(currentTableHit);
|
|
91651
|
+
if (shouldUseCellSel && this.#cellAnchor) {
|
|
91652
|
+
this.#handleCellDragSelection(currentTableHit, hit);
|
|
91653
|
+
return;
|
|
91654
|
+
}
|
|
91655
|
+
const anchor = this.#dragAnchor;
|
|
91656
|
+
const head = hit.pos;
|
|
91657
|
+
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, head, this.#dragExtensionMode);
|
|
91658
|
+
try {
|
|
91659
|
+
const tr = editor.state.tr.setSelection(TextSelection$1.create(editor.state.doc, selAnchor, selHead));
|
|
91660
|
+
editor.view?.dispatch(tr);
|
|
91661
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
91662
|
+
} catch (error) {
|
|
91663
|
+
console.warn("[SELECTION] Failed to extend selection during drag:", error);
|
|
91664
|
+
}
|
|
91665
|
+
}
|
|
91666
|
+
#handleCellDragSelection(currentTableHit, hit) {
|
|
91667
|
+
const headCellPos = currentTableHit ? this.#getCellPosFromTableHit(currentTableHit) : null;
|
|
91668
|
+
if (headCellPos === null) return;
|
|
91669
|
+
if (this.#cellDragMode !== "active") {
|
|
91670
|
+
this.#cellDragMode = "active";
|
|
91671
|
+
}
|
|
91672
|
+
const editor = this.#deps?.getEditor();
|
|
91673
|
+
if (!editor) return;
|
|
91674
|
+
try {
|
|
91675
|
+
const doc2 = editor.state.doc;
|
|
91676
|
+
const anchorCellPos = this.#cellAnchor.cellPos;
|
|
91677
|
+
const clampedAnchor = Math.max(0, Math.min(anchorCellPos, doc2.content.size));
|
|
91678
|
+
const clampedHead = Math.max(0, Math.min(headCellPos, doc2.content.size));
|
|
91679
|
+
const cellSelection = CellSelection.create(doc2, clampedAnchor, clampedHead);
|
|
91680
|
+
const tr = editor.state.tr.setSelection(cellSelection);
|
|
91681
|
+
editor.view?.dispatch(tr);
|
|
91682
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
91683
|
+
} catch (error) {
|
|
91684
|
+
console.warn("[CELL-SELECTION] Failed to create CellSelection, falling back to TextSelection:", error);
|
|
91685
|
+
const anchor = this.#dragAnchor;
|
|
91686
|
+
const head = hit.pos;
|
|
91687
|
+
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, head, this.#dragExtensionMode);
|
|
91688
|
+
try {
|
|
91689
|
+
const tr = editor.state.tr.setSelection(TextSelection$1.create(editor.state.doc, selAnchor, selHead));
|
|
91690
|
+
editor.view?.dispatch(tr);
|
|
91691
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
91692
|
+
} catch {
|
|
91693
|
+
}
|
|
91694
|
+
}
|
|
91695
|
+
}
|
|
91696
|
+
#handleHover(normalized) {
|
|
91697
|
+
if (!this.#deps) return;
|
|
91698
|
+
const sessionMode = this.#deps.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
91699
|
+
if (sessionMode !== "body") {
|
|
91700
|
+
this.#callbacks.clearHoverRegion?.();
|
|
91701
|
+
return;
|
|
91702
|
+
}
|
|
91703
|
+
if (this.#deps.getDocumentMode() === "viewing") {
|
|
91704
|
+
this.#callbacks.clearHoverRegion?.();
|
|
91705
|
+
return;
|
|
91706
|
+
}
|
|
91707
|
+
const region = this.#callbacks.hitTestHeaderFooterRegion?.(normalized.x, normalized.y);
|
|
91708
|
+
if (!region) {
|
|
91709
|
+
this.#callbacks.clearHoverRegion?.();
|
|
91710
|
+
return;
|
|
91711
|
+
}
|
|
91712
|
+
const currentHover = this.#deps.getHeaderFooterSession()?.hoverRegion;
|
|
91713
|
+
if (currentHover && currentHover.kind === region.kind && currentHover.pageIndex === region.pageIndex && currentHover.sectionType === region.sectionType) {
|
|
91714
|
+
return;
|
|
91715
|
+
}
|
|
91716
|
+
this.#deps.getHeaderFooterSession()?.renderHover(region);
|
|
91717
|
+
this.#callbacks.renderHoverRegion?.(region);
|
|
91718
|
+
}
|
|
91719
|
+
#handleMarginClickEnd(event, pendingMarginClick) {
|
|
91720
|
+
const sessionMode = this.#deps?.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
91721
|
+
if (sessionMode !== "body" || this.#deps?.isViewLocked()) {
|
|
91722
|
+
this.#clearDragPointerState();
|
|
91723
|
+
return;
|
|
91724
|
+
}
|
|
91725
|
+
const editor = this.#deps?.getEditor();
|
|
91726
|
+
const doc2 = editor?.state?.doc;
|
|
91727
|
+
if (!doc2) {
|
|
91728
|
+
this.#clearDragPointerState();
|
|
91729
|
+
return;
|
|
91730
|
+
}
|
|
91731
|
+
const epochMapper = this.#deps?.getEpochMapper();
|
|
91732
|
+
if (!epochMapper) {
|
|
91733
|
+
this.#clearDragPointerState();
|
|
91734
|
+
return;
|
|
91735
|
+
}
|
|
91736
|
+
if (pendingMarginClick.kind === "aboveFirstLine") {
|
|
91737
|
+
const pos = this.#getFirstTextPosition();
|
|
91738
|
+
try {
|
|
91739
|
+
const tr = editor.state.tr.setSelection(TextSelection$1.create(doc2, pos));
|
|
91740
|
+
editor.view?.dispatch(tr);
|
|
91741
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
91742
|
+
} catch {
|
|
91743
|
+
}
|
|
91744
|
+
this.#debugLastHit = { source: "margin", pos: null, layoutEpoch: null, mappedPos: pos };
|
|
91745
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
91746
|
+
this.#clearDragPointerState();
|
|
91747
|
+
return;
|
|
91748
|
+
}
|
|
91749
|
+
if (pendingMarginClick.kind === "right") {
|
|
91750
|
+
const mappedEnd2 = epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
91751
|
+
pendingMarginClick.pmEnd,
|
|
91752
|
+
pendingMarginClick.layoutEpoch,
|
|
91753
|
+
1
|
|
91754
|
+
);
|
|
91755
|
+
if (!mappedEnd2.ok) {
|
|
91756
|
+
this.#callbacks.setPendingDocChange?.();
|
|
91757
|
+
this.#callbacks.scheduleRerender?.();
|
|
91758
|
+
this.#clearDragPointerState();
|
|
91759
|
+
return;
|
|
91760
|
+
}
|
|
91761
|
+
const caretPos = Math.max(0, Math.min(mappedEnd2.pos, doc2.content.size));
|
|
91762
|
+
try {
|
|
91763
|
+
const tr = editor.state.tr.setSelection(TextSelection$1.create(doc2, caretPos));
|
|
91764
|
+
editor.view?.dispatch(tr);
|
|
91765
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
91766
|
+
} catch {
|
|
91767
|
+
}
|
|
91768
|
+
this.#debugLastHit = {
|
|
91769
|
+
source: "margin",
|
|
91770
|
+
pos: pendingMarginClick.pmEnd,
|
|
91771
|
+
layoutEpoch: pendingMarginClick.layoutEpoch,
|
|
91772
|
+
mappedPos: caretPos
|
|
91773
|
+
};
|
|
91774
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
91775
|
+
this.#clearDragPointerState();
|
|
91776
|
+
return;
|
|
91777
|
+
}
|
|
91778
|
+
const mappedStart = epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
91779
|
+
pendingMarginClick.pmStart,
|
|
91780
|
+
pendingMarginClick.layoutEpoch,
|
|
91781
|
+
1
|
|
91782
|
+
);
|
|
91783
|
+
const mappedEnd = epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
91784
|
+
pendingMarginClick.pmEnd,
|
|
91785
|
+
pendingMarginClick.layoutEpoch,
|
|
91786
|
+
-1
|
|
91787
|
+
);
|
|
91788
|
+
if (!mappedStart.ok || !mappedEnd.ok) {
|
|
91789
|
+
this.#callbacks.setPendingDocChange?.();
|
|
91790
|
+
this.#callbacks.scheduleRerender?.();
|
|
91791
|
+
this.#clearDragPointerState();
|
|
91792
|
+
return;
|
|
91793
|
+
}
|
|
91794
|
+
const selFrom = Math.max(0, Math.min(Math.min(mappedStart.pos, mappedEnd.pos), doc2.content.size));
|
|
91795
|
+
const selTo = Math.max(0, Math.min(Math.max(mappedStart.pos, mappedEnd.pos), doc2.content.size));
|
|
91796
|
+
try {
|
|
91797
|
+
const tr = editor.state.tr.setSelection(TextSelection$1.create(doc2, selFrom, selTo));
|
|
91798
|
+
editor.view?.dispatch(tr);
|
|
91799
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
91800
|
+
} catch {
|
|
91801
|
+
}
|
|
91802
|
+
this.#debugLastHit = {
|
|
91803
|
+
source: "margin",
|
|
91804
|
+
pos: pendingMarginClick.pmStart,
|
|
91805
|
+
layoutEpoch: pendingMarginClick.layoutEpoch,
|
|
91806
|
+
mappedPos: selFrom
|
|
91807
|
+
};
|
|
91808
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
91809
|
+
this.#clearDragPointerState();
|
|
91810
|
+
}
|
|
91811
|
+
#clearDragPointerState() {
|
|
91812
|
+
this.#dragLastPointer = null;
|
|
91813
|
+
this.#dragLastRawHit = null;
|
|
91814
|
+
this.#dragUsedPageNotMountedFallback = false;
|
|
91815
|
+
}
|
|
91816
|
+
#focusHeaderFooterShortcut(kind) {
|
|
91817
|
+
const pageIndex = this.#callbacks.getCurrentPageIndex?.() ?? 0;
|
|
91818
|
+
const region = this.#callbacks.findRegionForPage?.(kind, pageIndex);
|
|
91819
|
+
if (!region) {
|
|
91820
|
+
this.#callbacks.emitHeaderFooterEditBlocked?.("missingRegion");
|
|
91821
|
+
return;
|
|
91822
|
+
}
|
|
91823
|
+
this.#callbacks.activateHeaderFooterRegion?.(region);
|
|
91824
|
+
}
|
|
91825
|
+
#focusEditorAtFirstPosition() {
|
|
91826
|
+
const editor = this.#deps?.getEditor();
|
|
91827
|
+
const editorDom = editor?.view?.dom;
|
|
91828
|
+
if (!editorDom) return;
|
|
91829
|
+
const validPos = this.#getFirstTextPosition();
|
|
91830
|
+
const doc2 = editor?.state?.doc;
|
|
91831
|
+
if (doc2) {
|
|
91832
|
+
try {
|
|
91833
|
+
const tr = editor.state.tr.setSelection(TextSelection$1.create(doc2, validPos));
|
|
91834
|
+
editor.view?.dispatch(tr);
|
|
91835
|
+
} catch {
|
|
91836
|
+
}
|
|
91837
|
+
}
|
|
91838
|
+
editorDom.focus();
|
|
91839
|
+
editor?.view?.focus();
|
|
91840
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
91841
|
+
}
|
|
91842
|
+
#focusEditor() {
|
|
91843
|
+
if (document.activeElement instanceof HTMLElement) {
|
|
91844
|
+
document.activeElement.blur();
|
|
91845
|
+
}
|
|
91846
|
+
const editor = this.#deps?.getEditor();
|
|
91847
|
+
const editorDom = editor?.view?.dom;
|
|
91848
|
+
if (editorDom) {
|
|
91849
|
+
editorDom.focus();
|
|
91850
|
+
editor?.view?.focus();
|
|
91851
|
+
}
|
|
91852
|
+
}
|
|
91853
|
+
}
|
|
91854
|
+
const createDefaultScheduler = () => {
|
|
91855
|
+
if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
|
|
91856
|
+
return {
|
|
91857
|
+
requestAnimationFrame: (cb) => window.requestAnimationFrame(cb),
|
|
91858
|
+
cancelAnimationFrame: (handle2) => window.cancelAnimationFrame(handle2)
|
|
91859
|
+
};
|
|
91860
|
+
}
|
|
91861
|
+
const anyGlobal = globalThis;
|
|
91862
|
+
if (typeof anyGlobal.requestAnimationFrame === "function" && typeof anyGlobal.cancelAnimationFrame === "function") {
|
|
91863
|
+
return {
|
|
91864
|
+
requestAnimationFrame: (cb) => anyGlobal.requestAnimationFrame(cb),
|
|
91865
|
+
cancelAnimationFrame: (handle2) => anyGlobal.cancelAnimationFrame(handle2)
|
|
91866
|
+
};
|
|
91867
|
+
}
|
|
91868
|
+
return {
|
|
91869
|
+
requestAnimationFrame: (cb) => {
|
|
91870
|
+
const handle2 = anyGlobal.setTimeout?.(() => cb(Date.now()), 0);
|
|
91871
|
+
return handle2;
|
|
91872
|
+
},
|
|
91873
|
+
cancelAnimationFrame: (handle2) => {
|
|
91874
|
+
anyGlobal.clearTimeout?.(handle2);
|
|
91875
|
+
}
|
|
91876
|
+
};
|
|
91877
|
+
};
|
|
91878
|
+
class SelectionSyncCoordinator extends EventEmitter$1 {
|
|
91879
|
+
#docEpoch = 0;
|
|
91880
|
+
#layoutEpoch = 0;
|
|
91881
|
+
#layoutUpdating = false;
|
|
91882
|
+
#pending = false;
|
|
91883
|
+
#scheduled = false;
|
|
91884
|
+
#rafHandle = null;
|
|
91885
|
+
#scheduler;
|
|
91886
|
+
/**
|
|
91887
|
+
* Creates a new SelectionSyncCoordinator.
|
|
91888
|
+
*
|
|
91889
|
+
* @param options - Configuration options
|
|
91890
|
+
* @param options.scheduler - Custom scheduler for animation frames (useful for testing), defaults to platform scheduler
|
|
91891
|
+
*/
|
|
91892
|
+
constructor(options) {
|
|
91893
|
+
super();
|
|
91894
|
+
this.#scheduler = options?.scheduler ?? createDefaultScheduler();
|
|
91895
|
+
}
|
|
91896
|
+
/**
|
|
91897
|
+
* Gets the current document epoch.
|
|
91898
|
+
*
|
|
91899
|
+
* @returns The document epoch (increments on each document-changing transaction)
|
|
91900
|
+
*/
|
|
91901
|
+
getDocEpoch() {
|
|
91902
|
+
return this.#docEpoch;
|
|
91903
|
+
}
|
|
91904
|
+
/**
|
|
91905
|
+
* Gets the current layout epoch.
|
|
91906
|
+
*
|
|
91907
|
+
* @returns The epoch of the document version currently painted in the DOM
|
|
91908
|
+
*/
|
|
91909
|
+
getLayoutEpoch() {
|
|
91910
|
+
return this.#layoutEpoch;
|
|
91911
|
+
}
|
|
91912
|
+
/**
|
|
91913
|
+
* Checks if a layout update is currently in progress.
|
|
91914
|
+
*
|
|
91915
|
+
* @returns True if between onLayoutStart() and onLayoutComplete(), false otherwise
|
|
91916
|
+
*/
|
|
91917
|
+
isLayoutUpdating() {
|
|
91918
|
+
return this.#layoutUpdating;
|
|
91919
|
+
}
|
|
91920
|
+
/**
|
|
91921
|
+
* Updates the document epoch and triggers conditional rendering.
|
|
91922
|
+
*
|
|
91923
|
+
* @param epoch - The new document epoch (must be finite and non-negative)
|
|
91924
|
+
*
|
|
91925
|
+
* @remarks
|
|
91926
|
+
* When the document epoch changes:
|
|
91927
|
+
* 1. Any scheduled render is cancelled (layout will be out of sync)
|
|
91928
|
+
* 2. If layout has already caught up, rendering is rescheduled
|
|
91929
|
+
*
|
|
91930
|
+
* Calling with the same epoch as the current value is a no-op.
|
|
91931
|
+
* Invalid epoch values are silently ignored.
|
|
91932
|
+
*/
|
|
91933
|
+
setDocEpoch(epoch) {
|
|
91934
|
+
if (!Number.isFinite(epoch) || epoch < 0) return;
|
|
91935
|
+
if (epoch === this.#docEpoch) return;
|
|
91936
|
+
this.#docEpoch = epoch;
|
|
91937
|
+
this.#cancelScheduledRender();
|
|
91938
|
+
this.#maybeSchedule();
|
|
91939
|
+
}
|
|
91940
|
+
/**
|
|
91941
|
+
* Notifies the coordinator that layout computation has started.
|
|
91942
|
+
*
|
|
91943
|
+
* @remarks
|
|
91944
|
+
* Marks the layout as updating and cancels any scheduled renders, since the DOM
|
|
91945
|
+
* is about to change and current position data will be stale.
|
|
91946
|
+
*
|
|
91947
|
+
* Safe to call multiple times (e.g., if layouts overlap) - subsequent calls are ignored
|
|
91948
|
+
* until onLayoutComplete() is called.
|
|
91949
|
+
*/
|
|
91950
|
+
onLayoutStart() {
|
|
91951
|
+
if (this.#layoutUpdating) return;
|
|
91952
|
+
this.#layoutUpdating = true;
|
|
91953
|
+
this.#cancelScheduledRender();
|
|
91954
|
+
}
|
|
91955
|
+
/**
|
|
91956
|
+
* Notifies the coordinator that layout painting has completed.
|
|
91957
|
+
*
|
|
91958
|
+
* @param layoutEpoch - The document epoch that was just painted to the DOM
|
|
91959
|
+
*
|
|
91960
|
+
* @remarks
|
|
91961
|
+
* Marks the layout as no longer updating, records the new layout epoch, and attempts
|
|
91962
|
+
* to schedule rendering if conditions are now safe.
|
|
91963
|
+
*
|
|
91964
|
+
* If the layoutEpoch is invalid (not a finite non-negative number), it is ignored and
|
|
91965
|
+
* the previous layoutEpoch value is retained.
|
|
91966
|
+
*
|
|
91967
|
+
* This method is the primary trigger for selection rendering - if there's a pending
|
|
91968
|
+
* render request and layoutEpoch >= docEpoch, a render event will be scheduled.
|
|
91969
|
+
*/
|
|
91970
|
+
onLayoutComplete(layoutEpoch) {
|
|
91971
|
+
this.#layoutUpdating = false;
|
|
91972
|
+
if (Number.isFinite(layoutEpoch) && layoutEpoch >= 0) {
|
|
91973
|
+
this.#layoutEpoch = layoutEpoch;
|
|
91974
|
+
}
|
|
91975
|
+
this.#maybeSchedule();
|
|
91976
|
+
}
|
|
91977
|
+
/**
|
|
91978
|
+
* Notifies the coordinator that layout was aborted without completing.
|
|
91979
|
+
*
|
|
91980
|
+
* @remarks
|
|
91981
|
+
* Marks the layout as no longer updating (without updating layoutEpoch) and attempts
|
|
91982
|
+
* to schedule rendering if conditions are safe.
|
|
91983
|
+
*
|
|
91984
|
+
* Use this when layout computation is cancelled or fails partway through.
|
|
91985
|
+
*/
|
|
91986
|
+
onLayoutAbort() {
|
|
91987
|
+
this.#layoutUpdating = false;
|
|
91988
|
+
this.#maybeSchedule();
|
|
91989
|
+
}
|
|
91990
|
+
/**
|
|
91991
|
+
* Requests that selection rendering occur when conditions become safe.
|
|
91992
|
+
*
|
|
91993
|
+
* @param options - Rendering options
|
|
91994
|
+
* @param options.immediate - If true, attempts to render immediately (synchronously) if safe, defaults to false
|
|
91995
|
+
*
|
|
91996
|
+
* @remarks
|
|
91997
|
+
* Marks a render as pending and schedules it to occur on the next animation frame if
|
|
91998
|
+
* conditions are safe (layout not updating, layoutEpoch >= docEpoch).
|
|
91999
|
+
*
|
|
92000
|
+
* If options.immediate is true, also attempts a synchronous render before scheduling.
|
|
92001
|
+
* Use immediate rendering sparingly, as it can cause multiple renders per frame.
|
|
92002
|
+
*
|
|
92003
|
+
* Multiple calls are coalesced - only one render will occur per animation frame.
|
|
92004
|
+
*/
|
|
92005
|
+
requestRender(options) {
|
|
92006
|
+
this.#pending = true;
|
|
92007
|
+
if (options?.immediate) {
|
|
92008
|
+
this.flushNow();
|
|
92009
|
+
}
|
|
92010
|
+
this.#maybeSchedule();
|
|
92011
|
+
}
|
|
92012
|
+
/**
|
|
92013
|
+
* Attempts to render selection immediately (synchronously) if conditions are safe.
|
|
92014
|
+
*
|
|
92015
|
+
* @remarks
|
|
92016
|
+
* If there's a pending render request and conditions are safe (layout not updating,
|
|
92017
|
+
* layoutEpoch >= docEpoch), this method:
|
|
92018
|
+
* 1. Cancels any scheduled asynchronous render
|
|
92019
|
+
* 2. Clears the pending flag
|
|
92020
|
+
* 3. Emits the 'render' event synchronously
|
|
92021
|
+
*
|
|
92022
|
+
* If no render is pending or conditions are not safe, this is a no-op.
|
|
92023
|
+
*
|
|
92024
|
+
* Use this for immediate selection updates in response to user actions (e.g., click
|
|
92025
|
+
* handlers) where waiting for the next animation frame would cause noticeable lag.
|
|
92026
|
+
*/
|
|
92027
|
+
flushNow() {
|
|
92028
|
+
if (!this.#pending) return;
|
|
92029
|
+
if (!this.#isSafeToRender()) return;
|
|
92030
|
+
this.#cancelScheduledRender();
|
|
92031
|
+
this.#pending = false;
|
|
92032
|
+
this.emit("render", { docEpoch: this.#docEpoch, layoutEpoch: this.#layoutEpoch });
|
|
92033
|
+
}
|
|
92034
|
+
/**
|
|
92035
|
+
* Permanently tears down the coordinator, cancelling pending renders and removing all listeners.
|
|
92036
|
+
*
|
|
92037
|
+
* @remarks
|
|
92038
|
+
* After calling destroy(), this instance should not be used again. All scheduled renders
|
|
92039
|
+
* are cancelled and all event listeners are removed.
|
|
92040
|
+
*
|
|
92041
|
+
* Safe to call multiple times.
|
|
92042
|
+
*/
|
|
92043
|
+
destroy() {
|
|
92044
|
+
this.#cancelScheduledRender();
|
|
92045
|
+
this.removeAllListeners();
|
|
92046
|
+
}
|
|
92047
|
+
#isSafeToRender() {
|
|
92048
|
+
return !this.#layoutUpdating && this.#layoutEpoch >= this.#docEpoch;
|
|
92049
|
+
}
|
|
92050
|
+
#maybeSchedule() {
|
|
92051
|
+
if (!this.#pending) return;
|
|
92052
|
+
if (!this.#isSafeToRender()) return;
|
|
92053
|
+
if (this.#scheduled) return;
|
|
92054
|
+
this.#scheduled = true;
|
|
92055
|
+
this.#rafHandle = this.#scheduler.requestAnimationFrame(() => {
|
|
92056
|
+
this.#scheduled = false;
|
|
92057
|
+
this.#rafHandle = null;
|
|
92058
|
+
if (!this.#pending) return;
|
|
92059
|
+
if (!this.#isSafeToRender()) return;
|
|
92060
|
+
this.#pending = false;
|
|
92061
|
+
this.emit("render", { docEpoch: this.#docEpoch, layoutEpoch: this.#layoutEpoch });
|
|
92062
|
+
});
|
|
92063
|
+
}
|
|
92064
|
+
#cancelScheduledRender() {
|
|
92065
|
+
if (this.#rafHandle != null) {
|
|
92066
|
+
try {
|
|
92067
|
+
this.#scheduler.cancelAnimationFrame(this.#rafHandle);
|
|
92068
|
+
} finally {
|
|
92069
|
+
this.#rafHandle = null;
|
|
92070
|
+
}
|
|
92071
|
+
}
|
|
92072
|
+
this.#scheduled = false;
|
|
92073
|
+
}
|
|
92074
|
+
}
|
|
92075
|
+
const uiSurfaces = /* @__PURE__ */ new WeakSet();
|
|
92076
|
+
function isInRegisteredSurface(event) {
|
|
91148
92077
|
const path2 = typeof event.composedPath === "function" ? event.composedPath() : [];
|
|
91149
92078
|
if (path2.length > 0) {
|
|
91150
92079
|
for (const node22 of path2) {
|
|
@@ -91500,37 +92429,6 @@ ${o}
|
|
|
91500
92429
|
return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
91501
92430
|
}
|
|
91502
92431
|
}
|
|
91503
|
-
const WORD_CHARACTER_REGEX = /[\p{L}\p{N}'\u2018\u2019_~-]/u;
|
|
91504
|
-
function isWordCharacter(char) {
|
|
91505
|
-
if (!char) {
|
|
91506
|
-
return false;
|
|
91507
|
-
}
|
|
91508
|
-
return WORD_CHARACTER_REGEX.test(char);
|
|
91509
|
-
}
|
|
91510
|
-
function calculateExtendedSelection(blocks2, anchor, head, mode) {
|
|
91511
|
-
if (mode === "word") {
|
|
91512
|
-
const anchorBounds = findWordBoundaries(blocks2, anchor);
|
|
91513
|
-
const headBounds = findWordBoundaries(blocks2, head);
|
|
91514
|
-
if (anchorBounds && headBounds) {
|
|
91515
|
-
if (head >= anchor) {
|
|
91516
|
-
return { selAnchor: anchorBounds.from, selHead: headBounds.to };
|
|
91517
|
-
} else {
|
|
91518
|
-
return { selAnchor: anchorBounds.to, selHead: headBounds.from };
|
|
91519
|
-
}
|
|
91520
|
-
}
|
|
91521
|
-
} else if (mode === "para") {
|
|
91522
|
-
const anchorBounds = findParagraphBoundaries(blocks2, anchor);
|
|
91523
|
-
const headBounds = findParagraphBoundaries(blocks2, head);
|
|
91524
|
-
if (anchorBounds && headBounds) {
|
|
91525
|
-
if (head >= anchor) {
|
|
91526
|
-
return { selAnchor: anchorBounds.from, selHead: headBounds.to };
|
|
91527
|
-
} else {
|
|
91528
|
-
return { selAnchor: anchorBounds.to, selHead: headBounds.from };
|
|
91529
|
-
}
|
|
91530
|
-
}
|
|
91531
|
-
}
|
|
91532
|
-
return { selAnchor: anchor, selHead: head };
|
|
91533
|
-
}
|
|
91534
92432
|
function getAtomNodeTypes(schema) {
|
|
91535
92433
|
if (!schema) return [];
|
|
91536
92434
|
const types2 = [];
|
|
@@ -91586,104 +92484,6 @@ ${o}
|
|
|
91586
92484
|
if (!ok2) return null;
|
|
91587
92485
|
return map2;
|
|
91588
92486
|
}
|
|
91589
|
-
function registerPointerClick(event, previous2, options) {
|
|
91590
|
-
const time2 = event.timeStamp ?? performance.now();
|
|
91591
|
-
const timeDelta = time2 - previous2.lastClickTime;
|
|
91592
|
-
const withinTime = timeDelta <= options.timeThresholdMs;
|
|
91593
|
-
const distanceX = Math.abs(event.clientX - previous2.lastClickPosition.x);
|
|
91594
|
-
const distanceY = Math.abs(event.clientY - previous2.lastClickPosition.y);
|
|
91595
|
-
const withinDistance = distanceX <= options.distanceThresholdPx && distanceY <= options.distanceThresholdPx;
|
|
91596
|
-
const clickCount = withinTime && withinDistance ? Math.min(previous2.clickCount + 1, options.maxClickCount) : 1;
|
|
91597
|
-
return {
|
|
91598
|
-
clickCount,
|
|
91599
|
-
lastClickTime: time2,
|
|
91600
|
-
lastClickPosition: { x: event.clientX, y: event.clientY }
|
|
91601
|
-
};
|
|
91602
|
-
}
|
|
91603
|
-
function getFirstTextPosition(doc2) {
|
|
91604
|
-
if (!doc2 || !doc2.content) {
|
|
91605
|
-
return 1;
|
|
91606
|
-
}
|
|
91607
|
-
let validPos = 1;
|
|
91608
|
-
doc2.nodesBetween(0, doc2.content.size, (node2, pos) => {
|
|
91609
|
-
if (node2.isTextblock) {
|
|
91610
|
-
validPos = pos + 1;
|
|
91611
|
-
return false;
|
|
91612
|
-
}
|
|
91613
|
-
return true;
|
|
91614
|
-
});
|
|
91615
|
-
return validPos;
|
|
91616
|
-
}
|
|
91617
|
-
function computeWordSelectionRangeAt(state, pos) {
|
|
91618
|
-
if (!state?.doc) {
|
|
91619
|
-
return null;
|
|
91620
|
-
}
|
|
91621
|
-
if (pos < 0 || pos > state.doc.content.size) {
|
|
91622
|
-
return null;
|
|
91623
|
-
}
|
|
91624
|
-
const textblockPos = findNearestTextblockResolvedPos(state.doc, pos);
|
|
91625
|
-
if (!textblockPos) {
|
|
91626
|
-
return null;
|
|
91627
|
-
}
|
|
91628
|
-
const parentStart = textblockPos.start();
|
|
91629
|
-
const parentEnd = textblockPos.end();
|
|
91630
|
-
const sampleEnd = Math.min(pos + 1, parentEnd);
|
|
91631
|
-
const charAtPos = state.doc.textBetween(pos, sampleEnd, "\0", "\0");
|
|
91632
|
-
if (!isWordCharacter(charAtPos)) {
|
|
91633
|
-
return null;
|
|
91634
|
-
}
|
|
91635
|
-
let startPos = pos;
|
|
91636
|
-
while (startPos > parentStart) {
|
|
91637
|
-
const prevChar = state.doc.textBetween(startPos - 1, startPos, "\0", "\0");
|
|
91638
|
-
if (!isWordCharacter(prevChar)) {
|
|
91639
|
-
break;
|
|
91640
|
-
}
|
|
91641
|
-
startPos -= 1;
|
|
91642
|
-
}
|
|
91643
|
-
let endPos = pos;
|
|
91644
|
-
while (endPos < parentEnd) {
|
|
91645
|
-
const nextChar = state.doc.textBetween(endPos, endPos + 1, "\0", "\0");
|
|
91646
|
-
if (!isWordCharacter(nextChar)) {
|
|
91647
|
-
break;
|
|
91648
|
-
}
|
|
91649
|
-
endPos += 1;
|
|
91650
|
-
}
|
|
91651
|
-
if (startPos === endPos) {
|
|
91652
|
-
return null;
|
|
91653
|
-
}
|
|
91654
|
-
return { from: startPos, to: endPos };
|
|
91655
|
-
}
|
|
91656
|
-
function computeParagraphSelectionRangeAt(state, pos) {
|
|
91657
|
-
if (!state?.doc) {
|
|
91658
|
-
return null;
|
|
91659
|
-
}
|
|
91660
|
-
const textblockPos = findNearestTextblockResolvedPos(state.doc, pos);
|
|
91661
|
-
if (!textblockPos) {
|
|
91662
|
-
return null;
|
|
91663
|
-
}
|
|
91664
|
-
return { from: textblockPos.start(), to: textblockPos.end() };
|
|
91665
|
-
}
|
|
91666
|
-
function findNearestTextblockResolvedPos(doc2, pos) {
|
|
91667
|
-
const $pos = doc2.resolve(pos);
|
|
91668
|
-
let textblockPos = $pos;
|
|
91669
|
-
while (textblockPos.depth > 0) {
|
|
91670
|
-
if (textblockPos.parent?.isTextblock) {
|
|
91671
|
-
break;
|
|
91672
|
-
}
|
|
91673
|
-
if (!textblockPos.parent || textblockPos.depth === 0) {
|
|
91674
|
-
break;
|
|
91675
|
-
}
|
|
91676
|
-
const beforePos = textblockPos.before();
|
|
91677
|
-
if (beforePos < 0 || beforePos > doc2.content.size) {
|
|
91678
|
-
return null;
|
|
91679
|
-
}
|
|
91680
|
-
textblockPos = doc2.resolve(beforePos);
|
|
91681
|
-
}
|
|
91682
|
-
if (!textblockPos.parent?.isTextblock) {
|
|
91683
|
-
return null;
|
|
91684
|
-
}
|
|
91685
|
-
return textblockPos;
|
|
91686
|
-
}
|
|
91687
92487
|
function syncHiddenEditorA11yAttributes(pmDom, documentMode) {
|
|
91688
92488
|
const element2 = pmDom;
|
|
91689
92489
|
if (!(element2 instanceof HTMLElement)) return;
|
|
@@ -92353,314 +93153,255 @@ ${o}
|
|
|
92353
93153
|
}
|
|
92354
93154
|
return true;
|
|
92355
93155
|
}
|
|
92356
|
-
|
|
92357
|
-
|
|
92358
|
-
|
|
92359
|
-
|
|
92360
|
-
|
|
92361
|
-
|
|
92362
|
-
|
|
92363
|
-
|
|
92364
|
-
|
|
92365
|
-
|
|
92366
|
-
|
|
92367
|
-
|
|
92368
|
-
|
|
92369
|
-
const
|
|
92370
|
-
const
|
|
92371
|
-
|
|
92372
|
-
|
|
92373
|
-
|
|
92374
|
-
|
|
92375
|
-
doc2.descendants((node2, pos) => {
|
|
92376
|
-
if (node2.type.name === "table") {
|
|
92377
|
-
if (currentTableIndex === targetTableIndex) {
|
|
92378
|
-
tablePos = pos;
|
|
92379
|
-
return false;
|
|
92380
|
-
}
|
|
92381
|
-
currentTableIndex++;
|
|
92382
|
-
}
|
|
92383
|
-
return true;
|
|
92384
|
-
});
|
|
92385
|
-
} catch (error) {
|
|
92386
|
-
console.error("[getCellPosFromTableHit] Error during document traversal:", error);
|
|
92387
|
-
return null;
|
|
92388
|
-
}
|
|
92389
|
-
if (tablePos === null) return null;
|
|
92390
|
-
const tableNode = doc2.nodeAt(tablePos);
|
|
92391
|
-
if (!tableNode || tableNode.type.name !== "table") return null;
|
|
92392
|
-
const targetRowIndex = tableHit.cellRowIndex;
|
|
92393
|
-
const targetColIndex = tableHit.cellColIndex;
|
|
92394
|
-
if (targetRowIndex >= tableNode.childCount) {
|
|
92395
|
-
console.warn("[getCellPosFromTableHit] Target row index out of bounds:", {
|
|
92396
|
-
targetRowIndex,
|
|
92397
|
-
tableChildCount: tableNode.childCount
|
|
92398
|
-
});
|
|
92399
|
-
return null;
|
|
92400
|
-
}
|
|
92401
|
-
let currentPos = tablePos + 1;
|
|
92402
|
-
for (let r2 = 0; r2 < tableNode.childCount && r2 <= targetRowIndex; r2++) {
|
|
92403
|
-
const row2 = tableNode.child(r2);
|
|
92404
|
-
if (r2 === targetRowIndex) {
|
|
92405
|
-
currentPos += 1;
|
|
92406
|
-
let logicalCol = 0;
|
|
92407
|
-
for (let cellIndex = 0; cellIndex < row2.childCount; cellIndex++) {
|
|
92408
|
-
const cell2 = row2.child(cellIndex);
|
|
92409
|
-
const rawColspan = cell2.attrs?.colspan;
|
|
92410
|
-
const colspan = typeof rawColspan === "number" && Number.isFinite(rawColspan) && rawColspan > 0 ? rawColspan : 1;
|
|
92411
|
-
if (targetColIndex >= logicalCol && targetColIndex < logicalCol + colspan) {
|
|
92412
|
-
return currentPos;
|
|
92413
|
-
}
|
|
92414
|
-
currentPos += cell2.nodeSize;
|
|
92415
|
-
logicalCol += colspan;
|
|
92416
|
-
}
|
|
92417
|
-
console.warn("[getCellPosFromTableHit] Target column not found in row:", {
|
|
92418
|
-
targetColIndex,
|
|
92419
|
-
logicalColReached: logicalCol,
|
|
92420
|
-
rowCellCount: row2.childCount
|
|
92421
|
-
});
|
|
92422
|
-
return null;
|
|
92423
|
-
} else {
|
|
92424
|
-
currentPos += row2.nodeSize;
|
|
93156
|
+
const INTERNAL_MIME_TYPE = "application/x-field-annotation";
|
|
93157
|
+
const FIELD_ANNOTATION_DATA_TYPE = "fieldAnnotation";
|
|
93158
|
+
function isValidFieldAnnotationAttributes(attrs) {
|
|
93159
|
+
if (!attrs || typeof attrs !== "object") return false;
|
|
93160
|
+
const a2 = attrs;
|
|
93161
|
+
return typeof a2.fieldId === "string" && typeof a2.fieldType === "string" && typeof a2.displayLabel === "string" && typeof a2.type === "string";
|
|
93162
|
+
}
|
|
93163
|
+
function parseIntSafe$1(value) {
|
|
93164
|
+
if (!value) return void 0;
|
|
93165
|
+
const parsed = parseInt(value, 10);
|
|
93166
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
93167
|
+
}
|
|
93168
|
+
function extractFieldAnnotationData(element2) {
|
|
93169
|
+
const dataset = element2.dataset;
|
|
93170
|
+
const attributes = {};
|
|
93171
|
+
for (const key2 in dataset) {
|
|
93172
|
+
const value = dataset[key2];
|
|
93173
|
+
if (value !== void 0) {
|
|
93174
|
+
attributes[key2] = value;
|
|
92425
93175
|
}
|
|
92426
93176
|
}
|
|
92427
|
-
return
|
|
93177
|
+
return {
|
|
93178
|
+
fieldId: dataset.fieldId,
|
|
93179
|
+
fieldType: dataset.fieldType,
|
|
93180
|
+
variant: dataset.variant ?? dataset.type,
|
|
93181
|
+
displayLabel: dataset.displayLabel,
|
|
93182
|
+
pmStart: parseIntSafe$1(dataset.pmStart),
|
|
93183
|
+
pmEnd: parseIntSafe$1(dataset.pmEnd),
|
|
93184
|
+
attributes
|
|
93185
|
+
};
|
|
92428
93186
|
}
|
|
92429
|
-
function
|
|
92430
|
-
if (!
|
|
92431
|
-
const
|
|
92432
|
-
|
|
92433
|
-
if (targetTableIndex === -1) return null;
|
|
92434
|
-
let tablePos = null;
|
|
92435
|
-
let currentTableIndex = 0;
|
|
92436
|
-
doc2.descendants((node2, pos) => {
|
|
92437
|
-
if (node2.type.name === "table") {
|
|
92438
|
-
if (currentTableIndex === targetTableIndex) {
|
|
92439
|
-
tablePos = pos;
|
|
92440
|
-
return false;
|
|
92441
|
-
}
|
|
92442
|
-
currentTableIndex++;
|
|
92443
|
-
}
|
|
92444
|
-
return true;
|
|
92445
|
-
});
|
|
92446
|
-
return tablePos;
|
|
93187
|
+
function hasFieldAnnotationData(event) {
|
|
93188
|
+
if (!event.dataTransfer) return false;
|
|
93189
|
+
const types2 = event.dataTransfer.types;
|
|
93190
|
+
return types2.includes(INTERNAL_MIME_TYPE) || types2.includes(FIELD_ANNOTATION_DATA_TYPE);
|
|
92447
93191
|
}
|
|
92448
|
-
function
|
|
92449
|
-
|
|
92450
|
-
if (!currentTableHit) return cellDragMode === "active";
|
|
92451
|
-
if (currentTableHit.block.id !== cellAnchor.tableBlockId) {
|
|
92452
|
-
return cellDragMode === "active";
|
|
92453
|
-
}
|
|
92454
|
-
const sameCell = currentTableHit.cellRowIndex === cellAnchor.cellRowIndex && currentTableHit.cellColIndex === cellAnchor.cellColIndex;
|
|
92455
|
-
if (!sameCell) {
|
|
92456
|
-
return true;
|
|
92457
|
-
}
|
|
92458
|
-
return cellDragMode === "active";
|
|
93192
|
+
function isInternalDrag(event) {
|
|
93193
|
+
return event.dataTransfer?.types?.includes(INTERNAL_MIME_TYPE) ?? false;
|
|
92459
93194
|
}
|
|
92460
|
-
function
|
|
92461
|
-
if (!
|
|
93195
|
+
function extractDragData(event) {
|
|
93196
|
+
if (!event.dataTransfer) return null;
|
|
93197
|
+
let jsonData = event.dataTransfer.getData(INTERNAL_MIME_TYPE);
|
|
93198
|
+
if (!jsonData) {
|
|
93199
|
+
jsonData = event.dataTransfer.getData(FIELD_ANNOTATION_DATA_TYPE);
|
|
93200
|
+
}
|
|
93201
|
+
if (!jsonData) return null;
|
|
93202
|
+
try {
|
|
93203
|
+
const parsed = JSON.parse(jsonData);
|
|
93204
|
+
return parsed.sourceField ?? parsed.attributes ?? parsed;
|
|
93205
|
+
} catch {
|
|
92462
93206
|
return null;
|
|
92463
93207
|
}
|
|
92464
|
-
|
|
92465
|
-
|
|
92466
|
-
|
|
92467
|
-
|
|
92468
|
-
|
|
92469
|
-
|
|
92470
|
-
|
|
92471
|
-
|
|
93208
|
+
}
|
|
93209
|
+
class DragDropManager {
|
|
93210
|
+
#deps = null;
|
|
93211
|
+
// Bound handlers for cleanup
|
|
93212
|
+
#boundHandleDragStart = null;
|
|
93213
|
+
#boundHandleDragOver = null;
|
|
93214
|
+
#boundHandleDrop = null;
|
|
93215
|
+
#boundHandleDragEnd = null;
|
|
93216
|
+
#boundHandleDragLeave = null;
|
|
93217
|
+
#boundHandleWindowDragOver = null;
|
|
93218
|
+
#boundHandleWindowDrop = null;
|
|
93219
|
+
// ==========================================================================
|
|
93220
|
+
// Setup
|
|
93221
|
+
// ==========================================================================
|
|
93222
|
+
setDependencies(deps) {
|
|
93223
|
+
this.#deps = deps;
|
|
92472
93224
|
}
|
|
92473
|
-
|
|
92474
|
-
|
|
92475
|
-
|
|
92476
|
-
|
|
92477
|
-
|
|
92478
|
-
|
|
92479
|
-
|
|
92480
|
-
|
|
92481
|
-
|
|
92482
|
-
|
|
92483
|
-
|
|
93225
|
+
bind() {
|
|
93226
|
+
if (!this.#deps) return;
|
|
93227
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
93228
|
+
const painterHost = this.#deps.getPainterHost();
|
|
93229
|
+
this.#boundHandleDragStart = this.#handleDragStart.bind(this);
|
|
93230
|
+
this.#boundHandleDragOver = this.#handleDragOver.bind(this);
|
|
93231
|
+
this.#boundHandleDrop = this.#handleDrop.bind(this);
|
|
93232
|
+
this.#boundHandleDragEnd = this.#handleDragEnd.bind(this);
|
|
93233
|
+
this.#boundHandleDragLeave = this.#handleDragLeave.bind(this);
|
|
93234
|
+
this.#boundHandleWindowDragOver = this.#handleWindowDragOver.bind(this);
|
|
93235
|
+
this.#boundHandleWindowDrop = this.#handleWindowDrop.bind(this);
|
|
93236
|
+
painterHost.addEventListener("dragstart", this.#boundHandleDragStart);
|
|
93237
|
+
painterHost.addEventListener("dragend", this.#boundHandleDragEnd);
|
|
93238
|
+
painterHost.addEventListener("dragleave", this.#boundHandleDragLeave);
|
|
93239
|
+
viewportHost.addEventListener("dragover", this.#boundHandleDragOver);
|
|
93240
|
+
viewportHost.addEventListener("drop", this.#boundHandleDrop);
|
|
93241
|
+
window.addEventListener("dragover", this.#boundHandleWindowDragOver, false);
|
|
93242
|
+
window.addEventListener("drop", this.#boundHandleWindowDrop, false);
|
|
93243
|
+
}
|
|
93244
|
+
unbind() {
|
|
93245
|
+
if (!this.#deps) return;
|
|
93246
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
93247
|
+
const painterHost = this.#deps.getPainterHost();
|
|
93248
|
+
if (this.#boundHandleDragStart) {
|
|
93249
|
+
painterHost.removeEventListener("dragstart", this.#boundHandleDragStart);
|
|
93250
|
+
}
|
|
93251
|
+
if (this.#boundHandleDragEnd) {
|
|
93252
|
+
painterHost.removeEventListener("dragend", this.#boundHandleDragEnd);
|
|
93253
|
+
}
|
|
93254
|
+
if (this.#boundHandleDragLeave) {
|
|
93255
|
+
painterHost.removeEventListener("dragleave", this.#boundHandleDragLeave);
|
|
93256
|
+
}
|
|
93257
|
+
if (this.#boundHandleDragOver) {
|
|
93258
|
+
viewportHost.removeEventListener("dragover", this.#boundHandleDragOver);
|
|
93259
|
+
}
|
|
93260
|
+
if (this.#boundHandleDrop) {
|
|
93261
|
+
viewportHost.removeEventListener("drop", this.#boundHandleDrop);
|
|
93262
|
+
}
|
|
93263
|
+
if (this.#boundHandleWindowDragOver) {
|
|
93264
|
+
window.removeEventListener("dragover", this.#boundHandleWindowDragOver, false);
|
|
93265
|
+
}
|
|
93266
|
+
if (this.#boundHandleWindowDrop) {
|
|
93267
|
+
window.removeEventListener("drop", this.#boundHandleWindowDrop, false);
|
|
93268
|
+
}
|
|
93269
|
+
this.#boundHandleDragStart = null;
|
|
93270
|
+
this.#boundHandleDragOver = null;
|
|
93271
|
+
this.#boundHandleDrop = null;
|
|
93272
|
+
this.#boundHandleDragEnd = null;
|
|
93273
|
+
this.#boundHandleDragLeave = null;
|
|
93274
|
+
this.#boundHandleWindowDragOver = null;
|
|
93275
|
+
this.#boundHandleWindowDrop = null;
|
|
92484
93276
|
}
|
|
92485
|
-
|
|
92486
|
-
|
|
93277
|
+
destroy() {
|
|
93278
|
+
this.unbind();
|
|
93279
|
+
this.#deps = null;
|
|
92487
93280
|
}
|
|
92488
|
-
|
|
92489
|
-
|
|
92490
|
-
|
|
92491
|
-
|
|
92492
|
-
|
|
92493
|
-
|
|
92494
|
-
|
|
92495
|
-
|
|
92496
|
-
|
|
92497
|
-
const FIELD_ANNOTATION_DATA_TYPE = "fieldAnnotation";
|
|
92498
|
-
function setupInternalFieldAnnotationDragHandlers({
|
|
92499
|
-
painterHost,
|
|
92500
|
-
getActiveEditor,
|
|
92501
|
-
hitTest,
|
|
92502
|
-
scheduleSelectionUpdate
|
|
92503
|
-
}) {
|
|
92504
|
-
return createDragHandler(painterHost, {
|
|
92505
|
-
onDragOver: (event) => {
|
|
92506
|
-
if (!event.hasFieldAnnotation || event.event.clientX === 0) {
|
|
92507
|
-
return;
|
|
92508
|
-
}
|
|
92509
|
-
const activeEditor = getActiveEditor();
|
|
92510
|
-
if (!activeEditor?.isEditable) {
|
|
92511
|
-
return;
|
|
92512
|
-
}
|
|
92513
|
-
const hit = hitTest(event.clientX, event.clientY);
|
|
92514
|
-
const doc2 = activeEditor.state?.doc;
|
|
92515
|
-
if (!hit || !doc2) {
|
|
92516
|
-
return;
|
|
92517
|
-
}
|
|
92518
|
-
const pos = Math.min(Math.max(hit.pos, 1), doc2.content.size);
|
|
92519
|
-
const currentSelection = activeEditor.state.selection;
|
|
92520
|
-
if (currentSelection instanceof TextSelection$1 && currentSelection.from === pos && currentSelection.to === pos) {
|
|
92521
|
-
return;
|
|
92522
|
-
}
|
|
92523
|
-
try {
|
|
92524
|
-
const tr = activeEditor.state.tr.setSelection(TextSelection$1.create(doc2, pos)).setMeta("addToHistory", false);
|
|
92525
|
-
activeEditor.view?.dispatch(tr);
|
|
92526
|
-
scheduleSelectionUpdate();
|
|
92527
|
-
} catch {
|
|
92528
|
-
}
|
|
92529
|
-
},
|
|
92530
|
-
onDrop: (event) => {
|
|
92531
|
-
event.event.preventDefault();
|
|
92532
|
-
event.event.stopPropagation();
|
|
92533
|
-
if (event.pmPosition === null) {
|
|
92534
|
-
return;
|
|
92535
|
-
}
|
|
92536
|
-
const activeEditor = getActiveEditor();
|
|
92537
|
-
const { state, view } = activeEditor;
|
|
92538
|
-
if (!state || !view) {
|
|
92539
|
-
return;
|
|
92540
|
-
}
|
|
92541
|
-
const fieldId = event.data.fieldId;
|
|
92542
|
-
if (fieldId) {
|
|
92543
|
-
const targetPos = event.pmPosition;
|
|
92544
|
-
const pmStart = event.data.pmStart;
|
|
92545
|
-
let sourceStart = null;
|
|
92546
|
-
let sourceEnd = null;
|
|
92547
|
-
let sourceNode = null;
|
|
92548
|
-
if (pmStart != null) {
|
|
92549
|
-
const nodeAt = state.doc.nodeAt(pmStart);
|
|
92550
|
-
if (nodeAt?.type?.name === "fieldAnnotation") {
|
|
92551
|
-
sourceStart = pmStart;
|
|
92552
|
-
sourceEnd = pmStart + nodeAt.nodeSize;
|
|
92553
|
-
sourceNode = nodeAt;
|
|
92554
|
-
}
|
|
92555
|
-
}
|
|
92556
|
-
if (sourceStart == null || sourceEnd == null || !sourceNode) {
|
|
92557
|
-
state.doc.descendants((node2, pos) => {
|
|
92558
|
-
if (node2.type.name === "fieldAnnotation" && node2.attrs.fieldId === fieldId) {
|
|
92559
|
-
sourceStart = pos;
|
|
92560
|
-
sourceEnd = pos + node2.nodeSize;
|
|
92561
|
-
sourceNode = node2;
|
|
92562
|
-
return false;
|
|
92563
|
-
}
|
|
92564
|
-
return true;
|
|
92565
|
-
});
|
|
92566
|
-
}
|
|
92567
|
-
if (sourceStart === null || sourceEnd === null || !sourceNode) {
|
|
92568
|
-
return;
|
|
92569
|
-
}
|
|
92570
|
-
if (targetPos >= sourceStart && targetPos <= sourceEnd) {
|
|
92571
|
-
return;
|
|
92572
|
-
}
|
|
92573
|
-
const tr = state.tr;
|
|
92574
|
-
tr.delete(sourceStart, sourceEnd);
|
|
92575
|
-
const mappedTarget = tr.mapping.map(targetPos);
|
|
92576
|
-
if (mappedTarget < 0 || mappedTarget > tr.doc.content.size) {
|
|
92577
|
-
return;
|
|
92578
|
-
}
|
|
92579
|
-
tr.insert(mappedTarget, sourceNode);
|
|
92580
|
-
tr.setMeta("uiEvent", "drop");
|
|
92581
|
-
view.dispatch(tr);
|
|
92582
|
-
return;
|
|
92583
|
-
}
|
|
92584
|
-
const attrs = event.data.attributes;
|
|
92585
|
-
if (attrs && isValidFieldAnnotationAttributes(attrs)) {
|
|
92586
|
-
const inserted = activeEditor.commands?.addFieldAnnotation?.(event.pmPosition, attrs, true);
|
|
92587
|
-
if (inserted) {
|
|
92588
|
-
scheduleSelectionUpdate();
|
|
92589
|
-
}
|
|
92590
|
-
return;
|
|
92591
|
-
}
|
|
92592
|
-
activeEditor.emit("fieldAnnotationDropped", {
|
|
92593
|
-
sourceField: event.data,
|
|
92594
|
-
editor: activeEditor,
|
|
92595
|
-
coordinates: { pos: event.pmPosition }
|
|
92596
|
-
});
|
|
92597
|
-
}
|
|
92598
|
-
});
|
|
92599
|
-
}
|
|
92600
|
-
function createExternalFieldAnnotationDragOverHandler({
|
|
92601
|
-
getActiveEditor,
|
|
92602
|
-
hitTest,
|
|
92603
|
-
scheduleSelectionUpdate
|
|
92604
|
-
}) {
|
|
92605
|
-
return (event) => {
|
|
92606
|
-
const activeEditor = getActiveEditor();
|
|
92607
|
-
if (!activeEditor?.isEditable) {
|
|
93281
|
+
// ==========================================================================
|
|
93282
|
+
// Event Handlers
|
|
93283
|
+
// ==========================================================================
|
|
93284
|
+
/**
|
|
93285
|
+
* Handle dragstart for internal field annotations.
|
|
93286
|
+
*/
|
|
93287
|
+
#handleDragStart(event) {
|
|
93288
|
+
const target = event.target;
|
|
93289
|
+
if (!target?.dataset?.draggable || target.dataset.draggable !== "true") {
|
|
92608
93290
|
return;
|
|
92609
93291
|
}
|
|
92610
|
-
|
|
93292
|
+
const data = extractFieldAnnotationData(target);
|
|
92611
93293
|
if (event.dataTransfer) {
|
|
92612
|
-
|
|
93294
|
+
const jsonData = JSON.stringify({
|
|
93295
|
+
attributes: data.attributes,
|
|
93296
|
+
sourceField: data
|
|
93297
|
+
});
|
|
93298
|
+
event.dataTransfer.setData(INTERNAL_MIME_TYPE, jsonData);
|
|
93299
|
+
event.dataTransfer.setData(FIELD_ANNOTATION_DATA_TYPE, jsonData);
|
|
93300
|
+
event.dataTransfer.setData("text/plain", data.displayLabel ?? "Field Annotation");
|
|
93301
|
+
event.dataTransfer.setDragImage(target, 0, 0);
|
|
93302
|
+
event.dataTransfer.effectAllowed = "move";
|
|
92613
93303
|
}
|
|
92614
|
-
|
|
92615
|
-
|
|
92616
|
-
|
|
92617
|
-
|
|
93304
|
+
}
|
|
93305
|
+
/**
|
|
93306
|
+
* Handle dragover - update cursor position to show drop location.
|
|
93307
|
+
*/
|
|
93308
|
+
#handleDragOver(event) {
|
|
93309
|
+
if (!this.#deps) return;
|
|
93310
|
+
if (!hasFieldAnnotationData(event)) return;
|
|
93311
|
+
const activeEditor = this.#deps.getActiveEditor();
|
|
93312
|
+
if (!activeEditor?.isEditable) return;
|
|
93313
|
+
event.preventDefault();
|
|
93314
|
+
if (event.dataTransfer) {
|
|
93315
|
+
event.dataTransfer.dropEffect = isInternalDrag(event) ? "move" : "copy";
|
|
92618
93316
|
}
|
|
92619
|
-
const hit = hitTest(event.clientX, event.clientY);
|
|
93317
|
+
const hit = this.#deps.hitTest(event.clientX, event.clientY);
|
|
92620
93318
|
const doc2 = activeEditor.state?.doc;
|
|
92621
|
-
if (!hit || !doc2)
|
|
92622
|
-
return;
|
|
92623
|
-
}
|
|
93319
|
+
if (!hit || !doc2) return;
|
|
92624
93320
|
const pos = Math.min(Math.max(hit.pos, 1), doc2.content.size);
|
|
92625
93321
|
const currentSelection = activeEditor.state.selection;
|
|
92626
|
-
|
|
92627
|
-
if (isSameCursor) {
|
|
93322
|
+
if (currentSelection instanceof TextSelection$1 && currentSelection.from === pos && currentSelection.to === pos) {
|
|
92628
93323
|
return;
|
|
92629
93324
|
}
|
|
92630
93325
|
try {
|
|
92631
93326
|
const tr = activeEditor.state.tr.setSelection(TextSelection$1.create(doc2, pos)).setMeta("addToHistory", false);
|
|
92632
93327
|
activeEditor.view?.dispatch(tr);
|
|
92633
|
-
scheduleSelectionUpdate();
|
|
92634
|
-
} catch
|
|
92635
|
-
}
|
|
92636
|
-
};
|
|
92637
|
-
}
|
|
92638
|
-
function createExternalFieldAnnotationDropHandler({
|
|
92639
|
-
getActiveEditor,
|
|
92640
|
-
hitTest,
|
|
92641
|
-
scheduleSelectionUpdate
|
|
92642
|
-
}) {
|
|
92643
|
-
return (event) => {
|
|
92644
|
-
const activeEditor = getActiveEditor();
|
|
92645
|
-
if (!activeEditor?.isEditable) {
|
|
92646
|
-
return;
|
|
92647
|
-
}
|
|
92648
|
-
if (event.dataTransfer?.types?.includes("application/x-field-annotation")) {
|
|
92649
|
-
return;
|
|
93328
|
+
this.#deps.scheduleSelectionUpdate();
|
|
93329
|
+
} catch {
|
|
92650
93330
|
}
|
|
93331
|
+
}
|
|
93332
|
+
/**
|
|
93333
|
+
* Handle drop - either move internal annotation or insert external one.
|
|
93334
|
+
*/
|
|
93335
|
+
#handleDrop(event) {
|
|
93336
|
+
if (!this.#deps) return;
|
|
93337
|
+
if (!hasFieldAnnotationData(event)) return;
|
|
92651
93338
|
event.preventDefault();
|
|
92652
93339
|
event.stopPropagation();
|
|
92653
|
-
const
|
|
92654
|
-
if (!
|
|
93340
|
+
const activeEditor = this.#deps.getActiveEditor();
|
|
93341
|
+
if (!activeEditor?.isEditable) return;
|
|
93342
|
+
const { state, view } = activeEditor;
|
|
93343
|
+
if (!state || !view) return;
|
|
93344
|
+
const hit = this.#deps.hitTest(event.clientX, event.clientY);
|
|
93345
|
+
const fallbackPos = state.selection?.from ?? state.doc?.content.size ?? null;
|
|
93346
|
+
const dropPos = hit?.pos ?? fallbackPos;
|
|
93347
|
+
if (dropPos == null) return;
|
|
93348
|
+
if (isInternalDrag(event)) {
|
|
93349
|
+
this.#handleInternalDrop(event, dropPos);
|
|
92655
93350
|
return;
|
|
92656
93351
|
}
|
|
92657
|
-
|
|
92658
|
-
|
|
92659
|
-
|
|
92660
|
-
|
|
92661
|
-
|
|
92662
|
-
|
|
93352
|
+
this.#handleExternalDrop(event, dropPos);
|
|
93353
|
+
}
|
|
93354
|
+
/**
|
|
93355
|
+
* Handle internal drop - move field annotation within document.
|
|
93356
|
+
*/
|
|
93357
|
+
#handleInternalDrop(event, targetPos) {
|
|
93358
|
+
if (!this.#deps) return;
|
|
93359
|
+
const activeEditor = this.#deps.getActiveEditor();
|
|
93360
|
+
const { state, view } = activeEditor;
|
|
93361
|
+
if (!state || !view) return;
|
|
93362
|
+
const data = extractDragData(event);
|
|
93363
|
+
if (!data?.fieldId) return;
|
|
93364
|
+
const pmStart = data.pmStart;
|
|
93365
|
+
let sourceStart = null;
|
|
93366
|
+
let sourceEnd = null;
|
|
93367
|
+
let sourceNode = null;
|
|
93368
|
+
if (pmStart != null) {
|
|
93369
|
+
const nodeAt = state.doc.nodeAt(pmStart);
|
|
93370
|
+
if (nodeAt?.type?.name === "fieldAnnotation") {
|
|
93371
|
+
sourceStart = pmStart;
|
|
93372
|
+
sourceEnd = pmStart + nodeAt.nodeSize;
|
|
93373
|
+
sourceNode = nodeAt;
|
|
93374
|
+
}
|
|
92663
93375
|
}
|
|
93376
|
+
if (sourceStart == null || sourceEnd == null || !sourceNode) {
|
|
93377
|
+
state.doc.descendants((node2, pos) => {
|
|
93378
|
+
if (node2.type.name === "fieldAnnotation" && node2.attrs.fieldId === data.fieldId) {
|
|
93379
|
+
sourceStart = pos;
|
|
93380
|
+
sourceEnd = pos + node2.nodeSize;
|
|
93381
|
+
sourceNode = node2;
|
|
93382
|
+
return false;
|
|
93383
|
+
}
|
|
93384
|
+
return true;
|
|
93385
|
+
});
|
|
93386
|
+
}
|
|
93387
|
+
if (sourceStart === null || sourceEnd === null || !sourceNode) return;
|
|
93388
|
+
if (targetPos >= sourceStart && targetPos <= sourceEnd) return;
|
|
93389
|
+
const tr = state.tr;
|
|
93390
|
+
tr.delete(sourceStart, sourceEnd);
|
|
93391
|
+
const mappedTarget = tr.mapping.map(targetPos);
|
|
93392
|
+
if (mappedTarget < 0 || mappedTarget > tr.doc.content.size) return;
|
|
93393
|
+
tr.insert(mappedTarget, sourceNode);
|
|
93394
|
+
tr.setMeta("uiEvent", "drop");
|
|
93395
|
+
view.dispatch(tr);
|
|
93396
|
+
}
|
|
93397
|
+
/**
|
|
93398
|
+
* Handle external drop - insert new field annotation.
|
|
93399
|
+
*/
|
|
93400
|
+
#handleExternalDrop(event, pos) {
|
|
93401
|
+
if (!this.#deps) return;
|
|
93402
|
+
const activeEditor = this.#deps.getActiveEditor();
|
|
93403
|
+
const fieldAnnotationData = event.dataTransfer?.getData(FIELD_ANNOTATION_DATA_TYPE);
|
|
93404
|
+
if (!fieldAnnotationData) return;
|
|
92664
93405
|
let parsedData = null;
|
|
92665
93406
|
try {
|
|
92666
93407
|
parsedData = JSON.parse(fieldAnnotationData);
|
|
@@ -92671,7 +93412,7 @@ ${o}
|
|
|
92671
93412
|
activeEditor.emit?.("fieldAnnotationDropped", {
|
|
92672
93413
|
sourceField,
|
|
92673
93414
|
editor: activeEditor,
|
|
92674
|
-
coordinates:
|
|
93415
|
+
coordinates: this.#deps.hitTest(event.clientX, event.clientY),
|
|
92675
93416
|
pos
|
|
92676
93417
|
});
|
|
92677
93418
|
if (attributes && isValidFieldAnnotationAttributes(attributes)) {
|
|
@@ -92681,14 +93422,49 @@ ${o}
|
|
|
92681
93422
|
if (tr) {
|
|
92682
93423
|
activeEditor.view?.dispatch(tr);
|
|
92683
93424
|
}
|
|
92684
|
-
scheduleSelectionUpdate();
|
|
93425
|
+
this.#deps.scheduleSelectionUpdate();
|
|
92685
93426
|
}
|
|
92686
93427
|
const editorDom = activeEditor.view?.dom;
|
|
92687
93428
|
if (editorDom) {
|
|
92688
93429
|
editorDom.focus();
|
|
92689
93430
|
activeEditor.view?.focus();
|
|
92690
93431
|
}
|
|
92691
|
-
}
|
|
93432
|
+
}
|
|
93433
|
+
#handleDragEnd(_event) {
|
|
93434
|
+
this.#deps?.getPainterHost()?.classList.remove("drag-over");
|
|
93435
|
+
}
|
|
93436
|
+
#handleDragLeave(event) {
|
|
93437
|
+
const painterHost = this.#deps?.getPainterHost();
|
|
93438
|
+
if (!painterHost) return;
|
|
93439
|
+
const relatedTarget = event.relatedTarget;
|
|
93440
|
+
if (!relatedTarget || !painterHost.contains(relatedTarget)) {
|
|
93441
|
+
painterHost.classList.remove("drag-over");
|
|
93442
|
+
}
|
|
93443
|
+
}
|
|
93444
|
+
/**
|
|
93445
|
+
* Window-level dragover to allow drops on overlay elements.
|
|
93446
|
+
*/
|
|
93447
|
+
#handleWindowDragOver(event) {
|
|
93448
|
+
if (!hasFieldAnnotationData(event)) return;
|
|
93449
|
+
const viewportHost = this.#deps?.getViewportHost();
|
|
93450
|
+
const target = event.target;
|
|
93451
|
+
if (viewportHost?.contains(target)) return;
|
|
93452
|
+
event.preventDefault();
|
|
93453
|
+
if (event.dataTransfer) {
|
|
93454
|
+
event.dataTransfer.dropEffect = isInternalDrag(event) ? "move" : "copy";
|
|
93455
|
+
}
|
|
93456
|
+
this.#handleDragOver(event);
|
|
93457
|
+
}
|
|
93458
|
+
/**
|
|
93459
|
+
* Window-level drop to catch drops on overlay elements.
|
|
93460
|
+
*/
|
|
93461
|
+
#handleWindowDrop(event) {
|
|
93462
|
+
if (!hasFieldAnnotationData(event)) return;
|
|
93463
|
+
const viewportHost = this.#deps?.getViewportHost();
|
|
93464
|
+
const target = event.target;
|
|
93465
|
+
if (viewportHost?.contains(target)) return;
|
|
93466
|
+
this.#handleDrop(event);
|
|
93467
|
+
}
|
|
92692
93468
|
}
|
|
92693
93469
|
var SectionType = /* @__PURE__ */ ((SectionType2) => {
|
|
92694
93470
|
SectionType2["CONTINUOUS"] = "continuous";
|
|
@@ -94696,16 +95472,17 @@ ${o}
|
|
|
94696
95472
|
const afterRaw = pickNumber(source.after);
|
|
94697
95473
|
const lineRaw = pickNumber(source.line);
|
|
94698
95474
|
const lineRule = normalizeLineRule(source.lineRule);
|
|
95475
|
+
const resolvedLineRule = lineRule ?? (lineRaw != null ? "auto" : void 0);
|
|
94699
95476
|
const beforeAutospacing = toBooleanFlag(source.beforeAutospacing ?? source.beforeAutoSpacing);
|
|
94700
95477
|
const afterAutospacing = toBooleanFlag(source.afterAutospacing ?? source.afterAutoSpacing);
|
|
94701
95478
|
const contextualSpacing = toBooleanFlag(source.contextualSpacing);
|
|
94702
95479
|
const before = beforeRaw != null ? twipsToPx$1(beforeRaw) : pickNumber(source.lineSpaceBefore);
|
|
94703
95480
|
const after = afterRaw != null ? twipsToPx$1(afterRaw) : pickNumber(source.lineSpaceAfter);
|
|
94704
|
-
const line = normalizeLineValue(lineRaw,
|
|
95481
|
+
const line = normalizeLineValue(lineRaw, resolvedLineRule);
|
|
94705
95482
|
if (before != null) spacing.before = before;
|
|
94706
95483
|
if (after != null) spacing.after = after;
|
|
94707
95484
|
if (line != null) spacing.line = line;
|
|
94708
|
-
if (
|
|
95485
|
+
if (resolvedLineRule) spacing.lineRule = resolvedLineRule;
|
|
94709
95486
|
if (beforeAutospacing != null) spacing.beforeAutospacing = beforeAutospacing;
|
|
94710
95487
|
if (afterAutospacing != null) spacing.afterAutospacing = afterAutospacing;
|
|
94711
95488
|
if (contextualSpacing != null) spacing.contextualSpacing = contextualSpacing;
|
|
@@ -104521,8 +105298,6 @@ ${o}
|
|
|
104521
105298
|
const DEFAULT_VIRTUALIZED_PAGE_GAP = 72;
|
|
104522
105299
|
const DEFAULT_PAGE_GAP = 24;
|
|
104523
105300
|
const DEFAULT_HORIZONTAL_PAGE_GAP = 20;
|
|
104524
|
-
const MULTI_CLICK_TIME_THRESHOLD_MS = 400;
|
|
104525
|
-
const MULTI_CLICK_DISTANCE_THRESHOLD_PX = 5;
|
|
104526
105301
|
const HEADER_FOOTER_INIT_BUDGET_MS = 200;
|
|
104527
105302
|
const MAX_ZOOM_WARNING_THRESHOLD = 10;
|
|
104528
105303
|
const MAX_SELECTION_RECTS_PER_USER = 100;
|
|
@@ -104578,7 +105353,7 @@ ${o}
|
|
|
104578
105353
|
#layoutState = { blocks: [], measures: [], layout: null, bookmarks: /* @__PURE__ */ new Map() };
|
|
104579
105354
|
#domPainter = null;
|
|
104580
105355
|
#pageGeometryHelper = null;
|
|
104581
|
-
#
|
|
105356
|
+
#dragDropManager = null;
|
|
104582
105357
|
#layoutError = null;
|
|
104583
105358
|
#layoutErrorState = "healthy";
|
|
104584
105359
|
#errorBanner = null;
|
|
@@ -104595,9 +105370,6 @@ ${o}
|
|
|
104595
105370
|
#htmlAnnotationMeasureAttempts = 0;
|
|
104596
105371
|
#domPositionIndex = new DomPositionIndex();
|
|
104597
105372
|
#domIndexObserverManager = null;
|
|
104598
|
-
#debugLastPointer = null;
|
|
104599
|
-
#debugLastHit = null;
|
|
104600
|
-
#pendingMarginClick = null;
|
|
104601
105373
|
#rafHandle = null;
|
|
104602
105374
|
#editorListeners = [];
|
|
104603
105375
|
#sectionMetadata = [];
|
|
@@ -104614,25 +105386,7 @@ ${o}
|
|
|
104614
105386
|
#ariaLiveRegion = null;
|
|
104615
105387
|
#a11ySelectionAnnounceTimeout = null;
|
|
104616
105388
|
#a11yLastAnnouncedSelectionKey = null;
|
|
104617
|
-
#clickCount = 0;
|
|
104618
|
-
#lastClickTime = 0;
|
|
104619
|
-
#lastClickPosition = { x: 0, y: 0 };
|
|
104620
|
-
#lastSelectedImageBlockId = null;
|
|
104621
105389
|
#lastSelectedFieldAnnotation = null;
|
|
104622
|
-
// Drag selection state
|
|
104623
|
-
#dragAnchor = null;
|
|
104624
|
-
#dragAnchorPageIndex = null;
|
|
104625
|
-
#isDragging = false;
|
|
104626
|
-
#dragExtensionMode = "char";
|
|
104627
|
-
#dragLastPointer = null;
|
|
104628
|
-
#dragLastRawHit = null;
|
|
104629
|
-
#dragUsedPageNotMountedFallback = false;
|
|
104630
|
-
#suppressFocusInFromDraggable = false;
|
|
104631
|
-
// Cell selection drag state
|
|
104632
|
-
// Tracks cell-specific context when drag starts in a table for multi-cell selection
|
|
104633
|
-
#cellAnchor = null;
|
|
104634
|
-
/** Cell drag mode state machine: 'none' = not in table, 'pending' = in table but haven't crossed cell boundary, 'active' = crossed cell boundary */
|
|
104635
|
-
#cellDragMode = "none";
|
|
104636
105390
|
// Remote cursor/presence state management
|
|
104637
105391
|
/** Manager for remote cursor rendering and awareness subscriptions */
|
|
104638
105392
|
#remoteCursorManager = null;
|
|
@@ -104640,6 +105394,9 @@ ${o}
|
|
|
104640
105394
|
#remoteCursorOverlay = null;
|
|
104641
105395
|
/** DOM element for rendering local selection/caret (dual-layer overlay architecture) */
|
|
104642
105396
|
#localSelectionLayer = null;
|
|
105397
|
+
// Editor input management
|
|
105398
|
+
/** Manager for pointer events, focus, drag selection, and click handling */
|
|
105399
|
+
#editorInputManager = null;
|
|
104643
105400
|
constructor(options) {
|
|
104644
105401
|
super();
|
|
104645
105402
|
if (!options?.element) {
|
|
@@ -104848,6 +105605,7 @@ ${o}
|
|
|
104848
105605
|
this.#setupHeaderFooterSession();
|
|
104849
105606
|
this.#applyZoom();
|
|
104850
105607
|
this.#setupEditorListeners();
|
|
105608
|
+
this.#initializeEditorInputManager();
|
|
104851
105609
|
this.#setupPointerHandlers();
|
|
104852
105610
|
this.#setupDragHandlers();
|
|
104853
105611
|
this.#setupInputBridge();
|
|
@@ -105769,8 +106527,8 @@ ${o}
|
|
|
105769
106527
|
docEpoch: this.#epochMapper.getCurrentEpoch(),
|
|
105770
106528
|
layoutEpoch: this.#layoutEpoch,
|
|
105771
106529
|
selection,
|
|
105772
|
-
lastPointer: this.#debugLastPointer,
|
|
105773
|
-
lastHit: this.#debugLastHit
|
|
106530
|
+
lastPointer: this.#editorInputManager?.debugLastPointer ?? null,
|
|
106531
|
+
lastHit: this.#editorInputManager?.debugLastHit ?? null
|
|
105774
106532
|
});
|
|
105775
106533
|
} catch {
|
|
105776
106534
|
}
|
|
@@ -106163,15 +106921,12 @@ ${o}
|
|
|
106163
106921
|
this.#editorListeners = [];
|
|
106164
106922
|
this.#domIndexObserverManager?.destroy();
|
|
106165
106923
|
this.#domIndexObserverManager = null;
|
|
106166
|
-
|
|
106167
|
-
|
|
106168
|
-
|
|
106169
|
-
|
|
106170
|
-
|
|
106171
|
-
|
|
106172
|
-
this.#viewportHost?.removeEventListener("drop", this.#handleDrop);
|
|
106173
|
-
this.#visibleHost?.removeEventListener("keydown", this.#handleKeyDown);
|
|
106174
|
-
this.#visibleHost?.removeEventListener("focusin", this.#handleVisibleHostFocusIn);
|
|
106924
|
+
if (this.#editorInputManager) {
|
|
106925
|
+
safeCleanup(() => {
|
|
106926
|
+
this.#editorInputManager?.destroy();
|
|
106927
|
+
this.#editorInputManager = null;
|
|
106928
|
+
}, "Editor input manager");
|
|
106929
|
+
}
|
|
106175
106930
|
this.#inputBridge?.notifyTargetChanged();
|
|
106176
106931
|
this.#inputBridge?.destroy();
|
|
106177
106932
|
this.#inputBridge = null;
|
|
@@ -106179,7 +106934,6 @@ ${o}
|
|
|
106179
106934
|
clearTimeout(this.#a11ySelectionAnnounceTimeout);
|
|
106180
106935
|
this.#a11ySelectionAnnounceTimeout = null;
|
|
106181
106936
|
}
|
|
106182
|
-
this.#clearCellAnchor();
|
|
106183
106937
|
if (this.#options?.documentId) {
|
|
106184
106938
|
PresentationEditor.#instances.delete(this.#options.documentId);
|
|
106185
106939
|
}
|
|
@@ -106189,8 +106943,8 @@ ${o}
|
|
|
106189
106943
|
}, "Header/footer session manager");
|
|
106190
106944
|
this.#domPainter = null;
|
|
106191
106945
|
this.#pageGeometryHelper = null;
|
|
106192
|
-
this.#
|
|
106193
|
-
this.#
|
|
106946
|
+
this.#dragDropManager?.destroy();
|
|
106947
|
+
this.#dragDropManager = null;
|
|
106194
106948
|
this.#selectionOverlay?.remove();
|
|
106195
106949
|
this.#painterHost?.remove();
|
|
106196
106950
|
this.#hiddenHost?.remove();
|
|
@@ -106237,7 +106991,7 @@ ${o}
|
|
|
106237
106991
|
}
|
|
106238
106992
|
if (transaction?.docChanged) {
|
|
106239
106993
|
this.#updateLocalAwarenessCursor();
|
|
106240
|
-
this.#clearCellAnchor();
|
|
106994
|
+
this.#editorInputManager?.clearCellAnchor();
|
|
106241
106995
|
}
|
|
106242
106996
|
};
|
|
106243
106997
|
const handleSelection = () => {
|
|
@@ -106333,39 +107087,83 @@ ${o}
|
|
|
106333
107087
|
cursors: Array.from(this.#remoteCursorManager.state.values())
|
|
106334
107088
|
});
|
|
106335
107089
|
}
|
|
106336
|
-
/**
|
|
106337
|
-
* Render remote cursors from existing state without normalization.
|
|
106338
|
-
* Delegates to RemoteCursorManager.
|
|
106339
|
-
* @private
|
|
106340
|
-
*/
|
|
106341
|
-
#renderRemoteCursors() {
|
|
106342
|
-
this.#remoteCursorManager?.render(this.#getRemoteCursorRenderDeps());
|
|
106343
|
-
}
|
|
107090
|
+
/**
|
|
107091
|
+
* Render remote cursors from existing state without normalization.
|
|
107092
|
+
* Delegates to RemoteCursorManager.
|
|
107093
|
+
* @private
|
|
107094
|
+
*/
|
|
107095
|
+
#renderRemoteCursors() {
|
|
107096
|
+
this.#remoteCursorManager?.render(this.#getRemoteCursorRenderDeps());
|
|
107097
|
+
}
|
|
107098
|
+
/**
|
|
107099
|
+
* Initialize the EditorInputManager with dependencies and callbacks.
|
|
107100
|
+
* @private
|
|
107101
|
+
*/
|
|
107102
|
+
#initializeEditorInputManager() {
|
|
107103
|
+
this.#editorInputManager = new EditorInputManager();
|
|
107104
|
+
this.#editorInputManager.setDependencies({
|
|
107105
|
+
getActiveEditor: () => this.getActiveEditor(),
|
|
107106
|
+
getEditor: () => this.#editor,
|
|
107107
|
+
getLayoutState: () => this.#layoutState,
|
|
107108
|
+
getEpochMapper: () => this.#epochMapper,
|
|
107109
|
+
getViewportHost: () => this.#viewportHost,
|
|
107110
|
+
getVisibleHost: () => this.#visibleHost,
|
|
107111
|
+
getHeaderFooterSession: () => this.#headerFooterSession,
|
|
107112
|
+
getPageGeometryHelper: () => this.#pageGeometryHelper,
|
|
107113
|
+
getZoom: () => this.#layoutOptions.zoom ?? 1,
|
|
107114
|
+
isViewLocked: () => this.#isViewLocked(),
|
|
107115
|
+
getDocumentMode: () => this.#documentMode,
|
|
107116
|
+
getPageElement: (pageIndex) => this.#getPageElement(pageIndex),
|
|
107117
|
+
isSelectionAwareVirtualizationEnabled: () => this.#isSelectionAwareVirtualizationEnabled()
|
|
107118
|
+
});
|
|
107119
|
+
this.#editorInputManager.setCallbacks({
|
|
107120
|
+
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate(),
|
|
107121
|
+
scheduleRerender: () => this.#scheduleRerender(),
|
|
107122
|
+
setPendingDocChange: () => {
|
|
107123
|
+
this.#pendingDocChange = true;
|
|
107124
|
+
},
|
|
107125
|
+
updateSelectionVirtualizationPins: (options) => this.#updateSelectionVirtualizationPins(options),
|
|
107126
|
+
scheduleA11ySelectionAnnouncement: (options) => this.#scheduleA11ySelectionAnnouncement(options),
|
|
107127
|
+
goToAnchor: (href) => this.goToAnchor(href),
|
|
107128
|
+
emit: (event, payload) => this.emit(event, payload),
|
|
107129
|
+
normalizeClientPoint: (clientX, clientY) => this.#normalizeClientPoint(clientX, clientY),
|
|
107130
|
+
hitTestHeaderFooterRegion: (x2, y2) => this.#hitTestHeaderFooterRegion(x2, y2),
|
|
107131
|
+
exitHeaderFooterMode: () => this.#exitHeaderFooterMode(),
|
|
107132
|
+
activateHeaderFooterRegion: (region) => this.#activateHeaderFooterRegion(region),
|
|
107133
|
+
createDefaultHeaderFooter: (region) => this.#createDefaultHeaderFooter(region),
|
|
107134
|
+
emitHeaderFooterEditBlocked: (reason) => this.#emitHeaderFooterEditBlocked(reason),
|
|
107135
|
+
findRegionForPage: (kind, pageIndex) => this.#findRegionForPage(kind, pageIndex),
|
|
107136
|
+
getCurrentPageIndex: () => this.#getCurrentPageIndex(),
|
|
107137
|
+
resolveDescriptorForRegion: (region) => this.#resolveDescriptorForRegion(region),
|
|
107138
|
+
updateSelectionDebugHud: () => this.#updateSelectionDebugHud(),
|
|
107139
|
+
clearHoverRegion: () => this.#clearHoverRegion(),
|
|
107140
|
+
renderHoverRegion: (region) => this.#renderHoverRegion(region),
|
|
107141
|
+
focusEditorAfterImageSelection: () => this.#focusEditorAfterImageSelection(),
|
|
107142
|
+
resolveFieldAnnotationSelectionFromElement: (el) => this.#resolveFieldAnnotationSelectionFromElement(el),
|
|
107143
|
+
computePendingMarginClick: (pointerId, x2, y2) => this.#computePendingMarginClick(pointerId, x2, y2),
|
|
107144
|
+
selectWordAt: (pos) => this.#selectWordAt(pos),
|
|
107145
|
+
selectParagraphAt: (pos) => this.#selectParagraphAt(pos),
|
|
107146
|
+
finalizeDragSelectionWithDom: (pointer, dragAnchor, dragMode) => this.#finalizeDragSelectionWithDom(pointer, dragAnchor, dragMode),
|
|
107147
|
+
hitTestTable: (x2, y2) => this.#hitTestTable(x2, y2)
|
|
107148
|
+
});
|
|
107149
|
+
}
|
|
106344
107150
|
#setupPointerHandlers() {
|
|
106345
|
-
this.#
|
|
106346
|
-
this.#viewportHost.addEventListener("dblclick", this.#handleDoubleClick);
|
|
106347
|
-
this.#viewportHost.addEventListener("pointermove", this.#handlePointerMove);
|
|
106348
|
-
this.#viewportHost.addEventListener("pointerup", this.#handlePointerUp);
|
|
106349
|
-
this.#viewportHost.addEventListener("pointerleave", this.#handlePointerLeave);
|
|
106350
|
-
this.#viewportHost.addEventListener("dragover", this.#handleDragOver);
|
|
106351
|
-
this.#viewportHost.addEventListener("drop", this.#handleDrop);
|
|
106352
|
-
this.#visibleHost.addEventListener("keydown", this.#handleKeyDown);
|
|
106353
|
-
this.#visibleHost.addEventListener("focusin", this.#handleVisibleHostFocusIn);
|
|
107151
|
+
this.#editorInputManager?.bind();
|
|
106354
107152
|
}
|
|
106355
107153
|
/**
|
|
106356
|
-
* Sets up drag and drop handlers for field annotations
|
|
106357
|
-
* Uses the DragHandler from layout-bridge to handle drag events and map drop
|
|
106358
|
-
* coordinates to ProseMirror positions.
|
|
107154
|
+
* Sets up drag and drop handlers for field annotations.
|
|
106359
107155
|
*/
|
|
106360
107156
|
#setupDragHandlers() {
|
|
106361
|
-
this.#
|
|
106362
|
-
this.#
|
|
106363
|
-
this.#
|
|
106364
|
-
painterHost: this.#painterHost,
|
|
107157
|
+
this.#dragDropManager?.destroy();
|
|
107158
|
+
this.#dragDropManager = new DragDropManager();
|
|
107159
|
+
this.#dragDropManager.setDependencies({
|
|
106365
107160
|
getActiveEditor: () => this.getActiveEditor(),
|
|
106366
107161
|
hitTest: (clientX, clientY) => this.hitTest(clientX, clientY),
|
|
106367
|
-
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate()
|
|
107162
|
+
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate(),
|
|
107163
|
+
getViewportHost: () => this.#viewportHost,
|
|
107164
|
+
getPainterHost: () => this.#painterHost
|
|
106368
107165
|
});
|
|
107166
|
+
this.#dragDropManager.bind();
|
|
106369
107167
|
}
|
|
106370
107168
|
/**
|
|
106371
107169
|
* Focus the editor after image selection and schedule selection update.
|
|
@@ -106483,516 +107281,6 @@ ${o}
|
|
|
106483
107281
|
});
|
|
106484
107282
|
this.#headerFooterSession.initialize();
|
|
106485
107283
|
}
|
|
106486
|
-
#handlePointerDown = (event) => {
|
|
106487
|
-
if (event.button !== 0) {
|
|
106488
|
-
return;
|
|
106489
|
-
}
|
|
106490
|
-
if (event.ctrlKey && navigator.platform.includes("Mac")) {
|
|
106491
|
-
return;
|
|
106492
|
-
}
|
|
106493
|
-
this.#pendingMarginClick = null;
|
|
106494
|
-
const target = event.target;
|
|
106495
|
-
if (target?.closest?.(".superdoc-ruler-handle") != null) {
|
|
106496
|
-
return;
|
|
106497
|
-
}
|
|
106498
|
-
const linkEl = target?.closest?.("a.superdoc-link");
|
|
106499
|
-
if (linkEl) {
|
|
106500
|
-
const href = linkEl.getAttribute("href") ?? "";
|
|
106501
|
-
const isAnchorLink = href.startsWith("#") && href.length > 1;
|
|
106502
|
-
const isTocLink = linkEl.closest(".superdoc-toc-entry") !== null;
|
|
106503
|
-
if (isAnchorLink && isTocLink) {
|
|
106504
|
-
event.preventDefault();
|
|
106505
|
-
event.stopPropagation();
|
|
106506
|
-
this.goToAnchor(href);
|
|
106507
|
-
return;
|
|
106508
|
-
}
|
|
106509
|
-
event.preventDefault();
|
|
106510
|
-
event.stopPropagation();
|
|
106511
|
-
const linkClickEvent = new CustomEvent("superdoc-link-click", {
|
|
106512
|
-
bubbles: true,
|
|
106513
|
-
composed: true,
|
|
106514
|
-
detail: {
|
|
106515
|
-
href,
|
|
106516
|
-
target: linkEl.getAttribute("target"),
|
|
106517
|
-
rel: linkEl.getAttribute("rel"),
|
|
106518
|
-
tooltip: linkEl.getAttribute("title"),
|
|
106519
|
-
element: linkEl,
|
|
106520
|
-
clientX: event.clientX,
|
|
106521
|
-
clientY: event.clientY
|
|
106522
|
-
}
|
|
106523
|
-
});
|
|
106524
|
-
linkEl.dispatchEvent(linkClickEvent);
|
|
106525
|
-
return;
|
|
106526
|
-
}
|
|
106527
|
-
const annotationEl = target?.closest?.(".annotation[data-pm-start]");
|
|
106528
|
-
const isDraggableAnnotation = target?.closest?.('[data-draggable="true"]') != null;
|
|
106529
|
-
this.#suppressFocusInFromDraggable = isDraggableAnnotation;
|
|
106530
|
-
if (annotationEl) {
|
|
106531
|
-
if (!this.#editor.isEditable) {
|
|
106532
|
-
return;
|
|
106533
|
-
}
|
|
106534
|
-
const resolved = this.#resolveFieldAnnotationSelectionFromElement(annotationEl);
|
|
106535
|
-
if (resolved) {
|
|
106536
|
-
try {
|
|
106537
|
-
const tr = this.#editor.state.tr.setSelection(NodeSelection.create(this.#editor.state.doc, resolved.pos));
|
|
106538
|
-
this.#editor.view?.dispatch(tr);
|
|
106539
|
-
} catch {
|
|
106540
|
-
}
|
|
106541
|
-
this.#editor.emit("fieldAnnotationClicked", {
|
|
106542
|
-
editor: this.#editor,
|
|
106543
|
-
node: resolved.node,
|
|
106544
|
-
nodePos: resolved.pos,
|
|
106545
|
-
event,
|
|
106546
|
-
currentTarget: annotationEl
|
|
106547
|
-
});
|
|
106548
|
-
}
|
|
106549
|
-
return;
|
|
106550
|
-
}
|
|
106551
|
-
if (!this.#layoutState.layout) {
|
|
106552
|
-
if (!isDraggableAnnotation) {
|
|
106553
|
-
event.preventDefault();
|
|
106554
|
-
}
|
|
106555
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
106556
|
-
document.activeElement.blur();
|
|
106557
|
-
}
|
|
106558
|
-
const editorDom2 = this.#editor.view?.dom;
|
|
106559
|
-
if (!editorDom2) {
|
|
106560
|
-
return;
|
|
106561
|
-
}
|
|
106562
|
-
const validPos = this.#getFirstTextPosition();
|
|
106563
|
-
const doc22 = this.#editor?.state?.doc;
|
|
106564
|
-
if (doc22) {
|
|
106565
|
-
try {
|
|
106566
|
-
const tr = this.#editor.state.tr.setSelection(TextSelection$1.create(doc22, validPos));
|
|
106567
|
-
this.#editor.view?.dispatch(tr);
|
|
106568
|
-
} catch (error) {
|
|
106569
|
-
}
|
|
106570
|
-
}
|
|
106571
|
-
editorDom2.focus();
|
|
106572
|
-
this.#editor.view?.focus();
|
|
106573
|
-
this.#scheduleSelectionUpdate();
|
|
106574
|
-
return;
|
|
106575
|
-
}
|
|
106576
|
-
const normalizedPoint = this.#normalizeClientPoint(event.clientX, event.clientY);
|
|
106577
|
-
if (!normalizedPoint) {
|
|
106578
|
-
return;
|
|
106579
|
-
}
|
|
106580
|
-
const { x: x2, y: y2 } = normalizedPoint;
|
|
106581
|
-
this.#debugLastPointer = { clientX: event.clientX, clientY: event.clientY, x: x2, y: y2 };
|
|
106582
|
-
const sessionMode = this.#headerFooterSession?.session?.mode ?? "body";
|
|
106583
|
-
if (sessionMode !== "body") {
|
|
106584
|
-
const activeEditorHost = this.#headerFooterSession?.overlayManager?.getActiveEditorHost?.();
|
|
106585
|
-
const clickedInsideEditorHost = activeEditorHost && (activeEditorHost.contains(event.target) || activeEditorHost === event.target);
|
|
106586
|
-
if (clickedInsideEditorHost) {
|
|
106587
|
-
return;
|
|
106588
|
-
}
|
|
106589
|
-
const headerFooterRegion2 = this.#hitTestHeaderFooterRegion(x2, y2);
|
|
106590
|
-
if (!headerFooterRegion2) {
|
|
106591
|
-
this.#exitHeaderFooterMode();
|
|
106592
|
-
} else {
|
|
106593
|
-
return;
|
|
106594
|
-
}
|
|
106595
|
-
}
|
|
106596
|
-
const headerFooterRegion = this.#hitTestHeaderFooterRegion(x2, y2);
|
|
106597
|
-
if (headerFooterRegion) {
|
|
106598
|
-
return;
|
|
106599
|
-
}
|
|
106600
|
-
const rawHit = clickToPosition(
|
|
106601
|
-
this.#layoutState.layout,
|
|
106602
|
-
this.#layoutState.blocks,
|
|
106603
|
-
this.#layoutState.measures,
|
|
106604
|
-
{ x: x2, y: y2 },
|
|
106605
|
-
this.#viewportHost,
|
|
106606
|
-
event.clientX,
|
|
106607
|
-
event.clientY,
|
|
106608
|
-
this.#pageGeometryHelper ?? void 0
|
|
106609
|
-
);
|
|
106610
|
-
const doc2 = this.#editor.state?.doc;
|
|
106611
|
-
const mapped = rawHit && doc2 ? this.#epochMapper.mapPosFromLayoutToCurrentDetailed(rawHit.pos, rawHit.layoutEpoch, 1) : null;
|
|
106612
|
-
if (mapped && !mapped.ok) {
|
|
106613
|
-
debugLog("warn", "pointerdown mapping failed", mapped);
|
|
106614
|
-
}
|
|
106615
|
-
const hit = rawHit && doc2 && mapped?.ok ? { ...rawHit, pos: Math.max(0, Math.min(mapped.pos, doc2.content.size)), layoutEpoch: mapped.toEpoch } : null;
|
|
106616
|
-
this.#debugLastHit = hit ? { source: "dom", pos: rawHit?.pos ?? null, layoutEpoch: rawHit?.layoutEpoch ?? null, mappedPos: hit.pos } : { source: "none", pos: rawHit?.pos ?? null, layoutEpoch: rawHit?.layoutEpoch ?? null, mappedPos: null };
|
|
106617
|
-
this.#updateSelectionDebugHud();
|
|
106618
|
-
if (!isDraggableAnnotation) {
|
|
106619
|
-
event.preventDefault();
|
|
106620
|
-
}
|
|
106621
|
-
if (!rawHit) {
|
|
106622
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
106623
|
-
document.activeElement.blur();
|
|
106624
|
-
}
|
|
106625
|
-
const editorDom2 = this.#editor.view?.dom;
|
|
106626
|
-
if (editorDom2) {
|
|
106627
|
-
const validPos = this.#getFirstTextPosition();
|
|
106628
|
-
const doc22 = this.#editor?.state?.doc;
|
|
106629
|
-
if (doc22) {
|
|
106630
|
-
try {
|
|
106631
|
-
const tr = this.#editor.state.tr.setSelection(TextSelection$1.create(doc22, validPos));
|
|
106632
|
-
this.#editor.view?.dispatch(tr);
|
|
106633
|
-
} catch (error) {
|
|
106634
|
-
}
|
|
106635
|
-
}
|
|
106636
|
-
editorDom2.focus();
|
|
106637
|
-
this.#editor.view?.focus();
|
|
106638
|
-
this.#scheduleSelectionUpdate();
|
|
106639
|
-
}
|
|
106640
|
-
return;
|
|
106641
|
-
}
|
|
106642
|
-
if (!hit || !doc2) {
|
|
106643
|
-
this.#pendingDocChange = true;
|
|
106644
|
-
this.#scheduleRerender();
|
|
106645
|
-
return;
|
|
106646
|
-
}
|
|
106647
|
-
const fragmentHit = getFragmentAtPosition(
|
|
106648
|
-
this.#layoutState.layout,
|
|
106649
|
-
this.#layoutState.blocks,
|
|
106650
|
-
this.#layoutState.measures,
|
|
106651
|
-
rawHit.pos
|
|
106652
|
-
);
|
|
106653
|
-
const targetImg = event.target?.closest?.("img");
|
|
106654
|
-
const imgPmStart = targetImg?.dataset?.pmStart ? Number(targetImg.dataset.pmStart) : null;
|
|
106655
|
-
if (!Number.isNaN(imgPmStart) && imgPmStart != null) {
|
|
106656
|
-
const doc22 = this.#editor.state.doc;
|
|
106657
|
-
const imgLayoutEpochRaw = targetImg?.dataset?.layoutEpoch;
|
|
106658
|
-
const imgLayoutEpoch = imgLayoutEpochRaw != null ? Number(imgLayoutEpochRaw) : NaN;
|
|
106659
|
-
const rawLayoutEpoch = Number.isFinite(rawHit.layoutEpoch) ? rawHit.layoutEpoch : NaN;
|
|
106660
|
-
const effectiveEpoch = Number.isFinite(imgLayoutEpoch) && Number.isFinite(rawLayoutEpoch) ? Math.max(imgLayoutEpoch, rawLayoutEpoch) : Number.isFinite(imgLayoutEpoch) ? imgLayoutEpoch : rawHit.layoutEpoch;
|
|
106661
|
-
const mappedImg = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(imgPmStart, effectiveEpoch, 1);
|
|
106662
|
-
if (!mappedImg.ok) {
|
|
106663
|
-
debugLog("warn", "inline image mapping failed", mappedImg);
|
|
106664
|
-
this.#pendingDocChange = true;
|
|
106665
|
-
this.#scheduleRerender();
|
|
106666
|
-
return;
|
|
106667
|
-
}
|
|
106668
|
-
const clampedImgPos = Math.max(0, Math.min(mappedImg.pos, doc22.content.size));
|
|
106669
|
-
if (clampedImgPos < 0 || clampedImgPos >= doc22.content.size) {
|
|
106670
|
-
return;
|
|
106671
|
-
}
|
|
106672
|
-
const newSelectionId = `inline-${clampedImgPos}`;
|
|
106673
|
-
if (this.#lastSelectedImageBlockId && this.#lastSelectedImageBlockId !== newSelectionId) {
|
|
106674
|
-
this.emit("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
106675
|
-
}
|
|
106676
|
-
try {
|
|
106677
|
-
const tr = this.#editor.state.tr.setSelection(NodeSelection.create(doc22, clampedImgPos));
|
|
106678
|
-
this.#editor.view?.dispatch(tr);
|
|
106679
|
-
const selector = `.superdoc-inline-image[data-pm-start="${imgPmStart}"]`;
|
|
106680
|
-
const targetElement = this.#viewportHost.querySelector(selector);
|
|
106681
|
-
this.emit("imageSelected", {
|
|
106682
|
-
element: targetElement ?? targetImg,
|
|
106683
|
-
blockId: null,
|
|
106684
|
-
pmStart: clampedImgPos
|
|
106685
|
-
});
|
|
106686
|
-
this.#lastSelectedImageBlockId = newSelectionId;
|
|
106687
|
-
} catch (error) {
|
|
106688
|
-
}
|
|
106689
|
-
this.#focusEditorAfterImageSelection();
|
|
106690
|
-
return;
|
|
106691
|
-
}
|
|
106692
|
-
if (fragmentHit && (fragmentHit.fragment.kind === "image" || fragmentHit.fragment.kind === "drawing")) {
|
|
106693
|
-
const doc22 = this.#editor.state.doc;
|
|
106694
|
-
try {
|
|
106695
|
-
const tr = this.#editor.state.tr.setSelection(NodeSelection.create(doc22, hit.pos));
|
|
106696
|
-
this.#editor.view?.dispatch(tr);
|
|
106697
|
-
if (this.#lastSelectedImageBlockId && this.#lastSelectedImageBlockId !== fragmentHit.fragment.blockId) {
|
|
106698
|
-
this.emit("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
106699
|
-
}
|
|
106700
|
-
if (fragmentHit.fragment.kind === "image") {
|
|
106701
|
-
const targetElement = this.#viewportHost.querySelector(
|
|
106702
|
-
`.superdoc-image-fragment[data-pm-start="${fragmentHit.fragment.pmStart}"]`
|
|
106703
|
-
);
|
|
106704
|
-
if (targetElement) {
|
|
106705
|
-
this.emit("imageSelected", {
|
|
106706
|
-
element: targetElement,
|
|
106707
|
-
blockId: fragmentHit.fragment.blockId,
|
|
106708
|
-
pmStart: fragmentHit.fragment.pmStart
|
|
106709
|
-
});
|
|
106710
|
-
this.#lastSelectedImageBlockId = fragmentHit.fragment.blockId;
|
|
106711
|
-
}
|
|
106712
|
-
}
|
|
106713
|
-
} catch (error) {
|
|
106714
|
-
}
|
|
106715
|
-
this.#focusEditorAfterImageSelection();
|
|
106716
|
-
return;
|
|
106717
|
-
}
|
|
106718
|
-
if (this.#lastSelectedImageBlockId) {
|
|
106719
|
-
this.emit("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
106720
|
-
this.#lastSelectedImageBlockId = null;
|
|
106721
|
-
}
|
|
106722
|
-
if (event.shiftKey && this.#editor.state.selection.$anchor) {
|
|
106723
|
-
const anchor = this.#editor.state.selection.anchor;
|
|
106724
|
-
const head = hit.pos;
|
|
106725
|
-
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, head, this.#dragExtensionMode);
|
|
106726
|
-
try {
|
|
106727
|
-
const tr = this.#editor.state.tr.setSelection(TextSelection$1.create(this.#editor.state.doc, selAnchor, selHead));
|
|
106728
|
-
this.#editor.view?.dispatch(tr);
|
|
106729
|
-
this.#scheduleSelectionUpdate();
|
|
106730
|
-
} catch (error) {
|
|
106731
|
-
console.warn("[SELECTION] Failed to extend selection on shift+click:", {
|
|
106732
|
-
error,
|
|
106733
|
-
anchor,
|
|
106734
|
-
head,
|
|
106735
|
-
selAnchor,
|
|
106736
|
-
selHead,
|
|
106737
|
-
mode: this.#dragExtensionMode
|
|
106738
|
-
});
|
|
106739
|
-
}
|
|
106740
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
106741
|
-
document.activeElement.blur();
|
|
106742
|
-
}
|
|
106743
|
-
const editorDom2 = this.#editor.view?.dom;
|
|
106744
|
-
if (editorDom2) {
|
|
106745
|
-
editorDom2.focus();
|
|
106746
|
-
this.#editor.view?.focus();
|
|
106747
|
-
}
|
|
106748
|
-
return;
|
|
106749
|
-
}
|
|
106750
|
-
const clickDepth = this.#registerPointerClick(event);
|
|
106751
|
-
if (clickDepth === 1) {
|
|
106752
|
-
this.#dragAnchor = hit.pos;
|
|
106753
|
-
this.#dragAnchorPageIndex = hit.pageIndex;
|
|
106754
|
-
this.#pendingMarginClick = this.#computePendingMarginClick(event.pointerId, x2, y2);
|
|
106755
|
-
const tableHit = this.#hitTestTable(x2, y2);
|
|
106756
|
-
if (tableHit) {
|
|
106757
|
-
const tablePos = this.#getTablePosFromHit(tableHit);
|
|
106758
|
-
if (tablePos !== null) {
|
|
106759
|
-
this.#setCellAnchor(tableHit, tablePos);
|
|
106760
|
-
}
|
|
106761
|
-
} else {
|
|
106762
|
-
this.#clearCellAnchor();
|
|
106763
|
-
}
|
|
106764
|
-
} else {
|
|
106765
|
-
this.#pendingMarginClick = null;
|
|
106766
|
-
}
|
|
106767
|
-
this.#dragLastPointer = { clientX: event.clientX, clientY: event.clientY, x: x2, y: y2 };
|
|
106768
|
-
this.#dragLastRawHit = hit;
|
|
106769
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
106770
|
-
this.#isDragging = true;
|
|
106771
|
-
if (clickDepth >= 3) {
|
|
106772
|
-
this.#dragExtensionMode = "para";
|
|
106773
|
-
} else if (clickDepth === 2) {
|
|
106774
|
-
this.#dragExtensionMode = "word";
|
|
106775
|
-
} else {
|
|
106776
|
-
this.#dragExtensionMode = "char";
|
|
106777
|
-
}
|
|
106778
|
-
debugLog(
|
|
106779
|
-
"verbose",
|
|
106780
|
-
`Drag selection start ${JSON.stringify({
|
|
106781
|
-
pointer: { clientX: event.clientX, clientY: event.clientY, x: x2, y: y2 },
|
|
106782
|
-
clickDepth,
|
|
106783
|
-
extensionMode: this.#dragExtensionMode,
|
|
106784
|
-
anchor: this.#dragAnchor,
|
|
106785
|
-
anchorPageIndex: this.#dragAnchorPageIndex,
|
|
106786
|
-
rawHit: rawHit ? {
|
|
106787
|
-
pos: rawHit.pos,
|
|
106788
|
-
pageIndex: rawHit.pageIndex,
|
|
106789
|
-
blockId: rawHit.blockId,
|
|
106790
|
-
lineIndex: rawHit.lineIndex,
|
|
106791
|
-
layoutEpoch: rawHit.layoutEpoch
|
|
106792
|
-
} : null,
|
|
106793
|
-
mapped: mapped ? mapped.ok ? { ok: true, pos: mapped.pos, fromEpoch: mapped.fromEpoch, toEpoch: mapped.toEpoch } : {
|
|
106794
|
-
ok: false,
|
|
106795
|
-
reason: mapped.reason,
|
|
106796
|
-
fromEpoch: mapped.fromEpoch,
|
|
106797
|
-
toEpoch: mapped.toEpoch
|
|
106798
|
-
} : null,
|
|
106799
|
-
hit: hit ? { pos: hit.pos, pageIndex: hit.pageIndex, layoutEpoch: hit.layoutEpoch } : null
|
|
106800
|
-
})}`
|
|
106801
|
-
);
|
|
106802
|
-
if (typeof this.#viewportHost.setPointerCapture === "function") {
|
|
106803
|
-
this.#viewportHost.setPointerCapture(event.pointerId);
|
|
106804
|
-
}
|
|
106805
|
-
let handledByDepth = false;
|
|
106806
|
-
const sessionModeForDepth = this.#headerFooterSession?.session?.mode ?? "body";
|
|
106807
|
-
if (sessionModeForDepth === "body") {
|
|
106808
|
-
const selectionPos = clickDepth >= 2 && this.#dragAnchor !== null ? this.#dragAnchor : hit.pos;
|
|
106809
|
-
if (clickDepth >= 3) {
|
|
106810
|
-
handledByDepth = this.#selectParagraphAt(selectionPos);
|
|
106811
|
-
} else if (clickDepth === 2) {
|
|
106812
|
-
handledByDepth = this.#selectWordAt(selectionPos);
|
|
106813
|
-
}
|
|
106814
|
-
}
|
|
106815
|
-
if (!handledByDepth) {
|
|
106816
|
-
try {
|
|
106817
|
-
const doc22 = this.#editor.state.doc;
|
|
106818
|
-
let nextSelection = TextSelection$1.create(doc22, hit.pos);
|
|
106819
|
-
if (!nextSelection.$from.parent.inlineContent) {
|
|
106820
|
-
nextSelection = Selection.near(doc22.resolve(hit.pos), 1);
|
|
106821
|
-
}
|
|
106822
|
-
const tr = this.#editor.state.tr.setSelection(nextSelection);
|
|
106823
|
-
this.#editor.view?.dispatch(tr);
|
|
106824
|
-
} catch {
|
|
106825
|
-
}
|
|
106826
|
-
}
|
|
106827
|
-
this.#scheduleSelectionUpdate();
|
|
106828
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
106829
|
-
document.activeElement.blur();
|
|
106830
|
-
}
|
|
106831
|
-
const editorDom = this.#editor.view?.dom;
|
|
106832
|
-
if (!editorDom) {
|
|
106833
|
-
return;
|
|
106834
|
-
}
|
|
106835
|
-
editorDom.focus();
|
|
106836
|
-
this.#editor.view?.focus();
|
|
106837
|
-
};
|
|
106838
|
-
/**
|
|
106839
|
-
* Finds the first valid text position in the document.
|
|
106840
|
-
*
|
|
106841
|
-
* Traverses the document tree to locate the first textblock node (paragraph, heading, etc.)
|
|
106842
|
-
* and returns a position inside it. This is used when focusing the editor but no specific
|
|
106843
|
-
* position is available (e.g., clicking outside text content or before layout is ready).
|
|
106844
|
-
*
|
|
106845
|
-
* @returns The position inside the first textblock, or 1 if no textblock is found
|
|
106846
|
-
* @private
|
|
106847
|
-
*/
|
|
106848
|
-
#getFirstTextPosition() {
|
|
106849
|
-
return getFirstTextPosition(this.#editor?.state?.doc ?? null);
|
|
106850
|
-
}
|
|
106851
|
-
/**
|
|
106852
|
-
* Registers a pointer click event and tracks multi-click sequences (double, triple).
|
|
106853
|
-
*
|
|
106854
|
-
* This method implements multi-click detection by tracking the timing and position
|
|
106855
|
-
* of consecutive clicks. Clicks within 400ms and 5px of each other increment the
|
|
106856
|
-
* click count, up to a maximum of 3 (single, double, triple).
|
|
106857
|
-
*
|
|
106858
|
-
* @param event - The mouse event from the pointer down handler
|
|
106859
|
-
* @returns The current click count (1 = single, 2 = double, 3 = triple)
|
|
106860
|
-
* @private
|
|
106861
|
-
*/
|
|
106862
|
-
#registerPointerClick(event) {
|
|
106863
|
-
const nextState = registerPointerClick(
|
|
106864
|
-
event,
|
|
106865
|
-
{ clickCount: this.#clickCount, lastClickTime: this.#lastClickTime, lastClickPosition: this.#lastClickPosition },
|
|
106866
|
-
{
|
|
106867
|
-
timeThresholdMs: MULTI_CLICK_TIME_THRESHOLD_MS,
|
|
106868
|
-
distanceThresholdPx: MULTI_CLICK_DISTANCE_THRESHOLD_PX,
|
|
106869
|
-
maxClickCount: 3
|
|
106870
|
-
}
|
|
106871
|
-
);
|
|
106872
|
-
this.#clickCount = nextState.clickCount;
|
|
106873
|
-
this.#lastClickTime = nextState.lastClickTime;
|
|
106874
|
-
this.#lastClickPosition = nextState.lastClickPosition;
|
|
106875
|
-
return nextState.clickCount;
|
|
106876
|
-
}
|
|
106877
|
-
// ============================================================================
|
|
106878
|
-
// Cell Selection Utilities
|
|
106879
|
-
// ============================================================================
|
|
106880
|
-
/**
|
|
106881
|
-
* Gets the ProseMirror position at the start of a table cell from a table hit result.
|
|
106882
|
-
*
|
|
106883
|
-
* This method navigates the ProseMirror document structure to find the exact position where
|
|
106884
|
-
* a table cell begins. The position returned is suitable for use with CellSelection.create().
|
|
106885
|
-
*
|
|
106886
|
-
* Algorithm:
|
|
106887
|
-
* 1. Validate input (tableHit structure and cell indices)
|
|
106888
|
-
* 2. Traverse document to find the table node matching tableHit.block.id
|
|
106889
|
-
* 3. Navigate through table structure (table > row > cell) to target row
|
|
106890
|
-
* 4. Track logical column position accounting for colspan (handles merged cells)
|
|
106891
|
-
* 5. Return position when target column falls within a cell's span
|
|
106892
|
-
*
|
|
106893
|
-
* Merged cell handling:
|
|
106894
|
-
* - Does NOT assume 1:1 mapping between cell index and logical column
|
|
106895
|
-
* - Tracks cumulative logical column position by summing colspan values
|
|
106896
|
-
* - A cell with colspan=3 occupies logical columns [n, n+1, n+2]
|
|
106897
|
-
* - Finds the cell whose logical span contains the target column index
|
|
106898
|
-
*
|
|
106899
|
-
* Error handling:
|
|
106900
|
-
* - Input validation with console warnings for debugging
|
|
106901
|
-
* - Try-catch around document traversal (catches corrupted document errors)
|
|
106902
|
-
* - Bounds checking for row indices
|
|
106903
|
-
* - Null checks at each navigation step
|
|
106904
|
-
*
|
|
106905
|
-
* @param tableHit - The table hit result from hitTestTableFragment containing:
|
|
106906
|
-
* - block: TableBlock with the table's block ID
|
|
106907
|
-
* - cellRowIndex: 0-based row index of the target cell
|
|
106908
|
-
* - cellColIndex: 0-based logical column index of the target cell
|
|
106909
|
-
* @returns The PM position at the start of the cell, or null if:
|
|
106910
|
-
* - Invalid input (null tableHit, negative indices)
|
|
106911
|
-
* - Table not found in document
|
|
106912
|
-
* - Target row out of bounds
|
|
106913
|
-
* - Target column not found in row
|
|
106914
|
-
* - Document traversal error
|
|
106915
|
-
* @private
|
|
106916
|
-
*
|
|
106917
|
-
* @throws Never throws - all errors are caught and logged, returns null on failure
|
|
106918
|
-
*/
|
|
106919
|
-
#getCellPosFromTableHit(tableHit) {
|
|
106920
|
-
return getCellPosFromTableHit(tableHit, this.#editor.state?.doc ?? null, this.#layoutState.blocks);
|
|
106921
|
-
}
|
|
106922
|
-
/**
|
|
106923
|
-
* Gets the table position (start of table node) from a table hit result.
|
|
106924
|
-
*
|
|
106925
|
-
* @param tableHit - The table hit result from hitTestTableFragment
|
|
106926
|
-
* @returns The PM position at the start of the table, or null if not found
|
|
106927
|
-
* @private
|
|
106928
|
-
*/
|
|
106929
|
-
#getTablePosFromHit(tableHit) {
|
|
106930
|
-
return getTablePosFromHit(tableHit, this.#editor.state?.doc ?? null, this.#layoutState.blocks);
|
|
106931
|
-
}
|
|
106932
|
-
/**
|
|
106933
|
-
* Determines if the current drag should create a CellSelection instead of TextSelection.
|
|
106934
|
-
*
|
|
106935
|
-
* Implements a state machine for table cell selection:
|
|
106936
|
-
* - 'none': Not in a table, use TextSelection
|
|
106937
|
-
* - 'pending': Started drag in a table, but haven't crossed cell boundary yet
|
|
106938
|
-
* - 'active': Crossed cell boundary, use CellSelection
|
|
106939
|
-
*
|
|
106940
|
-
* State transitions:
|
|
106941
|
-
* - none → pending: When drag starts in a table cell (#setCellAnchor)
|
|
106942
|
-
* - pending → active: When drag crosses into a different cell (this method returns true)
|
|
106943
|
-
* - active → none: When drag ends (#clearCellAnchor)
|
|
106944
|
-
* - * → none: When document changes or clicking outside table
|
|
106945
|
-
*
|
|
106946
|
-
* Decision logic:
|
|
106947
|
-
* 1. No cell anchor → false (not in table drag mode)
|
|
106948
|
-
* 2. Current position outside table → return current state (stay in 'active' if already there)
|
|
106949
|
-
* 3. Different table → treat as outside table
|
|
106950
|
-
* 4. Different cell in same table → true (activate cell selection)
|
|
106951
|
-
* 5. Same cell → return current state (stay in 'active' if already there, else false)
|
|
106952
|
-
*
|
|
106953
|
-
* This state machine ensures:
|
|
106954
|
-
* - Text selection works normally within a single cell
|
|
106955
|
-
* - Cell selection activates smoothly when crossing cell boundaries
|
|
106956
|
-
* - Once activated, cell selection persists even if dragging back to anchor cell
|
|
106957
|
-
*
|
|
106958
|
-
* @param currentTableHit - The table hit result for the current pointer position, or null if not in a table
|
|
106959
|
-
* @returns true if we should create a CellSelection, false for TextSelection
|
|
106960
|
-
* @private
|
|
106961
|
-
*/
|
|
106962
|
-
#shouldUseCellSelection(currentTableHit) {
|
|
106963
|
-
return shouldUseCellSelection(currentTableHit, this.#cellAnchor, this.#cellDragMode);
|
|
106964
|
-
}
|
|
106965
|
-
/**
|
|
106966
|
-
* Stores the cell anchor when a drag operation starts inside a table cell.
|
|
106967
|
-
*
|
|
106968
|
-
* @param tableHit - The table hit result for the initial click position
|
|
106969
|
-
* @param tablePos - The PM position of the table node
|
|
106970
|
-
* @private
|
|
106971
|
-
*/
|
|
106972
|
-
#setCellAnchor(tableHit, tablePos) {
|
|
106973
|
-
const cellPos = this.#getCellPosFromTableHit(tableHit);
|
|
106974
|
-
if (cellPos === null) {
|
|
106975
|
-
return;
|
|
106976
|
-
}
|
|
106977
|
-
this.#cellAnchor = {
|
|
106978
|
-
tablePos,
|
|
106979
|
-
cellPos,
|
|
106980
|
-
cellRowIndex: tableHit.cellRowIndex,
|
|
106981
|
-
cellColIndex: tableHit.cellColIndex,
|
|
106982
|
-
tableBlockId: tableHit.block.id
|
|
106983
|
-
};
|
|
106984
|
-
this.#cellDragMode = "pending";
|
|
106985
|
-
}
|
|
106986
|
-
/**
|
|
106987
|
-
* Clears the cell drag state.
|
|
106988
|
-
* Called when drag ends or when clicking outside a table.
|
|
106989
|
-
*
|
|
106990
|
-
* @private
|
|
106991
|
-
*/
|
|
106992
|
-
#clearCellAnchor() {
|
|
106993
|
-
this.#cellAnchor = null;
|
|
106994
|
-
this.#cellDragMode = "none";
|
|
106995
|
-
}
|
|
106996
107284
|
/**
|
|
106997
107285
|
* Attempts to perform a table hit test for the given normalized coordinates.
|
|
106998
107286
|
*
|
|
@@ -107097,415 +107385,6 @@ ${o}
|
|
|
107097
107385
|
#calculateExtendedSelection(anchor, head, mode) {
|
|
107098
107386
|
return calculateExtendedSelection(this.#layoutState.blocks, anchor, head, mode);
|
|
107099
107387
|
}
|
|
107100
|
-
#handlePointerMove = (event) => {
|
|
107101
|
-
if (!this.#layoutState.layout) return;
|
|
107102
|
-
const normalized = this.#normalizeClientPoint(event.clientX, event.clientY);
|
|
107103
|
-
if (!normalized) return;
|
|
107104
|
-
if (this.#isDragging && this.#dragAnchor !== null && event.buttons & 1) {
|
|
107105
|
-
this.#pendingMarginClick = null;
|
|
107106
|
-
const prevPointer = this.#dragLastPointer;
|
|
107107
|
-
const prevRawHit = this.#dragLastRawHit;
|
|
107108
|
-
this.#dragLastPointer = { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y };
|
|
107109
|
-
const rawHit = clickToPosition(
|
|
107110
|
-
this.#layoutState.layout,
|
|
107111
|
-
this.#layoutState.blocks,
|
|
107112
|
-
this.#layoutState.measures,
|
|
107113
|
-
{ x: normalized.x, y: normalized.y },
|
|
107114
|
-
this.#viewportHost,
|
|
107115
|
-
event.clientX,
|
|
107116
|
-
event.clientY,
|
|
107117
|
-
this.#pageGeometryHelper ?? void 0
|
|
107118
|
-
);
|
|
107119
|
-
if (!rawHit) {
|
|
107120
|
-
debugLog(
|
|
107121
|
-
"verbose",
|
|
107122
|
-
`Drag selection update (no hit) ${JSON.stringify({
|
|
107123
|
-
pointer: { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y },
|
|
107124
|
-
prevPointer,
|
|
107125
|
-
anchor: this.#dragAnchor
|
|
107126
|
-
})}`
|
|
107127
|
-
);
|
|
107128
|
-
return;
|
|
107129
|
-
}
|
|
107130
|
-
const doc2 = this.#editor.state?.doc;
|
|
107131
|
-
if (!doc2) return;
|
|
107132
|
-
this.#dragLastRawHit = rawHit;
|
|
107133
|
-
const pageMounted = this.#getPageElement(rawHit.pageIndex) != null;
|
|
107134
|
-
if (!pageMounted && this.#isSelectionAwareVirtualizationEnabled()) {
|
|
107135
|
-
this.#dragUsedPageNotMountedFallback = true;
|
|
107136
|
-
debugLog("warn", "Geometry fallback", { reason: "page_not_mounted", pageIndex: rawHit.pageIndex });
|
|
107137
|
-
}
|
|
107138
|
-
this.#updateSelectionVirtualizationPins({ includeDragBuffer: true, extraPages: [rawHit.pageIndex] });
|
|
107139
|
-
const mappedHead = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(rawHit.pos, rawHit.layoutEpoch, 1);
|
|
107140
|
-
if (!mappedHead.ok) {
|
|
107141
|
-
debugLog("warn", "drag mapping failed", mappedHead);
|
|
107142
|
-
debugLog(
|
|
107143
|
-
"verbose",
|
|
107144
|
-
`Drag selection update (map failed) ${JSON.stringify({
|
|
107145
|
-
pointer: { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y },
|
|
107146
|
-
prevPointer,
|
|
107147
|
-
anchor: this.#dragAnchor,
|
|
107148
|
-
rawHit: {
|
|
107149
|
-
pos: rawHit.pos,
|
|
107150
|
-
pageIndex: rawHit.pageIndex,
|
|
107151
|
-
blockId: rawHit.blockId,
|
|
107152
|
-
lineIndex: rawHit.lineIndex,
|
|
107153
|
-
layoutEpoch: rawHit.layoutEpoch
|
|
107154
|
-
},
|
|
107155
|
-
mapped: {
|
|
107156
|
-
ok: false,
|
|
107157
|
-
reason: mappedHead.reason,
|
|
107158
|
-
fromEpoch: mappedHead.fromEpoch,
|
|
107159
|
-
toEpoch: mappedHead.toEpoch
|
|
107160
|
-
}
|
|
107161
|
-
})}`
|
|
107162
|
-
);
|
|
107163
|
-
return;
|
|
107164
|
-
}
|
|
107165
|
-
const hit = {
|
|
107166
|
-
...rawHit,
|
|
107167
|
-
pos: Math.max(0, Math.min(mappedHead.pos, doc2.content.size)),
|
|
107168
|
-
layoutEpoch: mappedHead.toEpoch
|
|
107169
|
-
};
|
|
107170
|
-
this.#debugLastHit = {
|
|
107171
|
-
source: pageMounted ? "dom" : "geometry",
|
|
107172
|
-
pos: rawHit.pos,
|
|
107173
|
-
layoutEpoch: rawHit.layoutEpoch,
|
|
107174
|
-
mappedPos: hit.pos
|
|
107175
|
-
};
|
|
107176
|
-
this.#updateSelectionDebugHud();
|
|
107177
|
-
const anchor = this.#dragAnchor;
|
|
107178
|
-
const head = hit.pos;
|
|
107179
|
-
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, head, this.#dragExtensionMode);
|
|
107180
|
-
debugLog(
|
|
107181
|
-
"verbose",
|
|
107182
|
-
`Drag selection update ${JSON.stringify({
|
|
107183
|
-
pointer: { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y },
|
|
107184
|
-
prevPointer,
|
|
107185
|
-
rawHit: {
|
|
107186
|
-
pos: rawHit.pos,
|
|
107187
|
-
pageIndex: rawHit.pageIndex,
|
|
107188
|
-
blockId: rawHit.blockId,
|
|
107189
|
-
lineIndex: rawHit.lineIndex,
|
|
107190
|
-
layoutEpoch: rawHit.layoutEpoch
|
|
107191
|
-
},
|
|
107192
|
-
prevRawHit: prevRawHit ? {
|
|
107193
|
-
pos: prevRawHit.pos,
|
|
107194
|
-
pageIndex: prevRawHit.pageIndex,
|
|
107195
|
-
blockId: prevRawHit.blockId,
|
|
107196
|
-
lineIndex: prevRawHit.lineIndex,
|
|
107197
|
-
layoutEpoch: prevRawHit.layoutEpoch
|
|
107198
|
-
} : null,
|
|
107199
|
-
mappedHead: { pos: mappedHead.pos, fromEpoch: mappedHead.fromEpoch, toEpoch: mappedHead.toEpoch },
|
|
107200
|
-
hit: { pos: hit.pos, pageIndex: hit.pageIndex, layoutEpoch: hit.layoutEpoch },
|
|
107201
|
-
anchor,
|
|
107202
|
-
head,
|
|
107203
|
-
selAnchor,
|
|
107204
|
-
selHead,
|
|
107205
|
-
direction: head >= anchor ? "down" : "up",
|
|
107206
|
-
selectionDirection: selHead >= selAnchor ? "down" : "up",
|
|
107207
|
-
extensionMode: this.#dragExtensionMode,
|
|
107208
|
-
hitSource: pageMounted ? "dom" : "geometry",
|
|
107209
|
-
pageMounted
|
|
107210
|
-
})}`
|
|
107211
|
-
);
|
|
107212
|
-
const currentTableHit = this.#hitTestTable(normalized.x, normalized.y);
|
|
107213
|
-
const shouldUseCellSel = this.#shouldUseCellSelection(currentTableHit);
|
|
107214
|
-
if (shouldUseCellSel && this.#cellAnchor) {
|
|
107215
|
-
const headCellPos = currentTableHit ? this.#getCellPosFromTableHit(currentTableHit) : null;
|
|
107216
|
-
if (headCellPos !== null) {
|
|
107217
|
-
if (this.#cellDragMode !== "active") {
|
|
107218
|
-
this.#cellDragMode = "active";
|
|
107219
|
-
}
|
|
107220
|
-
try {
|
|
107221
|
-
const doc22 = this.#editor.state.doc;
|
|
107222
|
-
const anchorCellPos = this.#cellAnchor.cellPos;
|
|
107223
|
-
const clampedAnchor = Math.max(0, Math.min(anchorCellPos, doc22.content.size));
|
|
107224
|
-
const clampedHead = Math.max(0, Math.min(headCellPos, doc22.content.size));
|
|
107225
|
-
const cellSelection = CellSelection.create(doc22, clampedAnchor, clampedHead);
|
|
107226
|
-
const tr = this.#editor.state.tr.setSelection(cellSelection);
|
|
107227
|
-
this.#editor.view?.dispatch(tr);
|
|
107228
|
-
this.#scheduleSelectionUpdate();
|
|
107229
|
-
} catch (error) {
|
|
107230
|
-
console.warn("[CELL-SELECTION] Failed to create CellSelection, falling back to TextSelection:", error);
|
|
107231
|
-
const anchor2 = this.#dragAnchor;
|
|
107232
|
-
const head2 = hit.pos;
|
|
107233
|
-
const { selAnchor: selAnchor2, selHead: selHead2 } = this.#calculateExtendedSelection(anchor2, head2, this.#dragExtensionMode);
|
|
107234
|
-
try {
|
|
107235
|
-
const tr = this.#editor.state.tr.setSelection(
|
|
107236
|
-
TextSelection$1.create(this.#editor.state.doc, selAnchor2, selHead2)
|
|
107237
|
-
);
|
|
107238
|
-
this.#editor.view?.dispatch(tr);
|
|
107239
|
-
this.#scheduleSelectionUpdate();
|
|
107240
|
-
} catch {
|
|
107241
|
-
}
|
|
107242
|
-
}
|
|
107243
|
-
return;
|
|
107244
|
-
}
|
|
107245
|
-
}
|
|
107246
|
-
try {
|
|
107247
|
-
const tr = this.#editor.state.tr.setSelection(TextSelection$1.create(this.#editor.state.doc, selAnchor, selHead));
|
|
107248
|
-
this.#editor.view?.dispatch(tr);
|
|
107249
|
-
this.#scheduleSelectionUpdate();
|
|
107250
|
-
} catch (error) {
|
|
107251
|
-
console.warn("[SELECTION] Failed to extend selection during drag:", {
|
|
107252
|
-
error,
|
|
107253
|
-
anchor,
|
|
107254
|
-
head,
|
|
107255
|
-
selAnchor,
|
|
107256
|
-
selHead,
|
|
107257
|
-
mode: this.#dragExtensionMode
|
|
107258
|
-
});
|
|
107259
|
-
}
|
|
107260
|
-
return;
|
|
107261
|
-
}
|
|
107262
|
-
const sessionMode = this.#headerFooterSession?.session?.mode ?? "body";
|
|
107263
|
-
if (sessionMode !== "body") {
|
|
107264
|
-
this.#clearHoverRegion();
|
|
107265
|
-
return;
|
|
107266
|
-
}
|
|
107267
|
-
if (this.#documentMode === "viewing") {
|
|
107268
|
-
this.#clearHoverRegion();
|
|
107269
|
-
return;
|
|
107270
|
-
}
|
|
107271
|
-
const region = this.#hitTestHeaderFooterRegion(normalized.x, normalized.y);
|
|
107272
|
-
if (!region) {
|
|
107273
|
-
this.#clearHoverRegion();
|
|
107274
|
-
return;
|
|
107275
|
-
}
|
|
107276
|
-
const currentHover = this.#headerFooterSession?.hoverRegion;
|
|
107277
|
-
if (currentHover && currentHover.kind === region.kind && currentHover.pageIndex === region.pageIndex && currentHover.sectionType === region.sectionType) {
|
|
107278
|
-
return;
|
|
107279
|
-
}
|
|
107280
|
-
this.#headerFooterSession?.renderHover(region);
|
|
107281
|
-
this.#renderHoverRegion(region);
|
|
107282
|
-
};
|
|
107283
|
-
#handlePointerLeave = () => {
|
|
107284
|
-
this.#clearHoverRegion();
|
|
107285
|
-
};
|
|
107286
|
-
#handleVisibleHostFocusIn = (event) => {
|
|
107287
|
-
if (isInRegisteredSurface(event)) {
|
|
107288
|
-
return;
|
|
107289
|
-
}
|
|
107290
|
-
if (this.#suppressFocusInFromDraggable) {
|
|
107291
|
-
this.#suppressFocusInFromDraggable = false;
|
|
107292
|
-
return;
|
|
107293
|
-
}
|
|
107294
|
-
const target = event.target;
|
|
107295
|
-
const activeTarget = this.#getActiveDomTarget();
|
|
107296
|
-
if (!activeTarget) {
|
|
107297
|
-
return;
|
|
107298
|
-
}
|
|
107299
|
-
const activeNode = activeTarget;
|
|
107300
|
-
const containsFn = typeof activeNode.contains === "function" ? activeNode.contains : null;
|
|
107301
|
-
if (target && (activeNode === target || containsFn && containsFn.call(activeNode, target))) {
|
|
107302
|
-
return;
|
|
107303
|
-
}
|
|
107304
|
-
try {
|
|
107305
|
-
if (activeTarget instanceof HTMLElement && typeof activeTarget.focus === "function") {
|
|
107306
|
-
activeTarget.focus?.({
|
|
107307
|
-
preventScroll: true
|
|
107308
|
-
});
|
|
107309
|
-
} else if (typeof activeTarget.focus === "function") {
|
|
107310
|
-
activeTarget.focus();
|
|
107311
|
-
}
|
|
107312
|
-
} catch {
|
|
107313
|
-
}
|
|
107314
|
-
try {
|
|
107315
|
-
this.getActiveEditor().view?.focus();
|
|
107316
|
-
} catch {
|
|
107317
|
-
}
|
|
107318
|
-
};
|
|
107319
|
-
#handlePointerUp = (event) => {
|
|
107320
|
-
this.#suppressFocusInFromDraggable = false;
|
|
107321
|
-
if (!this.#isDragging) return;
|
|
107322
|
-
if (typeof this.#viewportHost.hasPointerCapture === "function" && typeof this.#viewportHost.releasePointerCapture === "function" && this.#viewportHost.hasPointerCapture(event.pointerId)) {
|
|
107323
|
-
this.#viewportHost.releasePointerCapture(event.pointerId);
|
|
107324
|
-
}
|
|
107325
|
-
const pendingMarginClick = this.#pendingMarginClick;
|
|
107326
|
-
this.#pendingMarginClick = null;
|
|
107327
|
-
const dragAnchor = this.#dragAnchor;
|
|
107328
|
-
const dragMode = this.#dragExtensionMode;
|
|
107329
|
-
const dragUsedFallback = this.#dragUsedPageNotMountedFallback;
|
|
107330
|
-
const dragPointer = this.#dragLastPointer;
|
|
107331
|
-
this.#isDragging = false;
|
|
107332
|
-
if (this.#cellDragMode !== "none") {
|
|
107333
|
-
this.#cellDragMode = "none";
|
|
107334
|
-
}
|
|
107335
|
-
if (!pendingMarginClick || pendingMarginClick.pointerId !== event.pointerId) {
|
|
107336
|
-
this.#updateSelectionVirtualizationPins({ includeDragBuffer: false });
|
|
107337
|
-
if (dragUsedFallback && dragAnchor != null) {
|
|
107338
|
-
const pointer = dragPointer ?? { clientX: event.clientX, clientY: event.clientY };
|
|
107339
|
-
this.#finalizeDragSelectionWithDom(pointer, dragAnchor, dragMode);
|
|
107340
|
-
}
|
|
107341
|
-
this.#scheduleA11ySelectionAnnouncement({ immediate: true });
|
|
107342
|
-
this.#dragLastPointer = null;
|
|
107343
|
-
this.#dragLastRawHit = null;
|
|
107344
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
107345
|
-
return;
|
|
107346
|
-
}
|
|
107347
|
-
const sessionModeForDrag = this.#headerFooterSession?.session?.mode ?? "body";
|
|
107348
|
-
if (sessionModeForDrag !== "body" || this.#isViewLocked()) {
|
|
107349
|
-
this.#dragLastPointer = null;
|
|
107350
|
-
this.#dragLastRawHit = null;
|
|
107351
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
107352
|
-
return;
|
|
107353
|
-
}
|
|
107354
|
-
const doc2 = this.#editor.state?.doc;
|
|
107355
|
-
if (!doc2) {
|
|
107356
|
-
this.#dragLastPointer = null;
|
|
107357
|
-
this.#dragLastRawHit = null;
|
|
107358
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
107359
|
-
return;
|
|
107360
|
-
}
|
|
107361
|
-
if (pendingMarginClick.kind === "aboveFirstLine") {
|
|
107362
|
-
const pos = this.#getFirstTextPosition();
|
|
107363
|
-
try {
|
|
107364
|
-
const tr = this.#editor.state.tr.setSelection(TextSelection$1.create(doc2, pos));
|
|
107365
|
-
this.#editor.view?.dispatch(tr);
|
|
107366
|
-
this.#scheduleSelectionUpdate();
|
|
107367
|
-
} catch {
|
|
107368
|
-
}
|
|
107369
|
-
this.#debugLastHit = { source: "margin", pos: null, layoutEpoch: null, mappedPos: pos };
|
|
107370
|
-
this.#updateSelectionDebugHud();
|
|
107371
|
-
this.#dragLastPointer = null;
|
|
107372
|
-
this.#dragLastRawHit = null;
|
|
107373
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
107374
|
-
return;
|
|
107375
|
-
}
|
|
107376
|
-
if (pendingMarginClick.kind === "right") {
|
|
107377
|
-
const mappedEnd2 = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
107378
|
-
pendingMarginClick.pmEnd,
|
|
107379
|
-
pendingMarginClick.layoutEpoch,
|
|
107380
|
-
1
|
|
107381
|
-
);
|
|
107382
|
-
if (!mappedEnd2.ok) {
|
|
107383
|
-
debugLog("warn", "right margin mapping failed", mappedEnd2);
|
|
107384
|
-
this.#pendingDocChange = true;
|
|
107385
|
-
this.#scheduleRerender();
|
|
107386
|
-
this.#dragLastPointer = null;
|
|
107387
|
-
this.#dragLastRawHit = null;
|
|
107388
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
107389
|
-
return;
|
|
107390
|
-
}
|
|
107391
|
-
const caretPos = Math.max(0, Math.min(mappedEnd2.pos, doc2.content.size));
|
|
107392
|
-
try {
|
|
107393
|
-
const tr = this.#editor.state.tr.setSelection(TextSelection$1.create(doc2, caretPos));
|
|
107394
|
-
this.#editor.view?.dispatch(tr);
|
|
107395
|
-
this.#scheduleSelectionUpdate();
|
|
107396
|
-
} catch {
|
|
107397
|
-
}
|
|
107398
|
-
this.#debugLastHit = {
|
|
107399
|
-
source: "margin",
|
|
107400
|
-
pos: pendingMarginClick.pmEnd,
|
|
107401
|
-
layoutEpoch: pendingMarginClick.layoutEpoch,
|
|
107402
|
-
mappedPos: caretPos
|
|
107403
|
-
};
|
|
107404
|
-
this.#updateSelectionDebugHud();
|
|
107405
|
-
this.#dragLastPointer = null;
|
|
107406
|
-
this.#dragLastRawHit = null;
|
|
107407
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
107408
|
-
return;
|
|
107409
|
-
}
|
|
107410
|
-
const mappedStart = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
107411
|
-
pendingMarginClick.pmStart,
|
|
107412
|
-
pendingMarginClick.layoutEpoch,
|
|
107413
|
-
1
|
|
107414
|
-
);
|
|
107415
|
-
const mappedEnd = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
107416
|
-
pendingMarginClick.pmEnd,
|
|
107417
|
-
pendingMarginClick.layoutEpoch,
|
|
107418
|
-
-1
|
|
107419
|
-
);
|
|
107420
|
-
if (!mappedStart.ok || !mappedEnd.ok) {
|
|
107421
|
-
if (!mappedStart.ok) debugLog("warn", "left margin mapping failed (start)", mappedStart);
|
|
107422
|
-
if (!mappedEnd.ok) debugLog("warn", "left margin mapping failed (end)", mappedEnd);
|
|
107423
|
-
this.#pendingDocChange = true;
|
|
107424
|
-
this.#scheduleRerender();
|
|
107425
|
-
this.#dragLastPointer = null;
|
|
107426
|
-
this.#dragLastRawHit = null;
|
|
107427
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
107428
|
-
return;
|
|
107429
|
-
}
|
|
107430
|
-
const selFrom = Math.max(0, Math.min(Math.min(mappedStart.pos, mappedEnd.pos), doc2.content.size));
|
|
107431
|
-
const selTo = Math.max(0, Math.min(Math.max(mappedStart.pos, mappedEnd.pos), doc2.content.size));
|
|
107432
|
-
try {
|
|
107433
|
-
const tr = this.#editor.state.tr.setSelection(TextSelection$1.create(doc2, selFrom, selTo));
|
|
107434
|
-
this.#editor.view?.dispatch(tr);
|
|
107435
|
-
this.#scheduleSelectionUpdate();
|
|
107436
|
-
} catch {
|
|
107437
|
-
}
|
|
107438
|
-
this.#debugLastHit = {
|
|
107439
|
-
source: "margin",
|
|
107440
|
-
pos: pendingMarginClick.pmStart,
|
|
107441
|
-
layoutEpoch: pendingMarginClick.layoutEpoch,
|
|
107442
|
-
mappedPos: selFrom
|
|
107443
|
-
};
|
|
107444
|
-
this.#updateSelectionDebugHud();
|
|
107445
|
-
this.#dragLastPointer = null;
|
|
107446
|
-
this.#dragLastRawHit = null;
|
|
107447
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
107448
|
-
};
|
|
107449
|
-
#handleDragOver = createExternalFieldAnnotationDragOverHandler({
|
|
107450
|
-
getActiveEditor: () => this.getActiveEditor(),
|
|
107451
|
-
hitTest: (clientX, clientY) => this.hitTest(clientX, clientY),
|
|
107452
|
-
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate()
|
|
107453
|
-
});
|
|
107454
|
-
#handleDrop = createExternalFieldAnnotationDropHandler({
|
|
107455
|
-
getActiveEditor: () => this.getActiveEditor(),
|
|
107456
|
-
hitTest: (clientX, clientY) => this.hitTest(clientX, clientY),
|
|
107457
|
-
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate()
|
|
107458
|
-
});
|
|
107459
|
-
#handleDoubleClick = (event) => {
|
|
107460
|
-
if (event.button !== 0) return;
|
|
107461
|
-
if (!this.#layoutState.layout) return;
|
|
107462
|
-
const rect = this.#viewportHost.getBoundingClientRect();
|
|
107463
|
-
const zoom = this.#layoutOptions.zoom ?? 1;
|
|
107464
|
-
const scrollLeft = this.#visibleHost.scrollLeft ?? 0;
|
|
107465
|
-
const scrollTop = this.#visibleHost.scrollTop ?? 0;
|
|
107466
|
-
const x2 = (event.clientX - rect.left + scrollLeft) / zoom;
|
|
107467
|
-
const y2 = (event.clientY - rect.top + scrollTop) / zoom;
|
|
107468
|
-
const region = this.#hitTestHeaderFooterRegion(x2, y2);
|
|
107469
|
-
if (region) {
|
|
107470
|
-
event.preventDefault();
|
|
107471
|
-
event.stopPropagation();
|
|
107472
|
-
const descriptor = this.#resolveDescriptorForRegion(region);
|
|
107473
|
-
const hfManager = this.#headerFooterSession?.manager;
|
|
107474
|
-
if (!descriptor && hfManager) {
|
|
107475
|
-
this.#createDefaultHeaderFooter(region);
|
|
107476
|
-
hfManager.refresh();
|
|
107477
|
-
}
|
|
107478
|
-
this.#activateHeaderFooterRegion(region);
|
|
107479
|
-
} else if ((this.#headerFooterSession?.session?.mode ?? "body") !== "body") {
|
|
107480
|
-
this.#exitHeaderFooterMode();
|
|
107481
|
-
}
|
|
107482
|
-
};
|
|
107483
|
-
#handleKeyDown = (event) => {
|
|
107484
|
-
const sessionModeForKey = this.#headerFooterSession?.session?.mode ?? "body";
|
|
107485
|
-
if (event.key === "Escape" && sessionModeForKey !== "body") {
|
|
107486
|
-
event.preventDefault();
|
|
107487
|
-
this.#exitHeaderFooterMode();
|
|
107488
|
-
return;
|
|
107489
|
-
}
|
|
107490
|
-
if (event.ctrlKey && event.altKey && !event.shiftKey) {
|
|
107491
|
-
if (event.code === "KeyH") {
|
|
107492
|
-
event.preventDefault();
|
|
107493
|
-
this.#focusHeaderFooterShortcut("header");
|
|
107494
|
-
} else if (event.code === "KeyF") {
|
|
107495
|
-
event.preventDefault();
|
|
107496
|
-
this.#focusHeaderFooterShortcut("footer");
|
|
107497
|
-
}
|
|
107498
|
-
}
|
|
107499
|
-
};
|
|
107500
|
-
#focusHeaderFooterShortcut(kind) {
|
|
107501
|
-
const pageIndex = this.#getCurrentPageIndex();
|
|
107502
|
-
const region = this.#findRegionForPage(kind, pageIndex);
|
|
107503
|
-
if (!region) {
|
|
107504
|
-
this.#emitHeaderFooterEditBlocked("missingRegion");
|
|
107505
|
-
return;
|
|
107506
|
-
}
|
|
107507
|
-
this.#activateHeaderFooterRegion(region);
|
|
107508
|
-
}
|
|
107509
107388
|
#scheduleRerender() {
|
|
107510
107389
|
if (this.#renderScheduled) {
|
|
107511
107390
|
return;
|
|
@@ -107970,7 +107849,7 @@ ${o}
|
|
|
107970
107849
|
return;
|
|
107971
107850
|
}
|
|
107972
107851
|
this.#syncSelectedFieldAnnotationClass(selection);
|
|
107973
|
-
this.#updateSelectionVirtualizationPins({ includeDragBuffer: this.#isDragging });
|
|
107852
|
+
this.#updateSelectionVirtualizationPins({ includeDragBuffer: this.#editorInputManager?.isDragging ?? false });
|
|
107974
107853
|
if (selection instanceof CellSelection) {
|
|
107975
107854
|
try {
|
|
107976
107855
|
this.#localSelectionLayer.innerHTML = "";
|
|
@@ -108718,7 +108597,7 @@ ${o}
|
|
|
108718
108597
|
{
|
|
108719
108598
|
ariaLiveRegion: this.#ariaLiveRegion,
|
|
108720
108599
|
sessionMode,
|
|
108721
|
-
isDragging: this.#isDragging,
|
|
108600
|
+
isDragging: this.#editorInputManager?.isDragging ?? false,
|
|
108722
108601
|
visibleHost: this.#visibleHost,
|
|
108723
108602
|
currentTimeout: this.#a11ySelectionAnnounceTimeout,
|
|
108724
108603
|
announceNow: () => {
|
|
@@ -108846,9 +108725,9 @@ ${o}
|
|
|
108846
108725
|
} : null,
|
|
108847
108726
|
docSize,
|
|
108848
108727
|
includeDragBuffer: Boolean(options?.includeDragBuffer),
|
|
108849
|
-
isDragging: this.#isDragging,
|
|
108850
|
-
dragAnchorPageIndex: this.#dragAnchorPageIndex,
|
|
108851
|
-
dragLastHitPageIndex: this.#
|
|
108728
|
+
isDragging: this.#editorInputManager?.isDragging ?? false,
|
|
108729
|
+
dragAnchorPageIndex: this.#editorInputManager?.dragAnchorPageIndex ?? null,
|
|
108730
|
+
dragLastHitPageIndex: this.#editorInputManager?.dragLastHitPageIndex ?? null,
|
|
108852
108731
|
extraPages: options?.extraPages
|
|
108853
108732
|
});
|
|
108854
108733
|
painter.setVirtualizationPins(pins);
|
|
@@ -108862,9 +108741,10 @@ ${o}
|
|
|
108862
108741
|
}
|
|
108863
108742
|
const normalized = this.#normalizeClientPoint(pointer.clientX, pointer.clientY);
|
|
108864
108743
|
if (!normalized) return;
|
|
108744
|
+
const dragLastRawHit = this.#editorInputManager?.dragLastRawHit;
|
|
108865
108745
|
this.#updateSelectionVirtualizationPins({
|
|
108866
108746
|
includeDragBuffer: false,
|
|
108867
|
-
extraPages:
|
|
108747
|
+
extraPages: dragLastRawHit ? [dragLastRawHit.pageIndex] : void 0
|
|
108868
108748
|
});
|
|
108869
108749
|
const refined = clickToPosition(
|
|
108870
108750
|
layout,
|
|
@@ -108881,7 +108761,7 @@ ${o}
|
|
|
108881
108761
|
debugLog("warn", "Drag finalize: endpoint page still not mounted", { pageIndex: refined.pageIndex });
|
|
108882
108762
|
return;
|
|
108883
108763
|
}
|
|
108884
|
-
const prior =
|
|
108764
|
+
const prior = dragLastRawHit;
|
|
108885
108765
|
if (prior && (prior.pos !== refined.pos || prior.pageIndex !== refined.pageIndex)) {
|
|
108886
108766
|
debugLog("info", "Drag finalize refined hit", {
|
|
108887
108767
|
fromPos: prior.pos,
|
|
@@ -109084,7 +108964,7 @@ ${o}
|
|
|
109084
108964
|
localSelectionLayer,
|
|
109085
108965
|
blocks: this.#layoutState.blocks,
|
|
109086
108966
|
measures: this.#layoutState.measures,
|
|
109087
|
-
cellAnchorTableBlockId: this.#cellAnchor?.tableBlockId ?? null,
|
|
108967
|
+
cellAnchorTableBlockId: this.#editorInputManager?.cellAnchor?.tableBlockId ?? null,
|
|
109088
108968
|
convertPageLocalToOverlayCoords: (pageIndex, x2, y2) => this.#convertPageLocalToOverlayCoords(pageIndex, x2, y2)
|
|
109089
108969
|
});
|
|
109090
108970
|
}
|
|
@@ -153615,7 +153495,7 @@ ${reason}`);
|
|
|
153615
153495
|
this.config.colors = shuffleArray(this.config.colors);
|
|
153616
153496
|
this.userColorMap = /* @__PURE__ */ new Map();
|
|
153617
153497
|
this.colorIndex = 0;
|
|
153618
|
-
this.version = "1.8.0-next.
|
|
153498
|
+
this.version = "1.8.0-next.4";
|
|
153619
153499
|
this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
|
|
153620
153500
|
this.superdocId = config2.superdocId || v4();
|
|
153621
153501
|
this.colors = this.config.colors;
|