From 094bc842518ab214ef362018bc9a7b148fa42519 Mon Sep 17 00:00:00 2001 From: Sean Sartell Date: Fri, 13 Feb 2026 16:34:49 -0600 Subject: [PATCH 1/5] Tweak clipboard override APIs to have a boolean return to signify if they did anything or not --- src/mathquill.d.ts | 6 ++-- src/publicapi.ts | 6 ++-- src/services/saneKeyboardEvents.util.ts | 42 +++++++++++++++++++++---- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/mathquill.d.ts b/src/mathquill.d.ts index f68dfa358..904ba675e 100644 --- a/src/mathquill.d.ts +++ b/src/mathquill.d.ts @@ -122,9 +122,9 @@ declare namespace MathQuill { statelessClipboard?: boolean; onPaste?: () => void; onCut?: () => void; - overridePaste?: (event?: ClipboardEvent) => void; - overrideCopy?: (event?: ClipboardEvent) => void; - overrideCut?: (event?: ClipboardEvent) => void; + overridePaste?: (event?: ClipboardEvent) => boolean; + overrideCopy?: (event?: ClipboardEvent) => boolean; + overrideCut?: (event?: ClipboardEvent) => boolean; overrideTypedText?: (text: string) => void; overrideKeystroke?: (key: string, event: KeyboardEvent) => void; autoOperatorNames?: string; diff --git a/src/publicapi.ts b/src/publicapi.ts index d623871f6..4464deb57 100644 --- a/src/publicapi.ts +++ b/src/publicapi.ts @@ -113,9 +113,9 @@ class Options { disableCopyPaste?: boolean; statelessClipboard?: boolean; logAriaAlerts?: boolean; - overridePaste?: (event?: ClipboardEvent) => void; - overrideCopy?: (event?: ClipboardEvent) => void; - overrideCut?: (event?: ClipboardEvent) => void; + overridePaste?: (event?: ClipboardEvent) => boolean; + overrideCopy?: (event?: ClipboardEvent) => boolean; + overrideCut?: (event?: ClipboardEvent) => boolean; onPaste?: () => void; onCut?: () => void; overrideTypedText?: (text: string) => void; diff --git a/src/services/saneKeyboardEvents.util.ts b/src/services/saneKeyboardEvents.util.ts index f2c9a5b27..6f1fd1d15 100644 --- a/src/services/saneKeyboardEvents.util.ts +++ b/src/services/saneKeyboardEvents.util.ts @@ -350,8 +350,8 @@ var saneKeyboardEvents = (function () { const clipboardEvent = e instanceof ClipboardEvent ? e : undefined; if (clipboardEvent && controller.options?.overridePaste) { - controller.options.overridePaste(clipboardEvent); - return; + const earlyReturn = controller.options.overridePaste(clipboardEvent); + if (earlyReturn) return; } everyTick.listen(function pastedText() { @@ -406,8 +406,23 @@ var saneKeyboardEvents = (function () { const clipboardEvent = evt instanceof ClipboardEvent ? evt : undefined; if (clipboardEvent && controller.options?.overrideCut) { - controller.options.overrideCut(clipboardEvent); - return; + const earlyReturn = controller.options.overrideCut(clipboardEvent); + if (earlyReturn) return; + } + if (clipboardEvent?.clipboardData) { + const selection = controller.exportLatexSelection().selection; + if (selection.startIndex !== selection.endIndex) { + const text = selection.latex.slice(selection.startIndex, selection.endIndex); + clipboardEvent.clipboardData.setData( + 'text/plain', + text + ); + clipboardEvent.clipboardData.setData( + 'application/x-latex', + text + ); + evt.preventDefault(); + } } everyTick.listenOnce(function () { controller.cut(); @@ -417,8 +432,23 @@ var saneKeyboardEvents = (function () { const clipboardEvent = evt instanceof ClipboardEvent ? evt : undefined; if (clipboardEvent && controller.options?.overrideCopy) { - controller.options.overrideCopy(clipboardEvent); - return; + const earlyReturn = controller.options.overrideCopy(clipboardEvent); + if (earlyReturn) return; + } + if (clipboardEvent?.clipboardData) { + const selection = controller.exportLatexSelection().selection; + if (selection.startIndex !== selection.endIndex) { + const text = selection.latex.slice(selection.startIndex, selection.endIndex) + clipboardEvent.clipboardData.setData( + 'text/plain', + text + ); + clipboardEvent.clipboardData.setData( + 'application/x-latex', + text + ); + evt.preventDefault(); + } } everyTick.listenOnce(function () { controller.copy(); From d53cc5ea7dede7cd7ccf50f107389d9c678da90b Mon Sep 17 00:00:00 2001 From: Sean Sartell Date: Thu, 19 Feb 2026 08:14:11 -0600 Subject: [PATCH 2/5] Fix prettier issues --- src/services/saneKeyboardEvents.util.ts | 26 ++++++++++--------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/services/saneKeyboardEvents.util.ts b/src/services/saneKeyboardEvents.util.ts index 6f1fd1d15..f29d50ace 100644 --- a/src/services/saneKeyboardEvents.util.ts +++ b/src/services/saneKeyboardEvents.util.ts @@ -412,15 +412,12 @@ var saneKeyboardEvents = (function () { if (clipboardEvent?.clipboardData) { const selection = controller.exportLatexSelection().selection; if (selection.startIndex !== selection.endIndex) { - const text = selection.latex.slice(selection.startIndex, selection.endIndex); - clipboardEvent.clipboardData.setData( - 'text/plain', - text - ); - clipboardEvent.clipboardData.setData( - 'application/x-latex', - text + const text = selection.latex.slice( + selection.startIndex, + selection.endIndex ); + clipboardEvent.clipboardData.setData('text/plain', text); + clipboardEvent.clipboardData.setData('application/x-latex', text); evt.preventDefault(); } } @@ -438,15 +435,12 @@ var saneKeyboardEvents = (function () { if (clipboardEvent?.clipboardData) { const selection = controller.exportLatexSelection().selection; if (selection.startIndex !== selection.endIndex) { - const text = selection.latex.slice(selection.startIndex, selection.endIndex) - clipboardEvent.clipboardData.setData( - 'text/plain', - text - ); - clipboardEvent.clipboardData.setData( - 'application/x-latex', - text + const text = selection.latex.slice( + selection.startIndex, + selection.endIndex ); + clipboardEvent.clipboardData.setData('text/plain', text); + clipboardEvent.clipboardData.setData('application/x-latex', text); evt.preventDefault(); } } From 1eb0229618dc2a055448ef76f29e4ebc8caaf3d0 Mon Sep 17 00:00:00 2001 From: ssartell Date: Mon, 23 Feb 2026 10:18:22 -0600 Subject: [PATCH 3/5] Fix PR issues --- src/services/saneKeyboardEvents.util.ts | 51 ++++++++++--------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/src/services/saneKeyboardEvents.util.ts b/src/services/saneKeyboardEvents.util.ts index f29d50ace..f4ef16ae3 100644 --- a/src/services/saneKeyboardEvents.util.ts +++ b/src/services/saneKeyboardEvents.util.ts @@ -366,6 +366,24 @@ var saneKeyboardEvents = (function () { everyTick.trigger(e); } + function updateClipboardData(e: ClipboardEvent | undefined) { + if (e?.clipboardData) { + const selection = controller.exportLatexSelection().selection; + if (selection.startIndex !== selection.endIndex) { + const text = selection.latex.slice(selection.startIndex, selection.endIndex); + e.clipboardData.setData( + 'text/plain', + text + ); + e.clipboardData.setData( + 'application/x-latex', + text + ); + e.preventDefault(); + } + } + } + if (controller.KIND_OF_MQ === 'StaticMath') { controller.addTextareaEventListeners({ keydown: (evt) => { @@ -409,21 +427,8 @@ var saneKeyboardEvents = (function () { const earlyReturn = controller.options.overrideCut(clipboardEvent); if (earlyReturn) return; } - if (clipboardEvent?.clipboardData) { - const selection = controller.exportLatexSelection().selection; - if (selection.startIndex !== selection.endIndex) { - const text = selection.latex.slice( - selection.startIndex, - selection.endIndex - ); - clipboardEvent.clipboardData.setData('text/plain', text); - clipboardEvent.clipboardData.setData('application/x-latex', text); - evt.preventDefault(); - } - } - everyTick.listenOnce(function () { - controller.cut(); - }); + updateClipboardData(clipboardEvent); + controller.cut(); }, copy: function (evt: Event) { const clipboardEvent = @@ -432,21 +437,7 @@ var saneKeyboardEvents = (function () { const earlyReturn = controller.options.overrideCopy(clipboardEvent); if (earlyReturn) return; } - if (clipboardEvent?.clipboardData) { - const selection = controller.exportLatexSelection().selection; - if (selection.startIndex !== selection.endIndex) { - const text = selection.latex.slice( - selection.startIndex, - selection.endIndex - ); - clipboardEvent.clipboardData.setData('text/plain', text); - clipboardEvent.clipboardData.setData('application/x-latex', text); - evt.preventDefault(); - } - } - everyTick.listenOnce(function () { - controller.copy(); - }); + updateClipboardData(clipboardEvent); }, paste: onPaste, input: onInput From c9ec244fc05e350c24ca2904747b4a3ef88de059 Mon Sep 17 00:00:00 2001 From: ssartell Date: Mon, 23 Feb 2026 10:19:38 -0600 Subject: [PATCH 4/5] Fix prettier again --- src/services/saneKeyboardEvents.util.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/services/saneKeyboardEvents.util.ts b/src/services/saneKeyboardEvents.util.ts index f4ef16ae3..6941ddc27 100644 --- a/src/services/saneKeyboardEvents.util.ts +++ b/src/services/saneKeyboardEvents.util.ts @@ -370,15 +370,12 @@ var saneKeyboardEvents = (function () { if (e?.clipboardData) { const selection = controller.exportLatexSelection().selection; if (selection.startIndex !== selection.endIndex) { - const text = selection.latex.slice(selection.startIndex, selection.endIndex); - e.clipboardData.setData( - 'text/plain', - text - ); - e.clipboardData.setData( - 'application/x-latex', - text + const text = selection.latex.slice( + selection.startIndex, + selection.endIndex ); + e.clipboardData.setData('text/plain', text); + e.clipboardData.setData('application/x-latex', text); e.preventDefault(); } } From d97a705bceae403d97519721b7a24547b3b691a7 Mon Sep 17 00:00:00 2001 From: ssartell Date: Mon, 23 Feb 2026 10:43:07 -0600 Subject: [PATCH 5/5] Revert back to async cut to avoid potential regressions --- src/services/saneKeyboardEvents.util.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/saneKeyboardEvents.util.ts b/src/services/saneKeyboardEvents.util.ts index 6941ddc27..54b736a29 100644 --- a/src/services/saneKeyboardEvents.util.ts +++ b/src/services/saneKeyboardEvents.util.ts @@ -425,7 +425,9 @@ var saneKeyboardEvents = (function () { if (earlyReturn) return; } updateClipboardData(clipboardEvent); - controller.cut(); + everyTick.listenOnce(function () { + controller.cut(); + }); }, copy: function (evt: Event) { const clipboardEvent =