Skip to content

Instantly share code, notes, and snippets.

@shgysk8zer0
Created February 3, 2026 20:53
Show Gist options
  • Select an option

  • Save shgysk8zer0/656f8228aca22dd25c1af34ff49b5487 to your computer and use it in GitHub Desktop.

Select an option

Save shgysk8zer0/656f8228aca22dd25c1af34ff49b5487 to your computer and use it in GitHub Desktop.
{
/**
* Creates a modal prompt that resolves with the input value or null.
* @param {Object} options
* @param {AbortSignal} [options.signal] - Optional signal to close the dialog.
* @returns {Promise<string|null>}
*/
async function createAsyncPrompt({ signal: sig } = {}) {
const { promise, resolve, reject } = Promise.withResolvers();
if (sig?.aborted) {
reject(signal.reason);
} else {
// 1. Create Elements
const controller = new AbortController();
const signal = sig instanceof AbortSignal ? AbortSignal.any([sig, controller.signal]) : controller.signal;
const dialog = document.createElement('dialog');
const form = document.createElement('form');
const input = document.createElement('input');
const cancelBtn = document.createElement('button');
const confirmBtn = document.createElement('button');
// 2. Configure Elements
form.method = 'dialog';
input.type = 'text';
input.name = 'userInput';
input.required = true;
input.required = true;
cancelBtn.type = 'button';
cancelBtn.command = 'request-close';
cancelBtn.commandForElement = dialog;
cancelBtn.textContent = 'Cancel';
confirmBtn.value = 'confirm'; // Sets dialog.returnValue to 'confirm'
confirmBtn.textContent = 'OK';
confirmBtn.type = 'submit';
// 3. Assemble
form.appendChild(input);
form.appendChild(cancelBtn);
form.appendChild(confirmBtn);
dialog.appendChild(form);
document.body.appendChild(dialog);
// 4. Stop keydown propagation
dialog.addEventListener('keydown', (e) => e.stopPropagation(), { signal });
// 5. Handle Closing & Cleanup
dialog.addEventListener('close', () => {
// Check if the signal caused the close or if it was a manual cancel
const result = dialog.returnValue === 'confirm' ? input.value : null;
controller.abort();
dialog.remove(); // Remove from DOM
resolve(result);
}, { signal });
// 6. Handle AbortSignal
if (sig instanceof AbortSignal) {
sig.addEventListener('abort', ({ target }) => {
reject(sig.reason);
dialog.close();
dialog.remove();
}, { signal: controller.signal, once: true });
}
// 7. Show
dialog.showModal();
}
return await promise;
}
const btn = document.createElement('button');
btn.type = 'button';
btn.textContent = 'Better Scan';
btn.addEventListener('click', ({ target }) => {
createAsyncPrompt().then(input => {
if (typeof input === 'string' && input.length !== 0) {
for (const char of input) {
document.documentElement.dispatchEvent(new KeyboardEvent('keydown', {
key: char,
bubbles: true, // Allow event to bubble up if needed
cancelable: true,
view: window
}));
}
document.documentElement.dispatchEvent(new KeyboardEvent('keydown', {
key: 'Enter',
bubbles: true, // Allow event to bubble up if needed
cancelable: true,
view: window
}));
}
}, console.error);
});
document.getElementById('sidebar').append(btn);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment