Cybrkyd's Git Repositories

jottings - commit: 79cd033

commit 79cd033d4dab52112105f6782757069c835e93989eb20014fd30eb4c0ca4e93b
author Cybrkyd <git@cybrkyd.com> 2026-06-18 19:35:52 +0100
committer Cybrkyd <git@cybrkyd.com> 2026-06-18 19:35:52 +0100

Commit Message

Context menu for cut/copy/paste

📊 Diffstat

resources/js/app.js 93
1 files changed, 86 insertions(+), 7 deletions(-)

Diff

diff --git a/resources/js/app.js b/resources/js/app.js
index 93a649c..665388e 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -24,6 +24,12 @@ const spellMenu = document.getElementById('spellMenu');
const spellMenuItems = document.getElementById('spellMenuItems');
const spellMenuAdd = document.getElementById('spellMenuAdd');
+ // DOM references for general context menu
+ const contextMenu = document.getElementById('contextMenu');
+ const contextCut = document.getElementById('contextCut');
+ const contextCopy = document.getElementById('contextCopy');
+ const contextPaste = document.getElementById('contextPaste');
+
// Editor text helpers
// innerText is out; textContent is a little more sane for flat run of text nodes
@@ -333,28 +339,52 @@ function drawWavyLines(rects) {
function handleEditorContextMenu(e) {
e.preventDefault();
- if (!spellReady || !spell) return;
+ // Hide any other open menu first
+ hideSpellMenu();
+ hideContextMenu();
+
+ if (!spellReady || !spell) {
+ showContextMenu(e.clientX, e.clientY);
+ return;
+ }
let range;
if (document.caretRangeFromPoint) {
range = document.caretRangeFromPoint(e.clientX, e.clientY);
} else if (document.caretPositionFromPoint) {
const pos = document.caretPositionFromPoint(e.clientX, e.clientY);
- if (!pos) return;
+ if (!pos) {
+ showContextMenu(e.clientX, e.clientY);
+ return;
+ }
range = document.createRange();
range.setStart(pos.offsetNode, pos.offset);
range.collapse(true);
} else {
+ showContextMenu(e.clientX, e.clientY);
return;
}
- if (!range) return;
+ if (!range || !editor.contains(range.startContainer)) {
+ showContextMenu(e.clientX, e.clientY);
+ return;
+ }
const wordInfo = expandRangeToWord(range);
- if (!wordInfo) return;
+ if (!wordInfo) {
+ showContextMenu(e.clientX, e.clientY);
+ return;
+ }
const { word, wordRange } = wordInfo;
+ // If the word is correct, show general menu
+ if (isCorrect(word)) {
+ showContextMenu(e.clientX, e.clientY);
+ return;
+ }
+
+ // Otherwise show spellcheck menu
menuTargetWord = word;
menuTargetRange = wordRange;
@@ -475,6 +505,28 @@ function hideSpellMenu() {
menuTargetRange = null;
}
+ // General context menu helpers
+ function showContextMenu(x, y) {
+ contextMenu.style.left = '-9999px';
+ contextMenu.style.top = '-9999px';
+ contextMenu.classList.add('visible');
+
+ const menuW = contextMenu.offsetWidth;
+ const menuH = contextMenu.offsetHeight;
+ const vw = window.innerWidth;
+ const vh = window.innerHeight;
+
+ const left = Math.min(x, vw - menuW - 8);
+ const top = Math.min(y, vh - menuH - 8);
+
+ contextMenu.style.left = `${Math.max(0, left)}px`;
+ contextMenu.style.top = `${Math.max(0, top)}px`;
+ }
+
+ function hideContextMenu() {
+ contextMenu.classList.remove('visible');
+ }
+
// FILE SYSTEM HELPERS
async function getDataPath(filename) {
const home = await Neutralino.os.getEnv('HOME');
@@ -847,16 +899,43 @@ spellMenuAdd.addEventListener("mousedown", async (e) => {
}
});
- // Close menu on outside click or Escape
- document.addEventListener("mousedown", (e) => {
+ // Context menu item actions
+ contextCut.addEventListener('mousedown', (e) => {
+ e.preventDefault();
+ editor.focus();
+ document.execCommand('cut');
+ hideContextMenu();
+ });
+
+ contextCopy.addEventListener('mousedown', (e) => {
+ e.preventDefault();
+ editor.focus();
+ document.execCommand('copy');
+ hideContextMenu();
+ });
+
+ contextPaste.addEventListener('mousedown', (e) => {
+ e.preventDefault();
+ editor.focus();
+ document.execCommand('paste');
+ hideContextMenu();
+ });
+
+ // Close menus on outside click
+ document.addEventListener('mousedown', (e) => {
if (!spellMenu.contains(e.target)) {
hideSpellMenu();
}
+ if (!contextMenu.contains(e.target)) {
+ hideContextMenu();
+ }
});
- document.addEventListener("keydown", (e) => {
+ // Close menus on Escape
+ document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
hideSpellMenu();
+ hideContextMenu();
}
});