Added Queued Pastes
Some checks failed
/ publish (push) Failing after 1m21s

This commit is contained in:
Leon Mika 2026-09-16 21:52:03 +10:00
parent 40e5379eb3
commit de63c48da2
9 changed files with 415 additions and 1 deletions

View file

@ -0,0 +1,125 @@
import { firstLineRange, currentLine, advance, copyText, adjustCopiedLine, trimTrailing } from './queue.mjs';
import { serializePrefs, parsePrefs } from './prefs.mjs';
const pasteField = document.getElementById('pasteField');
const queueSwitch = document.getElementById('queueSwitch');
const advanceBtn = document.getElementById('advanceBtn');
const focusSwitch = document.getElementById('focusSwitch');
const newlineSwitch = document.getElementById('newlineSwitch');
const savedPrefs = parsePrefs(localStorage.getItem('queued-paste-prefs'));
newlineSwitch.checked = savedPrefs.newLines;
focusSwitch.checked = savedPrefs.advanceFocus;
function savePrefs() {
localStorage.setItem('queued-paste-prefs', serializePrefs({
newLines: newlineSwitch.checked,
advanceFocus: focusSwitch.checked,
}));
}
// Focus-based advancing is a heuristic: a paste outside this page cannot be
// observed, so a window blur followed by a refocus is treated as one paste.
// blurredSinceAdvance gates that, and armedAt suppresses the focus events
// fired by clipboard permission prompts right after arming.
let blurredSinceAdvance = false;
let armedAt = 0;
async function copyCurrentLine() {
await navigator.clipboard.writeText(copyText(pasteField.value, newlineSwitch.checked));
}
function selectCurrentLine() {
const { start, end } = firstLineRange(pasteField.value);
pasteField.setSelectionRange(start, end);
}
function setQueueing(on) {
queueSwitch.checked = on;
pasteField.readOnly = on;
pasteField.classList.toggle('paste-active', on);
advanceBtn.disabled = !on;
if (!on) {
pasteField.setSelectionRange(pasteField.value.length, pasteField.value.length);
}
}
async function startQueueing() {
pasteField.value = trimTrailing(pasteField.value);
if (pasteField.value === '') {
// Nothing to queue.
setQueueing(false);
return;
}
try {
await copyCurrentLine();
} catch (err) {
alert('Could not access the clipboard: ' + err.message);
setQueueing(false);
return;
}
setQueueing(true);
selectCurrentLine();
blurredSinceAdvance = false;
armedAt = Date.now();
}
async function advanceQueue() {
pasteField.value = advance(pasteField.value);
if (pasteField.value === '') {
setQueueing(false);
return;
}
try {
await copyCurrentLine();
} catch (err) {
alert('Could not copy the next line: ' + err.message);
setQueueing(false);
return;
}
selectCurrentLine();
}
queueSwitch.addEventListener('change', () => {
if (queueSwitch.checked) {
startQueueing();
} else {
setQueueing(false);
}
});
advanceBtn.addEventListener('click', () => {
advanceQueue();
});
focusSwitch.addEventListener('change', () => {
blurredSinceAdvance = false;
armedAt = Date.now();
savePrefs();
});
newlineSwitch.addEventListener('change', async () => {
savePrefs();
if (!queueSwitch.checked) return;
try {
const clipboardText = await navigator.clipboard.readText();
const adjusted = adjustCopiedLine(clipboardText, currentLine(pasteField.value), newlineSwitch.checked);
if (adjusted !== null) {
await navigator.clipboard.writeText(adjusted);
}
} catch (err) {
alert('Could not update the clipboard: ' + err.message);
}
});
window.addEventListener('blur', () => {
blurredSinceAdvance = true;
});
window.addEventListener('focus', () => {
if (!queueSwitch.checked || !focusSwitch.checked) return;
if (!blurredSinceAdvance) return;
if (Date.now() - armedAt < 1000) return;
blurredSinceAdvance = false;
advanceQueue();
});