Add popup help to elements

This commit is contained in:
2026-03-27 08:29:11 +00:00
parent b8d3dc48ab
commit ea381fe433
11 changed files with 233 additions and 4 deletions

86
www/bootstrap.js vendored
View File

@@ -28,6 +28,86 @@ function fitCanvas(canvas) {
}
}
// ── Tooltip helpers ───────────────────────────────────────────────────────────
/** Minimal markdown → HTML converter for the module description tooltips. */
function mdToHtml(md) {
const lines = md.split("\n");
const out = [];
let inUl = false;
for (const raw of lines) {
const line = raw.trimEnd();
if (line.startsWith("## ")) {
if (inUl) { out.push("</ul>"); inUl = false; }
out.push(`<h2>${esc(line.slice(3))}</h2>`);
} else if (line.startsWith("> ")) {
if (inUl) { out.push("</ul>"); inUl = false; }
out.push(`<blockquote>${inline(line.slice(2))}</blockquote>`);
} else if (line.startsWith("- ")) {
if (!inUl) { out.push("<ul>"); inUl = true; }
out.push(`<li>${inline(line.slice(2))}</li>`);
} else {
if (inUl) { out.push("</ul>"); inUl = false; }
if (line === "") {
out.push("<br>");
} else {
out.push(`<p>${inline(line)}</p>`);
}
}
}
if (inUl) out.push("</ul>");
return out.join("");
}
function esc(s) {
return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
}
function inline(s) {
// `code`, **bold**
return esc(s)
.replace(/`([^`]+)`/g, "<code>$1</code>")
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
}
const _tooltip = { el: null };
function getTooltipEl() {
if (!_tooltip.el) _tooltip.el = document.getElementById("tooltip");
return _tooltip.el;
}
function updateTooltip(clientX, clientY, markdown) {
const el = getTooltipEl();
if (!el) return;
if (!markdown) { hideTooltip(); return; }
el.innerHTML = mdToHtml(markdown);
el.style.display = "block";
// Position: prefer right of cursor, flip left if off-screen
const margin = 14;
const vw = window.innerWidth;
const vh = window.innerHeight;
let left = clientX + margin;
let top = clientY + margin;
el.style.left = "0";
el.style.top = "0";
const tw = el.offsetWidth;
const th = el.offsetHeight;
if (left + tw > vw - 4) left = clientX - tw - margin;
if (top + th > vh - 4) top = clientY - th - margin;
el.style.left = left + "px";
el.style.top = top + "px";
}
function hideTooltip() {
const el = getTooltipEl();
if (el) el.style.display = "none";
}
// ── Resize handle ─────────────────────────────────────────────────────────────
function initResizeHandle() {
@@ -434,8 +514,12 @@ async function bootstrap() {
// ── Patch bay pointer events ──────────────────────────────────────────
pbCanvas.addEventListener("pointerdown", e => patchbay.on_pointer_down(e.offsetX, e.offsetY));
pbCanvas.addEventListener("pointermove", e => patchbay.on_pointer_move(e.offsetX, e.offsetY));
pbCanvas.addEventListener("pointermove", e => {
patchbay.on_pointer_move(e.offsetX, e.offsetY);
updateTooltip(e.clientX, e.clientY, patchbay.get_tooltip_at(e.offsetX, e.offsetY));
});
pbCanvas.addEventListener("pointerup", e => patchbay.on_pointer_up(e.offsetX, e.offsetY));
pbCanvas.addEventListener("pointerleave", () => hideTooltip());
pbCanvas.addEventListener("dblclick", e => patchbay.on_double_click(e.offsetX, e.offsetY));
// ── Patch router: Web Audio graph driven by patch bay topology ────────