Setup 3d6d3d6aeacf482284707f50d129315d07a593e09b5d68af852abe5aa0ed4c92
Portable 6c8c5ef212bfccb377e17404e83a18cf2f09771d0eb3e0ec86a85587efdcfc9e
Three address-bar suggestion fixes.
Per-row ✕ delete on hover. Clicks on the X call address-forget instead
of address-pick; the row disappears optimistically in the picker and
main drops the entry from history + persists. Sender-URL gated to the
picker's own file:// origin.
Enter with a highlighted suggestion now navigates to THAT url. The
URL input handler tracks a pickerHasCursor flag that flips true on
ArrowDown/ArrowUp and false on any input; Enter with cursor forwards
to the picker's own submit path via addressCursor("enter") — before
this fix, Enter always ran goURL against the typed letters, which
submitted them as a web search instead of opening the selected url.
Address bar reliably shows the picked URL. onAddressPicked now arms
an overrideUrlBarUntil = now+1500ms flag; the onTabs handler treats
that window as "force write the url", bypassing the focus-guard that
was leaving the bar blank when blur() hadn't landed yet.
Deployed: scp + sia-upload of both trees, verified LIVE 0.3.4.
66 lines
3.6 KiB
HTML
66 lines
3.6 KiB
HTML
<!doctype html>
|
|
<html><head><meta charset="utf-8">
|
|
<style>
|
|
:root { color-scheme: light dark; font-family: system-ui, sans-serif; }
|
|
html, body { margin: 0; background: transparent; }
|
|
.menu { background: #1c222c; border: 1px solid #ffffff26; border-radius: 10px; box-shadow: 0 12px 34px #000c; overflow: hidden; color: #e7eaf1; }
|
|
.item { display: flex; align-items: center; gap: 10px; padding: 8px 14px; cursor: pointer; font-size: 13px; border-bottom: 1px solid #ffffff08; }
|
|
.item:last-child { border-bottom: 0; }
|
|
.item:hover, .item.on { background: #ffffff12; }
|
|
.item .ic { flex: none; font-size: 12px; color: #7f8aa0; width: 14px; text-align: center; }
|
|
.item .txt { flex: 1; overflow: hidden; }
|
|
.item .url { color: #e7eaf1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; }
|
|
.item .ttl { color: #7f8aa0; font-size: 11.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
.item .del { flex: none; padding: 2px 8px; border-radius: 5px; color: #7f8aa0; font-size: 14px;
|
|
opacity: 0; cursor: pointer; user-select: none; }
|
|
.item:hover .del { opacity: .65; }
|
|
.item .del:hover { opacity: 1; color: #f6768a; background: rgba(246,118,138,.12); }
|
|
.empty { padding: 14px; color: #7f8aa0; font-size: 12.5px; text-align: center; }
|
|
@media (prefers-color-scheme: light) {
|
|
.menu { background: #ffffff; border-color: rgba(0,0,0,.15); color: #1a1f28; }
|
|
.item:hover, .item.on { background: rgba(0,0,0,.05); }
|
|
.item { border-bottom-color: rgba(0,0,0,.06); }
|
|
.item .url { color: #1a1f28; }
|
|
.item .ttl, .item .ic, .item .del, .empty { color: #7b8494; }
|
|
}
|
|
</style></head>
|
|
<body>
|
|
<div class="menu"><div id="list"></div></div>
|
|
<script>
|
|
const $ = (id) => document.getElementById(id);
|
|
const esc = (s) => String(s || "").replace(/</g, "<").replace(/"/g, """);
|
|
let items = []; let cursor = -1;
|
|
function report() { requestAnimationFrame(() => { try { window.picker.resize(document.querySelector(".menu").offsetHeight); } catch (e) {} }); }
|
|
function paint() {
|
|
const list = $("list");
|
|
if (!items.length) { list.innerHTML = `<div class="empty">No history yet.</div>`; report(); return; }
|
|
list.innerHTML = items.map((h, i) =>
|
|
`<div class="item ${i === cursor ? "on" : ""}" data-url="${esc(h.url)}"><span class="ic">🕘</span><span class="txt"><div class="url">${esc(h.url)}</div>${h.title ? `<div class="ttl">${esc(h.title)}</div>` : ""}</span><span class="del" data-del="${esc(h.url)}" title="Remove from history">✕</span></div>`
|
|
).join("");
|
|
list.querySelectorAll(".item").forEach((el) => el.onclick = (ev) => {
|
|
// Delete-x click removes the entry instead of navigating.
|
|
if (ev.target.dataset.del) {
|
|
ev.stopPropagation();
|
|
const url = ev.target.dataset.del;
|
|
window.picker.forget(url);
|
|
// Optimistically drop it from the list so the UI reacts before main
|
|
// pushes the refreshed suggestions.
|
|
items = items.filter((h) => h.url !== url);
|
|
if (cursor >= items.length) cursor = items.length - 1;
|
|
paint();
|
|
return;
|
|
}
|
|
window.picker.pick(el.dataset.url);
|
|
});
|
|
report();
|
|
}
|
|
window.picker.onSuggestions((data) => { items = data.suggestions || []; cursor = -1; paint(); });
|
|
window.picker.onCursor((dir) => {
|
|
if (!items.length) return;
|
|
if (dir === "next") cursor = (cursor + 1) % items.length;
|
|
else if (dir === "prev") cursor = (cursor - 1 + items.length) % items.length;
|
|
else if (dir === "enter") { if (cursor >= 0) window.picker.pick(items[cursor].url); return; }
|
|
paint();
|
|
});
|
|
</script>
|
|
</body></html>
|