alert / confirm / prompt from a page came up as bare OS message boxes titled "theseus-navigator" (the package name), with no hint of who was asking and nothing of the browser's styling — the PDF Editor's "Delete signature?" was the reported case. The session preload replaces the page's three functions with wrappers that hand the call to the isolated world through a DOM event, which asks main synchronously and writes the answer back; pages see Chrome's return values (confirm → boolean, prompt → string or null) and no new global. Main answers from a sheet hanging under the toolbar, in the same surface as add-on approvals, that names who is asking: the site's host, the add-on's name for an add-on page or panel (identified by its path under the profile's extensions directory), or Theseus for its own pages. The sheet belongs to the tab that asked — hidden while another tab is in front, back when its tab returns — and a closing tab or window answers "cancel" so no renderer stays blocked. Windows without the chrome (installed apps, plain windows) get a native box with a proper title, and app.name now reads "Theseus Navigator" for whatever else still shows one.
86 lines
5.2 KiB
HTML
86 lines
5.2 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Dialog</title>
|
|
<style>
|
|
/* Same surface tokens as approval.html so page dialogs and add-on approvals
|
|
read as one family. */
|
|
:root { color-scheme: light dark;
|
|
--surface:#1c222c; --surface2:#0f1621; --line:rgba(255,255,255,.12);
|
|
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d; }
|
|
@media (prefers-color-scheme: light) {
|
|
:root { --surface:#ffffff; --surface2:#f1f4fa; --line:rgba(0,0,0,.12);
|
|
--ink:#1a1f2b; --mut:#5c6577; --dim:#8a93a5; --acid:#0AC18E; }
|
|
}
|
|
* { box-sizing: border-box; }
|
|
html, body { margin: 0; height: 100%; background: transparent; }
|
|
body { font: 13px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; color: var(--ink); }
|
|
.promptmask { position: fixed; inset: 0; background: rgba(0,0,0,.35);
|
|
display: grid; place-items: start center; padding-top: 0; }
|
|
/* A sheet hanging from the toolbar, the way Chrome anchors page dialogs:
|
|
the page stays visible and dimmed behind it. */
|
|
.promptbox { background: var(--surface); border: 1px solid var(--line); border-top: none;
|
|
border-radius: 0 0 12px 12px; padding: 14px 18px 14px;
|
|
width: min(480px, calc(100vw - 32px)); max-height: calc(100vh - 24px); overflow: auto;
|
|
box-shadow: 0 20px 60px #000b; animation: drop .12s ease-out; }
|
|
@keyframes drop { from { transform: translateY(-8px); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
.who { display: flex; align-items: center; gap: 7px; color: var(--dim); font-size: 11.5px; margin-bottom: 8px; min-width: 0; }
|
|
.who .src { color: var(--mut); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
.who .src.site { font: 600 12px/1.3 ui-monospace, "Cascadia Code", Consolas, monospace; }
|
|
.body { white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ink); font-size: 13.5px; margin: 0 0 12px; max-height: 40vh; overflow: auto; }
|
|
.body:empty { display: none; }
|
|
input.pin { display: block; width: 100%; margin: 0 0 12px; padding: 8px 10px; border-radius: 7px;
|
|
background: var(--surface2); color: var(--ink); border: 1px solid var(--line); font: inherit; font-size: 13px; }
|
|
input.pin:focus { outline: none; border-color: rgb(from var(--acid) r g b / .6); }
|
|
.pact { display: flex; gap: 6px; justify-content: flex-end; }
|
|
.pbtn { padding: 7px 16px; border-radius: 7px; border: 1px solid var(--line); background: var(--surface2);
|
|
color: var(--ink); cursor: pointer; font: inherit; font-size: 12.5px; min-width: 76px; }
|
|
.pbtn:hover { border-color: rgb(from var(--acid) r g b / .35); }
|
|
.pbtn.primary { background: var(--acid); color: #0b0e14; border-color: transparent; font-weight: 650; }
|
|
.pbtn:focus-visible { outline: 2px solid rgb(from var(--acid) r g b / .6); outline-offset: 1px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<script>
|
|
const esc = (s) => String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&":"&","<":"<",">":">",'"':""","'":"'" })[c]);
|
|
let current = null;
|
|
function finish(ok) {
|
|
if (!current) return;
|
|
const { reqId, kind } = current;
|
|
const input = document.getElementById("pin");
|
|
const value = kind === "prompt" ? (ok ? (input ? input.value : "") : null) : null;
|
|
current = null;
|
|
document.body.innerHTML = "";
|
|
window.jsdialog.answer(reqId, !!ok, value);
|
|
}
|
|
window.jsdialog.onShow((req) => {
|
|
current = req;
|
|
const who = req.who || { label: "This page", kind: "site" };
|
|
const icon = who.kind === "addon" ? "🧩" : who.kind === "app" ? "⛓" : "🌐";
|
|
const verb = req.kind === "prompt" ? "asks" : "says";
|
|
document.body.innerHTML =
|
|
`<div class="promptmask"><div class="promptbox" role="${req.kind === "alert" ? "alertdialog" : "dialog"}" aria-modal="true">
|
|
<div class="who"><span>${icon}</span><span class="src ${esc(who.kind)}" title="${esc(who.label)}">${esc(who.label)}</span><span>${verb}</span></div>
|
|
<div class="body">${esc(req.message)}</div>
|
|
${req.kind === "prompt" ? `<input class="pin" id="pin" type="text" value="${esc(req.def ?? "")}" spellcheck="false">` : ""}
|
|
<div class="pact">
|
|
${req.kind === "alert" ? "" : `<button class="pbtn" type="button" id="cancel">Cancel</button>`}
|
|
<button class="pbtn primary" type="button" id="ok">OK</button>
|
|
</div>
|
|
</div></div>`;
|
|
document.getElementById("ok").addEventListener("click", () => finish(true));
|
|
const c = document.getElementById("cancel"); if (c) c.addEventListener("click", () => finish(false));
|
|
const mask = document.querySelector(".promptmask");
|
|
mask.addEventListener("mousedown", (e) => { if (e.target === mask) finish(req.kind === "alert"); });
|
|
const input = document.getElementById("pin");
|
|
setTimeout(() => { if (input) { input.focus(); input.select(); } else document.getElementById("ok").focus(); }, 0);
|
|
});
|
|
document.addEventListener("keydown", (e) => {
|
|
if (!current) return;
|
|
if (e.key === "Escape") { e.preventDefault(); finish(current.kind === "alert"); }
|
|
else if (e.key === "Enter" && !(e.target && e.target.id === "cancel")) { e.preventDefault(); finish(true); }
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|