diff --git a/packages/super-editor/src/editors/v1/core/Editor.ts b/packages/super-editor/src/editors/v1/core/Editor.ts index 065356d8a2..16745537d1 100644 --- a/packages/super-editor/src/editors/v1/core/Editor.ts +++ b/packages/super-editor/src/editors/v1/core/Editor.ts @@ -424,6 +424,9 @@ export class Editor extends EventEmitter { onCommentsLoaded: () => null, onCommentClicked: () => null, onCommentLocationsUpdate: () => null, + onPointerDown: () => null, + onPointerUp: () => null, + onRightClick: () => null, onDocumentLocked: () => null, onFirstRender: () => null, onCollaborationReady: () => null, @@ -761,6 +764,9 @@ export class Editor extends EventEmitter { this.on('list-definitions-change', this.options.onListDefinitionsChange!); this.on('fonts-resolved', this.options.onFontsResolved!); this.on('exception', this.options.onException!); + this.on('pointerDown', this.options.onPointerDown!); + this.on('pointerUp', this.options.onPointerUp!); + this.on('rightClick', this.options.onRightClick!); } /** @@ -1159,6 +1165,9 @@ export class Editor extends EventEmitter { this.on('list-definitions-change', this.options.onListDefinitionsChange!); this.on('fonts-resolved', this.options.onFontsResolved!); this.on('exception', this.options.onException!); + this.on('pointerDown', this.options.onPointerDown!); + this.on('pointerUp', this.options.onPointerUp!); + this.on('rightClick', this.options.onRightClick!); if (!shouldMountRenderer) { this.#emitCreateAsync(); @@ -1235,6 +1244,9 @@ export class Editor extends EventEmitter { this.on('commentClick', this.options.onCommentClicked!); this.on('locked', this.options.onDocumentLocked!); this.on('list-definitions-change', this.options.onListDefinitionsChange!); + this.on('pointerDown', this.options.onPointerDown!); + this.on('pointerUp', this.options.onPointerUp!); + this.on('rightClick', this.options.onRightClick!); if (!shouldMountRenderer) { this.#emitCreateAsync(); diff --git a/packages/super-editor/src/editors/v1/core/presentation-editor/pointer-events/EditorInputManager.ts b/packages/super-editor/src/editors/v1/core/presentation-editor/pointer-events/EditorInputManager.ts index 2c82bf6bef..028e6ffb7e 100644 --- a/packages/super-editor/src/editors/v1/core/presentation-editor/pointer-events/EditorInputManager.ts +++ b/packages/super-editor/src/editors/v1/core/presentation-editor/pointer-events/EditorInputManager.ts @@ -1007,6 +1007,16 @@ export class EditorInputManager { #handlePointerDown(event: PointerEvent): void { if (!this.#deps) return; + // Emit local-only pointer events for external consumers (e.g. debugging trackpad issues) + // Emit directly on the Editor instance so consumers can use editor.on('pointerDown', ...) + const editor = this.#deps.getEditor(); + editor.emit?.('pointerDown', { editor, event }); + + // Emit rightClick for secondary button (button 2) or Ctrl+Click on Mac + if (event.button === 2 || (event.ctrlKey && navigator.platform.includes('Mac'))) { + editor.emit?.('rightClick', { editor, event }); + } + // Return early for non-left clicks if (event.button !== 0) return; @@ -1038,7 +1048,6 @@ export class EditorInputManager { return; } - const editor = this.#deps.getEditor(); if (this.#handleSingleCommentHighlightClick(event, target, editor)) { return; } @@ -1325,6 +1334,11 @@ export class EditorInputManager { #handlePointerUp(event: PointerEvent): void { if (!this.#deps) return; + // Emit local-only pointer event for external consumers (e.g. debugging trackpad issues) + // Emit directly on the Editor instance so consumers can use editor.on('pointerUp', ...) + const editor = this.#deps.getEditor(); + editor.emit?.('pointerUp', { editor, event }); + this.#suppressFocusInFromDraggable = false; if (!this.#isDragging) { diff --git a/packages/super-editor/src/editors/v1/core/types/EditorConfig.ts b/packages/super-editor/src/editors/v1/core/types/EditorConfig.ts index 7f7bf85339..0cb4a3423c 100644 --- a/packages/super-editor/src/editors/v1/core/types/EditorConfig.ts +++ b/packages/super-editor/src/editors/v1/core/types/EditorConfig.ts @@ -511,6 +511,15 @@ export interface EditorOptions { /** Host-provided permission hook */ permissionResolver?: ((params: PermissionParams) => boolean | undefined) | null; + /** Called on pointer down events (local only, not broadcast via collaboration) */ + onPointerDown?: (params: { editor: Editor; event: PointerEvent }) => void; + + /** Called on pointer up events (local only, not broadcast via collaboration) */ + onPointerUp?: (params: { editor: Editor; event: PointerEvent }) => void; + + /** Called on right-click (local only, not broadcast via collaboration) */ + onRightClick?: (params: { editor: Editor; event: MouseEvent }) => void; + /** * Custom resolver for the link click popover. * Called when a user clicks a link to determine which popover to show. diff --git a/packages/super-editor/src/editors/v1/core/types/EditorEvents.ts b/packages/super-editor/src/editors/v1/core/types/EditorEvents.ts index dfd9d65cf2..9b872adc97 100644 --- a/packages/super-editor/src/editors/v1/core/types/EditorEvents.ts +++ b/packages/super-editor/src/editors/v1/core/types/EditorEvents.ts @@ -148,4 +148,13 @@ export interface EditorEventMap extends DefaultEventMap { /** Called when document protection state changes (init, local mutation, or remote sync). */ protectionChanged: [{ editor: Editor; state: DocumentProtectionState; source: ProtectionChangeSource }]; + + /** Called on pointer down (local only, not broadcast via collaboration) */ + pointerDown: [{ editor: Editor; event: PointerEvent }]; + + /** Called on pointer up (local only, not broadcast via collaboration) */ + pointerUp: [{ editor: Editor; event: PointerEvent }]; + + /** Called on right-click (local only, not broadcast via collaboration) */ + rightClick: [{ editor: Editor; event: MouseEvent }]; } diff --git a/packages/superdoc/src/dev/components/SuperdocDev.vue b/packages/superdoc/src/dev/components/SuperdocDev.vue index 448e4ba0b0..63eb1a3803 100644 --- a/packages/superdoc/src/dev/components/SuperdocDev.vue +++ b/packages/superdoc/src/dev/components/SuperdocDev.vue @@ -1096,6 +1096,19 @@ const onEditorCreate = ({ editor }) => { editor.on('fieldAnnotationDoubleClicked', (params) => { console.log('fieldAnnotationDoubleClicked', { params }); }); + + // SD-2494: Pointer event observability for debugging trackpad/right-click selection issues + editor.on('pointerDown', (params) => { + console.log('pointerDown', { params }); + }); + + editor.on('pointerUp', (params) => { + console.log('pointerUp', { params }); + }); + + editor.on('rightClick', (params) => { + console.log('rightClick', { params }); + }); }; watch(