Add popup help to elements
This commit is contained in:
86
www/bootstrap.js
vendored
86
www/bootstrap.js
vendored
@@ -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,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
}
|
||||
|
||||
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 ────────
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user