@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const jszip = require("./jszip-C8_CqJxM.cjs");
|
|
3
3
|
const helpers$1 = require("./helpers-nOdwpmwb.cjs");
|
|
4
|
-
const superEditor_converter = require("./SuperConverter-
|
|
4
|
+
const superEditor_converter = require("./SuperConverter-z513kcRv.cjs");
|
|
5
5
|
const vue = require("./vue-De9wkgLl.cjs");
|
|
6
6
|
require("./jszip.min-BPh2MMAa.cjs");
|
|
7
7
|
const eventemitter3 = require("./eventemitter3-BQuRcMPI.cjs");
|
|
@@ -16102,7 +16102,7 @@ const canUseDOM = () => {
|
|
|
16102
16102
|
return false;
|
|
16103
16103
|
}
|
|
16104
16104
|
};
|
|
16105
|
-
const summaryVersion = "1.8.0-next.
|
|
16105
|
+
const summaryVersion = "1.8.0-next.4";
|
|
16106
16106
|
const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
|
|
16107
16107
|
const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
|
|
16108
16108
|
function mapAttributes(attrs) {
|
|
@@ -18759,7 +18759,7 @@ class Editor extends EventEmitter {
|
|
|
18759
18759
|
* Process collaboration migrations
|
|
18760
18760
|
*/
|
|
18761
18761
|
processCollaborationMigrations() {
|
|
18762
|
-
console.debug("[checkVersionMigrations] Current editor version", "1.8.0-next.
|
|
18762
|
+
console.debug("[checkVersionMigrations] Current editor version", "1.8.0-next.4");
|
|
18763
18763
|
if (!this.options.ydoc) return;
|
|
18764
18764
|
const metaMap = this.options.ydoc.getMap("meta");
|
|
18765
18765
|
let docVersion = metaMap.get("version");
|
|
@@ -28801,24 +28801,6 @@ class DomPainter {
|
|
|
28801
28801
|
}
|
|
28802
28802
|
elem.setAttribute("role", "link");
|
|
28803
28803
|
elem.setAttribute("tabindex", "0");
|
|
28804
|
-
elem.addEventListener("click", (event) => {
|
|
28805
|
-
event.preventDefault();
|
|
28806
|
-
event.stopPropagation();
|
|
28807
|
-
const linkClickEvent = new CustomEvent("superdoc-link-click", {
|
|
28808
|
-
bubbles: true,
|
|
28809
|
-
composed: true,
|
|
28810
|
-
detail: {
|
|
28811
|
-
href: linkData.href,
|
|
28812
|
-
target: linkData.target,
|
|
28813
|
-
rel: linkData.rel,
|
|
28814
|
-
tooltip: linkData.tooltip,
|
|
28815
|
-
element: elem,
|
|
28816
|
-
clientX: event.clientX,
|
|
28817
|
-
clientY: event.clientY
|
|
28818
|
-
}
|
|
28819
|
-
});
|
|
28820
|
-
elem.dispatchEvent(linkClickEvent);
|
|
28821
|
-
});
|
|
28822
28804
|
}
|
|
28823
28805
|
/**
|
|
28824
28806
|
* Render a single run as an HTML element (span or anchor).
|
|
@@ -40156,255 +40138,6 @@ var Priority = /* @__PURE__ */ ((Priority2) => {
|
|
|
40156
40138
|
/** P3: Heavy debounce for full document layout */
|
|
40157
40139
|
[Priority.P3]: 150
|
|
40158
40140
|
});
|
|
40159
|
-
const DEFAULT_MIME_TYPE$1 = "application/x-field-annotation";
|
|
40160
|
-
const LEGACY_MIME_TYPE = "fieldAnnotation";
|
|
40161
|
-
function parseIntSafe$1(value) {
|
|
40162
|
-
if (!value) return void 0;
|
|
40163
|
-
const parsed = parseInt(value, 10);
|
|
40164
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
40165
|
-
}
|
|
40166
|
-
function extractFieldAnnotationData(element) {
|
|
40167
|
-
const dataset = element.dataset;
|
|
40168
|
-
const attributes = {};
|
|
40169
|
-
for (const key2 in dataset) {
|
|
40170
|
-
const value = dataset[key2];
|
|
40171
|
-
if (value !== void 0) {
|
|
40172
|
-
attributes[key2] = value;
|
|
40173
|
-
}
|
|
40174
|
-
}
|
|
40175
|
-
return {
|
|
40176
|
-
fieldId: dataset.fieldId,
|
|
40177
|
-
fieldType: dataset.fieldType,
|
|
40178
|
-
variant: dataset.variant ?? dataset.type,
|
|
40179
|
-
displayLabel: dataset.displayLabel,
|
|
40180
|
-
pmStart: parseIntSafe$1(dataset.pmStart),
|
|
40181
|
-
pmEnd: parseIntSafe$1(dataset.pmEnd),
|
|
40182
|
-
attributes
|
|
40183
|
-
};
|
|
40184
|
-
}
|
|
40185
|
-
class DragHandler {
|
|
40186
|
-
/**
|
|
40187
|
-
* Creates a new DragHandler instance.
|
|
40188
|
-
*
|
|
40189
|
-
* @param container - The DOM container element (typically .superdoc-layout)
|
|
40190
|
-
* @param config - Configuration options and callbacks
|
|
40191
|
-
*/
|
|
40192
|
-
constructor(container, config = {}) {
|
|
40193
|
-
this.container = container;
|
|
40194
|
-
this.config = config;
|
|
40195
|
-
this.mimeType = config.mimeType ?? DEFAULT_MIME_TYPE$1;
|
|
40196
|
-
this.boundHandlers = {
|
|
40197
|
-
dragstart: this.handleDragStart.bind(this),
|
|
40198
|
-
dragover: this.handleDragOver.bind(this),
|
|
40199
|
-
drop: this.handleDrop.bind(this),
|
|
40200
|
-
dragend: this.handleDragEnd.bind(this),
|
|
40201
|
-
dragleave: this.handleDragLeave.bind(this)
|
|
40202
|
-
};
|
|
40203
|
-
this.windowDragoverHandler = this.handleWindowDragOver.bind(this);
|
|
40204
|
-
this.windowDropHandler = this.handleWindowDrop.bind(this);
|
|
40205
|
-
this.attachListeners();
|
|
40206
|
-
}
|
|
40207
|
-
/**
|
|
40208
|
-
* Attaches event listeners to the container and window.
|
|
40209
|
-
*/
|
|
40210
|
-
attachListeners() {
|
|
40211
|
-
this.container.addEventListener("dragstart", this.boundHandlers.dragstart);
|
|
40212
|
-
this.container.addEventListener("dragover", this.boundHandlers.dragover);
|
|
40213
|
-
this.container.addEventListener("drop", this.boundHandlers.drop);
|
|
40214
|
-
this.container.addEventListener("dragend", this.boundHandlers.dragend);
|
|
40215
|
-
this.container.addEventListener("dragleave", this.boundHandlers.dragleave);
|
|
40216
|
-
window.addEventListener("dragover", this.windowDragoverHandler, false);
|
|
40217
|
-
window.addEventListener("drop", this.windowDropHandler, false);
|
|
40218
|
-
}
|
|
40219
|
-
/**
|
|
40220
|
-
* Removes event listeners from the container and window.
|
|
40221
|
-
*/
|
|
40222
|
-
removeListeners() {
|
|
40223
|
-
this.container.removeEventListener("dragstart", this.boundHandlers.dragstart);
|
|
40224
|
-
this.container.removeEventListener("dragover", this.boundHandlers.dragover);
|
|
40225
|
-
this.container.removeEventListener("drop", this.boundHandlers.drop);
|
|
40226
|
-
this.container.removeEventListener("dragend", this.boundHandlers.dragend);
|
|
40227
|
-
this.container.removeEventListener("dragleave", this.boundHandlers.dragleave);
|
|
40228
|
-
window.removeEventListener("dragover", this.windowDragoverHandler, false);
|
|
40229
|
-
window.removeEventListener("drop", this.windowDropHandler, false);
|
|
40230
|
-
}
|
|
40231
|
-
/**
|
|
40232
|
-
* Handles dragover at window level to allow drops on overlay elements.
|
|
40233
|
-
* This ensures preventDefault is called even when dragging over selection
|
|
40234
|
-
* highlights or other UI elements that sit on top of the layout content.
|
|
40235
|
-
*/
|
|
40236
|
-
handleWindowDragOver(event) {
|
|
40237
|
-
if (this.hasFieldAnnotationData(event)) {
|
|
40238
|
-
event.preventDefault();
|
|
40239
|
-
if (event.dataTransfer) {
|
|
40240
|
-
event.dataTransfer.dropEffect = "move";
|
|
40241
|
-
}
|
|
40242
|
-
const target = event.target;
|
|
40243
|
-
if (!this.container.contains(target)) {
|
|
40244
|
-
this.config.onDragOver?.({
|
|
40245
|
-
event,
|
|
40246
|
-
clientX: event.clientX,
|
|
40247
|
-
clientY: event.clientY,
|
|
40248
|
-
hasFieldAnnotation: true
|
|
40249
|
-
});
|
|
40250
|
-
}
|
|
40251
|
-
}
|
|
40252
|
-
}
|
|
40253
|
-
/**
|
|
40254
|
-
* Handles drop at window level to catch drops on overlay elements.
|
|
40255
|
-
* If the drop target is outside the container, we process it here.
|
|
40256
|
-
*/
|
|
40257
|
-
handleWindowDrop(event) {
|
|
40258
|
-
if (this.hasFieldAnnotationData(event)) {
|
|
40259
|
-
const target = event.target;
|
|
40260
|
-
if (!this.container.contains(target)) {
|
|
40261
|
-
this.handleDrop(event);
|
|
40262
|
-
}
|
|
40263
|
-
}
|
|
40264
|
-
}
|
|
40265
|
-
/**
|
|
40266
|
-
* Handles the dragstart event.
|
|
40267
|
-
* Sets up dataTransfer with field annotation data and drag image.
|
|
40268
|
-
*/
|
|
40269
|
-
handleDragStart(event) {
|
|
40270
|
-
const target = event.target;
|
|
40271
|
-
if (!target?.dataset?.draggable || target.dataset.draggable !== "true") {
|
|
40272
|
-
return;
|
|
40273
|
-
}
|
|
40274
|
-
const data = extractFieldAnnotationData(target);
|
|
40275
|
-
if (event.dataTransfer) {
|
|
40276
|
-
const jsonData = JSON.stringify({
|
|
40277
|
-
attributes: data.attributes,
|
|
40278
|
-
sourceField: data
|
|
40279
|
-
});
|
|
40280
|
-
event.dataTransfer.setData(this.mimeType, jsonData);
|
|
40281
|
-
event.dataTransfer.setData(LEGACY_MIME_TYPE, jsonData);
|
|
40282
|
-
event.dataTransfer.setData("text/plain", data.displayLabel ?? "Field Annotation");
|
|
40283
|
-
event.dataTransfer.setDragImage(target, 0, 0);
|
|
40284
|
-
event.dataTransfer.effectAllowed = "move";
|
|
40285
|
-
}
|
|
40286
|
-
this.config.onDragStart?.({
|
|
40287
|
-
event,
|
|
40288
|
-
element: target,
|
|
40289
|
-
data
|
|
40290
|
-
});
|
|
40291
|
-
}
|
|
40292
|
-
/**
|
|
40293
|
-
* Handles the dragover event.
|
|
40294
|
-
* Provides visual feedback and determines if drop is allowed.
|
|
40295
|
-
*/
|
|
40296
|
-
handleDragOver(event) {
|
|
40297
|
-
const hasFieldAnnotation = this.hasFieldAnnotationData(event);
|
|
40298
|
-
if (hasFieldAnnotation) {
|
|
40299
|
-
event.preventDefault();
|
|
40300
|
-
if (event.dataTransfer) {
|
|
40301
|
-
event.dataTransfer.dropEffect = "move";
|
|
40302
|
-
}
|
|
40303
|
-
this.container.classList.add("drag-over");
|
|
40304
|
-
}
|
|
40305
|
-
this.config.onDragOver?.({
|
|
40306
|
-
event,
|
|
40307
|
-
clientX: event.clientX,
|
|
40308
|
-
clientY: event.clientY,
|
|
40309
|
-
hasFieldAnnotation
|
|
40310
|
-
});
|
|
40311
|
-
}
|
|
40312
|
-
/**
|
|
40313
|
-
* Handles the dragleave event.
|
|
40314
|
-
* Removes visual feedback when drag leaves the container.
|
|
40315
|
-
*/
|
|
40316
|
-
handleDragLeave(event) {
|
|
40317
|
-
const relatedTarget = event.relatedTarget;
|
|
40318
|
-
if (!relatedTarget || !this.container.contains(relatedTarget)) {
|
|
40319
|
-
this.container.classList.remove("drag-over");
|
|
40320
|
-
}
|
|
40321
|
-
}
|
|
40322
|
-
/**
|
|
40323
|
-
* Handles the drop event.
|
|
40324
|
-
* Maps drop coordinates to ProseMirror position and emits drop event.
|
|
40325
|
-
*/
|
|
40326
|
-
handleDrop(event) {
|
|
40327
|
-
this.container.classList.remove("drag-over");
|
|
40328
|
-
if (!this.hasFieldAnnotationData(event)) {
|
|
40329
|
-
return;
|
|
40330
|
-
}
|
|
40331
|
-
event.preventDefault();
|
|
40332
|
-
const data = this.extractDragData(event);
|
|
40333
|
-
if (!data) {
|
|
40334
|
-
return;
|
|
40335
|
-
}
|
|
40336
|
-
const pmPosition = clickToPositionDom(this.container, event.clientX, event.clientY);
|
|
40337
|
-
this.config.onDrop?.({
|
|
40338
|
-
event,
|
|
40339
|
-
data,
|
|
40340
|
-
pmPosition,
|
|
40341
|
-
clientX: event.clientX,
|
|
40342
|
-
clientY: event.clientY
|
|
40343
|
-
});
|
|
40344
|
-
}
|
|
40345
|
-
/**
|
|
40346
|
-
* Handles the dragend event.
|
|
40347
|
-
* Cleans up drag state.
|
|
40348
|
-
*/
|
|
40349
|
-
handleDragEnd(event) {
|
|
40350
|
-
this.container.classList.remove("drag-over");
|
|
40351
|
-
this.config.onDragEnd?.(event);
|
|
40352
|
-
}
|
|
40353
|
-
/**
|
|
40354
|
-
* Checks if a drag event contains field annotation data.
|
|
40355
|
-
*/
|
|
40356
|
-
hasFieldAnnotationData(event) {
|
|
40357
|
-
if (!event.dataTransfer) {
|
|
40358
|
-
return false;
|
|
40359
|
-
}
|
|
40360
|
-
const types = event.dataTransfer.types;
|
|
40361
|
-
return types.includes(this.mimeType) || types.includes(LEGACY_MIME_TYPE);
|
|
40362
|
-
}
|
|
40363
|
-
/**
|
|
40364
|
-
* Extracts field annotation data from a drag event's dataTransfer.
|
|
40365
|
-
*/
|
|
40366
|
-
extractDragData(event) {
|
|
40367
|
-
if (!event.dataTransfer) {
|
|
40368
|
-
return null;
|
|
40369
|
-
}
|
|
40370
|
-
let jsonData = event.dataTransfer.getData(this.mimeType);
|
|
40371
|
-
if (!jsonData) {
|
|
40372
|
-
jsonData = event.dataTransfer.getData(LEGACY_MIME_TYPE);
|
|
40373
|
-
}
|
|
40374
|
-
if (!jsonData) {
|
|
40375
|
-
return null;
|
|
40376
|
-
}
|
|
40377
|
-
try {
|
|
40378
|
-
const parsed = JSON.parse(jsonData);
|
|
40379
|
-
return parsed.sourceField ?? parsed.attributes ?? parsed;
|
|
40380
|
-
} catch {
|
|
40381
|
-
return null;
|
|
40382
|
-
}
|
|
40383
|
-
}
|
|
40384
|
-
/**
|
|
40385
|
-
* Updates the configuration options.
|
|
40386
|
-
*
|
|
40387
|
-
* @param config - New configuration options to merge
|
|
40388
|
-
*/
|
|
40389
|
-
updateConfig(config) {
|
|
40390
|
-
this.config = { ...this.config, ...config };
|
|
40391
|
-
if (config.mimeType) {
|
|
40392
|
-
this.mimeType = config.mimeType;
|
|
40393
|
-
}
|
|
40394
|
-
}
|
|
40395
|
-
/**
|
|
40396
|
-
* Destroys the drag handler and removes all event listeners.
|
|
40397
|
-
* Call this when the layout engine is unmounted or the container is removed.
|
|
40398
|
-
*/
|
|
40399
|
-
destroy() {
|
|
40400
|
-
this.removeListeners();
|
|
40401
|
-
this.container.classList.remove("drag-over");
|
|
40402
|
-
}
|
|
40403
|
-
}
|
|
40404
|
-
function createDragHandler(container, config = {}) {
|
|
40405
|
-
const handler = new DragHandler(container, config);
|
|
40406
|
-
return () => handler.destroy();
|
|
40407
|
-
}
|
|
40408
40141
|
const isAtomicFragment = (fragment) => {
|
|
40409
40142
|
return fragment.kind === "drawing" || fragment.kind === "image";
|
|
40410
40143
|
};
|
|
@@ -41968,230 +41701,1432 @@ class RemoteCursorManager {
|
|
|
41968
41701
|
this.#isSetup = false;
|
|
41969
41702
|
}
|
|
41970
41703
|
}
|
|
41971
|
-
const
|
|
41972
|
-
|
|
41973
|
-
|
|
41974
|
-
|
|
41975
|
-
cancelAnimationFrame: (handle) => window.cancelAnimationFrame(handle)
|
|
41976
|
-
};
|
|
41704
|
+
const WORD_CHARACTER_REGEX = /[\p{L}\p{N}'\u2018\u2019_~-]/u;
|
|
41705
|
+
function isWordCharacter(char) {
|
|
41706
|
+
if (!char) {
|
|
41707
|
+
return false;
|
|
41977
41708
|
}
|
|
41978
|
-
|
|
41979
|
-
|
|
41980
|
-
|
|
41981
|
-
|
|
41982
|
-
|
|
41983
|
-
|
|
41709
|
+
return WORD_CHARACTER_REGEX.test(char);
|
|
41710
|
+
}
|
|
41711
|
+
function calculateExtendedSelection(blocks, anchor, head, mode) {
|
|
41712
|
+
if (mode === "word") {
|
|
41713
|
+
const anchorBounds = findWordBoundaries(blocks, anchor);
|
|
41714
|
+
const headBounds = findWordBoundaries(blocks, head);
|
|
41715
|
+
if (anchorBounds && headBounds) {
|
|
41716
|
+
if (head >= anchor) {
|
|
41717
|
+
return { selAnchor: anchorBounds.from, selHead: headBounds.to };
|
|
41718
|
+
} else {
|
|
41719
|
+
return { selAnchor: anchorBounds.to, selHead: headBounds.from };
|
|
41720
|
+
}
|
|
41721
|
+
}
|
|
41722
|
+
} else if (mode === "para") {
|
|
41723
|
+
const anchorBounds = findParagraphBoundaries(blocks, anchor);
|
|
41724
|
+
const headBounds = findParagraphBoundaries(blocks, head);
|
|
41725
|
+
if (anchorBounds && headBounds) {
|
|
41726
|
+
if (head >= anchor) {
|
|
41727
|
+
return { selAnchor: anchorBounds.from, selHead: headBounds.to };
|
|
41728
|
+
} else {
|
|
41729
|
+
return { selAnchor: anchorBounds.to, selHead: headBounds.from };
|
|
41730
|
+
}
|
|
41731
|
+
}
|
|
41984
41732
|
}
|
|
41733
|
+
return { selAnchor: anchor, selHead: head };
|
|
41734
|
+
}
|
|
41735
|
+
function registerPointerClick(event, previous, options) {
|
|
41736
|
+
const time2 = event.timeStamp ?? performance.now();
|
|
41737
|
+
const timeDelta = time2 - previous.lastClickTime;
|
|
41738
|
+
const withinTime = timeDelta <= options.timeThresholdMs;
|
|
41739
|
+
const distanceX = Math.abs(event.clientX - previous.lastClickPosition.x);
|
|
41740
|
+
const distanceY = Math.abs(event.clientY - previous.lastClickPosition.y);
|
|
41741
|
+
const withinDistance = distanceX <= options.distanceThresholdPx && distanceY <= options.distanceThresholdPx;
|
|
41742
|
+
const clickCount = withinTime && withinDistance ? Math.min(previous.clickCount + 1, options.maxClickCount) : 1;
|
|
41985
41743
|
return {
|
|
41986
|
-
|
|
41987
|
-
|
|
41988
|
-
|
|
41989
|
-
},
|
|
41990
|
-
cancelAnimationFrame: (handle) => {
|
|
41991
|
-
anyGlobal.clearTimeout?.(handle);
|
|
41992
|
-
}
|
|
41744
|
+
clickCount,
|
|
41745
|
+
lastClickTime: time2,
|
|
41746
|
+
lastClickPosition: { x: event.clientX, y: event.clientY }
|
|
41993
41747
|
};
|
|
41994
|
-
}
|
|
41995
|
-
|
|
41996
|
-
|
|
41997
|
-
|
|
41998
|
-
#layoutUpdating = false;
|
|
41999
|
-
#pending = false;
|
|
42000
|
-
#scheduled = false;
|
|
42001
|
-
#rafHandle = null;
|
|
42002
|
-
#scheduler;
|
|
42003
|
-
/**
|
|
42004
|
-
* Creates a new SelectionSyncCoordinator.
|
|
42005
|
-
*
|
|
42006
|
-
* @param options - Configuration options
|
|
42007
|
-
* @param options.scheduler - Custom scheduler for animation frames (useful for testing), defaults to platform scheduler
|
|
42008
|
-
*/
|
|
42009
|
-
constructor(options) {
|
|
42010
|
-
super();
|
|
42011
|
-
this.#scheduler = options?.scheduler ?? createDefaultScheduler();
|
|
41748
|
+
}
|
|
41749
|
+
function getFirstTextPosition(doc2) {
|
|
41750
|
+
if (!doc2 || !doc2.content) {
|
|
41751
|
+
return 1;
|
|
42012
41752
|
}
|
|
42013
|
-
|
|
42014
|
-
|
|
42015
|
-
|
|
42016
|
-
|
|
42017
|
-
|
|
42018
|
-
|
|
42019
|
-
return
|
|
41753
|
+
let validPos = 1;
|
|
41754
|
+
doc2.nodesBetween(0, doc2.content.size, (node, pos) => {
|
|
41755
|
+
if (node.isTextblock) {
|
|
41756
|
+
validPos = pos + 1;
|
|
41757
|
+
return false;
|
|
41758
|
+
}
|
|
41759
|
+
return true;
|
|
41760
|
+
});
|
|
41761
|
+
return validPos;
|
|
41762
|
+
}
|
|
41763
|
+
function computeWordSelectionRangeAt(state, pos) {
|
|
41764
|
+
if (!state?.doc) {
|
|
41765
|
+
return null;
|
|
42020
41766
|
}
|
|
42021
|
-
|
|
42022
|
-
|
|
42023
|
-
*
|
|
42024
|
-
* @returns The epoch of the document version currently painted in the DOM
|
|
42025
|
-
*/
|
|
42026
|
-
getLayoutEpoch() {
|
|
42027
|
-
return this.#layoutEpoch;
|
|
41767
|
+
if (pos < 0 || pos > state.doc.content.size) {
|
|
41768
|
+
return null;
|
|
42028
41769
|
}
|
|
42029
|
-
|
|
42030
|
-
|
|
42031
|
-
|
|
42032
|
-
* @returns True if between onLayoutStart() and onLayoutComplete(), false otherwise
|
|
42033
|
-
*/
|
|
42034
|
-
isLayoutUpdating() {
|
|
42035
|
-
return this.#layoutUpdating;
|
|
41770
|
+
const textblockPos = findNearestTextblockResolvedPos(state.doc, pos);
|
|
41771
|
+
if (!textblockPos) {
|
|
41772
|
+
return null;
|
|
42036
41773
|
}
|
|
42037
|
-
|
|
42038
|
-
|
|
42039
|
-
|
|
42040
|
-
|
|
42041
|
-
|
|
42042
|
-
|
|
42043
|
-
* When the document epoch changes:
|
|
42044
|
-
* 1. Any scheduled render is cancelled (layout will be out of sync)
|
|
42045
|
-
* 2. If layout has already caught up, rendering is rescheduled
|
|
42046
|
-
*
|
|
42047
|
-
* Calling with the same epoch as the current value is a no-op.
|
|
42048
|
-
* Invalid epoch values are silently ignored.
|
|
42049
|
-
*/
|
|
42050
|
-
setDocEpoch(epoch) {
|
|
42051
|
-
if (!Number.isFinite(epoch) || epoch < 0) return;
|
|
42052
|
-
if (epoch === this.#docEpoch) return;
|
|
42053
|
-
this.#docEpoch = epoch;
|
|
42054
|
-
this.#cancelScheduledRender();
|
|
42055
|
-
this.#maybeSchedule();
|
|
41774
|
+
const parentStart = textblockPos.start();
|
|
41775
|
+
const parentEnd = textblockPos.end();
|
|
41776
|
+
const sampleEnd = Math.min(pos + 1, parentEnd);
|
|
41777
|
+
const charAtPos = state.doc.textBetween(pos, sampleEnd, "\0", "\0");
|
|
41778
|
+
if (!isWordCharacter(charAtPos)) {
|
|
41779
|
+
return null;
|
|
42056
41780
|
}
|
|
42057
|
-
|
|
42058
|
-
|
|
42059
|
-
|
|
42060
|
-
|
|
42061
|
-
|
|
42062
|
-
|
|
42063
|
-
|
|
42064
|
-
* Safe to call multiple times (e.g., if layouts overlap) - subsequent calls are ignored
|
|
42065
|
-
* until onLayoutComplete() is called.
|
|
42066
|
-
*/
|
|
42067
|
-
onLayoutStart() {
|
|
42068
|
-
if (this.#layoutUpdating) return;
|
|
42069
|
-
this.#layoutUpdating = true;
|
|
42070
|
-
this.#cancelScheduledRender();
|
|
41781
|
+
let startPos = pos;
|
|
41782
|
+
while (startPos > parentStart) {
|
|
41783
|
+
const prevChar = state.doc.textBetween(startPos - 1, startPos, "\0", "\0");
|
|
41784
|
+
if (!isWordCharacter(prevChar)) {
|
|
41785
|
+
break;
|
|
41786
|
+
}
|
|
41787
|
+
startPos -= 1;
|
|
42071
41788
|
}
|
|
42072
|
-
|
|
42073
|
-
|
|
42074
|
-
|
|
42075
|
-
|
|
42076
|
-
|
|
42077
|
-
* @remarks
|
|
42078
|
-
* Marks the layout as no longer updating, records the new layout epoch, and attempts
|
|
42079
|
-
* to schedule rendering if conditions are now safe.
|
|
42080
|
-
*
|
|
42081
|
-
* If the layoutEpoch is invalid (not a finite non-negative number), it is ignored and
|
|
42082
|
-
* the previous layoutEpoch value is retained.
|
|
42083
|
-
*
|
|
42084
|
-
* This method is the primary trigger for selection rendering - if there's a pending
|
|
42085
|
-
* render request and layoutEpoch >= docEpoch, a render event will be scheduled.
|
|
42086
|
-
*/
|
|
42087
|
-
onLayoutComplete(layoutEpoch) {
|
|
42088
|
-
this.#layoutUpdating = false;
|
|
42089
|
-
if (Number.isFinite(layoutEpoch) && layoutEpoch >= 0) {
|
|
42090
|
-
this.#layoutEpoch = layoutEpoch;
|
|
41789
|
+
let endPos = pos;
|
|
41790
|
+
while (endPos < parentEnd) {
|
|
41791
|
+
const nextChar = state.doc.textBetween(endPos, endPos + 1, "\0", "\0");
|
|
41792
|
+
if (!isWordCharacter(nextChar)) {
|
|
41793
|
+
break;
|
|
42091
41794
|
}
|
|
42092
|
-
|
|
41795
|
+
endPos += 1;
|
|
42093
41796
|
}
|
|
42094
|
-
|
|
42095
|
-
|
|
42096
|
-
*
|
|
42097
|
-
* @remarks
|
|
42098
|
-
* Marks the layout as no longer updating (without updating layoutEpoch) and attempts
|
|
42099
|
-
* to schedule rendering if conditions are safe.
|
|
42100
|
-
*
|
|
42101
|
-
* Use this when layout computation is cancelled or fails partway through.
|
|
42102
|
-
*/
|
|
42103
|
-
onLayoutAbort() {
|
|
42104
|
-
this.#layoutUpdating = false;
|
|
42105
|
-
this.#maybeSchedule();
|
|
41797
|
+
if (startPos === endPos) {
|
|
41798
|
+
return null;
|
|
42106
41799
|
}
|
|
42107
|
-
|
|
42108
|
-
|
|
42109
|
-
|
|
42110
|
-
|
|
42111
|
-
|
|
42112
|
-
|
|
42113
|
-
|
|
42114
|
-
|
|
42115
|
-
|
|
42116
|
-
|
|
42117
|
-
|
|
42118
|
-
|
|
42119
|
-
|
|
42120
|
-
|
|
42121
|
-
|
|
42122
|
-
|
|
42123
|
-
|
|
42124
|
-
|
|
42125
|
-
this.flushNow();
|
|
41800
|
+
return { from: startPos, to: endPos };
|
|
41801
|
+
}
|
|
41802
|
+
function computeParagraphSelectionRangeAt(state, pos) {
|
|
41803
|
+
if (!state?.doc) {
|
|
41804
|
+
return null;
|
|
41805
|
+
}
|
|
41806
|
+
const textblockPos = findNearestTextblockResolvedPos(state.doc, pos);
|
|
41807
|
+
if (!textblockPos) {
|
|
41808
|
+
return null;
|
|
41809
|
+
}
|
|
41810
|
+
return { from: textblockPos.start(), to: textblockPos.end() };
|
|
41811
|
+
}
|
|
41812
|
+
function findNearestTextblockResolvedPos(doc2, pos) {
|
|
41813
|
+
const $pos = doc2.resolve(pos);
|
|
41814
|
+
let textblockPos = $pos;
|
|
41815
|
+
while (textblockPos.depth > 0) {
|
|
41816
|
+
if (textblockPos.parent?.isTextblock) {
|
|
41817
|
+
break;
|
|
42126
41818
|
}
|
|
42127
|
-
|
|
41819
|
+
if (!textblockPos.parent || textblockPos.depth === 0) {
|
|
41820
|
+
break;
|
|
41821
|
+
}
|
|
41822
|
+
const beforePos = textblockPos.before();
|
|
41823
|
+
if (beforePos < 0 || beforePos > doc2.content.size) {
|
|
41824
|
+
return null;
|
|
41825
|
+
}
|
|
41826
|
+
textblockPos = doc2.resolve(beforePos);
|
|
42128
41827
|
}
|
|
42129
|
-
|
|
42130
|
-
|
|
42131
|
-
*
|
|
42132
|
-
* @remarks
|
|
42133
|
-
* If there's a pending render request and conditions are safe (layout not updating,
|
|
42134
|
-
* layoutEpoch >= docEpoch), this method:
|
|
42135
|
-
* 1. Cancels any scheduled asynchronous render
|
|
42136
|
-
* 2. Clears the pending flag
|
|
42137
|
-
* 3. Emits the 'render' event synchronously
|
|
42138
|
-
*
|
|
42139
|
-
* If no render is pending or conditions are not safe, this is a no-op.
|
|
42140
|
-
*
|
|
42141
|
-
* Use this for immediate selection updates in response to user actions (e.g., click
|
|
42142
|
-
* handlers) where waiting for the next animation frame would cause noticeable lag.
|
|
42143
|
-
*/
|
|
42144
|
-
flushNow() {
|
|
42145
|
-
if (!this.#pending) return;
|
|
42146
|
-
if (!this.#isSafeToRender()) return;
|
|
42147
|
-
this.#cancelScheduledRender();
|
|
42148
|
-
this.#pending = false;
|
|
42149
|
-
this.emit("render", { docEpoch: this.#docEpoch, layoutEpoch: this.#layoutEpoch });
|
|
41828
|
+
if (!textblockPos.parent?.isTextblock) {
|
|
41829
|
+
return null;
|
|
42150
41830
|
}
|
|
42151
|
-
|
|
42152
|
-
|
|
42153
|
-
|
|
42154
|
-
|
|
42155
|
-
|
|
42156
|
-
|
|
42157
|
-
*
|
|
42158
|
-
* Safe to call multiple times.
|
|
42159
|
-
*/
|
|
42160
|
-
destroy() {
|
|
42161
|
-
this.#cancelScheduledRender();
|
|
42162
|
-
this.removeAllListeners();
|
|
41831
|
+
return textblockPos;
|
|
41832
|
+
}
|
|
41833
|
+
function getCellPosFromTableHit(tableHit, doc2, blocks) {
|
|
41834
|
+
if (!tableHit || !tableHit.block || typeof tableHit.block.id !== "string") {
|
|
41835
|
+
console.warn("[getCellPosFromTableHit] Invalid tableHit input:", tableHit);
|
|
41836
|
+
return null;
|
|
42163
41837
|
}
|
|
42164
|
-
|
|
42165
|
-
|
|
41838
|
+
if (typeof tableHit.cellRowIndex !== "number" || typeof tableHit.cellColIndex !== "number" || tableHit.cellRowIndex < 0 || tableHit.cellColIndex < 0) {
|
|
41839
|
+
console.warn("[getCellPosFromTableHit] Invalid cell indices:", {
|
|
41840
|
+
row: tableHit.cellRowIndex,
|
|
41841
|
+
col: tableHit.cellColIndex
|
|
41842
|
+
});
|
|
41843
|
+
return null;
|
|
42166
41844
|
}
|
|
42167
|
-
|
|
42168
|
-
|
|
42169
|
-
|
|
42170
|
-
|
|
42171
|
-
|
|
42172
|
-
|
|
42173
|
-
|
|
42174
|
-
|
|
42175
|
-
if (
|
|
42176
|
-
|
|
42177
|
-
|
|
42178
|
-
|
|
41845
|
+
if (!doc2) return null;
|
|
41846
|
+
const tableBlocks = blocks.filter((b2) => b2.kind === "table");
|
|
41847
|
+
const targetTableIndex = tableBlocks.findIndex((b2) => b2.id === tableHit.block.id);
|
|
41848
|
+
if (targetTableIndex === -1) return null;
|
|
41849
|
+
let tablePos = null;
|
|
41850
|
+
let currentTableIndex = 0;
|
|
41851
|
+
try {
|
|
41852
|
+
doc2.descendants((node, pos) => {
|
|
41853
|
+
if (node.type.name === "table") {
|
|
41854
|
+
if (currentTableIndex === targetTableIndex) {
|
|
41855
|
+
tablePos = pos;
|
|
41856
|
+
return false;
|
|
41857
|
+
}
|
|
41858
|
+
currentTableIndex++;
|
|
41859
|
+
}
|
|
41860
|
+
return true;
|
|
42179
41861
|
});
|
|
41862
|
+
} catch (error) {
|
|
41863
|
+
console.error("[getCellPosFromTableHit] Error during document traversal:", error);
|
|
41864
|
+
return null;
|
|
42180
41865
|
}
|
|
42181
|
-
|
|
42182
|
-
|
|
42183
|
-
|
|
42184
|
-
|
|
42185
|
-
|
|
42186
|
-
|
|
41866
|
+
if (tablePos === null) return null;
|
|
41867
|
+
const tableNode = doc2.nodeAt(tablePos);
|
|
41868
|
+
if (!tableNode || tableNode.type.name !== "table") return null;
|
|
41869
|
+
const targetRowIndex = tableHit.cellRowIndex;
|
|
41870
|
+
const targetColIndex = tableHit.cellColIndex;
|
|
41871
|
+
if (targetRowIndex >= tableNode.childCount) {
|
|
41872
|
+
console.warn("[getCellPosFromTableHit] Target row index out of bounds:", {
|
|
41873
|
+
targetRowIndex,
|
|
41874
|
+
tableChildCount: tableNode.childCount
|
|
41875
|
+
});
|
|
41876
|
+
return null;
|
|
41877
|
+
}
|
|
41878
|
+
let currentPos = tablePos + 1;
|
|
41879
|
+
for (let r2 = 0; r2 < tableNode.childCount && r2 <= targetRowIndex; r2++) {
|
|
41880
|
+
const row = tableNode.child(r2);
|
|
41881
|
+
if (r2 === targetRowIndex) {
|
|
41882
|
+
currentPos += 1;
|
|
41883
|
+
let logicalCol = 0;
|
|
41884
|
+
for (let cellIndex = 0; cellIndex < row.childCount; cellIndex++) {
|
|
41885
|
+
const cell = row.child(cellIndex);
|
|
41886
|
+
const rawColspan = cell.attrs?.colspan;
|
|
41887
|
+
const colspan = typeof rawColspan === "number" && Number.isFinite(rawColspan) && rawColspan > 0 ? rawColspan : 1;
|
|
41888
|
+
if (targetColIndex >= logicalCol && targetColIndex < logicalCol + colspan) {
|
|
41889
|
+
return currentPos;
|
|
41890
|
+
}
|
|
41891
|
+
currentPos += cell.nodeSize;
|
|
41892
|
+
logicalCol += colspan;
|
|
42187
41893
|
}
|
|
41894
|
+
console.warn("[getCellPosFromTableHit] Target column not found in row:", {
|
|
41895
|
+
targetColIndex,
|
|
41896
|
+
logicalColReached: logicalCol,
|
|
41897
|
+
rowCellCount: row.childCount
|
|
41898
|
+
});
|
|
41899
|
+
return null;
|
|
41900
|
+
} else {
|
|
41901
|
+
currentPos += row.nodeSize;
|
|
42188
41902
|
}
|
|
42189
|
-
this.#scheduled = false;
|
|
42190
41903
|
}
|
|
41904
|
+
return null;
|
|
42191
41905
|
}
|
|
42192
|
-
|
|
42193
|
-
|
|
42194
|
-
const
|
|
41906
|
+
function getTablePosFromHit(tableHit, doc2, blocks) {
|
|
41907
|
+
if (!doc2) return null;
|
|
41908
|
+
const tableBlocks = blocks.filter((b2) => b2.kind === "table");
|
|
41909
|
+
const targetTableIndex = tableBlocks.findIndex((b2) => b2.id === tableHit.block.id);
|
|
41910
|
+
if (targetTableIndex === -1) return null;
|
|
41911
|
+
let tablePos = null;
|
|
41912
|
+
let currentTableIndex = 0;
|
|
41913
|
+
doc2.descendants((node, pos) => {
|
|
41914
|
+
if (node.type.name === "table") {
|
|
41915
|
+
if (currentTableIndex === targetTableIndex) {
|
|
41916
|
+
tablePos = pos;
|
|
41917
|
+
return false;
|
|
41918
|
+
}
|
|
41919
|
+
currentTableIndex++;
|
|
41920
|
+
}
|
|
41921
|
+
return true;
|
|
41922
|
+
});
|
|
41923
|
+
return tablePos;
|
|
41924
|
+
}
|
|
41925
|
+
function shouldUseCellSelection(currentTableHit, cellAnchor, cellDragMode) {
|
|
41926
|
+
if (!cellAnchor) return false;
|
|
41927
|
+
if (!currentTableHit) return cellDragMode === "active";
|
|
41928
|
+
if (currentTableHit.block.id !== cellAnchor.tableBlockId) {
|
|
41929
|
+
return cellDragMode === "active";
|
|
41930
|
+
}
|
|
41931
|
+
const sameCell = currentTableHit.cellRowIndex === cellAnchor.cellRowIndex && currentTableHit.cellColIndex === cellAnchor.cellColIndex;
|
|
41932
|
+
if (!sameCell) {
|
|
41933
|
+
return true;
|
|
41934
|
+
}
|
|
41935
|
+
return cellDragMode === "active";
|
|
41936
|
+
}
|
|
41937
|
+
function hitTestTable(layout, blocks, measures, normalizedX, normalizedY, configuredPageHeight, pageGapFallback, geometryHelper) {
|
|
41938
|
+
if (!layout) {
|
|
41939
|
+
return null;
|
|
41940
|
+
}
|
|
41941
|
+
let pageY = 0;
|
|
41942
|
+
let pageHit = null;
|
|
41943
|
+
if (geometryHelper) {
|
|
41944
|
+
const idx = geometryHelper.getPageIndexAtY(normalizedY) ?? geometryHelper.getNearestPageIndex(normalizedY);
|
|
41945
|
+
if (idx != null && layout.pages[idx]) {
|
|
41946
|
+
pageHit = { pageIndex: idx, page: layout.pages[idx] };
|
|
41947
|
+
pageY = geometryHelper.getPageTop(idx);
|
|
41948
|
+
}
|
|
41949
|
+
}
|
|
41950
|
+
if (!pageHit) {
|
|
41951
|
+
const gap = layout.pageGap ?? pageGapFallback;
|
|
41952
|
+
for (let i = 0; i < layout.pages.length; i++) {
|
|
41953
|
+
const page = layout.pages[i];
|
|
41954
|
+
const pageHeight = page.size?.h ?? configuredPageHeight;
|
|
41955
|
+
if (normalizedY >= pageY && normalizedY < pageY + pageHeight) {
|
|
41956
|
+
pageHit = { pageIndex: i, page };
|
|
41957
|
+
break;
|
|
41958
|
+
}
|
|
41959
|
+
pageY += pageHeight + gap;
|
|
41960
|
+
}
|
|
41961
|
+
}
|
|
41962
|
+
if (!pageHit) {
|
|
41963
|
+
return null;
|
|
41964
|
+
}
|
|
41965
|
+
const pageRelativeY = normalizedY - pageY;
|
|
41966
|
+
const point = { x: normalizedX, y: pageRelativeY };
|
|
41967
|
+
return hitTestTableFragment(pageHit, blocks, measures, point);
|
|
41968
|
+
}
|
|
41969
|
+
const MULTI_CLICK_TIME_THRESHOLD_MS = 400;
|
|
41970
|
+
const MULTI_CLICK_DISTANCE_THRESHOLD_PX = 5;
|
|
41971
|
+
class EditorInputManager {
|
|
41972
|
+
// Dependencies
|
|
41973
|
+
#deps = null;
|
|
41974
|
+
#callbacks = {};
|
|
41975
|
+
// Drag selection state
|
|
41976
|
+
#isDragging = false;
|
|
41977
|
+
#dragAnchor = null;
|
|
41978
|
+
#dragAnchorPageIndex = null;
|
|
41979
|
+
#dragExtensionMode = "char";
|
|
41980
|
+
#dragLastPointer = null;
|
|
41981
|
+
#dragLastRawHit = null;
|
|
41982
|
+
#dragUsedPageNotMountedFallback = false;
|
|
41983
|
+
// Click tracking for multi-click detection
|
|
41984
|
+
#clickCount = 0;
|
|
41985
|
+
#lastClickTime = 0;
|
|
41986
|
+
#lastClickPosition = null;
|
|
41987
|
+
// Cell selection state
|
|
41988
|
+
#cellAnchor = null;
|
|
41989
|
+
#cellDragMode = "none";
|
|
41990
|
+
// Margin click state
|
|
41991
|
+
#pendingMarginClick = null;
|
|
41992
|
+
// Image selection state
|
|
41993
|
+
#lastSelectedImageBlockId = null;
|
|
41994
|
+
// Focus suppression (for draggable annotations)
|
|
41995
|
+
#suppressFocusInFromDraggable = false;
|
|
41996
|
+
// Debug state
|
|
41997
|
+
#debugLastPointer = null;
|
|
41998
|
+
#debugLastHit = null;
|
|
41999
|
+
// Bound handlers for event listener cleanup
|
|
42000
|
+
#boundHandlePointerDown = null;
|
|
42001
|
+
#boundHandlePointerMove = null;
|
|
42002
|
+
#boundHandlePointerUp = null;
|
|
42003
|
+
#boundHandlePointerLeave = null;
|
|
42004
|
+
#boundHandleDoubleClick = null;
|
|
42005
|
+
#boundHandleClick = null;
|
|
42006
|
+
#boundHandleKeyDown = null;
|
|
42007
|
+
#boundHandleFocusIn = null;
|
|
42008
|
+
// ==========================================================================
|
|
42009
|
+
// Constructor
|
|
42010
|
+
// ==========================================================================
|
|
42011
|
+
constructor() {
|
|
42012
|
+
}
|
|
42013
|
+
// ==========================================================================
|
|
42014
|
+
// Setup Methods
|
|
42015
|
+
// ==========================================================================
|
|
42016
|
+
/**
|
|
42017
|
+
* Set dependencies from PresentationEditor.
|
|
42018
|
+
*/
|
|
42019
|
+
setDependencies(deps) {
|
|
42020
|
+
this.#deps = deps;
|
|
42021
|
+
}
|
|
42022
|
+
/**
|
|
42023
|
+
* Set callbacks for events.
|
|
42024
|
+
*/
|
|
42025
|
+
setCallbacks(callbacks2) {
|
|
42026
|
+
this.#callbacks = callbacks2;
|
|
42027
|
+
}
|
|
42028
|
+
/**
|
|
42029
|
+
* Bind event listeners to DOM elements.
|
|
42030
|
+
*/
|
|
42031
|
+
bind() {
|
|
42032
|
+
if (!this.#deps) return;
|
|
42033
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
42034
|
+
const visibleHost = this.#deps.getVisibleHost();
|
|
42035
|
+
viewportHost.ownerDocument ?? document;
|
|
42036
|
+
this.#boundHandlePointerDown = this.#handlePointerDown.bind(this);
|
|
42037
|
+
this.#boundHandlePointerMove = this.#handlePointerMove.bind(this);
|
|
42038
|
+
this.#boundHandlePointerUp = this.#handlePointerUp.bind(this);
|
|
42039
|
+
this.#boundHandlePointerLeave = this.#handlePointerLeave.bind(this);
|
|
42040
|
+
this.#boundHandleDoubleClick = this.#handleDoubleClick.bind(this);
|
|
42041
|
+
this.#boundHandleClick = this.#handleClick.bind(this);
|
|
42042
|
+
this.#boundHandleKeyDown = this.#handleKeyDown.bind(this);
|
|
42043
|
+
this.#boundHandleFocusIn = this.#handleFocusIn.bind(this);
|
|
42044
|
+
viewportHost.addEventListener("pointerdown", this.#boundHandlePointerDown);
|
|
42045
|
+
viewportHost.addEventListener("pointermove", this.#boundHandlePointerMove);
|
|
42046
|
+
viewportHost.addEventListener("pointerup", this.#boundHandlePointerUp);
|
|
42047
|
+
viewportHost.addEventListener("pointerleave", this.#boundHandlePointerLeave);
|
|
42048
|
+
viewportHost.addEventListener("dblclick", this.#boundHandleDoubleClick);
|
|
42049
|
+
viewportHost.addEventListener("click", this.#boundHandleClick);
|
|
42050
|
+
const container = viewportHost.closest(".presentation-editor");
|
|
42051
|
+
if (container) {
|
|
42052
|
+
container.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
42053
|
+
}
|
|
42054
|
+
visibleHost.addEventListener("focusin", this.#boundHandleFocusIn);
|
|
42055
|
+
}
|
|
42056
|
+
/**
|
|
42057
|
+
* Unbind event listeners.
|
|
42058
|
+
*/
|
|
42059
|
+
unbind() {
|
|
42060
|
+
if (!this.#deps) return;
|
|
42061
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
42062
|
+
const visibleHost = this.#deps.getVisibleHost();
|
|
42063
|
+
if (this.#boundHandlePointerDown) {
|
|
42064
|
+
viewportHost.removeEventListener("pointerdown", this.#boundHandlePointerDown);
|
|
42065
|
+
}
|
|
42066
|
+
if (this.#boundHandlePointerMove) {
|
|
42067
|
+
viewportHost.removeEventListener("pointermove", this.#boundHandlePointerMove);
|
|
42068
|
+
}
|
|
42069
|
+
if (this.#boundHandlePointerUp) {
|
|
42070
|
+
viewportHost.removeEventListener("pointerup", this.#boundHandlePointerUp);
|
|
42071
|
+
}
|
|
42072
|
+
if (this.#boundHandlePointerLeave) {
|
|
42073
|
+
viewportHost.removeEventListener("pointerleave", this.#boundHandlePointerLeave);
|
|
42074
|
+
}
|
|
42075
|
+
if (this.#boundHandleDoubleClick) {
|
|
42076
|
+
viewportHost.removeEventListener("dblclick", this.#boundHandleDoubleClick);
|
|
42077
|
+
}
|
|
42078
|
+
if (this.#boundHandleClick) {
|
|
42079
|
+
viewportHost.removeEventListener("click", this.#boundHandleClick);
|
|
42080
|
+
}
|
|
42081
|
+
if (this.#boundHandleKeyDown) {
|
|
42082
|
+
const container = viewportHost.closest(".presentation-editor");
|
|
42083
|
+
if (container) {
|
|
42084
|
+
container.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
42085
|
+
}
|
|
42086
|
+
}
|
|
42087
|
+
if (this.#boundHandleFocusIn) {
|
|
42088
|
+
visibleHost.removeEventListener("focusin", this.#boundHandleFocusIn);
|
|
42089
|
+
}
|
|
42090
|
+
this.#boundHandlePointerDown = null;
|
|
42091
|
+
this.#boundHandlePointerMove = null;
|
|
42092
|
+
this.#boundHandlePointerUp = null;
|
|
42093
|
+
this.#boundHandlePointerLeave = null;
|
|
42094
|
+
this.#boundHandleDoubleClick = null;
|
|
42095
|
+
this.#boundHandleClick = null;
|
|
42096
|
+
this.#boundHandleKeyDown = null;
|
|
42097
|
+
this.#boundHandleFocusIn = null;
|
|
42098
|
+
}
|
|
42099
|
+
/**
|
|
42100
|
+
* Destroy the manager and clean up.
|
|
42101
|
+
*/
|
|
42102
|
+
destroy() {
|
|
42103
|
+
this.unbind();
|
|
42104
|
+
this.#deps = null;
|
|
42105
|
+
this.#callbacks = {};
|
|
42106
|
+
this.#clearDragState();
|
|
42107
|
+
this.#clearCellAnchor();
|
|
42108
|
+
}
|
|
42109
|
+
// ==========================================================================
|
|
42110
|
+
// Public Getters
|
|
42111
|
+
// ==========================================================================
|
|
42112
|
+
/** Whether currently dragging */
|
|
42113
|
+
get isDragging() {
|
|
42114
|
+
return this.#isDragging;
|
|
42115
|
+
}
|
|
42116
|
+
/** Current drag anchor position */
|
|
42117
|
+
get dragAnchor() {
|
|
42118
|
+
return this.#dragAnchor;
|
|
42119
|
+
}
|
|
42120
|
+
/** Cell anchor state for table selection */
|
|
42121
|
+
get cellAnchor() {
|
|
42122
|
+
return this.#cellAnchor;
|
|
42123
|
+
}
|
|
42124
|
+
/** Debug last pointer position */
|
|
42125
|
+
get debugLastPointer() {
|
|
42126
|
+
return this.#debugLastPointer;
|
|
42127
|
+
}
|
|
42128
|
+
/** Debug last hit */
|
|
42129
|
+
get debugLastHit() {
|
|
42130
|
+
return this.#debugLastHit;
|
|
42131
|
+
}
|
|
42132
|
+
/** Last selected image block ID */
|
|
42133
|
+
get lastSelectedImageBlockId() {
|
|
42134
|
+
return this.#lastSelectedImageBlockId;
|
|
42135
|
+
}
|
|
42136
|
+
/** Drag anchor page index */
|
|
42137
|
+
get dragAnchorPageIndex() {
|
|
42138
|
+
return this.#dragAnchorPageIndex;
|
|
42139
|
+
}
|
|
42140
|
+
/** Get the page index from the last raw hit during drag */
|
|
42141
|
+
get dragLastHitPageIndex() {
|
|
42142
|
+
return this.#dragLastRawHit?.pageIndex ?? null;
|
|
42143
|
+
}
|
|
42144
|
+
/** Get the last raw hit during drag (for finalization) */
|
|
42145
|
+
get dragLastRawHit() {
|
|
42146
|
+
return this.#dragLastRawHit;
|
|
42147
|
+
}
|
|
42148
|
+
// ==========================================================================
|
|
42149
|
+
// Public Methods
|
|
42150
|
+
// ==========================================================================
|
|
42151
|
+
/**
|
|
42152
|
+
* Clear cell anchor (used when document changes).
|
|
42153
|
+
*/
|
|
42154
|
+
clearCellAnchor() {
|
|
42155
|
+
this.#clearCellAnchor();
|
|
42156
|
+
}
|
|
42157
|
+
/**
|
|
42158
|
+
* Set suppress focus in flag (for draggable annotations).
|
|
42159
|
+
*/
|
|
42160
|
+
setSuppressFocusInFromDraggable(value) {
|
|
42161
|
+
this.#suppressFocusInFromDraggable = value;
|
|
42162
|
+
}
|
|
42163
|
+
// ==========================================================================
|
|
42164
|
+
// Private Helper Methods
|
|
42165
|
+
// ==========================================================================
|
|
42166
|
+
#clearDragState() {
|
|
42167
|
+
this.#isDragging = false;
|
|
42168
|
+
this.#dragAnchor = null;
|
|
42169
|
+
this.#dragAnchorPageIndex = null;
|
|
42170
|
+
this.#dragExtensionMode = "char";
|
|
42171
|
+
this.#dragLastPointer = null;
|
|
42172
|
+
this.#dragLastRawHit = null;
|
|
42173
|
+
this.#dragUsedPageNotMountedFallback = false;
|
|
42174
|
+
}
|
|
42175
|
+
#clearCellAnchor() {
|
|
42176
|
+
this.#cellAnchor = null;
|
|
42177
|
+
this.#cellDragMode = "none";
|
|
42178
|
+
}
|
|
42179
|
+
#registerPointerClick(event) {
|
|
42180
|
+
const nextState = registerPointerClick(
|
|
42181
|
+
event,
|
|
42182
|
+
{
|
|
42183
|
+
clickCount: this.#clickCount,
|
|
42184
|
+
lastClickTime: this.#lastClickTime,
|
|
42185
|
+
lastClickPosition: this.#lastClickPosition ?? { x: 0, y: 0 }
|
|
42186
|
+
},
|
|
42187
|
+
{
|
|
42188
|
+
timeThresholdMs: MULTI_CLICK_TIME_THRESHOLD_MS,
|
|
42189
|
+
distanceThresholdPx: MULTI_CLICK_DISTANCE_THRESHOLD_PX,
|
|
42190
|
+
maxClickCount: 3
|
|
42191
|
+
}
|
|
42192
|
+
);
|
|
42193
|
+
this.#clickCount = nextState.clickCount;
|
|
42194
|
+
this.#lastClickTime = nextState.lastClickTime;
|
|
42195
|
+
this.#lastClickPosition = nextState.lastClickPosition;
|
|
42196
|
+
return nextState.clickCount;
|
|
42197
|
+
}
|
|
42198
|
+
#getFirstTextPosition() {
|
|
42199
|
+
const editor = this.#deps?.getEditor();
|
|
42200
|
+
return getFirstTextPosition(editor?.state?.doc ?? null);
|
|
42201
|
+
}
|
|
42202
|
+
#calculateExtendedSelection(anchor, head, mode) {
|
|
42203
|
+
const layoutState = this.#deps?.getLayoutState();
|
|
42204
|
+
return calculateExtendedSelection(layoutState?.blocks ?? [], anchor, head, mode);
|
|
42205
|
+
}
|
|
42206
|
+
#shouldUseCellSelection(currentTableHit) {
|
|
42207
|
+
return shouldUseCellSelection(currentTableHit, this.#cellAnchor, this.#cellDragMode);
|
|
42208
|
+
}
|
|
42209
|
+
#getCellPosFromTableHit(tableHit) {
|
|
42210
|
+
const editor = this.#deps?.getEditor();
|
|
42211
|
+
const layoutState = this.#deps?.getLayoutState();
|
|
42212
|
+
return getCellPosFromTableHit(tableHit, editor?.state?.doc ?? null, layoutState?.blocks ?? []);
|
|
42213
|
+
}
|
|
42214
|
+
#getTablePosFromHit(tableHit) {
|
|
42215
|
+
const editor = this.#deps?.getEditor();
|
|
42216
|
+
const layoutState = this.#deps?.getLayoutState();
|
|
42217
|
+
return getTablePosFromHit(tableHit, editor?.state?.doc ?? null, layoutState?.blocks ?? []);
|
|
42218
|
+
}
|
|
42219
|
+
#setCellAnchor(tableHit, tablePos) {
|
|
42220
|
+
const cellPos = this.#getCellPosFromTableHit(tableHit);
|
|
42221
|
+
if (cellPos === null) return;
|
|
42222
|
+
this.#cellAnchor = {
|
|
42223
|
+
tablePos,
|
|
42224
|
+
cellPos,
|
|
42225
|
+
cellRowIndex: tableHit.cellRowIndex,
|
|
42226
|
+
cellColIndex: tableHit.cellColIndex,
|
|
42227
|
+
tableBlockId: tableHit.block.id
|
|
42228
|
+
};
|
|
42229
|
+
this.#cellDragMode = "pending";
|
|
42230
|
+
}
|
|
42231
|
+
#hitTestTable(x, y2) {
|
|
42232
|
+
return this.#callbacks.hitTestTable?.(x, y2) ?? null;
|
|
42233
|
+
}
|
|
42234
|
+
// ==========================================================================
|
|
42235
|
+
// Event Handlers
|
|
42236
|
+
// ==========================================================================
|
|
42237
|
+
/**
|
|
42238
|
+
* Handle click events - specifically for link navigation prevention.
|
|
42239
|
+
*
|
|
42240
|
+
* Link handling is split between pointerdown and click:
|
|
42241
|
+
* - pointerdown: dispatches superdoc-link-click event (for popover/UI response)
|
|
42242
|
+
* - click: prevents default navigation (preventDefault only works on click, not pointerdown)
|
|
42243
|
+
*
|
|
42244
|
+
* This also handles keyboard activation (Enter/Space) which triggers click but not pointerdown.
|
|
42245
|
+
*/
|
|
42246
|
+
#handleClick(event) {
|
|
42247
|
+
const target = event.target;
|
|
42248
|
+
const linkEl = target?.closest?.("a.superdoc-link");
|
|
42249
|
+
if (linkEl) {
|
|
42250
|
+
event.preventDefault();
|
|
42251
|
+
if (!event.pointerId && event.detail === 0) {
|
|
42252
|
+
this.#handleLinkClick(event, linkEl);
|
|
42253
|
+
}
|
|
42254
|
+
}
|
|
42255
|
+
}
|
|
42256
|
+
#handlePointerDown(event) {
|
|
42257
|
+
if (!this.#deps) return;
|
|
42258
|
+
if (event.button !== 0) return;
|
|
42259
|
+
if (event.ctrlKey && navigator.platform.includes("Mac")) return;
|
|
42260
|
+
this.#pendingMarginClick = null;
|
|
42261
|
+
const target = event.target;
|
|
42262
|
+
if (target?.closest?.(".superdoc-ruler-handle") != null) return;
|
|
42263
|
+
const linkEl = target?.closest?.("a.superdoc-link");
|
|
42264
|
+
if (linkEl) {
|
|
42265
|
+
this.#handleLinkClick(event, linkEl);
|
|
42266
|
+
return;
|
|
42267
|
+
}
|
|
42268
|
+
const annotationEl = target?.closest?.(".annotation[data-pm-start]");
|
|
42269
|
+
const isDraggableAnnotation = target?.closest?.('[data-draggable="true"]') != null;
|
|
42270
|
+
this.#suppressFocusInFromDraggable = isDraggableAnnotation;
|
|
42271
|
+
if (annotationEl) {
|
|
42272
|
+
this.#handleAnnotationClick(event, annotationEl);
|
|
42273
|
+
return;
|
|
42274
|
+
}
|
|
42275
|
+
const layoutState = this.#deps.getLayoutState();
|
|
42276
|
+
if (!layoutState.layout) {
|
|
42277
|
+
this.#handleClickWithoutLayout(event, isDraggableAnnotation);
|
|
42278
|
+
return;
|
|
42279
|
+
}
|
|
42280
|
+
const normalizedPoint = this.#callbacks.normalizeClientPoint?.(event.clientX, event.clientY);
|
|
42281
|
+
if (!normalizedPoint) return;
|
|
42282
|
+
const { x, y: y2 } = normalizedPoint;
|
|
42283
|
+
this.#debugLastPointer = { clientX: event.clientX, clientY: event.clientY, x, y: y2 };
|
|
42284
|
+
const sessionMode = this.#deps.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
42285
|
+
if (sessionMode !== "body") {
|
|
42286
|
+
if (this.#handleClickInHeaderFooterMode(event, x, y2)) return;
|
|
42287
|
+
}
|
|
42288
|
+
const headerFooterRegion = this.#callbacks.hitTestHeaderFooterRegion?.(x, y2);
|
|
42289
|
+
if (headerFooterRegion) return;
|
|
42290
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
42291
|
+
const pageGeometryHelper = this.#deps.getPageGeometryHelper();
|
|
42292
|
+
const rawHit = clickToPosition(
|
|
42293
|
+
layoutState.layout,
|
|
42294
|
+
layoutState.blocks,
|
|
42295
|
+
layoutState.measures,
|
|
42296
|
+
{ x, y: y2 },
|
|
42297
|
+
viewportHost,
|
|
42298
|
+
event.clientX,
|
|
42299
|
+
event.clientY,
|
|
42300
|
+
pageGeometryHelper ?? void 0
|
|
42301
|
+
);
|
|
42302
|
+
const editor = this.#deps.getEditor();
|
|
42303
|
+
const doc2 = editor.state?.doc;
|
|
42304
|
+
const epochMapper = this.#deps.getEpochMapper();
|
|
42305
|
+
const mapped = rawHit && doc2 ? epochMapper.mapPosFromLayoutToCurrentDetailed(rawHit.pos, rawHit.layoutEpoch, 1) : null;
|
|
42306
|
+
if (mapped && !mapped.ok) {
|
|
42307
|
+
debugLog("warn", "pointerdown mapping failed", mapped);
|
|
42308
|
+
}
|
|
42309
|
+
const hit = rawHit && doc2 && mapped?.ok ? { ...rawHit, pos: Math.max(0, Math.min(mapped.pos, doc2.content.size)), layoutEpoch: mapped.toEpoch } : null;
|
|
42310
|
+
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 };
|
|
42311
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
42312
|
+
if (!isDraggableAnnotation) {
|
|
42313
|
+
event.preventDefault();
|
|
42314
|
+
}
|
|
42315
|
+
if (!rawHit) {
|
|
42316
|
+
this.#focusEditorAtFirstPosition();
|
|
42317
|
+
return;
|
|
42318
|
+
}
|
|
42319
|
+
if (!hit || !doc2) {
|
|
42320
|
+
this.#callbacks.setPendingDocChange?.();
|
|
42321
|
+
this.#callbacks.scheduleRerender?.();
|
|
42322
|
+
return;
|
|
42323
|
+
}
|
|
42324
|
+
const fragmentHit = getFragmentAtPosition(layoutState.layout, layoutState.blocks, layoutState.measures, rawHit.pos);
|
|
42325
|
+
const targetImg = event.target?.closest?.("img");
|
|
42326
|
+
if (this.#handleInlineImageClick(event, targetImg, rawHit, doc2, epochMapper)) return;
|
|
42327
|
+
if (this.#handleFragmentClick(event, fragmentHit, hit, doc2)) return;
|
|
42328
|
+
if (this.#lastSelectedImageBlockId) {
|
|
42329
|
+
this.#callbacks.emit?.("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
42330
|
+
this.#lastSelectedImageBlockId = null;
|
|
42331
|
+
}
|
|
42332
|
+
if (event.shiftKey && editor.state.selection.$anchor) {
|
|
42333
|
+
this.#handleShiftClick(event, hit.pos);
|
|
42334
|
+
return;
|
|
42335
|
+
}
|
|
42336
|
+
const clickDepth = this.#registerPointerClick(event);
|
|
42337
|
+
if (clickDepth === 1) {
|
|
42338
|
+
this.#dragAnchor = hit.pos;
|
|
42339
|
+
this.#dragAnchorPageIndex = hit.pageIndex;
|
|
42340
|
+
this.#pendingMarginClick = this.#callbacks.computePendingMarginClick?.(event.pointerId, x, y2) ?? null;
|
|
42341
|
+
const tableHit = this.#hitTestTable(x, y2);
|
|
42342
|
+
if (tableHit) {
|
|
42343
|
+
const tablePos = this.#getTablePosFromHit(tableHit);
|
|
42344
|
+
if (tablePos !== null) {
|
|
42345
|
+
this.#setCellAnchor(tableHit, tablePos);
|
|
42346
|
+
}
|
|
42347
|
+
} else {
|
|
42348
|
+
this.#clearCellAnchor();
|
|
42349
|
+
}
|
|
42350
|
+
} else {
|
|
42351
|
+
this.#pendingMarginClick = null;
|
|
42352
|
+
}
|
|
42353
|
+
this.#dragLastPointer = { clientX: event.clientX, clientY: event.clientY, x, y: y2 };
|
|
42354
|
+
this.#dragLastRawHit = hit;
|
|
42355
|
+
this.#dragUsedPageNotMountedFallback = false;
|
|
42356
|
+
this.#isDragging = true;
|
|
42357
|
+
if (clickDepth >= 3) {
|
|
42358
|
+
this.#dragExtensionMode = "para";
|
|
42359
|
+
} else if (clickDepth === 2) {
|
|
42360
|
+
this.#dragExtensionMode = "word";
|
|
42361
|
+
} else {
|
|
42362
|
+
this.#dragExtensionMode = "char";
|
|
42363
|
+
}
|
|
42364
|
+
if (typeof viewportHost.setPointerCapture === "function") {
|
|
42365
|
+
viewportHost.setPointerCapture(event.pointerId);
|
|
42366
|
+
}
|
|
42367
|
+
let handledByDepth = false;
|
|
42368
|
+
const sessionModeForDepth = this.#deps.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
42369
|
+
if (sessionModeForDepth === "body") {
|
|
42370
|
+
const selectionPos = clickDepth >= 2 && this.#dragAnchor !== null ? this.#dragAnchor : hit.pos;
|
|
42371
|
+
if (clickDepth >= 3) {
|
|
42372
|
+
handledByDepth = this.#callbacks.selectParagraphAt?.(selectionPos) ?? false;
|
|
42373
|
+
} else if (clickDepth === 2) {
|
|
42374
|
+
handledByDepth = this.#callbacks.selectWordAt?.(selectionPos) ?? false;
|
|
42375
|
+
}
|
|
42376
|
+
}
|
|
42377
|
+
if (!handledByDepth) {
|
|
42378
|
+
try {
|
|
42379
|
+
let nextSelection = superEditor_converter.TextSelection.create(doc2, hit.pos);
|
|
42380
|
+
if (!nextSelection.$from.parent.inlineContent) {
|
|
42381
|
+
nextSelection = superEditor_converter.Selection.near(doc2.resolve(hit.pos), 1);
|
|
42382
|
+
}
|
|
42383
|
+
const tr = editor.state.tr.setSelection(nextSelection);
|
|
42384
|
+
editor.view?.dispatch(tr);
|
|
42385
|
+
} catch {
|
|
42386
|
+
}
|
|
42387
|
+
}
|
|
42388
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
42389
|
+
this.#focusEditor();
|
|
42390
|
+
}
|
|
42391
|
+
#handlePointerMove(event) {
|
|
42392
|
+
if (!this.#deps) return;
|
|
42393
|
+
const layoutState = this.#deps.getLayoutState();
|
|
42394
|
+
if (!layoutState.layout) return;
|
|
42395
|
+
const normalized = this.#callbacks.normalizeClientPoint?.(event.clientX, event.clientY);
|
|
42396
|
+
if (!normalized) return;
|
|
42397
|
+
if (this.#isDragging && this.#dragAnchor !== null && event.buttons & 1) {
|
|
42398
|
+
this.#handleDragSelection(event, normalized);
|
|
42399
|
+
return;
|
|
42400
|
+
}
|
|
42401
|
+
this.#handleHover(normalized);
|
|
42402
|
+
}
|
|
42403
|
+
#handlePointerUp(event) {
|
|
42404
|
+
if (!this.#deps) return;
|
|
42405
|
+
this.#suppressFocusInFromDraggable = false;
|
|
42406
|
+
if (!this.#isDragging) return;
|
|
42407
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
42408
|
+
if (typeof viewportHost.hasPointerCapture === "function" && typeof viewportHost.releasePointerCapture === "function" && viewportHost.hasPointerCapture(event.pointerId)) {
|
|
42409
|
+
viewportHost.releasePointerCapture(event.pointerId);
|
|
42410
|
+
}
|
|
42411
|
+
const pendingMarginClick = this.#pendingMarginClick;
|
|
42412
|
+
this.#pendingMarginClick = null;
|
|
42413
|
+
const dragAnchor = this.#dragAnchor;
|
|
42414
|
+
const dragMode = this.#dragExtensionMode;
|
|
42415
|
+
const dragUsedFallback = this.#dragUsedPageNotMountedFallback;
|
|
42416
|
+
const dragPointer = this.#dragLastPointer;
|
|
42417
|
+
this.#isDragging = false;
|
|
42418
|
+
if (this.#cellDragMode !== "none") {
|
|
42419
|
+
this.#cellDragMode = "none";
|
|
42420
|
+
}
|
|
42421
|
+
if (!pendingMarginClick || pendingMarginClick.pointerId !== event.pointerId) {
|
|
42422
|
+
this.#callbacks.updateSelectionVirtualizationPins?.({ includeDragBuffer: false });
|
|
42423
|
+
if (dragUsedFallback && dragAnchor != null) {
|
|
42424
|
+
const pointer = dragPointer ?? { clientX: event.clientX, clientY: event.clientY };
|
|
42425
|
+
this.#callbacks.finalizeDragSelectionWithDom?.(pointer, dragAnchor, dragMode);
|
|
42426
|
+
}
|
|
42427
|
+
this.#callbacks.scheduleA11ySelectionAnnouncement?.({ immediate: true });
|
|
42428
|
+
this.#dragLastPointer = null;
|
|
42429
|
+
this.#dragLastRawHit = null;
|
|
42430
|
+
this.#dragUsedPageNotMountedFallback = false;
|
|
42431
|
+
return;
|
|
42432
|
+
}
|
|
42433
|
+
this.#handleMarginClickEnd(event, pendingMarginClick);
|
|
42434
|
+
}
|
|
42435
|
+
#handlePointerLeave() {
|
|
42436
|
+
this.#callbacks.clearHoverRegion?.();
|
|
42437
|
+
}
|
|
42438
|
+
#handleDoubleClick(event) {
|
|
42439
|
+
if (!this.#deps) return;
|
|
42440
|
+
if (event.button !== 0) return;
|
|
42441
|
+
const layoutState = this.#deps.getLayoutState();
|
|
42442
|
+
if (!layoutState.layout) return;
|
|
42443
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
42444
|
+
const visibleHost = this.#deps.getVisibleHost();
|
|
42445
|
+
const zoom = this.#deps.getZoom();
|
|
42446
|
+
const rect = viewportHost.getBoundingClientRect();
|
|
42447
|
+
const scrollLeft = visibleHost.scrollLeft ?? 0;
|
|
42448
|
+
const scrollTop = visibleHost.scrollTop ?? 0;
|
|
42449
|
+
const x = (event.clientX - rect.left + scrollLeft) / zoom;
|
|
42450
|
+
const y2 = (event.clientY - rect.top + scrollTop) / zoom;
|
|
42451
|
+
const region = this.#callbacks.hitTestHeaderFooterRegion?.(x, y2);
|
|
42452
|
+
if (region) {
|
|
42453
|
+
event.preventDefault();
|
|
42454
|
+
event.stopPropagation();
|
|
42455
|
+
const descriptor = this.#callbacks.resolveDescriptorForRegion?.(region);
|
|
42456
|
+
const hfManager = this.#deps.getHeaderFooterSession()?.manager;
|
|
42457
|
+
if (!descriptor && hfManager) {
|
|
42458
|
+
this.#callbacks.createDefaultHeaderFooter?.(region);
|
|
42459
|
+
hfManager.refresh();
|
|
42460
|
+
}
|
|
42461
|
+
this.#callbacks.activateHeaderFooterRegion?.(region);
|
|
42462
|
+
} else if ((this.#deps.getHeaderFooterSession()?.session?.mode ?? "body") !== "body") {
|
|
42463
|
+
this.#callbacks.exitHeaderFooterMode?.();
|
|
42464
|
+
}
|
|
42465
|
+
}
|
|
42466
|
+
#handleKeyDown(event) {
|
|
42467
|
+
if (!this.#deps) return;
|
|
42468
|
+
const sessionMode = this.#deps.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
42469
|
+
if (event.key === "Escape" && sessionMode !== "body") {
|
|
42470
|
+
event.preventDefault();
|
|
42471
|
+
this.#callbacks.exitHeaderFooterMode?.();
|
|
42472
|
+
return;
|
|
42473
|
+
}
|
|
42474
|
+
if (event.ctrlKey && event.altKey && !event.shiftKey) {
|
|
42475
|
+
if (event.code === "KeyH") {
|
|
42476
|
+
event.preventDefault();
|
|
42477
|
+
this.#focusHeaderFooterShortcut("header");
|
|
42478
|
+
} else if (event.code === "KeyF") {
|
|
42479
|
+
event.preventDefault();
|
|
42480
|
+
this.#focusHeaderFooterShortcut("footer");
|
|
42481
|
+
}
|
|
42482
|
+
}
|
|
42483
|
+
}
|
|
42484
|
+
#handleFocusIn(event) {
|
|
42485
|
+
if (!this.#deps) return;
|
|
42486
|
+
if (this.#suppressFocusInFromDraggable) {
|
|
42487
|
+
this.#suppressFocusInFromDraggable = false;
|
|
42488
|
+
return;
|
|
42489
|
+
}
|
|
42490
|
+
try {
|
|
42491
|
+
this.#deps.getActiveEditor().view?.focus();
|
|
42492
|
+
} catch {
|
|
42493
|
+
}
|
|
42494
|
+
}
|
|
42495
|
+
// ==========================================================================
|
|
42496
|
+
// Handler Helpers
|
|
42497
|
+
// ==========================================================================
|
|
42498
|
+
#handleLinkClick(event, linkEl) {
|
|
42499
|
+
const href = linkEl.getAttribute("href") ?? "";
|
|
42500
|
+
const isAnchorLink = href.startsWith("#") && href.length > 1;
|
|
42501
|
+
const isTocLink = linkEl.closest(".superdoc-toc-entry") !== null;
|
|
42502
|
+
if (isAnchorLink && isTocLink) {
|
|
42503
|
+
event.preventDefault();
|
|
42504
|
+
event.stopPropagation();
|
|
42505
|
+
this.#callbacks.goToAnchor?.(href);
|
|
42506
|
+
return;
|
|
42507
|
+
}
|
|
42508
|
+
event.preventDefault();
|
|
42509
|
+
event.stopPropagation();
|
|
42510
|
+
const linkClickEvent = new CustomEvent("superdoc-link-click", {
|
|
42511
|
+
bubbles: true,
|
|
42512
|
+
composed: true,
|
|
42513
|
+
detail: {
|
|
42514
|
+
href,
|
|
42515
|
+
target: linkEl.getAttribute("target"),
|
|
42516
|
+
rel: linkEl.getAttribute("rel"),
|
|
42517
|
+
tooltip: linkEl.getAttribute("title"),
|
|
42518
|
+
element: linkEl,
|
|
42519
|
+
clientX: event.clientX,
|
|
42520
|
+
clientY: event.clientY
|
|
42521
|
+
}
|
|
42522
|
+
});
|
|
42523
|
+
linkEl.dispatchEvent(linkClickEvent);
|
|
42524
|
+
}
|
|
42525
|
+
#handleAnnotationClick(event, annotationEl) {
|
|
42526
|
+
const editor = this.#deps?.getEditor();
|
|
42527
|
+
if (!editor?.isEditable) return;
|
|
42528
|
+
const resolved = this.#callbacks.resolveFieldAnnotationSelectionFromElement?.(annotationEl);
|
|
42529
|
+
if (resolved) {
|
|
42530
|
+
try {
|
|
42531
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.NodeSelection.create(editor.state.doc, resolved.pos));
|
|
42532
|
+
editor.view?.dispatch(tr);
|
|
42533
|
+
} catch {
|
|
42534
|
+
}
|
|
42535
|
+
editor.emit("fieldAnnotationClicked", {
|
|
42536
|
+
editor,
|
|
42537
|
+
node: resolved.node,
|
|
42538
|
+
nodePos: resolved.pos,
|
|
42539
|
+
event,
|
|
42540
|
+
currentTarget: annotationEl
|
|
42541
|
+
});
|
|
42542
|
+
}
|
|
42543
|
+
}
|
|
42544
|
+
#handleClickWithoutLayout(event, isDraggableAnnotation) {
|
|
42545
|
+
if (!isDraggableAnnotation) {
|
|
42546
|
+
event.preventDefault();
|
|
42547
|
+
}
|
|
42548
|
+
if (document.activeElement instanceof HTMLElement) {
|
|
42549
|
+
document.activeElement.blur();
|
|
42550
|
+
}
|
|
42551
|
+
this.#focusEditorAtFirstPosition();
|
|
42552
|
+
}
|
|
42553
|
+
#handleClickInHeaderFooterMode(event, x, y2) {
|
|
42554
|
+
const session = this.#deps?.getHeaderFooterSession();
|
|
42555
|
+
const activeEditorHost = session?.overlayManager?.getActiveEditorHost?.();
|
|
42556
|
+
const clickedInsideEditorHost = activeEditorHost && (activeEditorHost.contains(event.target) || activeEditorHost === event.target);
|
|
42557
|
+
if (clickedInsideEditorHost) {
|
|
42558
|
+
return true;
|
|
42559
|
+
}
|
|
42560
|
+
const headerFooterRegion = this.#callbacks.hitTestHeaderFooterRegion?.(x, y2);
|
|
42561
|
+
if (!headerFooterRegion) {
|
|
42562
|
+
this.#callbacks.exitHeaderFooterMode?.();
|
|
42563
|
+
return false;
|
|
42564
|
+
}
|
|
42565
|
+
return true;
|
|
42566
|
+
}
|
|
42567
|
+
#handleInlineImageClick(event, targetImg, rawHit, doc2, epochMapper) {
|
|
42568
|
+
if (!targetImg) return false;
|
|
42569
|
+
const imgPmStart = targetImg.dataset?.pmStart ? Number(targetImg.dataset.pmStart) : null;
|
|
42570
|
+
if (Number.isNaN(imgPmStart) || imgPmStart == null) return false;
|
|
42571
|
+
const imgLayoutEpochRaw = targetImg.dataset?.layoutEpoch;
|
|
42572
|
+
const imgLayoutEpoch = imgLayoutEpochRaw != null ? Number(imgLayoutEpochRaw) : NaN;
|
|
42573
|
+
const rawLayoutEpoch = Number.isFinite(rawHit.layoutEpoch) ? rawHit.layoutEpoch : NaN;
|
|
42574
|
+
const effectiveEpoch = Number.isFinite(imgLayoutEpoch) && Number.isFinite(rawLayoutEpoch) ? Math.max(imgLayoutEpoch, rawLayoutEpoch) : Number.isFinite(imgLayoutEpoch) ? imgLayoutEpoch : rawHit.layoutEpoch;
|
|
42575
|
+
const mappedImg = epochMapper.mapPosFromLayoutToCurrentDetailed(imgPmStart, effectiveEpoch, 1);
|
|
42576
|
+
if (!mappedImg.ok) {
|
|
42577
|
+
debugLog("warn", "inline image mapping failed", mappedImg);
|
|
42578
|
+
this.#callbacks.setPendingDocChange?.();
|
|
42579
|
+
this.#callbacks.scheduleRerender?.();
|
|
42580
|
+
return true;
|
|
42581
|
+
}
|
|
42582
|
+
const clampedImgPos = Math.max(0, Math.min(mappedImg.pos, doc2.content.size));
|
|
42583
|
+
if (clampedImgPos < 0 || clampedImgPos >= doc2.content.size) return true;
|
|
42584
|
+
const newSelectionId = `inline-${clampedImgPos}`;
|
|
42585
|
+
if (this.#lastSelectedImageBlockId && this.#lastSelectedImageBlockId !== newSelectionId) {
|
|
42586
|
+
this.#callbacks.emit?.("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
42587
|
+
}
|
|
42588
|
+
const editor = this.#deps?.getEditor();
|
|
42589
|
+
try {
|
|
42590
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.NodeSelection.create(doc2, clampedImgPos));
|
|
42591
|
+
editor.view?.dispatch(tr);
|
|
42592
|
+
const selector = `.superdoc-inline-image[data-pm-start="${imgPmStart}"]`;
|
|
42593
|
+
const viewportHost = this.#deps?.getViewportHost();
|
|
42594
|
+
const targetElement = viewportHost?.querySelector(selector);
|
|
42595
|
+
this.#callbacks.emit?.("imageSelected", {
|
|
42596
|
+
element: targetElement ?? targetImg,
|
|
42597
|
+
blockId: null,
|
|
42598
|
+
pmStart: clampedImgPos
|
|
42599
|
+
});
|
|
42600
|
+
this.#lastSelectedImageBlockId = newSelectionId;
|
|
42601
|
+
} catch (error) {
|
|
42602
|
+
if (vue.process$1.env.NODE_ENV === "development") {
|
|
42603
|
+
console.warn(`[EditorInputManager] Failed to create NodeSelection for inline image:`, error);
|
|
42604
|
+
}
|
|
42605
|
+
}
|
|
42606
|
+
this.#callbacks.focusEditorAfterImageSelection?.();
|
|
42607
|
+
return true;
|
|
42608
|
+
}
|
|
42609
|
+
#handleFragmentClick(event, fragmentHit, hit, doc2) {
|
|
42610
|
+
if (!fragmentHit) return false;
|
|
42611
|
+
if (fragmentHit.fragment.kind !== "image" && fragmentHit.fragment.kind !== "drawing") return false;
|
|
42612
|
+
const editor = this.#deps?.getEditor();
|
|
42613
|
+
try {
|
|
42614
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.NodeSelection.create(doc2, hit.pos));
|
|
42615
|
+
editor.view?.dispatch(tr);
|
|
42616
|
+
if (this.#lastSelectedImageBlockId && this.#lastSelectedImageBlockId !== fragmentHit.fragment.blockId) {
|
|
42617
|
+
this.#callbacks.emit?.("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
42618
|
+
}
|
|
42619
|
+
if (fragmentHit.fragment.kind === "image") {
|
|
42620
|
+
const viewportHost = this.#deps?.getViewportHost();
|
|
42621
|
+
const targetElement = viewportHost?.querySelector(
|
|
42622
|
+
`.superdoc-image-fragment[data-pm-start="${fragmentHit.fragment.pmStart}"]`
|
|
42623
|
+
);
|
|
42624
|
+
if (targetElement) {
|
|
42625
|
+
this.#callbacks.emit?.("imageSelected", {
|
|
42626
|
+
element: targetElement,
|
|
42627
|
+
blockId: fragmentHit.fragment.blockId,
|
|
42628
|
+
pmStart: fragmentHit.fragment.pmStart
|
|
42629
|
+
});
|
|
42630
|
+
this.#lastSelectedImageBlockId = fragmentHit.fragment.blockId;
|
|
42631
|
+
}
|
|
42632
|
+
}
|
|
42633
|
+
} catch (error) {
|
|
42634
|
+
if (vue.process$1.env.NODE_ENV === "development") {
|
|
42635
|
+
console.warn("[EditorInputManager] Failed to create NodeSelection for atomic fragment:", error);
|
|
42636
|
+
}
|
|
42637
|
+
}
|
|
42638
|
+
this.#callbacks.focusEditorAfterImageSelection?.();
|
|
42639
|
+
return true;
|
|
42640
|
+
}
|
|
42641
|
+
#handleShiftClick(event, headPos) {
|
|
42642
|
+
const editor = this.#deps?.getEditor();
|
|
42643
|
+
if (!editor) return;
|
|
42644
|
+
const anchor = editor.state.selection.anchor;
|
|
42645
|
+
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, headPos, this.#dragExtensionMode);
|
|
42646
|
+
try {
|
|
42647
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.TextSelection.create(editor.state.doc, selAnchor, selHead));
|
|
42648
|
+
editor.view?.dispatch(tr);
|
|
42649
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
42650
|
+
} catch (error) {
|
|
42651
|
+
console.warn("[SELECTION] Failed to extend selection on shift+click:", error);
|
|
42652
|
+
}
|
|
42653
|
+
this.#focusEditor();
|
|
42654
|
+
}
|
|
42655
|
+
#handleDragSelection(event, normalized) {
|
|
42656
|
+
if (!this.#deps) return;
|
|
42657
|
+
this.#pendingMarginClick = null;
|
|
42658
|
+
this.#dragLastPointer;
|
|
42659
|
+
this.#dragLastPointer = { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y };
|
|
42660
|
+
const layoutState = this.#deps.getLayoutState();
|
|
42661
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
42662
|
+
const pageGeometryHelper = this.#deps.getPageGeometryHelper();
|
|
42663
|
+
const rawHit = clickToPosition(
|
|
42664
|
+
layoutState.layout,
|
|
42665
|
+
layoutState.blocks,
|
|
42666
|
+
layoutState.measures,
|
|
42667
|
+
{ x: normalized.x, y: normalized.y },
|
|
42668
|
+
viewportHost,
|
|
42669
|
+
event.clientX,
|
|
42670
|
+
event.clientY,
|
|
42671
|
+
pageGeometryHelper ?? void 0
|
|
42672
|
+
);
|
|
42673
|
+
if (!rawHit) return;
|
|
42674
|
+
const editor = this.#deps.getEditor();
|
|
42675
|
+
const doc2 = editor.state?.doc;
|
|
42676
|
+
if (!doc2) return;
|
|
42677
|
+
this.#dragLastRawHit = rawHit;
|
|
42678
|
+
const pageMounted = this.#deps.getPageElement(rawHit.pageIndex) != null;
|
|
42679
|
+
if (!pageMounted && this.#deps.isSelectionAwareVirtualizationEnabled()) {
|
|
42680
|
+
this.#dragUsedPageNotMountedFallback = true;
|
|
42681
|
+
}
|
|
42682
|
+
this.#callbacks.updateSelectionVirtualizationPins?.({ includeDragBuffer: true, extraPages: [rawHit.pageIndex] });
|
|
42683
|
+
const epochMapper = this.#deps.getEpochMapper();
|
|
42684
|
+
const mappedHead = epochMapper.mapPosFromLayoutToCurrentDetailed(rawHit.pos, rawHit.layoutEpoch, 1);
|
|
42685
|
+
if (!mappedHead.ok) {
|
|
42686
|
+
debugLog("warn", "drag mapping failed", mappedHead);
|
|
42687
|
+
return;
|
|
42688
|
+
}
|
|
42689
|
+
const hit = {
|
|
42690
|
+
...rawHit,
|
|
42691
|
+
pos: Math.max(0, Math.min(mappedHead.pos, doc2.content.size)),
|
|
42692
|
+
layoutEpoch: mappedHead.toEpoch
|
|
42693
|
+
};
|
|
42694
|
+
this.#debugLastHit = {
|
|
42695
|
+
source: pageMounted ? "dom" : "geometry",
|
|
42696
|
+
pos: rawHit.pos,
|
|
42697
|
+
layoutEpoch: rawHit.layoutEpoch,
|
|
42698
|
+
mappedPos: hit.pos
|
|
42699
|
+
};
|
|
42700
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
42701
|
+
const currentTableHit = this.#hitTestTable(normalized.x, normalized.y);
|
|
42702
|
+
const shouldUseCellSel = this.#shouldUseCellSelection(currentTableHit);
|
|
42703
|
+
if (shouldUseCellSel && this.#cellAnchor) {
|
|
42704
|
+
this.#handleCellDragSelection(currentTableHit, hit);
|
|
42705
|
+
return;
|
|
42706
|
+
}
|
|
42707
|
+
const anchor = this.#dragAnchor;
|
|
42708
|
+
const head = hit.pos;
|
|
42709
|
+
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, head, this.#dragExtensionMode);
|
|
42710
|
+
try {
|
|
42711
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.TextSelection.create(editor.state.doc, selAnchor, selHead));
|
|
42712
|
+
editor.view?.dispatch(tr);
|
|
42713
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
42714
|
+
} catch (error) {
|
|
42715
|
+
console.warn("[SELECTION] Failed to extend selection during drag:", error);
|
|
42716
|
+
}
|
|
42717
|
+
}
|
|
42718
|
+
#handleCellDragSelection(currentTableHit, hit) {
|
|
42719
|
+
const headCellPos = currentTableHit ? this.#getCellPosFromTableHit(currentTableHit) : null;
|
|
42720
|
+
if (headCellPos === null) return;
|
|
42721
|
+
if (this.#cellDragMode !== "active") {
|
|
42722
|
+
this.#cellDragMode = "active";
|
|
42723
|
+
}
|
|
42724
|
+
const editor = this.#deps?.getEditor();
|
|
42725
|
+
if (!editor) return;
|
|
42726
|
+
try {
|
|
42727
|
+
const doc2 = editor.state.doc;
|
|
42728
|
+
const anchorCellPos = this.#cellAnchor.cellPos;
|
|
42729
|
+
const clampedAnchor = Math.max(0, Math.min(anchorCellPos, doc2.content.size));
|
|
42730
|
+
const clampedHead = Math.max(0, Math.min(headCellPos, doc2.content.size));
|
|
42731
|
+
const cellSelection = CellSelection.create(doc2, clampedAnchor, clampedHead);
|
|
42732
|
+
const tr = editor.state.tr.setSelection(cellSelection);
|
|
42733
|
+
editor.view?.dispatch(tr);
|
|
42734
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
42735
|
+
} catch (error) {
|
|
42736
|
+
console.warn("[CELL-SELECTION] Failed to create CellSelection, falling back to TextSelection:", error);
|
|
42737
|
+
const anchor = this.#dragAnchor;
|
|
42738
|
+
const head = hit.pos;
|
|
42739
|
+
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, head, this.#dragExtensionMode);
|
|
42740
|
+
try {
|
|
42741
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.TextSelection.create(editor.state.doc, selAnchor, selHead));
|
|
42742
|
+
editor.view?.dispatch(tr);
|
|
42743
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
42744
|
+
} catch {
|
|
42745
|
+
}
|
|
42746
|
+
}
|
|
42747
|
+
}
|
|
42748
|
+
#handleHover(normalized) {
|
|
42749
|
+
if (!this.#deps) return;
|
|
42750
|
+
const sessionMode = this.#deps.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
42751
|
+
if (sessionMode !== "body") {
|
|
42752
|
+
this.#callbacks.clearHoverRegion?.();
|
|
42753
|
+
return;
|
|
42754
|
+
}
|
|
42755
|
+
if (this.#deps.getDocumentMode() === "viewing") {
|
|
42756
|
+
this.#callbacks.clearHoverRegion?.();
|
|
42757
|
+
return;
|
|
42758
|
+
}
|
|
42759
|
+
const region = this.#callbacks.hitTestHeaderFooterRegion?.(normalized.x, normalized.y);
|
|
42760
|
+
if (!region) {
|
|
42761
|
+
this.#callbacks.clearHoverRegion?.();
|
|
42762
|
+
return;
|
|
42763
|
+
}
|
|
42764
|
+
const currentHover = this.#deps.getHeaderFooterSession()?.hoverRegion;
|
|
42765
|
+
if (currentHover && currentHover.kind === region.kind && currentHover.pageIndex === region.pageIndex && currentHover.sectionType === region.sectionType) {
|
|
42766
|
+
return;
|
|
42767
|
+
}
|
|
42768
|
+
this.#deps.getHeaderFooterSession()?.renderHover(region);
|
|
42769
|
+
this.#callbacks.renderHoverRegion?.(region);
|
|
42770
|
+
}
|
|
42771
|
+
#handleMarginClickEnd(event, pendingMarginClick) {
|
|
42772
|
+
const sessionMode = this.#deps?.getHeaderFooterSession()?.session?.mode ?? "body";
|
|
42773
|
+
if (sessionMode !== "body" || this.#deps?.isViewLocked()) {
|
|
42774
|
+
this.#clearDragPointerState();
|
|
42775
|
+
return;
|
|
42776
|
+
}
|
|
42777
|
+
const editor = this.#deps?.getEditor();
|
|
42778
|
+
const doc2 = editor?.state?.doc;
|
|
42779
|
+
if (!doc2) {
|
|
42780
|
+
this.#clearDragPointerState();
|
|
42781
|
+
return;
|
|
42782
|
+
}
|
|
42783
|
+
const epochMapper = this.#deps?.getEpochMapper();
|
|
42784
|
+
if (!epochMapper) {
|
|
42785
|
+
this.#clearDragPointerState();
|
|
42786
|
+
return;
|
|
42787
|
+
}
|
|
42788
|
+
if (pendingMarginClick.kind === "aboveFirstLine") {
|
|
42789
|
+
const pos = this.#getFirstTextPosition();
|
|
42790
|
+
try {
|
|
42791
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc2, pos));
|
|
42792
|
+
editor.view?.dispatch(tr);
|
|
42793
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
42794
|
+
} catch {
|
|
42795
|
+
}
|
|
42796
|
+
this.#debugLastHit = { source: "margin", pos: null, layoutEpoch: null, mappedPos: pos };
|
|
42797
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
42798
|
+
this.#clearDragPointerState();
|
|
42799
|
+
return;
|
|
42800
|
+
}
|
|
42801
|
+
if (pendingMarginClick.kind === "right") {
|
|
42802
|
+
const mappedEnd2 = epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
42803
|
+
pendingMarginClick.pmEnd,
|
|
42804
|
+
pendingMarginClick.layoutEpoch,
|
|
42805
|
+
1
|
|
42806
|
+
);
|
|
42807
|
+
if (!mappedEnd2.ok) {
|
|
42808
|
+
this.#callbacks.setPendingDocChange?.();
|
|
42809
|
+
this.#callbacks.scheduleRerender?.();
|
|
42810
|
+
this.#clearDragPointerState();
|
|
42811
|
+
return;
|
|
42812
|
+
}
|
|
42813
|
+
const caretPos = Math.max(0, Math.min(mappedEnd2.pos, doc2.content.size));
|
|
42814
|
+
try {
|
|
42815
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc2, caretPos));
|
|
42816
|
+
editor.view?.dispatch(tr);
|
|
42817
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
42818
|
+
} catch {
|
|
42819
|
+
}
|
|
42820
|
+
this.#debugLastHit = {
|
|
42821
|
+
source: "margin",
|
|
42822
|
+
pos: pendingMarginClick.pmEnd,
|
|
42823
|
+
layoutEpoch: pendingMarginClick.layoutEpoch,
|
|
42824
|
+
mappedPos: caretPos
|
|
42825
|
+
};
|
|
42826
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
42827
|
+
this.#clearDragPointerState();
|
|
42828
|
+
return;
|
|
42829
|
+
}
|
|
42830
|
+
const mappedStart = epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
42831
|
+
pendingMarginClick.pmStart,
|
|
42832
|
+
pendingMarginClick.layoutEpoch,
|
|
42833
|
+
1
|
|
42834
|
+
);
|
|
42835
|
+
const mappedEnd = epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
42836
|
+
pendingMarginClick.pmEnd,
|
|
42837
|
+
pendingMarginClick.layoutEpoch,
|
|
42838
|
+
-1
|
|
42839
|
+
);
|
|
42840
|
+
if (!mappedStart.ok || !mappedEnd.ok) {
|
|
42841
|
+
this.#callbacks.setPendingDocChange?.();
|
|
42842
|
+
this.#callbacks.scheduleRerender?.();
|
|
42843
|
+
this.#clearDragPointerState();
|
|
42844
|
+
return;
|
|
42845
|
+
}
|
|
42846
|
+
const selFrom = Math.max(0, Math.min(Math.min(mappedStart.pos, mappedEnd.pos), doc2.content.size));
|
|
42847
|
+
const selTo = Math.max(0, Math.min(Math.max(mappedStart.pos, mappedEnd.pos), doc2.content.size));
|
|
42848
|
+
try {
|
|
42849
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc2, selFrom, selTo));
|
|
42850
|
+
editor.view?.dispatch(tr);
|
|
42851
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
42852
|
+
} catch {
|
|
42853
|
+
}
|
|
42854
|
+
this.#debugLastHit = {
|
|
42855
|
+
source: "margin",
|
|
42856
|
+
pos: pendingMarginClick.pmStart,
|
|
42857
|
+
layoutEpoch: pendingMarginClick.layoutEpoch,
|
|
42858
|
+
mappedPos: selFrom
|
|
42859
|
+
};
|
|
42860
|
+
this.#callbacks.updateSelectionDebugHud?.();
|
|
42861
|
+
this.#clearDragPointerState();
|
|
42862
|
+
}
|
|
42863
|
+
#clearDragPointerState() {
|
|
42864
|
+
this.#dragLastPointer = null;
|
|
42865
|
+
this.#dragLastRawHit = null;
|
|
42866
|
+
this.#dragUsedPageNotMountedFallback = false;
|
|
42867
|
+
}
|
|
42868
|
+
#focusHeaderFooterShortcut(kind) {
|
|
42869
|
+
const pageIndex = this.#callbacks.getCurrentPageIndex?.() ?? 0;
|
|
42870
|
+
const region = this.#callbacks.findRegionForPage?.(kind, pageIndex);
|
|
42871
|
+
if (!region) {
|
|
42872
|
+
this.#callbacks.emitHeaderFooterEditBlocked?.("missingRegion");
|
|
42873
|
+
return;
|
|
42874
|
+
}
|
|
42875
|
+
this.#callbacks.activateHeaderFooterRegion?.(region);
|
|
42876
|
+
}
|
|
42877
|
+
#focusEditorAtFirstPosition() {
|
|
42878
|
+
const editor = this.#deps?.getEditor();
|
|
42879
|
+
const editorDom = editor?.view?.dom;
|
|
42880
|
+
if (!editorDom) return;
|
|
42881
|
+
const validPos = this.#getFirstTextPosition();
|
|
42882
|
+
const doc2 = editor?.state?.doc;
|
|
42883
|
+
if (doc2) {
|
|
42884
|
+
try {
|
|
42885
|
+
const tr = editor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc2, validPos));
|
|
42886
|
+
editor.view?.dispatch(tr);
|
|
42887
|
+
} catch {
|
|
42888
|
+
}
|
|
42889
|
+
}
|
|
42890
|
+
editorDom.focus();
|
|
42891
|
+
editor?.view?.focus();
|
|
42892
|
+
this.#callbacks.scheduleSelectionUpdate?.();
|
|
42893
|
+
}
|
|
42894
|
+
#focusEditor() {
|
|
42895
|
+
if (document.activeElement instanceof HTMLElement) {
|
|
42896
|
+
document.activeElement.blur();
|
|
42897
|
+
}
|
|
42898
|
+
const editor = this.#deps?.getEditor();
|
|
42899
|
+
const editorDom = editor?.view?.dom;
|
|
42900
|
+
if (editorDom) {
|
|
42901
|
+
editorDom.focus();
|
|
42902
|
+
editor?.view?.focus();
|
|
42903
|
+
}
|
|
42904
|
+
}
|
|
42905
|
+
}
|
|
42906
|
+
const createDefaultScheduler = () => {
|
|
42907
|
+
if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
|
|
42908
|
+
return {
|
|
42909
|
+
requestAnimationFrame: (cb) => window.requestAnimationFrame(cb),
|
|
42910
|
+
cancelAnimationFrame: (handle) => window.cancelAnimationFrame(handle)
|
|
42911
|
+
};
|
|
42912
|
+
}
|
|
42913
|
+
const anyGlobal = globalThis;
|
|
42914
|
+
if (typeof anyGlobal.requestAnimationFrame === "function" && typeof anyGlobal.cancelAnimationFrame === "function") {
|
|
42915
|
+
return {
|
|
42916
|
+
requestAnimationFrame: (cb) => anyGlobal.requestAnimationFrame(cb),
|
|
42917
|
+
cancelAnimationFrame: (handle) => anyGlobal.cancelAnimationFrame(handle)
|
|
42918
|
+
};
|
|
42919
|
+
}
|
|
42920
|
+
return {
|
|
42921
|
+
requestAnimationFrame: (cb) => {
|
|
42922
|
+
const handle = anyGlobal.setTimeout?.(() => cb(Date.now()), 0);
|
|
42923
|
+
return handle;
|
|
42924
|
+
},
|
|
42925
|
+
cancelAnimationFrame: (handle) => {
|
|
42926
|
+
anyGlobal.clearTimeout?.(handle);
|
|
42927
|
+
}
|
|
42928
|
+
};
|
|
42929
|
+
};
|
|
42930
|
+
class SelectionSyncCoordinator extends EventEmitter {
|
|
42931
|
+
#docEpoch = 0;
|
|
42932
|
+
#layoutEpoch = 0;
|
|
42933
|
+
#layoutUpdating = false;
|
|
42934
|
+
#pending = false;
|
|
42935
|
+
#scheduled = false;
|
|
42936
|
+
#rafHandle = null;
|
|
42937
|
+
#scheduler;
|
|
42938
|
+
/**
|
|
42939
|
+
* Creates a new SelectionSyncCoordinator.
|
|
42940
|
+
*
|
|
42941
|
+
* @param options - Configuration options
|
|
42942
|
+
* @param options.scheduler - Custom scheduler for animation frames (useful for testing), defaults to platform scheduler
|
|
42943
|
+
*/
|
|
42944
|
+
constructor(options) {
|
|
42945
|
+
super();
|
|
42946
|
+
this.#scheduler = options?.scheduler ?? createDefaultScheduler();
|
|
42947
|
+
}
|
|
42948
|
+
/**
|
|
42949
|
+
* Gets the current document epoch.
|
|
42950
|
+
*
|
|
42951
|
+
* @returns The document epoch (increments on each document-changing transaction)
|
|
42952
|
+
*/
|
|
42953
|
+
getDocEpoch() {
|
|
42954
|
+
return this.#docEpoch;
|
|
42955
|
+
}
|
|
42956
|
+
/**
|
|
42957
|
+
* Gets the current layout epoch.
|
|
42958
|
+
*
|
|
42959
|
+
* @returns The epoch of the document version currently painted in the DOM
|
|
42960
|
+
*/
|
|
42961
|
+
getLayoutEpoch() {
|
|
42962
|
+
return this.#layoutEpoch;
|
|
42963
|
+
}
|
|
42964
|
+
/**
|
|
42965
|
+
* Checks if a layout update is currently in progress.
|
|
42966
|
+
*
|
|
42967
|
+
* @returns True if between onLayoutStart() and onLayoutComplete(), false otherwise
|
|
42968
|
+
*/
|
|
42969
|
+
isLayoutUpdating() {
|
|
42970
|
+
return this.#layoutUpdating;
|
|
42971
|
+
}
|
|
42972
|
+
/**
|
|
42973
|
+
* Updates the document epoch and triggers conditional rendering.
|
|
42974
|
+
*
|
|
42975
|
+
* @param epoch - The new document epoch (must be finite and non-negative)
|
|
42976
|
+
*
|
|
42977
|
+
* @remarks
|
|
42978
|
+
* When the document epoch changes:
|
|
42979
|
+
* 1. Any scheduled render is cancelled (layout will be out of sync)
|
|
42980
|
+
* 2. If layout has already caught up, rendering is rescheduled
|
|
42981
|
+
*
|
|
42982
|
+
* Calling with the same epoch as the current value is a no-op.
|
|
42983
|
+
* Invalid epoch values are silently ignored.
|
|
42984
|
+
*/
|
|
42985
|
+
setDocEpoch(epoch) {
|
|
42986
|
+
if (!Number.isFinite(epoch) || epoch < 0) return;
|
|
42987
|
+
if (epoch === this.#docEpoch) return;
|
|
42988
|
+
this.#docEpoch = epoch;
|
|
42989
|
+
this.#cancelScheduledRender();
|
|
42990
|
+
this.#maybeSchedule();
|
|
42991
|
+
}
|
|
42992
|
+
/**
|
|
42993
|
+
* Notifies the coordinator that layout computation has started.
|
|
42994
|
+
*
|
|
42995
|
+
* @remarks
|
|
42996
|
+
* Marks the layout as updating and cancels any scheduled renders, since the DOM
|
|
42997
|
+
* is about to change and current position data will be stale.
|
|
42998
|
+
*
|
|
42999
|
+
* Safe to call multiple times (e.g., if layouts overlap) - subsequent calls are ignored
|
|
43000
|
+
* until onLayoutComplete() is called.
|
|
43001
|
+
*/
|
|
43002
|
+
onLayoutStart() {
|
|
43003
|
+
if (this.#layoutUpdating) return;
|
|
43004
|
+
this.#layoutUpdating = true;
|
|
43005
|
+
this.#cancelScheduledRender();
|
|
43006
|
+
}
|
|
43007
|
+
/**
|
|
43008
|
+
* Notifies the coordinator that layout painting has completed.
|
|
43009
|
+
*
|
|
43010
|
+
* @param layoutEpoch - The document epoch that was just painted to the DOM
|
|
43011
|
+
*
|
|
43012
|
+
* @remarks
|
|
43013
|
+
* Marks the layout as no longer updating, records the new layout epoch, and attempts
|
|
43014
|
+
* to schedule rendering if conditions are now safe.
|
|
43015
|
+
*
|
|
43016
|
+
* If the layoutEpoch is invalid (not a finite non-negative number), it is ignored and
|
|
43017
|
+
* the previous layoutEpoch value is retained.
|
|
43018
|
+
*
|
|
43019
|
+
* This method is the primary trigger for selection rendering - if there's a pending
|
|
43020
|
+
* render request and layoutEpoch >= docEpoch, a render event will be scheduled.
|
|
43021
|
+
*/
|
|
43022
|
+
onLayoutComplete(layoutEpoch) {
|
|
43023
|
+
this.#layoutUpdating = false;
|
|
43024
|
+
if (Number.isFinite(layoutEpoch) && layoutEpoch >= 0) {
|
|
43025
|
+
this.#layoutEpoch = layoutEpoch;
|
|
43026
|
+
}
|
|
43027
|
+
this.#maybeSchedule();
|
|
43028
|
+
}
|
|
43029
|
+
/**
|
|
43030
|
+
* Notifies the coordinator that layout was aborted without completing.
|
|
43031
|
+
*
|
|
43032
|
+
* @remarks
|
|
43033
|
+
* Marks the layout as no longer updating (without updating layoutEpoch) and attempts
|
|
43034
|
+
* to schedule rendering if conditions are safe.
|
|
43035
|
+
*
|
|
43036
|
+
* Use this when layout computation is cancelled or fails partway through.
|
|
43037
|
+
*/
|
|
43038
|
+
onLayoutAbort() {
|
|
43039
|
+
this.#layoutUpdating = false;
|
|
43040
|
+
this.#maybeSchedule();
|
|
43041
|
+
}
|
|
43042
|
+
/**
|
|
43043
|
+
* Requests that selection rendering occur when conditions become safe.
|
|
43044
|
+
*
|
|
43045
|
+
* @param options - Rendering options
|
|
43046
|
+
* @param options.immediate - If true, attempts to render immediately (synchronously) if safe, defaults to false
|
|
43047
|
+
*
|
|
43048
|
+
* @remarks
|
|
43049
|
+
* Marks a render as pending and schedules it to occur on the next animation frame if
|
|
43050
|
+
* conditions are safe (layout not updating, layoutEpoch >= docEpoch).
|
|
43051
|
+
*
|
|
43052
|
+
* If options.immediate is true, also attempts a synchronous render before scheduling.
|
|
43053
|
+
* Use immediate rendering sparingly, as it can cause multiple renders per frame.
|
|
43054
|
+
*
|
|
43055
|
+
* Multiple calls are coalesced - only one render will occur per animation frame.
|
|
43056
|
+
*/
|
|
43057
|
+
requestRender(options) {
|
|
43058
|
+
this.#pending = true;
|
|
43059
|
+
if (options?.immediate) {
|
|
43060
|
+
this.flushNow();
|
|
43061
|
+
}
|
|
43062
|
+
this.#maybeSchedule();
|
|
43063
|
+
}
|
|
43064
|
+
/**
|
|
43065
|
+
* Attempts to render selection immediately (synchronously) if conditions are safe.
|
|
43066
|
+
*
|
|
43067
|
+
* @remarks
|
|
43068
|
+
* If there's a pending render request and conditions are safe (layout not updating,
|
|
43069
|
+
* layoutEpoch >= docEpoch), this method:
|
|
43070
|
+
* 1. Cancels any scheduled asynchronous render
|
|
43071
|
+
* 2. Clears the pending flag
|
|
43072
|
+
* 3. Emits the 'render' event synchronously
|
|
43073
|
+
*
|
|
43074
|
+
* If no render is pending or conditions are not safe, this is a no-op.
|
|
43075
|
+
*
|
|
43076
|
+
* Use this for immediate selection updates in response to user actions (e.g., click
|
|
43077
|
+
* handlers) where waiting for the next animation frame would cause noticeable lag.
|
|
43078
|
+
*/
|
|
43079
|
+
flushNow() {
|
|
43080
|
+
if (!this.#pending) return;
|
|
43081
|
+
if (!this.#isSafeToRender()) return;
|
|
43082
|
+
this.#cancelScheduledRender();
|
|
43083
|
+
this.#pending = false;
|
|
43084
|
+
this.emit("render", { docEpoch: this.#docEpoch, layoutEpoch: this.#layoutEpoch });
|
|
43085
|
+
}
|
|
43086
|
+
/**
|
|
43087
|
+
* Permanently tears down the coordinator, cancelling pending renders and removing all listeners.
|
|
43088
|
+
*
|
|
43089
|
+
* @remarks
|
|
43090
|
+
* After calling destroy(), this instance should not be used again. All scheduled renders
|
|
43091
|
+
* are cancelled and all event listeners are removed.
|
|
43092
|
+
*
|
|
43093
|
+
* Safe to call multiple times.
|
|
43094
|
+
*/
|
|
43095
|
+
destroy() {
|
|
43096
|
+
this.#cancelScheduledRender();
|
|
43097
|
+
this.removeAllListeners();
|
|
43098
|
+
}
|
|
43099
|
+
#isSafeToRender() {
|
|
43100
|
+
return !this.#layoutUpdating && this.#layoutEpoch >= this.#docEpoch;
|
|
43101
|
+
}
|
|
43102
|
+
#maybeSchedule() {
|
|
43103
|
+
if (!this.#pending) return;
|
|
43104
|
+
if (!this.#isSafeToRender()) return;
|
|
43105
|
+
if (this.#scheduled) return;
|
|
43106
|
+
this.#scheduled = true;
|
|
43107
|
+
this.#rafHandle = this.#scheduler.requestAnimationFrame(() => {
|
|
43108
|
+
this.#scheduled = false;
|
|
43109
|
+
this.#rafHandle = null;
|
|
43110
|
+
if (!this.#pending) return;
|
|
43111
|
+
if (!this.#isSafeToRender()) return;
|
|
43112
|
+
this.#pending = false;
|
|
43113
|
+
this.emit("render", { docEpoch: this.#docEpoch, layoutEpoch: this.#layoutEpoch });
|
|
43114
|
+
});
|
|
43115
|
+
}
|
|
43116
|
+
#cancelScheduledRender() {
|
|
43117
|
+
if (this.#rafHandle != null) {
|
|
43118
|
+
try {
|
|
43119
|
+
this.#scheduler.cancelAnimationFrame(this.#rafHandle);
|
|
43120
|
+
} finally {
|
|
43121
|
+
this.#rafHandle = null;
|
|
43122
|
+
}
|
|
43123
|
+
}
|
|
43124
|
+
this.#scheduled = false;
|
|
43125
|
+
}
|
|
43126
|
+
}
|
|
43127
|
+
const uiSurfaces = /* @__PURE__ */ new WeakSet();
|
|
43128
|
+
function isInRegisteredSurface(event) {
|
|
43129
|
+
const path = typeof event.composedPath === "function" ? event.composedPath() : [];
|
|
42195
43130
|
if (path.length > 0) {
|
|
42196
43131
|
for (const node2 of path) {
|
|
42197
43132
|
if (uiSurfaces.has(node2)) return true;
|
|
@@ -42552,37 +43487,6 @@ class PresentationInputBridge {
|
|
|
42552
43487
|
return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
42553
43488
|
}
|
|
42554
43489
|
}
|
|
42555
|
-
const WORD_CHARACTER_REGEX = /[\p{L}\p{N}'\u2018\u2019_~-]/u;
|
|
42556
|
-
function isWordCharacter(char) {
|
|
42557
|
-
if (!char) {
|
|
42558
|
-
return false;
|
|
42559
|
-
}
|
|
42560
|
-
return WORD_CHARACTER_REGEX.test(char);
|
|
42561
|
-
}
|
|
42562
|
-
function calculateExtendedSelection(blocks, anchor, head, mode) {
|
|
42563
|
-
if (mode === "word") {
|
|
42564
|
-
const anchorBounds = findWordBoundaries(blocks, anchor);
|
|
42565
|
-
const headBounds = findWordBoundaries(blocks, head);
|
|
42566
|
-
if (anchorBounds && headBounds) {
|
|
42567
|
-
if (head >= anchor) {
|
|
42568
|
-
return { selAnchor: anchorBounds.from, selHead: headBounds.to };
|
|
42569
|
-
} else {
|
|
42570
|
-
return { selAnchor: anchorBounds.to, selHead: headBounds.from };
|
|
42571
|
-
}
|
|
42572
|
-
}
|
|
42573
|
-
} else if (mode === "para") {
|
|
42574
|
-
const anchorBounds = findParagraphBoundaries(blocks, anchor);
|
|
42575
|
-
const headBounds = findParagraphBoundaries(blocks, head);
|
|
42576
|
-
if (anchorBounds && headBounds) {
|
|
42577
|
-
if (head >= anchor) {
|
|
42578
|
-
return { selAnchor: anchorBounds.from, selHead: headBounds.to };
|
|
42579
|
-
} else {
|
|
42580
|
-
return { selAnchor: anchorBounds.to, selHead: headBounds.from };
|
|
42581
|
-
}
|
|
42582
|
-
}
|
|
42583
|
-
}
|
|
42584
|
-
return { selAnchor: anchor, selHead: head };
|
|
42585
|
-
}
|
|
42586
43490
|
function getAtomNodeTypes(schema) {
|
|
42587
43491
|
if (!schema) return [];
|
|
42588
43492
|
const types = [];
|
|
@@ -42638,104 +43542,6 @@ function buildPositionMapFromPmDoc(pmDoc, jsonDoc) {
|
|
|
42638
43542
|
if (!ok) return null;
|
|
42639
43543
|
return map3;
|
|
42640
43544
|
}
|
|
42641
|
-
function registerPointerClick(event, previous, options) {
|
|
42642
|
-
const time2 = event.timeStamp ?? performance.now();
|
|
42643
|
-
const timeDelta = time2 - previous.lastClickTime;
|
|
42644
|
-
const withinTime = timeDelta <= options.timeThresholdMs;
|
|
42645
|
-
const distanceX = Math.abs(event.clientX - previous.lastClickPosition.x);
|
|
42646
|
-
const distanceY = Math.abs(event.clientY - previous.lastClickPosition.y);
|
|
42647
|
-
const withinDistance = distanceX <= options.distanceThresholdPx && distanceY <= options.distanceThresholdPx;
|
|
42648
|
-
const clickCount = withinTime && withinDistance ? Math.min(previous.clickCount + 1, options.maxClickCount) : 1;
|
|
42649
|
-
return {
|
|
42650
|
-
clickCount,
|
|
42651
|
-
lastClickTime: time2,
|
|
42652
|
-
lastClickPosition: { x: event.clientX, y: event.clientY }
|
|
42653
|
-
};
|
|
42654
|
-
}
|
|
42655
|
-
function getFirstTextPosition(doc2) {
|
|
42656
|
-
if (!doc2 || !doc2.content) {
|
|
42657
|
-
return 1;
|
|
42658
|
-
}
|
|
42659
|
-
let validPos = 1;
|
|
42660
|
-
doc2.nodesBetween(0, doc2.content.size, (node, pos) => {
|
|
42661
|
-
if (node.isTextblock) {
|
|
42662
|
-
validPos = pos + 1;
|
|
42663
|
-
return false;
|
|
42664
|
-
}
|
|
42665
|
-
return true;
|
|
42666
|
-
});
|
|
42667
|
-
return validPos;
|
|
42668
|
-
}
|
|
42669
|
-
function computeWordSelectionRangeAt(state, pos) {
|
|
42670
|
-
if (!state?.doc) {
|
|
42671
|
-
return null;
|
|
42672
|
-
}
|
|
42673
|
-
if (pos < 0 || pos > state.doc.content.size) {
|
|
42674
|
-
return null;
|
|
42675
|
-
}
|
|
42676
|
-
const textblockPos = findNearestTextblockResolvedPos(state.doc, pos);
|
|
42677
|
-
if (!textblockPos) {
|
|
42678
|
-
return null;
|
|
42679
|
-
}
|
|
42680
|
-
const parentStart = textblockPos.start();
|
|
42681
|
-
const parentEnd = textblockPos.end();
|
|
42682
|
-
const sampleEnd = Math.min(pos + 1, parentEnd);
|
|
42683
|
-
const charAtPos = state.doc.textBetween(pos, sampleEnd, "\0", "\0");
|
|
42684
|
-
if (!isWordCharacter(charAtPos)) {
|
|
42685
|
-
return null;
|
|
42686
|
-
}
|
|
42687
|
-
let startPos = pos;
|
|
42688
|
-
while (startPos > parentStart) {
|
|
42689
|
-
const prevChar = state.doc.textBetween(startPos - 1, startPos, "\0", "\0");
|
|
42690
|
-
if (!isWordCharacter(prevChar)) {
|
|
42691
|
-
break;
|
|
42692
|
-
}
|
|
42693
|
-
startPos -= 1;
|
|
42694
|
-
}
|
|
42695
|
-
let endPos = pos;
|
|
42696
|
-
while (endPos < parentEnd) {
|
|
42697
|
-
const nextChar = state.doc.textBetween(endPos, endPos + 1, "\0", "\0");
|
|
42698
|
-
if (!isWordCharacter(nextChar)) {
|
|
42699
|
-
break;
|
|
42700
|
-
}
|
|
42701
|
-
endPos += 1;
|
|
42702
|
-
}
|
|
42703
|
-
if (startPos === endPos) {
|
|
42704
|
-
return null;
|
|
42705
|
-
}
|
|
42706
|
-
return { from: startPos, to: endPos };
|
|
42707
|
-
}
|
|
42708
|
-
function computeParagraphSelectionRangeAt(state, pos) {
|
|
42709
|
-
if (!state?.doc) {
|
|
42710
|
-
return null;
|
|
42711
|
-
}
|
|
42712
|
-
const textblockPos = findNearestTextblockResolvedPos(state.doc, pos);
|
|
42713
|
-
if (!textblockPos) {
|
|
42714
|
-
return null;
|
|
42715
|
-
}
|
|
42716
|
-
return { from: textblockPos.start(), to: textblockPos.end() };
|
|
42717
|
-
}
|
|
42718
|
-
function findNearestTextblockResolvedPos(doc2, pos) {
|
|
42719
|
-
const $pos = doc2.resolve(pos);
|
|
42720
|
-
let textblockPos = $pos;
|
|
42721
|
-
while (textblockPos.depth > 0) {
|
|
42722
|
-
if (textblockPos.parent?.isTextblock) {
|
|
42723
|
-
break;
|
|
42724
|
-
}
|
|
42725
|
-
if (!textblockPos.parent || textblockPos.depth === 0) {
|
|
42726
|
-
break;
|
|
42727
|
-
}
|
|
42728
|
-
const beforePos = textblockPos.before();
|
|
42729
|
-
if (beforePos < 0 || beforePos > doc2.content.size) {
|
|
42730
|
-
return null;
|
|
42731
|
-
}
|
|
42732
|
-
textblockPos = doc2.resolve(beforePos);
|
|
42733
|
-
}
|
|
42734
|
-
if (!textblockPos.parent?.isTextblock) {
|
|
42735
|
-
return null;
|
|
42736
|
-
}
|
|
42737
|
-
return textblockPos;
|
|
42738
|
-
}
|
|
42739
43545
|
function syncHiddenEditorA11yAttributes(pmDom, documentMode) {
|
|
42740
43546
|
const element = pmDom;
|
|
42741
43547
|
if (!(element instanceof HTMLElement)) return;
|
|
@@ -43405,317 +44211,255 @@ async function goToAnchor({
|
|
|
43405
44211
|
}
|
|
43406
44212
|
return true;
|
|
43407
44213
|
}
|
|
43408
|
-
|
|
43409
|
-
|
|
43410
|
-
|
|
43411
|
-
|
|
43412
|
-
|
|
43413
|
-
|
|
43414
|
-
|
|
43415
|
-
|
|
43416
|
-
|
|
43417
|
-
|
|
43418
|
-
|
|
43419
|
-
|
|
43420
|
-
|
|
43421
|
-
const
|
|
43422
|
-
const
|
|
43423
|
-
|
|
43424
|
-
|
|
43425
|
-
|
|
43426
|
-
|
|
43427
|
-
doc2.descendants((node, pos) => {
|
|
43428
|
-
if (node.type.name === "table") {
|
|
43429
|
-
if (currentTableIndex === targetTableIndex) {
|
|
43430
|
-
tablePos = pos;
|
|
43431
|
-
return false;
|
|
43432
|
-
}
|
|
43433
|
-
currentTableIndex++;
|
|
43434
|
-
}
|
|
43435
|
-
return true;
|
|
43436
|
-
});
|
|
43437
|
-
} catch (error) {
|
|
43438
|
-
console.error("[getCellPosFromTableHit] Error during document traversal:", error);
|
|
43439
|
-
return null;
|
|
43440
|
-
}
|
|
43441
|
-
if (tablePos === null) return null;
|
|
43442
|
-
const tableNode = doc2.nodeAt(tablePos);
|
|
43443
|
-
if (!tableNode || tableNode.type.name !== "table") return null;
|
|
43444
|
-
const targetRowIndex = tableHit.cellRowIndex;
|
|
43445
|
-
const targetColIndex = tableHit.cellColIndex;
|
|
43446
|
-
if (targetRowIndex >= tableNode.childCount) {
|
|
43447
|
-
console.warn("[getCellPosFromTableHit] Target row index out of bounds:", {
|
|
43448
|
-
targetRowIndex,
|
|
43449
|
-
tableChildCount: tableNode.childCount
|
|
43450
|
-
});
|
|
43451
|
-
return null;
|
|
43452
|
-
}
|
|
43453
|
-
let currentPos = tablePos + 1;
|
|
43454
|
-
for (let r2 = 0; r2 < tableNode.childCount && r2 <= targetRowIndex; r2++) {
|
|
43455
|
-
const row = tableNode.child(r2);
|
|
43456
|
-
if (r2 === targetRowIndex) {
|
|
43457
|
-
currentPos += 1;
|
|
43458
|
-
let logicalCol = 0;
|
|
43459
|
-
for (let cellIndex = 0; cellIndex < row.childCount; cellIndex++) {
|
|
43460
|
-
const cell = row.child(cellIndex);
|
|
43461
|
-
const rawColspan = cell.attrs?.colspan;
|
|
43462
|
-
const colspan = typeof rawColspan === "number" && Number.isFinite(rawColspan) && rawColspan > 0 ? rawColspan : 1;
|
|
43463
|
-
if (targetColIndex >= logicalCol && targetColIndex < logicalCol + colspan) {
|
|
43464
|
-
return currentPos;
|
|
43465
|
-
}
|
|
43466
|
-
currentPos += cell.nodeSize;
|
|
43467
|
-
logicalCol += colspan;
|
|
43468
|
-
}
|
|
43469
|
-
console.warn("[getCellPosFromTableHit] Target column not found in row:", {
|
|
43470
|
-
targetColIndex,
|
|
43471
|
-
logicalColReached: logicalCol,
|
|
43472
|
-
rowCellCount: row.childCount
|
|
43473
|
-
});
|
|
43474
|
-
return null;
|
|
43475
|
-
} else {
|
|
43476
|
-
currentPos += row.nodeSize;
|
|
44214
|
+
const INTERNAL_MIME_TYPE = "application/x-field-annotation";
|
|
44215
|
+
const FIELD_ANNOTATION_DATA_TYPE = "fieldAnnotation";
|
|
44216
|
+
function isValidFieldAnnotationAttributes(attrs) {
|
|
44217
|
+
if (!attrs || typeof attrs !== "object") return false;
|
|
44218
|
+
const a = attrs;
|
|
44219
|
+
return typeof a.fieldId === "string" && typeof a.fieldType === "string" && typeof a.displayLabel === "string" && typeof a.type === "string";
|
|
44220
|
+
}
|
|
44221
|
+
function parseIntSafe$1(value) {
|
|
44222
|
+
if (!value) return void 0;
|
|
44223
|
+
const parsed = parseInt(value, 10);
|
|
44224
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
44225
|
+
}
|
|
44226
|
+
function extractFieldAnnotationData(element) {
|
|
44227
|
+
const dataset = element.dataset;
|
|
44228
|
+
const attributes = {};
|
|
44229
|
+
for (const key2 in dataset) {
|
|
44230
|
+
const value = dataset[key2];
|
|
44231
|
+
if (value !== void 0) {
|
|
44232
|
+
attributes[key2] = value;
|
|
43477
44233
|
}
|
|
43478
44234
|
}
|
|
43479
|
-
return
|
|
44235
|
+
return {
|
|
44236
|
+
fieldId: dataset.fieldId,
|
|
44237
|
+
fieldType: dataset.fieldType,
|
|
44238
|
+
variant: dataset.variant ?? dataset.type,
|
|
44239
|
+
displayLabel: dataset.displayLabel,
|
|
44240
|
+
pmStart: parseIntSafe$1(dataset.pmStart),
|
|
44241
|
+
pmEnd: parseIntSafe$1(dataset.pmEnd),
|
|
44242
|
+
attributes
|
|
44243
|
+
};
|
|
43480
44244
|
}
|
|
43481
|
-
function
|
|
43482
|
-
if (!
|
|
43483
|
-
const
|
|
43484
|
-
|
|
43485
|
-
if (targetTableIndex === -1) return null;
|
|
43486
|
-
let tablePos = null;
|
|
43487
|
-
let currentTableIndex = 0;
|
|
43488
|
-
doc2.descendants((node, pos) => {
|
|
43489
|
-
if (node.type.name === "table") {
|
|
43490
|
-
if (currentTableIndex === targetTableIndex) {
|
|
43491
|
-
tablePos = pos;
|
|
43492
|
-
return false;
|
|
43493
|
-
}
|
|
43494
|
-
currentTableIndex++;
|
|
43495
|
-
}
|
|
43496
|
-
return true;
|
|
43497
|
-
});
|
|
43498
|
-
return tablePos;
|
|
44245
|
+
function hasFieldAnnotationData(event) {
|
|
44246
|
+
if (!event.dataTransfer) return false;
|
|
44247
|
+
const types = event.dataTransfer.types;
|
|
44248
|
+
return types.includes(INTERNAL_MIME_TYPE) || types.includes(FIELD_ANNOTATION_DATA_TYPE);
|
|
43499
44249
|
}
|
|
43500
|
-
function
|
|
43501
|
-
|
|
43502
|
-
if (!currentTableHit) return cellDragMode === "active";
|
|
43503
|
-
if (currentTableHit.block.id !== cellAnchor.tableBlockId) {
|
|
43504
|
-
return cellDragMode === "active";
|
|
43505
|
-
}
|
|
43506
|
-
const sameCell = currentTableHit.cellRowIndex === cellAnchor.cellRowIndex && currentTableHit.cellColIndex === cellAnchor.cellColIndex;
|
|
43507
|
-
if (!sameCell) {
|
|
43508
|
-
return true;
|
|
43509
|
-
}
|
|
43510
|
-
return cellDragMode === "active";
|
|
44250
|
+
function isInternalDrag(event) {
|
|
44251
|
+
return event.dataTransfer?.types?.includes(INTERNAL_MIME_TYPE) ?? false;
|
|
43511
44252
|
}
|
|
43512
|
-
function
|
|
43513
|
-
if (!
|
|
44253
|
+
function extractDragData(event) {
|
|
44254
|
+
if (!event.dataTransfer) return null;
|
|
44255
|
+
let jsonData = event.dataTransfer.getData(INTERNAL_MIME_TYPE);
|
|
44256
|
+
if (!jsonData) {
|
|
44257
|
+
jsonData = event.dataTransfer.getData(FIELD_ANNOTATION_DATA_TYPE);
|
|
44258
|
+
}
|
|
44259
|
+
if (!jsonData) return null;
|
|
44260
|
+
try {
|
|
44261
|
+
const parsed = JSON.parse(jsonData);
|
|
44262
|
+
return parsed.sourceField ?? parsed.attributes ?? parsed;
|
|
44263
|
+
} catch {
|
|
43514
44264
|
return null;
|
|
43515
44265
|
}
|
|
43516
|
-
|
|
43517
|
-
|
|
43518
|
-
|
|
43519
|
-
|
|
43520
|
-
|
|
43521
|
-
|
|
43522
|
-
|
|
43523
|
-
|
|
44266
|
+
}
|
|
44267
|
+
class DragDropManager {
|
|
44268
|
+
#deps = null;
|
|
44269
|
+
// Bound handlers for cleanup
|
|
44270
|
+
#boundHandleDragStart = null;
|
|
44271
|
+
#boundHandleDragOver = null;
|
|
44272
|
+
#boundHandleDrop = null;
|
|
44273
|
+
#boundHandleDragEnd = null;
|
|
44274
|
+
#boundHandleDragLeave = null;
|
|
44275
|
+
#boundHandleWindowDragOver = null;
|
|
44276
|
+
#boundHandleWindowDrop = null;
|
|
44277
|
+
// ==========================================================================
|
|
44278
|
+
// Setup
|
|
44279
|
+
// ==========================================================================
|
|
44280
|
+
setDependencies(deps) {
|
|
44281
|
+
this.#deps = deps;
|
|
43524
44282
|
}
|
|
43525
|
-
|
|
43526
|
-
|
|
43527
|
-
|
|
43528
|
-
|
|
43529
|
-
|
|
43530
|
-
|
|
43531
|
-
|
|
43532
|
-
|
|
43533
|
-
|
|
43534
|
-
|
|
43535
|
-
|
|
44283
|
+
bind() {
|
|
44284
|
+
if (!this.#deps) return;
|
|
44285
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
44286
|
+
const painterHost = this.#deps.getPainterHost();
|
|
44287
|
+
this.#boundHandleDragStart = this.#handleDragStart.bind(this);
|
|
44288
|
+
this.#boundHandleDragOver = this.#handleDragOver.bind(this);
|
|
44289
|
+
this.#boundHandleDrop = this.#handleDrop.bind(this);
|
|
44290
|
+
this.#boundHandleDragEnd = this.#handleDragEnd.bind(this);
|
|
44291
|
+
this.#boundHandleDragLeave = this.#handleDragLeave.bind(this);
|
|
44292
|
+
this.#boundHandleWindowDragOver = this.#handleWindowDragOver.bind(this);
|
|
44293
|
+
this.#boundHandleWindowDrop = this.#handleWindowDrop.bind(this);
|
|
44294
|
+
painterHost.addEventListener("dragstart", this.#boundHandleDragStart);
|
|
44295
|
+
painterHost.addEventListener("dragend", this.#boundHandleDragEnd);
|
|
44296
|
+
painterHost.addEventListener("dragleave", this.#boundHandleDragLeave);
|
|
44297
|
+
viewportHost.addEventListener("dragover", this.#boundHandleDragOver);
|
|
44298
|
+
viewportHost.addEventListener("drop", this.#boundHandleDrop);
|
|
44299
|
+
window.addEventListener("dragover", this.#boundHandleWindowDragOver, false);
|
|
44300
|
+
window.addEventListener("drop", this.#boundHandleWindowDrop, false);
|
|
44301
|
+
}
|
|
44302
|
+
unbind() {
|
|
44303
|
+
if (!this.#deps) return;
|
|
44304
|
+
const viewportHost = this.#deps.getViewportHost();
|
|
44305
|
+
const painterHost = this.#deps.getPainterHost();
|
|
44306
|
+
if (this.#boundHandleDragStart) {
|
|
44307
|
+
painterHost.removeEventListener("dragstart", this.#boundHandleDragStart);
|
|
44308
|
+
}
|
|
44309
|
+
if (this.#boundHandleDragEnd) {
|
|
44310
|
+
painterHost.removeEventListener("dragend", this.#boundHandleDragEnd);
|
|
44311
|
+
}
|
|
44312
|
+
if (this.#boundHandleDragLeave) {
|
|
44313
|
+
painterHost.removeEventListener("dragleave", this.#boundHandleDragLeave);
|
|
44314
|
+
}
|
|
44315
|
+
if (this.#boundHandleDragOver) {
|
|
44316
|
+
viewportHost.removeEventListener("dragover", this.#boundHandleDragOver);
|
|
44317
|
+
}
|
|
44318
|
+
if (this.#boundHandleDrop) {
|
|
44319
|
+
viewportHost.removeEventListener("drop", this.#boundHandleDrop);
|
|
44320
|
+
}
|
|
44321
|
+
if (this.#boundHandleWindowDragOver) {
|
|
44322
|
+
window.removeEventListener("dragover", this.#boundHandleWindowDragOver, false);
|
|
44323
|
+
}
|
|
44324
|
+
if (this.#boundHandleWindowDrop) {
|
|
44325
|
+
window.removeEventListener("drop", this.#boundHandleWindowDrop, false);
|
|
44326
|
+
}
|
|
44327
|
+
this.#boundHandleDragStart = null;
|
|
44328
|
+
this.#boundHandleDragOver = null;
|
|
44329
|
+
this.#boundHandleDrop = null;
|
|
44330
|
+
this.#boundHandleDragEnd = null;
|
|
44331
|
+
this.#boundHandleDragLeave = null;
|
|
44332
|
+
this.#boundHandleWindowDragOver = null;
|
|
44333
|
+
this.#boundHandleWindowDrop = null;
|
|
43536
44334
|
}
|
|
43537
|
-
|
|
43538
|
-
|
|
44335
|
+
destroy() {
|
|
44336
|
+
this.unbind();
|
|
44337
|
+
this.#deps = null;
|
|
43539
44338
|
}
|
|
43540
|
-
|
|
43541
|
-
|
|
43542
|
-
|
|
43543
|
-
|
|
43544
|
-
|
|
43545
|
-
|
|
43546
|
-
|
|
43547
|
-
|
|
43548
|
-
|
|
43549
|
-
const FIELD_ANNOTATION_DATA_TYPE = "fieldAnnotation";
|
|
43550
|
-
function setupInternalFieldAnnotationDragHandlers({
|
|
43551
|
-
painterHost,
|
|
43552
|
-
getActiveEditor,
|
|
43553
|
-
hitTest,
|
|
43554
|
-
scheduleSelectionUpdate
|
|
43555
|
-
}) {
|
|
43556
|
-
return createDragHandler(painterHost, {
|
|
43557
|
-
onDragOver: (event) => {
|
|
43558
|
-
if (!event.hasFieldAnnotation || event.event.clientX === 0) {
|
|
43559
|
-
return;
|
|
43560
|
-
}
|
|
43561
|
-
const activeEditor = getActiveEditor();
|
|
43562
|
-
if (!activeEditor?.isEditable) {
|
|
43563
|
-
return;
|
|
43564
|
-
}
|
|
43565
|
-
const hit = hitTest(event.clientX, event.clientY);
|
|
43566
|
-
const doc2 = activeEditor.state?.doc;
|
|
43567
|
-
if (!hit || !doc2) {
|
|
43568
|
-
return;
|
|
43569
|
-
}
|
|
43570
|
-
const pos = Math.min(Math.max(hit.pos, 1), doc2.content.size);
|
|
43571
|
-
const currentSelection = activeEditor.state.selection;
|
|
43572
|
-
if (currentSelection instanceof superEditor_converter.TextSelection && currentSelection.from === pos && currentSelection.to === pos) {
|
|
43573
|
-
return;
|
|
43574
|
-
}
|
|
43575
|
-
try {
|
|
43576
|
-
const tr = activeEditor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc2, pos)).setMeta("addToHistory", false);
|
|
43577
|
-
activeEditor.view?.dispatch(tr);
|
|
43578
|
-
scheduleSelectionUpdate();
|
|
43579
|
-
} catch {
|
|
43580
|
-
}
|
|
43581
|
-
},
|
|
43582
|
-
onDrop: (event) => {
|
|
43583
|
-
event.event.preventDefault();
|
|
43584
|
-
event.event.stopPropagation();
|
|
43585
|
-
if (event.pmPosition === null) {
|
|
43586
|
-
return;
|
|
43587
|
-
}
|
|
43588
|
-
const activeEditor = getActiveEditor();
|
|
43589
|
-
const { state, view } = activeEditor;
|
|
43590
|
-
if (!state || !view) {
|
|
43591
|
-
return;
|
|
43592
|
-
}
|
|
43593
|
-
const fieldId = event.data.fieldId;
|
|
43594
|
-
if (fieldId) {
|
|
43595
|
-
const targetPos = event.pmPosition;
|
|
43596
|
-
const pmStart = event.data.pmStart;
|
|
43597
|
-
let sourceStart = null;
|
|
43598
|
-
let sourceEnd = null;
|
|
43599
|
-
let sourceNode = null;
|
|
43600
|
-
if (pmStart != null) {
|
|
43601
|
-
const nodeAt = state.doc.nodeAt(pmStart);
|
|
43602
|
-
if (nodeAt?.type?.name === "fieldAnnotation") {
|
|
43603
|
-
sourceStart = pmStart;
|
|
43604
|
-
sourceEnd = pmStart + nodeAt.nodeSize;
|
|
43605
|
-
sourceNode = nodeAt;
|
|
43606
|
-
}
|
|
43607
|
-
}
|
|
43608
|
-
if (sourceStart == null || sourceEnd == null || !sourceNode) {
|
|
43609
|
-
state.doc.descendants((node, pos) => {
|
|
43610
|
-
if (node.type.name === "fieldAnnotation" && node.attrs.fieldId === fieldId) {
|
|
43611
|
-
sourceStart = pos;
|
|
43612
|
-
sourceEnd = pos + node.nodeSize;
|
|
43613
|
-
sourceNode = node;
|
|
43614
|
-
return false;
|
|
43615
|
-
}
|
|
43616
|
-
return true;
|
|
43617
|
-
});
|
|
43618
|
-
}
|
|
43619
|
-
if (sourceStart === null || sourceEnd === null || !sourceNode) {
|
|
43620
|
-
return;
|
|
43621
|
-
}
|
|
43622
|
-
if (targetPos >= sourceStart && targetPos <= sourceEnd) {
|
|
43623
|
-
return;
|
|
43624
|
-
}
|
|
43625
|
-
const tr = state.tr;
|
|
43626
|
-
tr.delete(sourceStart, sourceEnd);
|
|
43627
|
-
const mappedTarget = tr.mapping.map(targetPos);
|
|
43628
|
-
if (mappedTarget < 0 || mappedTarget > tr.doc.content.size) {
|
|
43629
|
-
return;
|
|
43630
|
-
}
|
|
43631
|
-
tr.insert(mappedTarget, sourceNode);
|
|
43632
|
-
tr.setMeta("uiEvent", "drop");
|
|
43633
|
-
view.dispatch(tr);
|
|
43634
|
-
return;
|
|
43635
|
-
}
|
|
43636
|
-
const attrs = event.data.attributes;
|
|
43637
|
-
if (attrs && isValidFieldAnnotationAttributes(attrs)) {
|
|
43638
|
-
const inserted = activeEditor.commands?.addFieldAnnotation?.(event.pmPosition, attrs, true);
|
|
43639
|
-
if (inserted) {
|
|
43640
|
-
scheduleSelectionUpdate();
|
|
43641
|
-
}
|
|
43642
|
-
return;
|
|
43643
|
-
}
|
|
43644
|
-
activeEditor.emit("fieldAnnotationDropped", {
|
|
43645
|
-
sourceField: event.data,
|
|
43646
|
-
editor: activeEditor,
|
|
43647
|
-
coordinates: { pos: event.pmPosition }
|
|
43648
|
-
});
|
|
43649
|
-
}
|
|
43650
|
-
});
|
|
43651
|
-
}
|
|
43652
|
-
function createExternalFieldAnnotationDragOverHandler({
|
|
43653
|
-
getActiveEditor,
|
|
43654
|
-
hitTest,
|
|
43655
|
-
scheduleSelectionUpdate
|
|
43656
|
-
}) {
|
|
43657
|
-
return (event) => {
|
|
43658
|
-
const activeEditor = getActiveEditor();
|
|
43659
|
-
if (!activeEditor?.isEditable) {
|
|
44339
|
+
// ==========================================================================
|
|
44340
|
+
// Event Handlers
|
|
44341
|
+
// ==========================================================================
|
|
44342
|
+
/**
|
|
44343
|
+
* Handle dragstart for internal field annotations.
|
|
44344
|
+
*/
|
|
44345
|
+
#handleDragStart(event) {
|
|
44346
|
+
const target = event.target;
|
|
44347
|
+
if (!target?.dataset?.draggable || target.dataset.draggable !== "true") {
|
|
43660
44348
|
return;
|
|
43661
44349
|
}
|
|
43662
|
-
|
|
44350
|
+
const data = extractFieldAnnotationData(target);
|
|
43663
44351
|
if (event.dataTransfer) {
|
|
43664
|
-
|
|
44352
|
+
const jsonData = JSON.stringify({
|
|
44353
|
+
attributes: data.attributes,
|
|
44354
|
+
sourceField: data
|
|
44355
|
+
});
|
|
44356
|
+
event.dataTransfer.setData(INTERNAL_MIME_TYPE, jsonData);
|
|
44357
|
+
event.dataTransfer.setData(FIELD_ANNOTATION_DATA_TYPE, jsonData);
|
|
44358
|
+
event.dataTransfer.setData("text/plain", data.displayLabel ?? "Field Annotation");
|
|
44359
|
+
event.dataTransfer.setDragImage(target, 0, 0);
|
|
44360
|
+
event.dataTransfer.effectAllowed = "move";
|
|
43665
44361
|
}
|
|
43666
|
-
|
|
43667
|
-
|
|
43668
|
-
|
|
43669
|
-
|
|
44362
|
+
}
|
|
44363
|
+
/**
|
|
44364
|
+
* Handle dragover - update cursor position to show drop location.
|
|
44365
|
+
*/
|
|
44366
|
+
#handleDragOver(event) {
|
|
44367
|
+
if (!this.#deps) return;
|
|
44368
|
+
if (!hasFieldAnnotationData(event)) return;
|
|
44369
|
+
const activeEditor = this.#deps.getActiveEditor();
|
|
44370
|
+
if (!activeEditor?.isEditable) return;
|
|
44371
|
+
event.preventDefault();
|
|
44372
|
+
if (event.dataTransfer) {
|
|
44373
|
+
event.dataTransfer.dropEffect = isInternalDrag(event) ? "move" : "copy";
|
|
43670
44374
|
}
|
|
43671
|
-
const hit = hitTest(event.clientX, event.clientY);
|
|
44375
|
+
const hit = this.#deps.hitTest(event.clientX, event.clientY);
|
|
43672
44376
|
const doc2 = activeEditor.state?.doc;
|
|
43673
|
-
if (!hit || !doc2)
|
|
43674
|
-
return;
|
|
43675
|
-
}
|
|
44377
|
+
if (!hit || !doc2) return;
|
|
43676
44378
|
const pos = Math.min(Math.max(hit.pos, 1), doc2.content.size);
|
|
43677
44379
|
const currentSelection = activeEditor.state.selection;
|
|
43678
|
-
|
|
43679
|
-
if (isSameCursor) {
|
|
44380
|
+
if (currentSelection instanceof superEditor_converter.TextSelection && currentSelection.from === pos && currentSelection.to === pos) {
|
|
43680
44381
|
return;
|
|
43681
44382
|
}
|
|
43682
44383
|
try {
|
|
43683
44384
|
const tr = activeEditor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc2, pos)).setMeta("addToHistory", false);
|
|
43684
44385
|
activeEditor.view?.dispatch(tr);
|
|
43685
|
-
scheduleSelectionUpdate();
|
|
43686
|
-
} catch
|
|
43687
|
-
if (vue.process$1.env.NODE_ENV === "development") {
|
|
43688
|
-
console.debug("[PresentationEditor] Drag position update skipped:", error);
|
|
43689
|
-
}
|
|
43690
|
-
}
|
|
43691
|
-
};
|
|
43692
|
-
}
|
|
43693
|
-
function createExternalFieldAnnotationDropHandler({
|
|
43694
|
-
getActiveEditor,
|
|
43695
|
-
hitTest,
|
|
43696
|
-
scheduleSelectionUpdate
|
|
43697
|
-
}) {
|
|
43698
|
-
return (event) => {
|
|
43699
|
-
const activeEditor = getActiveEditor();
|
|
43700
|
-
if (!activeEditor?.isEditable) {
|
|
43701
|
-
return;
|
|
43702
|
-
}
|
|
43703
|
-
if (event.dataTransfer?.types?.includes("application/x-field-annotation")) {
|
|
43704
|
-
return;
|
|
44386
|
+
this.#deps.scheduleSelectionUpdate();
|
|
44387
|
+
} catch {
|
|
43705
44388
|
}
|
|
44389
|
+
}
|
|
44390
|
+
/**
|
|
44391
|
+
* Handle drop - either move internal annotation or insert external one.
|
|
44392
|
+
*/
|
|
44393
|
+
#handleDrop(event) {
|
|
44394
|
+
if (!this.#deps) return;
|
|
44395
|
+
if (!hasFieldAnnotationData(event)) return;
|
|
43706
44396
|
event.preventDefault();
|
|
43707
44397
|
event.stopPropagation();
|
|
43708
|
-
const
|
|
43709
|
-
if (!
|
|
44398
|
+
const activeEditor = this.#deps.getActiveEditor();
|
|
44399
|
+
if (!activeEditor?.isEditable) return;
|
|
44400
|
+
const { state, view } = activeEditor;
|
|
44401
|
+
if (!state || !view) return;
|
|
44402
|
+
const hit = this.#deps.hitTest(event.clientX, event.clientY);
|
|
44403
|
+
const fallbackPos = state.selection?.from ?? state.doc?.content.size ?? null;
|
|
44404
|
+
const dropPos = hit?.pos ?? fallbackPos;
|
|
44405
|
+
if (dropPos == null) return;
|
|
44406
|
+
if (isInternalDrag(event)) {
|
|
44407
|
+
this.#handleInternalDrop(event, dropPos);
|
|
43710
44408
|
return;
|
|
43711
44409
|
}
|
|
43712
|
-
|
|
43713
|
-
|
|
43714
|
-
|
|
43715
|
-
|
|
43716
|
-
|
|
43717
|
-
|
|
44410
|
+
this.#handleExternalDrop(event, dropPos);
|
|
44411
|
+
}
|
|
44412
|
+
/**
|
|
44413
|
+
* Handle internal drop - move field annotation within document.
|
|
44414
|
+
*/
|
|
44415
|
+
#handleInternalDrop(event, targetPos) {
|
|
44416
|
+
if (!this.#deps) return;
|
|
44417
|
+
const activeEditor = this.#deps.getActiveEditor();
|
|
44418
|
+
const { state, view } = activeEditor;
|
|
44419
|
+
if (!state || !view) return;
|
|
44420
|
+
const data = extractDragData(event);
|
|
44421
|
+
if (!data?.fieldId) return;
|
|
44422
|
+
const pmStart = data.pmStart;
|
|
44423
|
+
let sourceStart = null;
|
|
44424
|
+
let sourceEnd = null;
|
|
44425
|
+
let sourceNode = null;
|
|
44426
|
+
if (pmStart != null) {
|
|
44427
|
+
const nodeAt = state.doc.nodeAt(pmStart);
|
|
44428
|
+
if (nodeAt?.type?.name === "fieldAnnotation") {
|
|
44429
|
+
sourceStart = pmStart;
|
|
44430
|
+
sourceEnd = pmStart + nodeAt.nodeSize;
|
|
44431
|
+
sourceNode = nodeAt;
|
|
44432
|
+
}
|
|
44433
|
+
}
|
|
44434
|
+
if (sourceStart == null || sourceEnd == null || !sourceNode) {
|
|
44435
|
+
state.doc.descendants((node, pos) => {
|
|
44436
|
+
if (node.type.name === "fieldAnnotation" && node.attrs.fieldId === data.fieldId) {
|
|
44437
|
+
sourceStart = pos;
|
|
44438
|
+
sourceEnd = pos + node.nodeSize;
|
|
44439
|
+
sourceNode = node;
|
|
44440
|
+
return false;
|
|
44441
|
+
}
|
|
44442
|
+
return true;
|
|
44443
|
+
});
|
|
43718
44444
|
}
|
|
44445
|
+
if (sourceStart === null || sourceEnd === null || !sourceNode) return;
|
|
44446
|
+
if (targetPos >= sourceStart && targetPos <= sourceEnd) return;
|
|
44447
|
+
const tr = state.tr;
|
|
44448
|
+
tr.delete(sourceStart, sourceEnd);
|
|
44449
|
+
const mappedTarget = tr.mapping.map(targetPos);
|
|
44450
|
+
if (mappedTarget < 0 || mappedTarget > tr.doc.content.size) return;
|
|
44451
|
+
tr.insert(mappedTarget, sourceNode);
|
|
44452
|
+
tr.setMeta("uiEvent", "drop");
|
|
44453
|
+
view.dispatch(tr);
|
|
44454
|
+
}
|
|
44455
|
+
/**
|
|
44456
|
+
* Handle external drop - insert new field annotation.
|
|
44457
|
+
*/
|
|
44458
|
+
#handleExternalDrop(event, pos) {
|
|
44459
|
+
if (!this.#deps) return;
|
|
44460
|
+
const activeEditor = this.#deps.getActiveEditor();
|
|
44461
|
+
const fieldAnnotationData = event.dataTransfer?.getData(FIELD_ANNOTATION_DATA_TYPE);
|
|
44462
|
+
if (!fieldAnnotationData) return;
|
|
43719
44463
|
let parsedData = null;
|
|
43720
44464
|
try {
|
|
43721
44465
|
parsedData = JSON.parse(fieldAnnotationData);
|
|
@@ -43726,7 +44470,7 @@ function createExternalFieldAnnotationDropHandler({
|
|
|
43726
44470
|
activeEditor.emit?.("fieldAnnotationDropped", {
|
|
43727
44471
|
sourceField,
|
|
43728
44472
|
editor: activeEditor,
|
|
43729
|
-
coordinates:
|
|
44473
|
+
coordinates: this.#deps.hitTest(event.clientX, event.clientY),
|
|
43730
44474
|
pos
|
|
43731
44475
|
});
|
|
43732
44476
|
if (attributes && isValidFieldAnnotationAttributes(attributes)) {
|
|
@@ -43736,14 +44480,49 @@ function createExternalFieldAnnotationDropHandler({
|
|
|
43736
44480
|
if (tr) {
|
|
43737
44481
|
activeEditor.view?.dispatch(tr);
|
|
43738
44482
|
}
|
|
43739
|
-
scheduleSelectionUpdate();
|
|
44483
|
+
this.#deps.scheduleSelectionUpdate();
|
|
43740
44484
|
}
|
|
43741
44485
|
const editorDom = activeEditor.view?.dom;
|
|
43742
44486
|
if (editorDom) {
|
|
43743
44487
|
editorDom.focus();
|
|
43744
44488
|
activeEditor.view?.focus();
|
|
43745
44489
|
}
|
|
43746
|
-
}
|
|
44490
|
+
}
|
|
44491
|
+
#handleDragEnd(_event) {
|
|
44492
|
+
this.#deps?.getPainterHost()?.classList.remove("drag-over");
|
|
44493
|
+
}
|
|
44494
|
+
#handleDragLeave(event) {
|
|
44495
|
+
const painterHost = this.#deps?.getPainterHost();
|
|
44496
|
+
if (!painterHost) return;
|
|
44497
|
+
const relatedTarget = event.relatedTarget;
|
|
44498
|
+
if (!relatedTarget || !painterHost.contains(relatedTarget)) {
|
|
44499
|
+
painterHost.classList.remove("drag-over");
|
|
44500
|
+
}
|
|
44501
|
+
}
|
|
44502
|
+
/**
|
|
44503
|
+
* Window-level dragover to allow drops on overlay elements.
|
|
44504
|
+
*/
|
|
44505
|
+
#handleWindowDragOver(event) {
|
|
44506
|
+
if (!hasFieldAnnotationData(event)) return;
|
|
44507
|
+
const viewportHost = this.#deps?.getViewportHost();
|
|
44508
|
+
const target = event.target;
|
|
44509
|
+
if (viewportHost?.contains(target)) return;
|
|
44510
|
+
event.preventDefault();
|
|
44511
|
+
if (event.dataTransfer) {
|
|
44512
|
+
event.dataTransfer.dropEffect = isInternalDrag(event) ? "move" : "copy";
|
|
44513
|
+
}
|
|
44514
|
+
this.#handleDragOver(event);
|
|
44515
|
+
}
|
|
44516
|
+
/**
|
|
44517
|
+
* Window-level drop to catch drops on overlay elements.
|
|
44518
|
+
*/
|
|
44519
|
+
#handleWindowDrop(event) {
|
|
44520
|
+
if (!hasFieldAnnotationData(event)) return;
|
|
44521
|
+
const viewportHost = this.#deps?.getViewportHost();
|
|
44522
|
+
const target = event.target;
|
|
44523
|
+
if (viewportHost?.contains(target)) return;
|
|
44524
|
+
this.#handleDrop(event);
|
|
44525
|
+
}
|
|
43747
44526
|
}
|
|
43748
44527
|
var SectionType = /* @__PURE__ */ ((SectionType2) => {
|
|
43749
44528
|
SectionType2["CONTINUOUS"] = "continuous";
|
|
@@ -45797,16 +46576,17 @@ const normalizeParagraphSpacing = (value) => {
|
|
|
45797
46576
|
const afterRaw = pickNumber(source.after);
|
|
45798
46577
|
const lineRaw = pickNumber(source.line);
|
|
45799
46578
|
const lineRule = normalizeLineRule(source.lineRule);
|
|
46579
|
+
const resolvedLineRule = lineRule ?? (lineRaw != null ? "auto" : void 0);
|
|
45800
46580
|
const beforeAutospacing = toBooleanFlag(source.beforeAutospacing ?? source.beforeAutoSpacing);
|
|
45801
46581
|
const afterAutospacing = toBooleanFlag(source.afterAutospacing ?? source.afterAutoSpacing);
|
|
45802
46582
|
const contextualSpacing = toBooleanFlag(source.contextualSpacing);
|
|
45803
46583
|
const before = beforeRaw != null ? twipsToPx$1(beforeRaw) : pickNumber(source.lineSpaceBefore);
|
|
45804
46584
|
const after = afterRaw != null ? twipsToPx$1(afterRaw) : pickNumber(source.lineSpaceAfter);
|
|
45805
|
-
const line = normalizeLineValue(lineRaw,
|
|
46585
|
+
const line = normalizeLineValue(lineRaw, resolvedLineRule);
|
|
45806
46586
|
if (before != null) spacing.before = before;
|
|
45807
46587
|
if (after != null) spacing.after = after;
|
|
45808
46588
|
if (line != null) spacing.line = line;
|
|
45809
|
-
if (
|
|
46589
|
+
if (resolvedLineRule) spacing.lineRule = resolvedLineRule;
|
|
45810
46590
|
if (beforeAutospacing != null) spacing.beforeAutospacing = beforeAutospacing;
|
|
45811
46591
|
if (afterAutospacing != null) spacing.afterAutospacing = afterAutospacing;
|
|
45812
46592
|
if (contextualSpacing != null) spacing.contextualSpacing = contextualSpacing;
|
|
@@ -55622,8 +56402,6 @@ const DEFAULT_MARGINS = { top: 72, right: 72, bottom: 72, left: 72 };
|
|
|
55622
56402
|
const DEFAULT_VIRTUALIZED_PAGE_GAP = 72;
|
|
55623
56403
|
const DEFAULT_PAGE_GAP = 24;
|
|
55624
56404
|
const DEFAULT_HORIZONTAL_PAGE_GAP = 20;
|
|
55625
|
-
const MULTI_CLICK_TIME_THRESHOLD_MS = 400;
|
|
55626
|
-
const MULTI_CLICK_DISTANCE_THRESHOLD_PX = 5;
|
|
55627
56405
|
const HEADER_FOOTER_INIT_BUDGET_MS = 200;
|
|
55628
56406
|
const MAX_ZOOM_WARNING_THRESHOLD = 10;
|
|
55629
56407
|
const MAX_SELECTION_RECTS_PER_USER = 100;
|
|
@@ -55679,7 +56457,7 @@ class PresentationEditor extends EventEmitter {
|
|
|
55679
56457
|
#layoutState = { blocks: [], measures: [], layout: null, bookmarks: /* @__PURE__ */ new Map() };
|
|
55680
56458
|
#domPainter = null;
|
|
55681
56459
|
#pageGeometryHelper = null;
|
|
55682
|
-
#
|
|
56460
|
+
#dragDropManager = null;
|
|
55683
56461
|
#layoutError = null;
|
|
55684
56462
|
#layoutErrorState = "healthy";
|
|
55685
56463
|
#errorBanner = null;
|
|
@@ -55696,9 +56474,6 @@ class PresentationEditor extends EventEmitter {
|
|
|
55696
56474
|
#htmlAnnotationMeasureAttempts = 0;
|
|
55697
56475
|
#domPositionIndex = new DomPositionIndex();
|
|
55698
56476
|
#domIndexObserverManager = null;
|
|
55699
|
-
#debugLastPointer = null;
|
|
55700
|
-
#debugLastHit = null;
|
|
55701
|
-
#pendingMarginClick = null;
|
|
55702
56477
|
#rafHandle = null;
|
|
55703
56478
|
#editorListeners = [];
|
|
55704
56479
|
#sectionMetadata = [];
|
|
@@ -55715,25 +56490,7 @@ class PresentationEditor extends EventEmitter {
|
|
|
55715
56490
|
#ariaLiveRegion = null;
|
|
55716
56491
|
#a11ySelectionAnnounceTimeout = null;
|
|
55717
56492
|
#a11yLastAnnouncedSelectionKey = null;
|
|
55718
|
-
#clickCount = 0;
|
|
55719
|
-
#lastClickTime = 0;
|
|
55720
|
-
#lastClickPosition = { x: 0, y: 0 };
|
|
55721
|
-
#lastSelectedImageBlockId = null;
|
|
55722
56493
|
#lastSelectedFieldAnnotation = null;
|
|
55723
|
-
// Drag selection state
|
|
55724
|
-
#dragAnchor = null;
|
|
55725
|
-
#dragAnchorPageIndex = null;
|
|
55726
|
-
#isDragging = false;
|
|
55727
|
-
#dragExtensionMode = "char";
|
|
55728
|
-
#dragLastPointer = null;
|
|
55729
|
-
#dragLastRawHit = null;
|
|
55730
|
-
#dragUsedPageNotMountedFallback = false;
|
|
55731
|
-
#suppressFocusInFromDraggable = false;
|
|
55732
|
-
// Cell selection drag state
|
|
55733
|
-
// Tracks cell-specific context when drag starts in a table for multi-cell selection
|
|
55734
|
-
#cellAnchor = null;
|
|
55735
|
-
/** Cell drag mode state machine: 'none' = not in table, 'pending' = in table but haven't crossed cell boundary, 'active' = crossed cell boundary */
|
|
55736
|
-
#cellDragMode = "none";
|
|
55737
56494
|
// Remote cursor/presence state management
|
|
55738
56495
|
/** Manager for remote cursor rendering and awareness subscriptions */
|
|
55739
56496
|
#remoteCursorManager = null;
|
|
@@ -55741,6 +56498,9 @@ class PresentationEditor extends EventEmitter {
|
|
|
55741
56498
|
#remoteCursorOverlay = null;
|
|
55742
56499
|
/** DOM element for rendering local selection/caret (dual-layer overlay architecture) */
|
|
55743
56500
|
#localSelectionLayer = null;
|
|
56501
|
+
// Editor input management
|
|
56502
|
+
/** Manager for pointer events, focus, drag selection, and click handling */
|
|
56503
|
+
#editorInputManager = null;
|
|
55744
56504
|
constructor(options) {
|
|
55745
56505
|
super();
|
|
55746
56506
|
if (!options?.element) {
|
|
@@ -55949,6 +56709,7 @@ class PresentationEditor extends EventEmitter {
|
|
|
55949
56709
|
this.#setupHeaderFooterSession();
|
|
55950
56710
|
this.#applyZoom();
|
|
55951
56711
|
this.#setupEditorListeners();
|
|
56712
|
+
this.#initializeEditorInputManager();
|
|
55952
56713
|
this.#setupPointerHandlers();
|
|
55953
56714
|
this.#setupDragHandlers();
|
|
55954
56715
|
this.#setupInputBridge();
|
|
@@ -56873,8 +57634,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
56873
57634
|
docEpoch: this.#epochMapper.getCurrentEpoch(),
|
|
56874
57635
|
layoutEpoch: this.#layoutEpoch,
|
|
56875
57636
|
selection,
|
|
56876
|
-
lastPointer: this.#debugLastPointer,
|
|
56877
|
-
lastHit: this.#debugLastHit
|
|
57637
|
+
lastPointer: this.#editorInputManager?.debugLastPointer ?? null,
|
|
57638
|
+
lastHit: this.#editorInputManager?.debugLastHit ?? null
|
|
56878
57639
|
});
|
|
56879
57640
|
} catch {
|
|
56880
57641
|
}
|
|
@@ -57267,15 +58028,12 @@ class PresentationEditor extends EventEmitter {
|
|
|
57267
58028
|
this.#editorListeners = [];
|
|
57268
58029
|
this.#domIndexObserverManager?.destroy();
|
|
57269
58030
|
this.#domIndexObserverManager = null;
|
|
57270
|
-
|
|
57271
|
-
|
|
57272
|
-
|
|
57273
|
-
|
|
57274
|
-
|
|
57275
|
-
|
|
57276
|
-
this.#viewportHost?.removeEventListener("drop", this.#handleDrop);
|
|
57277
|
-
this.#visibleHost?.removeEventListener("keydown", this.#handleKeyDown);
|
|
57278
|
-
this.#visibleHost?.removeEventListener("focusin", this.#handleVisibleHostFocusIn);
|
|
58031
|
+
if (this.#editorInputManager) {
|
|
58032
|
+
safeCleanup(() => {
|
|
58033
|
+
this.#editorInputManager?.destroy();
|
|
58034
|
+
this.#editorInputManager = null;
|
|
58035
|
+
}, "Editor input manager");
|
|
58036
|
+
}
|
|
57279
58037
|
this.#inputBridge?.notifyTargetChanged();
|
|
57280
58038
|
this.#inputBridge?.destroy();
|
|
57281
58039
|
this.#inputBridge = null;
|
|
@@ -57283,7 +58041,6 @@ class PresentationEditor extends EventEmitter {
|
|
|
57283
58041
|
clearTimeout(this.#a11ySelectionAnnounceTimeout);
|
|
57284
58042
|
this.#a11ySelectionAnnounceTimeout = null;
|
|
57285
58043
|
}
|
|
57286
|
-
this.#clearCellAnchor();
|
|
57287
58044
|
if (this.#options?.documentId) {
|
|
57288
58045
|
PresentationEditor.#instances.delete(this.#options.documentId);
|
|
57289
58046
|
}
|
|
@@ -57293,8 +58050,8 @@ class PresentationEditor extends EventEmitter {
|
|
|
57293
58050
|
}, "Header/footer session manager");
|
|
57294
58051
|
this.#domPainter = null;
|
|
57295
58052
|
this.#pageGeometryHelper = null;
|
|
57296
|
-
this.#
|
|
57297
|
-
this.#
|
|
58053
|
+
this.#dragDropManager?.destroy();
|
|
58054
|
+
this.#dragDropManager = null;
|
|
57298
58055
|
this.#selectionOverlay?.remove();
|
|
57299
58056
|
this.#painterHost?.remove();
|
|
57300
58057
|
this.#hiddenHost?.remove();
|
|
@@ -57341,7 +58098,7 @@ class PresentationEditor extends EventEmitter {
|
|
|
57341
58098
|
}
|
|
57342
58099
|
if (transaction?.docChanged) {
|
|
57343
58100
|
this.#updateLocalAwarenessCursor();
|
|
57344
|
-
this.#clearCellAnchor();
|
|
58101
|
+
this.#editorInputManager?.clearCellAnchor();
|
|
57345
58102
|
}
|
|
57346
58103
|
};
|
|
57347
58104
|
const handleSelection = () => {
|
|
@@ -57445,31 +58202,75 @@ class PresentationEditor extends EventEmitter {
|
|
|
57445
58202
|
#renderRemoteCursors() {
|
|
57446
58203
|
this.#remoteCursorManager?.render(this.#getRemoteCursorRenderDeps());
|
|
57447
58204
|
}
|
|
58205
|
+
/**
|
|
58206
|
+
* Initialize the EditorInputManager with dependencies and callbacks.
|
|
58207
|
+
* @private
|
|
58208
|
+
*/
|
|
58209
|
+
#initializeEditorInputManager() {
|
|
58210
|
+
this.#editorInputManager = new EditorInputManager();
|
|
58211
|
+
this.#editorInputManager.setDependencies({
|
|
58212
|
+
getActiveEditor: () => this.getActiveEditor(),
|
|
58213
|
+
getEditor: () => this.#editor,
|
|
58214
|
+
getLayoutState: () => this.#layoutState,
|
|
58215
|
+
getEpochMapper: () => this.#epochMapper,
|
|
58216
|
+
getViewportHost: () => this.#viewportHost,
|
|
58217
|
+
getVisibleHost: () => this.#visibleHost,
|
|
58218
|
+
getHeaderFooterSession: () => this.#headerFooterSession,
|
|
58219
|
+
getPageGeometryHelper: () => this.#pageGeometryHelper,
|
|
58220
|
+
getZoom: () => this.#layoutOptions.zoom ?? 1,
|
|
58221
|
+
isViewLocked: () => this.#isViewLocked(),
|
|
58222
|
+
getDocumentMode: () => this.#documentMode,
|
|
58223
|
+
getPageElement: (pageIndex) => this.#getPageElement(pageIndex),
|
|
58224
|
+
isSelectionAwareVirtualizationEnabled: () => this.#isSelectionAwareVirtualizationEnabled()
|
|
58225
|
+
});
|
|
58226
|
+
this.#editorInputManager.setCallbacks({
|
|
58227
|
+
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate(),
|
|
58228
|
+
scheduleRerender: () => this.#scheduleRerender(),
|
|
58229
|
+
setPendingDocChange: () => {
|
|
58230
|
+
this.#pendingDocChange = true;
|
|
58231
|
+
},
|
|
58232
|
+
updateSelectionVirtualizationPins: (options) => this.#updateSelectionVirtualizationPins(options),
|
|
58233
|
+
scheduleA11ySelectionAnnouncement: (options) => this.#scheduleA11ySelectionAnnouncement(options),
|
|
58234
|
+
goToAnchor: (href) => this.goToAnchor(href),
|
|
58235
|
+
emit: (event, payload) => this.emit(event, payload),
|
|
58236
|
+
normalizeClientPoint: (clientX, clientY) => this.#normalizeClientPoint(clientX, clientY),
|
|
58237
|
+
hitTestHeaderFooterRegion: (x, y2) => this.#hitTestHeaderFooterRegion(x, y2),
|
|
58238
|
+
exitHeaderFooterMode: () => this.#exitHeaderFooterMode(),
|
|
58239
|
+
activateHeaderFooterRegion: (region) => this.#activateHeaderFooterRegion(region),
|
|
58240
|
+
createDefaultHeaderFooter: (region) => this.#createDefaultHeaderFooter(region),
|
|
58241
|
+
emitHeaderFooterEditBlocked: (reason) => this.#emitHeaderFooterEditBlocked(reason),
|
|
58242
|
+
findRegionForPage: (kind, pageIndex) => this.#findRegionForPage(kind, pageIndex),
|
|
58243
|
+
getCurrentPageIndex: () => this.#getCurrentPageIndex(),
|
|
58244
|
+
resolveDescriptorForRegion: (region) => this.#resolveDescriptorForRegion(region),
|
|
58245
|
+
updateSelectionDebugHud: () => this.#updateSelectionDebugHud(),
|
|
58246
|
+
clearHoverRegion: () => this.#clearHoverRegion(),
|
|
58247
|
+
renderHoverRegion: (region) => this.#renderHoverRegion(region),
|
|
58248
|
+
focusEditorAfterImageSelection: () => this.#focusEditorAfterImageSelection(),
|
|
58249
|
+
resolveFieldAnnotationSelectionFromElement: (el) => this.#resolveFieldAnnotationSelectionFromElement(el),
|
|
58250
|
+
computePendingMarginClick: (pointerId, x, y2) => this.#computePendingMarginClick(pointerId, x, y2),
|
|
58251
|
+
selectWordAt: (pos) => this.#selectWordAt(pos),
|
|
58252
|
+
selectParagraphAt: (pos) => this.#selectParagraphAt(pos),
|
|
58253
|
+
finalizeDragSelectionWithDom: (pointer, dragAnchor, dragMode) => this.#finalizeDragSelectionWithDom(pointer, dragAnchor, dragMode),
|
|
58254
|
+
hitTestTable: (x, y2) => this.#hitTestTable(x, y2)
|
|
58255
|
+
});
|
|
58256
|
+
}
|
|
57448
58257
|
#setupPointerHandlers() {
|
|
57449
|
-
this.#
|
|
57450
|
-
this.#viewportHost.addEventListener("dblclick", this.#handleDoubleClick);
|
|
57451
|
-
this.#viewportHost.addEventListener("pointermove", this.#handlePointerMove);
|
|
57452
|
-
this.#viewportHost.addEventListener("pointerup", this.#handlePointerUp);
|
|
57453
|
-
this.#viewportHost.addEventListener("pointerleave", this.#handlePointerLeave);
|
|
57454
|
-
this.#viewportHost.addEventListener("dragover", this.#handleDragOver);
|
|
57455
|
-
this.#viewportHost.addEventListener("drop", this.#handleDrop);
|
|
57456
|
-
this.#visibleHost.addEventListener("keydown", this.#handleKeyDown);
|
|
57457
|
-
this.#visibleHost.addEventListener("focusin", this.#handleVisibleHostFocusIn);
|
|
58258
|
+
this.#editorInputManager?.bind();
|
|
57458
58259
|
}
|
|
57459
58260
|
/**
|
|
57460
|
-
* Sets up drag and drop handlers for field annotations
|
|
57461
|
-
* Uses the DragHandler from layout-bridge to handle drag events and map drop
|
|
57462
|
-
* coordinates to ProseMirror positions.
|
|
58261
|
+
* Sets up drag and drop handlers for field annotations.
|
|
57463
58262
|
*/
|
|
57464
58263
|
#setupDragHandlers() {
|
|
57465
|
-
this.#
|
|
57466
|
-
this.#
|
|
57467
|
-
this.#
|
|
57468
|
-
painterHost: this.#painterHost,
|
|
58264
|
+
this.#dragDropManager?.destroy();
|
|
58265
|
+
this.#dragDropManager = new DragDropManager();
|
|
58266
|
+
this.#dragDropManager.setDependencies({
|
|
57469
58267
|
getActiveEditor: () => this.getActiveEditor(),
|
|
57470
58268
|
hitTest: (clientX, clientY) => this.hitTest(clientX, clientY),
|
|
57471
|
-
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate()
|
|
58269
|
+
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate(),
|
|
58270
|
+
getViewportHost: () => this.#viewportHost,
|
|
58271
|
+
getPainterHost: () => this.#painterHost
|
|
57472
58272
|
});
|
|
58273
|
+
this.#dragDropManager.bind();
|
|
57473
58274
|
}
|
|
57474
58275
|
/**
|
|
57475
58276
|
* Focus the editor after image selection and schedule selection update.
|
|
@@ -57587,536 +58388,6 @@ class PresentationEditor extends EventEmitter {
|
|
|
57587
58388
|
});
|
|
57588
58389
|
this.#headerFooterSession.initialize();
|
|
57589
58390
|
}
|
|
57590
|
-
#handlePointerDown = (event) => {
|
|
57591
|
-
if (event.button !== 0) {
|
|
57592
|
-
return;
|
|
57593
|
-
}
|
|
57594
|
-
if (event.ctrlKey && navigator.platform.includes("Mac")) {
|
|
57595
|
-
return;
|
|
57596
|
-
}
|
|
57597
|
-
this.#pendingMarginClick = null;
|
|
57598
|
-
const target = event.target;
|
|
57599
|
-
if (target?.closest?.(".superdoc-ruler-handle") != null) {
|
|
57600
|
-
return;
|
|
57601
|
-
}
|
|
57602
|
-
const linkEl = target?.closest?.("a.superdoc-link");
|
|
57603
|
-
if (linkEl) {
|
|
57604
|
-
const href = linkEl.getAttribute("href") ?? "";
|
|
57605
|
-
const isAnchorLink = href.startsWith("#") && href.length > 1;
|
|
57606
|
-
const isTocLink = linkEl.closest(".superdoc-toc-entry") !== null;
|
|
57607
|
-
if (isAnchorLink && isTocLink) {
|
|
57608
|
-
event.preventDefault();
|
|
57609
|
-
event.stopPropagation();
|
|
57610
|
-
this.goToAnchor(href);
|
|
57611
|
-
return;
|
|
57612
|
-
}
|
|
57613
|
-
event.preventDefault();
|
|
57614
|
-
event.stopPropagation();
|
|
57615
|
-
const linkClickEvent = new CustomEvent("superdoc-link-click", {
|
|
57616
|
-
bubbles: true,
|
|
57617
|
-
composed: true,
|
|
57618
|
-
detail: {
|
|
57619
|
-
href,
|
|
57620
|
-
target: linkEl.getAttribute("target"),
|
|
57621
|
-
rel: linkEl.getAttribute("rel"),
|
|
57622
|
-
tooltip: linkEl.getAttribute("title"),
|
|
57623
|
-
element: linkEl,
|
|
57624
|
-
clientX: event.clientX,
|
|
57625
|
-
clientY: event.clientY
|
|
57626
|
-
}
|
|
57627
|
-
});
|
|
57628
|
-
linkEl.dispatchEvent(linkClickEvent);
|
|
57629
|
-
return;
|
|
57630
|
-
}
|
|
57631
|
-
const annotationEl = target?.closest?.(".annotation[data-pm-start]");
|
|
57632
|
-
const isDraggableAnnotation = target?.closest?.('[data-draggable="true"]') != null;
|
|
57633
|
-
this.#suppressFocusInFromDraggable = isDraggableAnnotation;
|
|
57634
|
-
if (annotationEl) {
|
|
57635
|
-
if (!this.#editor.isEditable) {
|
|
57636
|
-
return;
|
|
57637
|
-
}
|
|
57638
|
-
const resolved = this.#resolveFieldAnnotationSelectionFromElement(annotationEl);
|
|
57639
|
-
if (resolved) {
|
|
57640
|
-
try {
|
|
57641
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.NodeSelection.create(this.#editor.state.doc, resolved.pos));
|
|
57642
|
-
this.#editor.view?.dispatch(tr);
|
|
57643
|
-
} catch {
|
|
57644
|
-
}
|
|
57645
|
-
this.#editor.emit("fieldAnnotationClicked", {
|
|
57646
|
-
editor: this.#editor,
|
|
57647
|
-
node: resolved.node,
|
|
57648
|
-
nodePos: resolved.pos,
|
|
57649
|
-
event,
|
|
57650
|
-
currentTarget: annotationEl
|
|
57651
|
-
});
|
|
57652
|
-
}
|
|
57653
|
-
return;
|
|
57654
|
-
}
|
|
57655
|
-
if (!this.#layoutState.layout) {
|
|
57656
|
-
if (!isDraggableAnnotation) {
|
|
57657
|
-
event.preventDefault();
|
|
57658
|
-
}
|
|
57659
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
57660
|
-
document.activeElement.blur();
|
|
57661
|
-
}
|
|
57662
|
-
const editorDom2 = this.#editor.view?.dom;
|
|
57663
|
-
if (!editorDom2) {
|
|
57664
|
-
return;
|
|
57665
|
-
}
|
|
57666
|
-
const validPos = this.#getFirstTextPosition();
|
|
57667
|
-
const doc22 = this.#editor?.state?.doc;
|
|
57668
|
-
if (doc22) {
|
|
57669
|
-
try {
|
|
57670
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc22, validPos));
|
|
57671
|
-
this.#editor.view?.dispatch(tr);
|
|
57672
|
-
} catch (error) {
|
|
57673
|
-
if (vue.process$1.env.NODE_ENV === "development") {
|
|
57674
|
-
console.warn("[PresentationEditor] Failed to set selection to first text position:", error);
|
|
57675
|
-
}
|
|
57676
|
-
}
|
|
57677
|
-
}
|
|
57678
|
-
editorDom2.focus();
|
|
57679
|
-
this.#editor.view?.focus();
|
|
57680
|
-
this.#scheduleSelectionUpdate();
|
|
57681
|
-
return;
|
|
57682
|
-
}
|
|
57683
|
-
const normalizedPoint = this.#normalizeClientPoint(event.clientX, event.clientY);
|
|
57684
|
-
if (!normalizedPoint) {
|
|
57685
|
-
return;
|
|
57686
|
-
}
|
|
57687
|
-
const { x, y: y2 } = normalizedPoint;
|
|
57688
|
-
this.#debugLastPointer = { clientX: event.clientX, clientY: event.clientY, x, y: y2 };
|
|
57689
|
-
const sessionMode = this.#headerFooterSession?.session?.mode ?? "body";
|
|
57690
|
-
if (sessionMode !== "body") {
|
|
57691
|
-
const activeEditorHost = this.#headerFooterSession?.overlayManager?.getActiveEditorHost?.();
|
|
57692
|
-
const clickedInsideEditorHost = activeEditorHost && (activeEditorHost.contains(event.target) || activeEditorHost === event.target);
|
|
57693
|
-
if (clickedInsideEditorHost) {
|
|
57694
|
-
return;
|
|
57695
|
-
}
|
|
57696
|
-
const headerFooterRegion2 = this.#hitTestHeaderFooterRegion(x, y2);
|
|
57697
|
-
if (!headerFooterRegion2) {
|
|
57698
|
-
this.#exitHeaderFooterMode();
|
|
57699
|
-
} else {
|
|
57700
|
-
return;
|
|
57701
|
-
}
|
|
57702
|
-
}
|
|
57703
|
-
const headerFooterRegion = this.#hitTestHeaderFooterRegion(x, y2);
|
|
57704
|
-
if (headerFooterRegion) {
|
|
57705
|
-
return;
|
|
57706
|
-
}
|
|
57707
|
-
const rawHit = clickToPosition(
|
|
57708
|
-
this.#layoutState.layout,
|
|
57709
|
-
this.#layoutState.blocks,
|
|
57710
|
-
this.#layoutState.measures,
|
|
57711
|
-
{ x, y: y2 },
|
|
57712
|
-
this.#viewportHost,
|
|
57713
|
-
event.clientX,
|
|
57714
|
-
event.clientY,
|
|
57715
|
-
this.#pageGeometryHelper ?? void 0
|
|
57716
|
-
);
|
|
57717
|
-
const doc2 = this.#editor.state?.doc;
|
|
57718
|
-
const mapped = rawHit && doc2 ? this.#epochMapper.mapPosFromLayoutToCurrentDetailed(rawHit.pos, rawHit.layoutEpoch, 1) : null;
|
|
57719
|
-
if (mapped && !mapped.ok) {
|
|
57720
|
-
debugLog("warn", "pointerdown mapping failed", mapped);
|
|
57721
|
-
}
|
|
57722
|
-
const hit = rawHit && doc2 && mapped?.ok ? { ...rawHit, pos: Math.max(0, Math.min(mapped.pos, doc2.content.size)), layoutEpoch: mapped.toEpoch } : null;
|
|
57723
|
-
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 };
|
|
57724
|
-
this.#updateSelectionDebugHud();
|
|
57725
|
-
if (!isDraggableAnnotation) {
|
|
57726
|
-
event.preventDefault();
|
|
57727
|
-
}
|
|
57728
|
-
if (!rawHit) {
|
|
57729
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
57730
|
-
document.activeElement.blur();
|
|
57731
|
-
}
|
|
57732
|
-
const editorDom2 = this.#editor.view?.dom;
|
|
57733
|
-
if (editorDom2) {
|
|
57734
|
-
const validPos = this.#getFirstTextPosition();
|
|
57735
|
-
const doc22 = this.#editor?.state?.doc;
|
|
57736
|
-
if (doc22) {
|
|
57737
|
-
try {
|
|
57738
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc22, validPos));
|
|
57739
|
-
this.#editor.view?.dispatch(tr);
|
|
57740
|
-
} catch (error) {
|
|
57741
|
-
if (vue.process$1.env.NODE_ENV === "development") {
|
|
57742
|
-
console.warn("[PresentationEditor] Failed to set selection to first text position:", error);
|
|
57743
|
-
}
|
|
57744
|
-
}
|
|
57745
|
-
}
|
|
57746
|
-
editorDom2.focus();
|
|
57747
|
-
this.#editor.view?.focus();
|
|
57748
|
-
this.#scheduleSelectionUpdate();
|
|
57749
|
-
}
|
|
57750
|
-
return;
|
|
57751
|
-
}
|
|
57752
|
-
if (!hit || !doc2) {
|
|
57753
|
-
this.#pendingDocChange = true;
|
|
57754
|
-
this.#scheduleRerender();
|
|
57755
|
-
return;
|
|
57756
|
-
}
|
|
57757
|
-
const fragmentHit = getFragmentAtPosition(
|
|
57758
|
-
this.#layoutState.layout,
|
|
57759
|
-
this.#layoutState.blocks,
|
|
57760
|
-
this.#layoutState.measures,
|
|
57761
|
-
rawHit.pos
|
|
57762
|
-
);
|
|
57763
|
-
const targetImg = event.target?.closest?.("img");
|
|
57764
|
-
const imgPmStart = targetImg?.dataset?.pmStart ? Number(targetImg.dataset.pmStart) : null;
|
|
57765
|
-
if (!Number.isNaN(imgPmStart) && imgPmStart != null) {
|
|
57766
|
-
const doc22 = this.#editor.state.doc;
|
|
57767
|
-
const imgLayoutEpochRaw = targetImg?.dataset?.layoutEpoch;
|
|
57768
|
-
const imgLayoutEpoch = imgLayoutEpochRaw != null ? Number(imgLayoutEpochRaw) : NaN;
|
|
57769
|
-
const rawLayoutEpoch = Number.isFinite(rawHit.layoutEpoch) ? rawHit.layoutEpoch : NaN;
|
|
57770
|
-
const effectiveEpoch = Number.isFinite(imgLayoutEpoch) && Number.isFinite(rawLayoutEpoch) ? Math.max(imgLayoutEpoch, rawLayoutEpoch) : Number.isFinite(imgLayoutEpoch) ? imgLayoutEpoch : rawHit.layoutEpoch;
|
|
57771
|
-
const mappedImg = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(imgPmStart, effectiveEpoch, 1);
|
|
57772
|
-
if (!mappedImg.ok) {
|
|
57773
|
-
debugLog("warn", "inline image mapping failed", mappedImg);
|
|
57774
|
-
this.#pendingDocChange = true;
|
|
57775
|
-
this.#scheduleRerender();
|
|
57776
|
-
return;
|
|
57777
|
-
}
|
|
57778
|
-
const clampedImgPos = Math.max(0, Math.min(mappedImg.pos, doc22.content.size));
|
|
57779
|
-
if (clampedImgPos < 0 || clampedImgPos >= doc22.content.size) {
|
|
57780
|
-
if (vue.process$1.env.NODE_ENV === "development") {
|
|
57781
|
-
console.warn(
|
|
57782
|
-
`[PresentationEditor] Invalid position ${clampedImgPos} for inline image (document size: ${doc22.content.size})`
|
|
57783
|
-
);
|
|
57784
|
-
}
|
|
57785
|
-
return;
|
|
57786
|
-
}
|
|
57787
|
-
const newSelectionId = `inline-${clampedImgPos}`;
|
|
57788
|
-
if (this.#lastSelectedImageBlockId && this.#lastSelectedImageBlockId !== newSelectionId) {
|
|
57789
|
-
this.emit("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
57790
|
-
}
|
|
57791
|
-
try {
|
|
57792
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.NodeSelection.create(doc22, clampedImgPos));
|
|
57793
|
-
this.#editor.view?.dispatch(tr);
|
|
57794
|
-
const selector = `.superdoc-inline-image[data-pm-start="${imgPmStart}"]`;
|
|
57795
|
-
const targetElement = this.#viewportHost.querySelector(selector);
|
|
57796
|
-
this.emit("imageSelected", {
|
|
57797
|
-
element: targetElement ?? targetImg,
|
|
57798
|
-
blockId: null,
|
|
57799
|
-
pmStart: clampedImgPos
|
|
57800
|
-
});
|
|
57801
|
-
this.#lastSelectedImageBlockId = newSelectionId;
|
|
57802
|
-
} catch (error) {
|
|
57803
|
-
if (vue.process$1.env.NODE_ENV === "development") {
|
|
57804
|
-
console.warn(
|
|
57805
|
-
`[PresentationEditor] Failed to create NodeSelection for inline image at position ${imgPmStart}:`,
|
|
57806
|
-
error
|
|
57807
|
-
);
|
|
57808
|
-
}
|
|
57809
|
-
}
|
|
57810
|
-
this.#focusEditorAfterImageSelection();
|
|
57811
|
-
return;
|
|
57812
|
-
}
|
|
57813
|
-
if (fragmentHit && (fragmentHit.fragment.kind === "image" || fragmentHit.fragment.kind === "drawing")) {
|
|
57814
|
-
const doc22 = this.#editor.state.doc;
|
|
57815
|
-
try {
|
|
57816
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.NodeSelection.create(doc22, hit.pos));
|
|
57817
|
-
this.#editor.view?.dispatch(tr);
|
|
57818
|
-
if (this.#lastSelectedImageBlockId && this.#lastSelectedImageBlockId !== fragmentHit.fragment.blockId) {
|
|
57819
|
-
this.emit("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
57820
|
-
}
|
|
57821
|
-
if (fragmentHit.fragment.kind === "image") {
|
|
57822
|
-
const targetElement = this.#viewportHost.querySelector(
|
|
57823
|
-
`.superdoc-image-fragment[data-pm-start="${fragmentHit.fragment.pmStart}"]`
|
|
57824
|
-
);
|
|
57825
|
-
if (targetElement) {
|
|
57826
|
-
this.emit("imageSelected", {
|
|
57827
|
-
element: targetElement,
|
|
57828
|
-
blockId: fragmentHit.fragment.blockId,
|
|
57829
|
-
pmStart: fragmentHit.fragment.pmStart
|
|
57830
|
-
});
|
|
57831
|
-
this.#lastSelectedImageBlockId = fragmentHit.fragment.blockId;
|
|
57832
|
-
}
|
|
57833
|
-
}
|
|
57834
|
-
} catch (error) {
|
|
57835
|
-
if (vue.process$1.env.NODE_ENV === "development") {
|
|
57836
|
-
console.warn("[PresentationEditor] Failed to create NodeSelection for atomic fragment:", error);
|
|
57837
|
-
}
|
|
57838
|
-
}
|
|
57839
|
-
this.#focusEditorAfterImageSelection();
|
|
57840
|
-
return;
|
|
57841
|
-
}
|
|
57842
|
-
if (this.#lastSelectedImageBlockId) {
|
|
57843
|
-
this.emit("imageDeselected", { blockId: this.#lastSelectedImageBlockId });
|
|
57844
|
-
this.#lastSelectedImageBlockId = null;
|
|
57845
|
-
}
|
|
57846
|
-
if (event.shiftKey && this.#editor.state.selection.$anchor) {
|
|
57847
|
-
const anchor = this.#editor.state.selection.anchor;
|
|
57848
|
-
const head = hit.pos;
|
|
57849
|
-
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, head, this.#dragExtensionMode);
|
|
57850
|
-
try {
|
|
57851
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.TextSelection.create(this.#editor.state.doc, selAnchor, selHead));
|
|
57852
|
-
this.#editor.view?.dispatch(tr);
|
|
57853
|
-
this.#scheduleSelectionUpdate();
|
|
57854
|
-
} catch (error) {
|
|
57855
|
-
console.warn("[SELECTION] Failed to extend selection on shift+click:", {
|
|
57856
|
-
error,
|
|
57857
|
-
anchor,
|
|
57858
|
-
head,
|
|
57859
|
-
selAnchor,
|
|
57860
|
-
selHead,
|
|
57861
|
-
mode: this.#dragExtensionMode
|
|
57862
|
-
});
|
|
57863
|
-
}
|
|
57864
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
57865
|
-
document.activeElement.blur();
|
|
57866
|
-
}
|
|
57867
|
-
const editorDom2 = this.#editor.view?.dom;
|
|
57868
|
-
if (editorDom2) {
|
|
57869
|
-
editorDom2.focus();
|
|
57870
|
-
this.#editor.view?.focus();
|
|
57871
|
-
}
|
|
57872
|
-
return;
|
|
57873
|
-
}
|
|
57874
|
-
const clickDepth = this.#registerPointerClick(event);
|
|
57875
|
-
if (clickDepth === 1) {
|
|
57876
|
-
this.#dragAnchor = hit.pos;
|
|
57877
|
-
this.#dragAnchorPageIndex = hit.pageIndex;
|
|
57878
|
-
this.#pendingMarginClick = this.#computePendingMarginClick(event.pointerId, x, y2);
|
|
57879
|
-
const tableHit = this.#hitTestTable(x, y2);
|
|
57880
|
-
if (tableHit) {
|
|
57881
|
-
const tablePos = this.#getTablePosFromHit(tableHit);
|
|
57882
|
-
if (tablePos !== null) {
|
|
57883
|
-
this.#setCellAnchor(tableHit, tablePos);
|
|
57884
|
-
}
|
|
57885
|
-
} else {
|
|
57886
|
-
this.#clearCellAnchor();
|
|
57887
|
-
}
|
|
57888
|
-
} else {
|
|
57889
|
-
this.#pendingMarginClick = null;
|
|
57890
|
-
}
|
|
57891
|
-
this.#dragLastPointer = { clientX: event.clientX, clientY: event.clientY, x, y: y2 };
|
|
57892
|
-
this.#dragLastRawHit = hit;
|
|
57893
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
57894
|
-
this.#isDragging = true;
|
|
57895
|
-
if (clickDepth >= 3) {
|
|
57896
|
-
this.#dragExtensionMode = "para";
|
|
57897
|
-
} else if (clickDepth === 2) {
|
|
57898
|
-
this.#dragExtensionMode = "word";
|
|
57899
|
-
} else {
|
|
57900
|
-
this.#dragExtensionMode = "char";
|
|
57901
|
-
}
|
|
57902
|
-
debugLog(
|
|
57903
|
-
"verbose",
|
|
57904
|
-
`Drag selection start ${JSON.stringify({
|
|
57905
|
-
pointer: { clientX: event.clientX, clientY: event.clientY, x, y: y2 },
|
|
57906
|
-
clickDepth,
|
|
57907
|
-
extensionMode: this.#dragExtensionMode,
|
|
57908
|
-
anchor: this.#dragAnchor,
|
|
57909
|
-
anchorPageIndex: this.#dragAnchorPageIndex,
|
|
57910
|
-
rawHit: rawHit ? {
|
|
57911
|
-
pos: rawHit.pos,
|
|
57912
|
-
pageIndex: rawHit.pageIndex,
|
|
57913
|
-
blockId: rawHit.blockId,
|
|
57914
|
-
lineIndex: rawHit.lineIndex,
|
|
57915
|
-
layoutEpoch: rawHit.layoutEpoch
|
|
57916
|
-
} : null,
|
|
57917
|
-
mapped: mapped ? mapped.ok ? { ok: true, pos: mapped.pos, fromEpoch: mapped.fromEpoch, toEpoch: mapped.toEpoch } : {
|
|
57918
|
-
ok: false,
|
|
57919
|
-
reason: mapped.reason,
|
|
57920
|
-
fromEpoch: mapped.fromEpoch,
|
|
57921
|
-
toEpoch: mapped.toEpoch
|
|
57922
|
-
} : null,
|
|
57923
|
-
hit: hit ? { pos: hit.pos, pageIndex: hit.pageIndex, layoutEpoch: hit.layoutEpoch } : null
|
|
57924
|
-
})}`
|
|
57925
|
-
);
|
|
57926
|
-
if (typeof this.#viewportHost.setPointerCapture === "function") {
|
|
57927
|
-
this.#viewportHost.setPointerCapture(event.pointerId);
|
|
57928
|
-
}
|
|
57929
|
-
let handledByDepth = false;
|
|
57930
|
-
const sessionModeForDepth = this.#headerFooterSession?.session?.mode ?? "body";
|
|
57931
|
-
if (sessionModeForDepth === "body") {
|
|
57932
|
-
const selectionPos = clickDepth >= 2 && this.#dragAnchor !== null ? this.#dragAnchor : hit.pos;
|
|
57933
|
-
if (clickDepth >= 3) {
|
|
57934
|
-
handledByDepth = this.#selectParagraphAt(selectionPos);
|
|
57935
|
-
} else if (clickDepth === 2) {
|
|
57936
|
-
handledByDepth = this.#selectWordAt(selectionPos);
|
|
57937
|
-
}
|
|
57938
|
-
}
|
|
57939
|
-
if (!handledByDepth) {
|
|
57940
|
-
try {
|
|
57941
|
-
const doc22 = this.#editor.state.doc;
|
|
57942
|
-
let nextSelection = superEditor_converter.TextSelection.create(doc22, hit.pos);
|
|
57943
|
-
if (!nextSelection.$from.parent.inlineContent) {
|
|
57944
|
-
nextSelection = superEditor_converter.Selection.near(doc22.resolve(hit.pos), 1);
|
|
57945
|
-
}
|
|
57946
|
-
const tr = this.#editor.state.tr.setSelection(nextSelection);
|
|
57947
|
-
this.#editor.view?.dispatch(tr);
|
|
57948
|
-
} catch {
|
|
57949
|
-
}
|
|
57950
|
-
}
|
|
57951
|
-
this.#scheduleSelectionUpdate();
|
|
57952
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
57953
|
-
document.activeElement.blur();
|
|
57954
|
-
}
|
|
57955
|
-
const editorDom = this.#editor.view?.dom;
|
|
57956
|
-
if (!editorDom) {
|
|
57957
|
-
return;
|
|
57958
|
-
}
|
|
57959
|
-
editorDom.focus();
|
|
57960
|
-
this.#editor.view?.focus();
|
|
57961
|
-
};
|
|
57962
|
-
/**
|
|
57963
|
-
* Finds the first valid text position in the document.
|
|
57964
|
-
*
|
|
57965
|
-
* Traverses the document tree to locate the first textblock node (paragraph, heading, etc.)
|
|
57966
|
-
* and returns a position inside it. This is used when focusing the editor but no specific
|
|
57967
|
-
* position is available (e.g., clicking outside text content or before layout is ready).
|
|
57968
|
-
*
|
|
57969
|
-
* @returns The position inside the first textblock, or 1 if no textblock is found
|
|
57970
|
-
* @private
|
|
57971
|
-
*/
|
|
57972
|
-
#getFirstTextPosition() {
|
|
57973
|
-
return getFirstTextPosition(this.#editor?.state?.doc ?? null);
|
|
57974
|
-
}
|
|
57975
|
-
/**
|
|
57976
|
-
* Registers a pointer click event and tracks multi-click sequences (double, triple).
|
|
57977
|
-
*
|
|
57978
|
-
* This method implements multi-click detection by tracking the timing and position
|
|
57979
|
-
* of consecutive clicks. Clicks within 400ms and 5px of each other increment the
|
|
57980
|
-
* click count, up to a maximum of 3 (single, double, triple).
|
|
57981
|
-
*
|
|
57982
|
-
* @param event - The mouse event from the pointer down handler
|
|
57983
|
-
* @returns The current click count (1 = single, 2 = double, 3 = triple)
|
|
57984
|
-
* @private
|
|
57985
|
-
*/
|
|
57986
|
-
#registerPointerClick(event) {
|
|
57987
|
-
const nextState = registerPointerClick(
|
|
57988
|
-
event,
|
|
57989
|
-
{ clickCount: this.#clickCount, lastClickTime: this.#lastClickTime, lastClickPosition: this.#lastClickPosition },
|
|
57990
|
-
{
|
|
57991
|
-
timeThresholdMs: MULTI_CLICK_TIME_THRESHOLD_MS,
|
|
57992
|
-
distanceThresholdPx: MULTI_CLICK_DISTANCE_THRESHOLD_PX,
|
|
57993
|
-
maxClickCount: 3
|
|
57994
|
-
}
|
|
57995
|
-
);
|
|
57996
|
-
this.#clickCount = nextState.clickCount;
|
|
57997
|
-
this.#lastClickTime = nextState.lastClickTime;
|
|
57998
|
-
this.#lastClickPosition = nextState.lastClickPosition;
|
|
57999
|
-
return nextState.clickCount;
|
|
58000
|
-
}
|
|
58001
|
-
// ============================================================================
|
|
58002
|
-
// Cell Selection Utilities
|
|
58003
|
-
// ============================================================================
|
|
58004
|
-
/**
|
|
58005
|
-
* Gets the ProseMirror position at the start of a table cell from a table hit result.
|
|
58006
|
-
*
|
|
58007
|
-
* This method navigates the ProseMirror document structure to find the exact position where
|
|
58008
|
-
* a table cell begins. The position returned is suitable for use with CellSelection.create().
|
|
58009
|
-
*
|
|
58010
|
-
* Algorithm:
|
|
58011
|
-
* 1. Validate input (tableHit structure and cell indices)
|
|
58012
|
-
* 2. Traverse document to find the table node matching tableHit.block.id
|
|
58013
|
-
* 3. Navigate through table structure (table > row > cell) to target row
|
|
58014
|
-
* 4. Track logical column position accounting for colspan (handles merged cells)
|
|
58015
|
-
* 5. Return position when target column falls within a cell's span
|
|
58016
|
-
*
|
|
58017
|
-
* Merged cell handling:
|
|
58018
|
-
* - Does NOT assume 1:1 mapping between cell index and logical column
|
|
58019
|
-
* - Tracks cumulative logical column position by summing colspan values
|
|
58020
|
-
* - A cell with colspan=3 occupies logical columns [n, n+1, n+2]
|
|
58021
|
-
* - Finds the cell whose logical span contains the target column index
|
|
58022
|
-
*
|
|
58023
|
-
* Error handling:
|
|
58024
|
-
* - Input validation with console warnings for debugging
|
|
58025
|
-
* - Try-catch around document traversal (catches corrupted document errors)
|
|
58026
|
-
* - Bounds checking for row indices
|
|
58027
|
-
* - Null checks at each navigation step
|
|
58028
|
-
*
|
|
58029
|
-
* @param tableHit - The table hit result from hitTestTableFragment containing:
|
|
58030
|
-
* - block: TableBlock with the table's block ID
|
|
58031
|
-
* - cellRowIndex: 0-based row index of the target cell
|
|
58032
|
-
* - cellColIndex: 0-based logical column index of the target cell
|
|
58033
|
-
* @returns The PM position at the start of the cell, or null if:
|
|
58034
|
-
* - Invalid input (null tableHit, negative indices)
|
|
58035
|
-
* - Table not found in document
|
|
58036
|
-
* - Target row out of bounds
|
|
58037
|
-
* - Target column not found in row
|
|
58038
|
-
* - Document traversal error
|
|
58039
|
-
* @private
|
|
58040
|
-
*
|
|
58041
|
-
* @throws Never throws - all errors are caught and logged, returns null on failure
|
|
58042
|
-
*/
|
|
58043
|
-
#getCellPosFromTableHit(tableHit) {
|
|
58044
|
-
return getCellPosFromTableHit(tableHit, this.#editor.state?.doc ?? null, this.#layoutState.blocks);
|
|
58045
|
-
}
|
|
58046
|
-
/**
|
|
58047
|
-
* Gets the table position (start of table node) from a table hit result.
|
|
58048
|
-
*
|
|
58049
|
-
* @param tableHit - The table hit result from hitTestTableFragment
|
|
58050
|
-
* @returns The PM position at the start of the table, or null if not found
|
|
58051
|
-
* @private
|
|
58052
|
-
*/
|
|
58053
|
-
#getTablePosFromHit(tableHit) {
|
|
58054
|
-
return getTablePosFromHit(tableHit, this.#editor.state?.doc ?? null, this.#layoutState.blocks);
|
|
58055
|
-
}
|
|
58056
|
-
/**
|
|
58057
|
-
* Determines if the current drag should create a CellSelection instead of TextSelection.
|
|
58058
|
-
*
|
|
58059
|
-
* Implements a state machine for table cell selection:
|
|
58060
|
-
* - 'none': Not in a table, use TextSelection
|
|
58061
|
-
* - 'pending': Started drag in a table, but haven't crossed cell boundary yet
|
|
58062
|
-
* - 'active': Crossed cell boundary, use CellSelection
|
|
58063
|
-
*
|
|
58064
|
-
* State transitions:
|
|
58065
|
-
* - none → pending: When drag starts in a table cell (#setCellAnchor)
|
|
58066
|
-
* - pending → active: When drag crosses into a different cell (this method returns true)
|
|
58067
|
-
* - active → none: When drag ends (#clearCellAnchor)
|
|
58068
|
-
* - * → none: When document changes or clicking outside table
|
|
58069
|
-
*
|
|
58070
|
-
* Decision logic:
|
|
58071
|
-
* 1. No cell anchor → false (not in table drag mode)
|
|
58072
|
-
* 2. Current position outside table → return current state (stay in 'active' if already there)
|
|
58073
|
-
* 3. Different table → treat as outside table
|
|
58074
|
-
* 4. Different cell in same table → true (activate cell selection)
|
|
58075
|
-
* 5. Same cell → return current state (stay in 'active' if already there, else false)
|
|
58076
|
-
*
|
|
58077
|
-
* This state machine ensures:
|
|
58078
|
-
* - Text selection works normally within a single cell
|
|
58079
|
-
* - Cell selection activates smoothly when crossing cell boundaries
|
|
58080
|
-
* - Once activated, cell selection persists even if dragging back to anchor cell
|
|
58081
|
-
*
|
|
58082
|
-
* @param currentTableHit - The table hit result for the current pointer position, or null if not in a table
|
|
58083
|
-
* @returns true if we should create a CellSelection, false for TextSelection
|
|
58084
|
-
* @private
|
|
58085
|
-
*/
|
|
58086
|
-
#shouldUseCellSelection(currentTableHit) {
|
|
58087
|
-
return shouldUseCellSelection(currentTableHit, this.#cellAnchor, this.#cellDragMode);
|
|
58088
|
-
}
|
|
58089
|
-
/**
|
|
58090
|
-
* Stores the cell anchor when a drag operation starts inside a table cell.
|
|
58091
|
-
*
|
|
58092
|
-
* @param tableHit - The table hit result for the initial click position
|
|
58093
|
-
* @param tablePos - The PM position of the table node
|
|
58094
|
-
* @private
|
|
58095
|
-
*/
|
|
58096
|
-
#setCellAnchor(tableHit, tablePos) {
|
|
58097
|
-
const cellPos = this.#getCellPosFromTableHit(tableHit);
|
|
58098
|
-
if (cellPos === null) {
|
|
58099
|
-
return;
|
|
58100
|
-
}
|
|
58101
|
-
this.#cellAnchor = {
|
|
58102
|
-
tablePos,
|
|
58103
|
-
cellPos,
|
|
58104
|
-
cellRowIndex: tableHit.cellRowIndex,
|
|
58105
|
-
cellColIndex: tableHit.cellColIndex,
|
|
58106
|
-
tableBlockId: tableHit.block.id
|
|
58107
|
-
};
|
|
58108
|
-
this.#cellDragMode = "pending";
|
|
58109
|
-
}
|
|
58110
|
-
/**
|
|
58111
|
-
* Clears the cell drag state.
|
|
58112
|
-
* Called when drag ends or when clicking outside a table.
|
|
58113
|
-
*
|
|
58114
|
-
* @private
|
|
58115
|
-
*/
|
|
58116
|
-
#clearCellAnchor() {
|
|
58117
|
-
this.#cellAnchor = null;
|
|
58118
|
-
this.#cellDragMode = "none";
|
|
58119
|
-
}
|
|
58120
58391
|
/**
|
|
58121
58392
|
* Attempts to perform a table hit test for the given normalized coordinates.
|
|
58122
58393
|
*
|
|
@@ -58227,415 +58498,6 @@ class PresentationEditor extends EventEmitter {
|
|
|
58227
58498
|
#calculateExtendedSelection(anchor, head, mode) {
|
|
58228
58499
|
return calculateExtendedSelection(this.#layoutState.blocks, anchor, head, mode);
|
|
58229
58500
|
}
|
|
58230
|
-
#handlePointerMove = (event) => {
|
|
58231
|
-
if (!this.#layoutState.layout) return;
|
|
58232
|
-
const normalized = this.#normalizeClientPoint(event.clientX, event.clientY);
|
|
58233
|
-
if (!normalized) return;
|
|
58234
|
-
if (this.#isDragging && this.#dragAnchor !== null && event.buttons & 1) {
|
|
58235
|
-
this.#pendingMarginClick = null;
|
|
58236
|
-
const prevPointer = this.#dragLastPointer;
|
|
58237
|
-
const prevRawHit = this.#dragLastRawHit;
|
|
58238
|
-
this.#dragLastPointer = { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y };
|
|
58239
|
-
const rawHit = clickToPosition(
|
|
58240
|
-
this.#layoutState.layout,
|
|
58241
|
-
this.#layoutState.blocks,
|
|
58242
|
-
this.#layoutState.measures,
|
|
58243
|
-
{ x: normalized.x, y: normalized.y },
|
|
58244
|
-
this.#viewportHost,
|
|
58245
|
-
event.clientX,
|
|
58246
|
-
event.clientY,
|
|
58247
|
-
this.#pageGeometryHelper ?? void 0
|
|
58248
|
-
);
|
|
58249
|
-
if (!rawHit) {
|
|
58250
|
-
debugLog(
|
|
58251
|
-
"verbose",
|
|
58252
|
-
`Drag selection update (no hit) ${JSON.stringify({
|
|
58253
|
-
pointer: { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y },
|
|
58254
|
-
prevPointer,
|
|
58255
|
-
anchor: this.#dragAnchor
|
|
58256
|
-
})}`
|
|
58257
|
-
);
|
|
58258
|
-
return;
|
|
58259
|
-
}
|
|
58260
|
-
const doc2 = this.#editor.state?.doc;
|
|
58261
|
-
if (!doc2) return;
|
|
58262
|
-
this.#dragLastRawHit = rawHit;
|
|
58263
|
-
const pageMounted = this.#getPageElement(rawHit.pageIndex) != null;
|
|
58264
|
-
if (!pageMounted && this.#isSelectionAwareVirtualizationEnabled()) {
|
|
58265
|
-
this.#dragUsedPageNotMountedFallback = true;
|
|
58266
|
-
debugLog("warn", "Geometry fallback", { reason: "page_not_mounted", pageIndex: rawHit.pageIndex });
|
|
58267
|
-
}
|
|
58268
|
-
this.#updateSelectionVirtualizationPins({ includeDragBuffer: true, extraPages: [rawHit.pageIndex] });
|
|
58269
|
-
const mappedHead = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(rawHit.pos, rawHit.layoutEpoch, 1);
|
|
58270
|
-
if (!mappedHead.ok) {
|
|
58271
|
-
debugLog("warn", "drag mapping failed", mappedHead);
|
|
58272
|
-
debugLog(
|
|
58273
|
-
"verbose",
|
|
58274
|
-
`Drag selection update (map failed) ${JSON.stringify({
|
|
58275
|
-
pointer: { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y },
|
|
58276
|
-
prevPointer,
|
|
58277
|
-
anchor: this.#dragAnchor,
|
|
58278
|
-
rawHit: {
|
|
58279
|
-
pos: rawHit.pos,
|
|
58280
|
-
pageIndex: rawHit.pageIndex,
|
|
58281
|
-
blockId: rawHit.blockId,
|
|
58282
|
-
lineIndex: rawHit.lineIndex,
|
|
58283
|
-
layoutEpoch: rawHit.layoutEpoch
|
|
58284
|
-
},
|
|
58285
|
-
mapped: {
|
|
58286
|
-
ok: false,
|
|
58287
|
-
reason: mappedHead.reason,
|
|
58288
|
-
fromEpoch: mappedHead.fromEpoch,
|
|
58289
|
-
toEpoch: mappedHead.toEpoch
|
|
58290
|
-
}
|
|
58291
|
-
})}`
|
|
58292
|
-
);
|
|
58293
|
-
return;
|
|
58294
|
-
}
|
|
58295
|
-
const hit = {
|
|
58296
|
-
...rawHit,
|
|
58297
|
-
pos: Math.max(0, Math.min(mappedHead.pos, doc2.content.size)),
|
|
58298
|
-
layoutEpoch: mappedHead.toEpoch
|
|
58299
|
-
};
|
|
58300
|
-
this.#debugLastHit = {
|
|
58301
|
-
source: pageMounted ? "dom" : "geometry",
|
|
58302
|
-
pos: rawHit.pos,
|
|
58303
|
-
layoutEpoch: rawHit.layoutEpoch,
|
|
58304
|
-
mappedPos: hit.pos
|
|
58305
|
-
};
|
|
58306
|
-
this.#updateSelectionDebugHud();
|
|
58307
|
-
const anchor = this.#dragAnchor;
|
|
58308
|
-
const head = hit.pos;
|
|
58309
|
-
const { selAnchor, selHead } = this.#calculateExtendedSelection(anchor, head, this.#dragExtensionMode);
|
|
58310
|
-
debugLog(
|
|
58311
|
-
"verbose",
|
|
58312
|
-
`Drag selection update ${JSON.stringify({
|
|
58313
|
-
pointer: { clientX: event.clientX, clientY: event.clientY, x: normalized.x, y: normalized.y },
|
|
58314
|
-
prevPointer,
|
|
58315
|
-
rawHit: {
|
|
58316
|
-
pos: rawHit.pos,
|
|
58317
|
-
pageIndex: rawHit.pageIndex,
|
|
58318
|
-
blockId: rawHit.blockId,
|
|
58319
|
-
lineIndex: rawHit.lineIndex,
|
|
58320
|
-
layoutEpoch: rawHit.layoutEpoch
|
|
58321
|
-
},
|
|
58322
|
-
prevRawHit: prevRawHit ? {
|
|
58323
|
-
pos: prevRawHit.pos,
|
|
58324
|
-
pageIndex: prevRawHit.pageIndex,
|
|
58325
|
-
blockId: prevRawHit.blockId,
|
|
58326
|
-
lineIndex: prevRawHit.lineIndex,
|
|
58327
|
-
layoutEpoch: prevRawHit.layoutEpoch
|
|
58328
|
-
} : null,
|
|
58329
|
-
mappedHead: { pos: mappedHead.pos, fromEpoch: mappedHead.fromEpoch, toEpoch: mappedHead.toEpoch },
|
|
58330
|
-
hit: { pos: hit.pos, pageIndex: hit.pageIndex, layoutEpoch: hit.layoutEpoch },
|
|
58331
|
-
anchor,
|
|
58332
|
-
head,
|
|
58333
|
-
selAnchor,
|
|
58334
|
-
selHead,
|
|
58335
|
-
direction: head >= anchor ? "down" : "up",
|
|
58336
|
-
selectionDirection: selHead >= selAnchor ? "down" : "up",
|
|
58337
|
-
extensionMode: this.#dragExtensionMode,
|
|
58338
|
-
hitSource: pageMounted ? "dom" : "geometry",
|
|
58339
|
-
pageMounted
|
|
58340
|
-
})}`
|
|
58341
|
-
);
|
|
58342
|
-
const currentTableHit = this.#hitTestTable(normalized.x, normalized.y);
|
|
58343
|
-
const shouldUseCellSel = this.#shouldUseCellSelection(currentTableHit);
|
|
58344
|
-
if (shouldUseCellSel && this.#cellAnchor) {
|
|
58345
|
-
const headCellPos = currentTableHit ? this.#getCellPosFromTableHit(currentTableHit) : null;
|
|
58346
|
-
if (headCellPos !== null) {
|
|
58347
|
-
if (this.#cellDragMode !== "active") {
|
|
58348
|
-
this.#cellDragMode = "active";
|
|
58349
|
-
}
|
|
58350
|
-
try {
|
|
58351
|
-
const doc22 = this.#editor.state.doc;
|
|
58352
|
-
const anchorCellPos = this.#cellAnchor.cellPos;
|
|
58353
|
-
const clampedAnchor = Math.max(0, Math.min(anchorCellPos, doc22.content.size));
|
|
58354
|
-
const clampedHead = Math.max(0, Math.min(headCellPos, doc22.content.size));
|
|
58355
|
-
const cellSelection = CellSelection.create(doc22, clampedAnchor, clampedHead);
|
|
58356
|
-
const tr = this.#editor.state.tr.setSelection(cellSelection);
|
|
58357
|
-
this.#editor.view?.dispatch(tr);
|
|
58358
|
-
this.#scheduleSelectionUpdate();
|
|
58359
|
-
} catch (error) {
|
|
58360
|
-
console.warn("[CELL-SELECTION] Failed to create CellSelection, falling back to TextSelection:", error);
|
|
58361
|
-
const anchor2 = this.#dragAnchor;
|
|
58362
|
-
const head2 = hit.pos;
|
|
58363
|
-
const { selAnchor: selAnchor2, selHead: selHead2 } = this.#calculateExtendedSelection(anchor2, head2, this.#dragExtensionMode);
|
|
58364
|
-
try {
|
|
58365
|
-
const tr = this.#editor.state.tr.setSelection(
|
|
58366
|
-
superEditor_converter.TextSelection.create(this.#editor.state.doc, selAnchor2, selHead2)
|
|
58367
|
-
);
|
|
58368
|
-
this.#editor.view?.dispatch(tr);
|
|
58369
|
-
this.#scheduleSelectionUpdate();
|
|
58370
|
-
} catch {
|
|
58371
|
-
}
|
|
58372
|
-
}
|
|
58373
|
-
return;
|
|
58374
|
-
}
|
|
58375
|
-
}
|
|
58376
|
-
try {
|
|
58377
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.TextSelection.create(this.#editor.state.doc, selAnchor, selHead));
|
|
58378
|
-
this.#editor.view?.dispatch(tr);
|
|
58379
|
-
this.#scheduleSelectionUpdate();
|
|
58380
|
-
} catch (error) {
|
|
58381
|
-
console.warn("[SELECTION] Failed to extend selection during drag:", {
|
|
58382
|
-
error,
|
|
58383
|
-
anchor,
|
|
58384
|
-
head,
|
|
58385
|
-
selAnchor,
|
|
58386
|
-
selHead,
|
|
58387
|
-
mode: this.#dragExtensionMode
|
|
58388
|
-
});
|
|
58389
|
-
}
|
|
58390
|
-
return;
|
|
58391
|
-
}
|
|
58392
|
-
const sessionMode = this.#headerFooterSession?.session?.mode ?? "body";
|
|
58393
|
-
if (sessionMode !== "body") {
|
|
58394
|
-
this.#clearHoverRegion();
|
|
58395
|
-
return;
|
|
58396
|
-
}
|
|
58397
|
-
if (this.#documentMode === "viewing") {
|
|
58398
|
-
this.#clearHoverRegion();
|
|
58399
|
-
return;
|
|
58400
|
-
}
|
|
58401
|
-
const region = this.#hitTestHeaderFooterRegion(normalized.x, normalized.y);
|
|
58402
|
-
if (!region) {
|
|
58403
|
-
this.#clearHoverRegion();
|
|
58404
|
-
return;
|
|
58405
|
-
}
|
|
58406
|
-
const currentHover = this.#headerFooterSession?.hoverRegion;
|
|
58407
|
-
if (currentHover && currentHover.kind === region.kind && currentHover.pageIndex === region.pageIndex && currentHover.sectionType === region.sectionType) {
|
|
58408
|
-
return;
|
|
58409
|
-
}
|
|
58410
|
-
this.#headerFooterSession?.renderHover(region);
|
|
58411
|
-
this.#renderHoverRegion(region);
|
|
58412
|
-
};
|
|
58413
|
-
#handlePointerLeave = () => {
|
|
58414
|
-
this.#clearHoverRegion();
|
|
58415
|
-
};
|
|
58416
|
-
#handleVisibleHostFocusIn = (event) => {
|
|
58417
|
-
if (isInRegisteredSurface(event)) {
|
|
58418
|
-
return;
|
|
58419
|
-
}
|
|
58420
|
-
if (this.#suppressFocusInFromDraggable) {
|
|
58421
|
-
this.#suppressFocusInFromDraggable = false;
|
|
58422
|
-
return;
|
|
58423
|
-
}
|
|
58424
|
-
const target = event.target;
|
|
58425
|
-
const activeTarget = this.#getActiveDomTarget();
|
|
58426
|
-
if (!activeTarget) {
|
|
58427
|
-
return;
|
|
58428
|
-
}
|
|
58429
|
-
const activeNode = activeTarget;
|
|
58430
|
-
const containsFn = typeof activeNode.contains === "function" ? activeNode.contains : null;
|
|
58431
|
-
if (target && (activeNode === target || containsFn && containsFn.call(activeNode, target))) {
|
|
58432
|
-
return;
|
|
58433
|
-
}
|
|
58434
|
-
try {
|
|
58435
|
-
if (activeTarget instanceof HTMLElement && typeof activeTarget.focus === "function") {
|
|
58436
|
-
activeTarget.focus?.({
|
|
58437
|
-
preventScroll: true
|
|
58438
|
-
});
|
|
58439
|
-
} else if (typeof activeTarget.focus === "function") {
|
|
58440
|
-
activeTarget.focus();
|
|
58441
|
-
}
|
|
58442
|
-
} catch {
|
|
58443
|
-
}
|
|
58444
|
-
try {
|
|
58445
|
-
this.getActiveEditor().view?.focus();
|
|
58446
|
-
} catch {
|
|
58447
|
-
}
|
|
58448
|
-
};
|
|
58449
|
-
#handlePointerUp = (event) => {
|
|
58450
|
-
this.#suppressFocusInFromDraggable = false;
|
|
58451
|
-
if (!this.#isDragging) return;
|
|
58452
|
-
if (typeof this.#viewportHost.hasPointerCapture === "function" && typeof this.#viewportHost.releasePointerCapture === "function" && this.#viewportHost.hasPointerCapture(event.pointerId)) {
|
|
58453
|
-
this.#viewportHost.releasePointerCapture(event.pointerId);
|
|
58454
|
-
}
|
|
58455
|
-
const pendingMarginClick = this.#pendingMarginClick;
|
|
58456
|
-
this.#pendingMarginClick = null;
|
|
58457
|
-
const dragAnchor = this.#dragAnchor;
|
|
58458
|
-
const dragMode = this.#dragExtensionMode;
|
|
58459
|
-
const dragUsedFallback = this.#dragUsedPageNotMountedFallback;
|
|
58460
|
-
const dragPointer = this.#dragLastPointer;
|
|
58461
|
-
this.#isDragging = false;
|
|
58462
|
-
if (this.#cellDragMode !== "none") {
|
|
58463
|
-
this.#cellDragMode = "none";
|
|
58464
|
-
}
|
|
58465
|
-
if (!pendingMarginClick || pendingMarginClick.pointerId !== event.pointerId) {
|
|
58466
|
-
this.#updateSelectionVirtualizationPins({ includeDragBuffer: false });
|
|
58467
|
-
if (dragUsedFallback && dragAnchor != null) {
|
|
58468
|
-
const pointer = dragPointer ?? { clientX: event.clientX, clientY: event.clientY };
|
|
58469
|
-
this.#finalizeDragSelectionWithDom(pointer, dragAnchor, dragMode);
|
|
58470
|
-
}
|
|
58471
|
-
this.#scheduleA11ySelectionAnnouncement({ immediate: true });
|
|
58472
|
-
this.#dragLastPointer = null;
|
|
58473
|
-
this.#dragLastRawHit = null;
|
|
58474
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
58475
|
-
return;
|
|
58476
|
-
}
|
|
58477
|
-
const sessionModeForDrag = this.#headerFooterSession?.session?.mode ?? "body";
|
|
58478
|
-
if (sessionModeForDrag !== "body" || this.#isViewLocked()) {
|
|
58479
|
-
this.#dragLastPointer = null;
|
|
58480
|
-
this.#dragLastRawHit = null;
|
|
58481
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
58482
|
-
return;
|
|
58483
|
-
}
|
|
58484
|
-
const doc2 = this.#editor.state?.doc;
|
|
58485
|
-
if (!doc2) {
|
|
58486
|
-
this.#dragLastPointer = null;
|
|
58487
|
-
this.#dragLastRawHit = null;
|
|
58488
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
58489
|
-
return;
|
|
58490
|
-
}
|
|
58491
|
-
if (pendingMarginClick.kind === "aboveFirstLine") {
|
|
58492
|
-
const pos = this.#getFirstTextPosition();
|
|
58493
|
-
try {
|
|
58494
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc2, pos));
|
|
58495
|
-
this.#editor.view?.dispatch(tr);
|
|
58496
|
-
this.#scheduleSelectionUpdate();
|
|
58497
|
-
} catch {
|
|
58498
|
-
}
|
|
58499
|
-
this.#debugLastHit = { source: "margin", pos: null, layoutEpoch: null, mappedPos: pos };
|
|
58500
|
-
this.#updateSelectionDebugHud();
|
|
58501
|
-
this.#dragLastPointer = null;
|
|
58502
|
-
this.#dragLastRawHit = null;
|
|
58503
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
58504
|
-
return;
|
|
58505
|
-
}
|
|
58506
|
-
if (pendingMarginClick.kind === "right") {
|
|
58507
|
-
const mappedEnd2 = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
58508
|
-
pendingMarginClick.pmEnd,
|
|
58509
|
-
pendingMarginClick.layoutEpoch,
|
|
58510
|
-
1
|
|
58511
|
-
);
|
|
58512
|
-
if (!mappedEnd2.ok) {
|
|
58513
|
-
debugLog("warn", "right margin mapping failed", mappedEnd2);
|
|
58514
|
-
this.#pendingDocChange = true;
|
|
58515
|
-
this.#scheduleRerender();
|
|
58516
|
-
this.#dragLastPointer = null;
|
|
58517
|
-
this.#dragLastRawHit = null;
|
|
58518
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
58519
|
-
return;
|
|
58520
|
-
}
|
|
58521
|
-
const caretPos = Math.max(0, Math.min(mappedEnd2.pos, doc2.content.size));
|
|
58522
|
-
try {
|
|
58523
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc2, caretPos));
|
|
58524
|
-
this.#editor.view?.dispatch(tr);
|
|
58525
|
-
this.#scheduleSelectionUpdate();
|
|
58526
|
-
} catch {
|
|
58527
|
-
}
|
|
58528
|
-
this.#debugLastHit = {
|
|
58529
|
-
source: "margin",
|
|
58530
|
-
pos: pendingMarginClick.pmEnd,
|
|
58531
|
-
layoutEpoch: pendingMarginClick.layoutEpoch,
|
|
58532
|
-
mappedPos: caretPos
|
|
58533
|
-
};
|
|
58534
|
-
this.#updateSelectionDebugHud();
|
|
58535
|
-
this.#dragLastPointer = null;
|
|
58536
|
-
this.#dragLastRawHit = null;
|
|
58537
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
58538
|
-
return;
|
|
58539
|
-
}
|
|
58540
|
-
const mappedStart = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
58541
|
-
pendingMarginClick.pmStart,
|
|
58542
|
-
pendingMarginClick.layoutEpoch,
|
|
58543
|
-
1
|
|
58544
|
-
);
|
|
58545
|
-
const mappedEnd = this.#epochMapper.mapPosFromLayoutToCurrentDetailed(
|
|
58546
|
-
pendingMarginClick.pmEnd,
|
|
58547
|
-
pendingMarginClick.layoutEpoch,
|
|
58548
|
-
-1
|
|
58549
|
-
);
|
|
58550
|
-
if (!mappedStart.ok || !mappedEnd.ok) {
|
|
58551
|
-
if (!mappedStart.ok) debugLog("warn", "left margin mapping failed (start)", mappedStart);
|
|
58552
|
-
if (!mappedEnd.ok) debugLog("warn", "left margin mapping failed (end)", mappedEnd);
|
|
58553
|
-
this.#pendingDocChange = true;
|
|
58554
|
-
this.#scheduleRerender();
|
|
58555
|
-
this.#dragLastPointer = null;
|
|
58556
|
-
this.#dragLastRawHit = null;
|
|
58557
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
58558
|
-
return;
|
|
58559
|
-
}
|
|
58560
|
-
const selFrom = Math.max(0, Math.min(Math.min(mappedStart.pos, mappedEnd.pos), doc2.content.size));
|
|
58561
|
-
const selTo = Math.max(0, Math.min(Math.max(mappedStart.pos, mappedEnd.pos), doc2.content.size));
|
|
58562
|
-
try {
|
|
58563
|
-
const tr = this.#editor.state.tr.setSelection(superEditor_converter.TextSelection.create(doc2, selFrom, selTo));
|
|
58564
|
-
this.#editor.view?.dispatch(tr);
|
|
58565
|
-
this.#scheduleSelectionUpdate();
|
|
58566
|
-
} catch {
|
|
58567
|
-
}
|
|
58568
|
-
this.#debugLastHit = {
|
|
58569
|
-
source: "margin",
|
|
58570
|
-
pos: pendingMarginClick.pmStart,
|
|
58571
|
-
layoutEpoch: pendingMarginClick.layoutEpoch,
|
|
58572
|
-
mappedPos: selFrom
|
|
58573
|
-
};
|
|
58574
|
-
this.#updateSelectionDebugHud();
|
|
58575
|
-
this.#dragLastPointer = null;
|
|
58576
|
-
this.#dragLastRawHit = null;
|
|
58577
|
-
this.#dragUsedPageNotMountedFallback = false;
|
|
58578
|
-
};
|
|
58579
|
-
#handleDragOver = createExternalFieldAnnotationDragOverHandler({
|
|
58580
|
-
getActiveEditor: () => this.getActiveEditor(),
|
|
58581
|
-
hitTest: (clientX, clientY) => this.hitTest(clientX, clientY),
|
|
58582
|
-
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate()
|
|
58583
|
-
});
|
|
58584
|
-
#handleDrop = createExternalFieldAnnotationDropHandler({
|
|
58585
|
-
getActiveEditor: () => this.getActiveEditor(),
|
|
58586
|
-
hitTest: (clientX, clientY) => this.hitTest(clientX, clientY),
|
|
58587
|
-
scheduleSelectionUpdate: () => this.#scheduleSelectionUpdate()
|
|
58588
|
-
});
|
|
58589
|
-
#handleDoubleClick = (event) => {
|
|
58590
|
-
if (event.button !== 0) return;
|
|
58591
|
-
if (!this.#layoutState.layout) return;
|
|
58592
|
-
const rect = this.#viewportHost.getBoundingClientRect();
|
|
58593
|
-
const zoom = this.#layoutOptions.zoom ?? 1;
|
|
58594
|
-
const scrollLeft = this.#visibleHost.scrollLeft ?? 0;
|
|
58595
|
-
const scrollTop = this.#visibleHost.scrollTop ?? 0;
|
|
58596
|
-
const x = (event.clientX - rect.left + scrollLeft) / zoom;
|
|
58597
|
-
const y2 = (event.clientY - rect.top + scrollTop) / zoom;
|
|
58598
|
-
const region = this.#hitTestHeaderFooterRegion(x, y2);
|
|
58599
|
-
if (region) {
|
|
58600
|
-
event.preventDefault();
|
|
58601
|
-
event.stopPropagation();
|
|
58602
|
-
const descriptor = this.#resolveDescriptorForRegion(region);
|
|
58603
|
-
const hfManager = this.#headerFooterSession?.manager;
|
|
58604
|
-
if (!descriptor && hfManager) {
|
|
58605
|
-
this.#createDefaultHeaderFooter(region);
|
|
58606
|
-
hfManager.refresh();
|
|
58607
|
-
}
|
|
58608
|
-
this.#activateHeaderFooterRegion(region);
|
|
58609
|
-
} else if ((this.#headerFooterSession?.session?.mode ?? "body") !== "body") {
|
|
58610
|
-
this.#exitHeaderFooterMode();
|
|
58611
|
-
}
|
|
58612
|
-
};
|
|
58613
|
-
#handleKeyDown = (event) => {
|
|
58614
|
-
const sessionModeForKey = this.#headerFooterSession?.session?.mode ?? "body";
|
|
58615
|
-
if (event.key === "Escape" && sessionModeForKey !== "body") {
|
|
58616
|
-
event.preventDefault();
|
|
58617
|
-
this.#exitHeaderFooterMode();
|
|
58618
|
-
return;
|
|
58619
|
-
}
|
|
58620
|
-
if (event.ctrlKey && event.altKey && !event.shiftKey) {
|
|
58621
|
-
if (event.code === "KeyH") {
|
|
58622
|
-
event.preventDefault();
|
|
58623
|
-
this.#focusHeaderFooterShortcut("header");
|
|
58624
|
-
} else if (event.code === "KeyF") {
|
|
58625
|
-
event.preventDefault();
|
|
58626
|
-
this.#focusHeaderFooterShortcut("footer");
|
|
58627
|
-
}
|
|
58628
|
-
}
|
|
58629
|
-
};
|
|
58630
|
-
#focusHeaderFooterShortcut(kind) {
|
|
58631
|
-
const pageIndex = this.#getCurrentPageIndex();
|
|
58632
|
-
const region = this.#findRegionForPage(kind, pageIndex);
|
|
58633
|
-
if (!region) {
|
|
58634
|
-
this.#emitHeaderFooterEditBlocked("missingRegion");
|
|
58635
|
-
return;
|
|
58636
|
-
}
|
|
58637
|
-
this.#activateHeaderFooterRegion(region);
|
|
58638
|
-
}
|
|
58639
58501
|
#scheduleRerender() {
|
|
58640
58502
|
if (this.#renderScheduled) {
|
|
58641
58503
|
return;
|
|
@@ -59106,7 +58968,7 @@ class PresentationEditor extends EventEmitter {
|
|
|
59106
58968
|
return;
|
|
59107
58969
|
}
|
|
59108
58970
|
this.#syncSelectedFieldAnnotationClass(selection);
|
|
59109
|
-
this.#updateSelectionVirtualizationPins({ includeDragBuffer: this.#isDragging });
|
|
58971
|
+
this.#updateSelectionVirtualizationPins({ includeDragBuffer: this.#editorInputManager?.isDragging ?? false });
|
|
59110
58972
|
if (selection instanceof CellSelection) {
|
|
59111
58973
|
try {
|
|
59112
58974
|
this.#localSelectionLayer.innerHTML = "";
|
|
@@ -59860,7 +59722,7 @@ class PresentationEditor extends EventEmitter {
|
|
|
59860
59722
|
{
|
|
59861
59723
|
ariaLiveRegion: this.#ariaLiveRegion,
|
|
59862
59724
|
sessionMode,
|
|
59863
|
-
isDragging: this.#isDragging,
|
|
59725
|
+
isDragging: this.#editorInputManager?.isDragging ?? false,
|
|
59864
59726
|
visibleHost: this.#visibleHost,
|
|
59865
59727
|
currentTimeout: this.#a11ySelectionAnnounceTimeout,
|
|
59866
59728
|
announceNow: () => {
|
|
@@ -59988,9 +59850,9 @@ class PresentationEditor extends EventEmitter {
|
|
|
59988
59850
|
} : null,
|
|
59989
59851
|
docSize,
|
|
59990
59852
|
includeDragBuffer: Boolean(options?.includeDragBuffer),
|
|
59991
|
-
isDragging: this.#isDragging,
|
|
59992
|
-
dragAnchorPageIndex: this.#dragAnchorPageIndex,
|
|
59993
|
-
dragLastHitPageIndex: this.#
|
|
59853
|
+
isDragging: this.#editorInputManager?.isDragging ?? false,
|
|
59854
|
+
dragAnchorPageIndex: this.#editorInputManager?.dragAnchorPageIndex ?? null,
|
|
59855
|
+
dragLastHitPageIndex: this.#editorInputManager?.dragLastHitPageIndex ?? null,
|
|
59994
59856
|
extraPages: options?.extraPages
|
|
59995
59857
|
});
|
|
59996
59858
|
painter.setVirtualizationPins(pins);
|
|
@@ -60004,9 +59866,10 @@ class PresentationEditor extends EventEmitter {
|
|
|
60004
59866
|
}
|
|
60005
59867
|
const normalized = this.#normalizeClientPoint(pointer.clientX, pointer.clientY);
|
|
60006
59868
|
if (!normalized) return;
|
|
59869
|
+
const dragLastRawHit = this.#editorInputManager?.dragLastRawHit;
|
|
60007
59870
|
this.#updateSelectionVirtualizationPins({
|
|
60008
59871
|
includeDragBuffer: false,
|
|
60009
|
-
extraPages:
|
|
59872
|
+
extraPages: dragLastRawHit ? [dragLastRawHit.pageIndex] : void 0
|
|
60010
59873
|
});
|
|
60011
59874
|
const refined = clickToPosition(
|
|
60012
59875
|
layout,
|
|
@@ -60023,7 +59886,7 @@ class PresentationEditor extends EventEmitter {
|
|
|
60023
59886
|
debugLog("warn", "Drag finalize: endpoint page still not mounted", { pageIndex: refined.pageIndex });
|
|
60024
59887
|
return;
|
|
60025
59888
|
}
|
|
60026
|
-
const prior =
|
|
59889
|
+
const prior = dragLastRawHit;
|
|
60027
59890
|
if (prior && (prior.pos !== refined.pos || prior.pageIndex !== refined.pageIndex)) {
|
|
60028
59891
|
debugLog("info", "Drag finalize refined hit", {
|
|
60029
59892
|
fromPos: prior.pos,
|
|
@@ -60226,7 +60089,7 @@ class PresentationEditor extends EventEmitter {
|
|
|
60226
60089
|
localSelectionLayer,
|
|
60227
60090
|
blocks: this.#layoutState.blocks,
|
|
60228
60091
|
measures: this.#layoutState.measures,
|
|
60229
|
-
cellAnchorTableBlockId: this.#cellAnchor?.tableBlockId ?? null,
|
|
60092
|
+
cellAnchorTableBlockId: this.#editorInputManager?.cellAnchor?.tableBlockId ?? null,
|
|
60230
60093
|
convertPageLocalToOverlayCoords: (pageIndex, x, y2) => this.#convertPageLocalToOverlayCoords(pageIndex, x, y2)
|
|
60231
60094
|
});
|
|
60232
60095
|
}
|