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 ────────

View File

@@ -151,10 +151,50 @@
z-index: 100; transition: opacity 0.4s;
}
#loader.hidden { opacity: 0; pointer-events: none; }
#tooltip {
position: fixed;
z-index: 200;
max-width: 320px;
background: #1a1e2c;
border: 1px solid #3b4560;
border-radius: 8px;
padding: 12px 14px;
font-size: 0.72rem;
line-height: 1.65;
color: #c8d0e0;
pointer-events: none;
box-shadow: 0 6px 28px rgba(0,0,0,0.75);
display: none;
}
#tooltip h2 {
font-size: 0.78rem;
color: #e8eaf6;
margin-bottom: 6px;
letter-spacing: 0.04em;
}
#tooltip strong { color: #a5d8ff; }
#tooltip code {
background: rgba(255,255,255,0.08);
border-radius: 3px;
padding: 0 3px;
font-size: 0.68rem;
color: #94e2d5;
}
#tooltip blockquote {
border-left: 2px solid #3b4560;
margin: 6px 0 0;
padding-left: 8px;
color: #8899aa;
}
#tooltip ul {
margin: 4px 0 0 14px;
}
</style>
</head>
<body>
<div id="loader">Loading WASM module…</div>
<div id="tooltip"></div>
<header>Analogue Synth Visualiser</header>