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