generated from coulomb/repo-seed
feat(dashboard): extend suggestions to TOC right margin + 1s shift delay
- Shift+click now works on #observablehq-toc links, KPI boxes, and [id] elements - _inferWidgetName detects TOC context and labels suggestions accordingly - Click handler adds inToc branch alongside existing inSidebar - _updateMode: 1-second setTimeout before activating highlight mode so normal Shift+typing doesn't flicker the UI; clears immediately on Shift release or window blur Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -144,14 +144,17 @@ function _ensureStyles() {
|
||||
.impr-mode-shift,
|
||||
.impr-mode-shift * { cursor: copy !important; }
|
||||
|
||||
/* Highlight "widget" elements so the user sees what can be annotated */
|
||||
/* Highlight annotatable elements in main content, left nav, and right TOC */
|
||||
.impr-mode-shift #observablehq-main figure,
|
||||
.impr-mode-shift #observablehq-main h2,
|
||||
.impr-mode-shift #observablehq-main h3,
|
||||
.impr-mode-shift #observablehq-main h4,
|
||||
.impr-mode-shift #observablehq-main [data-widget-name],
|
||||
.impr-mode-shift #observablehq-sidebar a,
|
||||
.impr-mode-shift #observablehq-sidebar summary {
|
||||
.impr-mode-shift #observablehq-sidebar summary,
|
||||
.impr-mode-shift #observablehq-toc a,
|
||||
.impr-mode-shift #observablehq-toc .kpi-infobox,
|
||||
.impr-mode-shift #observablehq-toc [id] {
|
||||
outline: 1px dashed rgba(99, 102, 241, 0.45);
|
||||
background: rgba(99, 102, 241, 0.055) !important;
|
||||
border-radius: 4px;
|
||||
@@ -163,7 +166,19 @@ function _ensureStyles() {
|
||||
|
||||
/* ── Widget name inference ─────────────────────────────────────────────── */
|
||||
function _inferWidgetName(target) {
|
||||
// 0. Sidebar navigation: nav link text or section heading
|
||||
// 0a. Right-margin TOC: link text, KPI box title, or nearest labelled container
|
||||
if (target.closest("#observablehq-toc")) {
|
||||
const link = target.closest("a");
|
||||
if (link) return (link.textContent.trim() || "TOC link") + " (TOC)";
|
||||
const kpiTitle = target.closest(".kpi-infobox")
|
||||
?.querySelector(".kpi-infobox-title");
|
||||
if (kpiTitle) return kpiTitle.textContent.trim() + " (sidebar widget)";
|
||||
const labelled = target.closest("[id]");
|
||||
if (labelled) return labelled.id.replace(/-/g, " ") + " (TOC)";
|
||||
return "Right margin";
|
||||
}
|
||||
|
||||
// 0b. Left sidebar navigation: nav link text or section heading
|
||||
if (target.closest("#observablehq-sidebar")) {
|
||||
const link = target.closest("a");
|
||||
if (link) return link.textContent.trim() || "Nav link";
|
||||
@@ -232,12 +247,20 @@ export function initImprovementModal({ apiBase = "http://127.0.0.1:8000", domain
|
||||
_initialized = true;
|
||||
_ensureStyles();
|
||||
|
||||
// Track modifier state via keydown, keyup, AND mousemove so mode
|
||||
// stays in sync even when focus changes between elements.
|
||||
// Track modifier state. Highlighting is delayed 1 s so normal Shift+typing
|
||||
// doesn't trigger the visual mode change; releasing Shift cancels immediately.
|
||||
let _shiftTimer = null;
|
||||
function _updateMode(e) {
|
||||
if (e.shiftKey) {
|
||||
document.body.classList.add("impr-mode-shift");
|
||||
if (!_shiftTimer && !document.body.classList.contains("impr-mode-shift")) {
|
||||
_shiftTimer = setTimeout(() => {
|
||||
document.body.classList.add("impr-mode-shift");
|
||||
_shiftTimer = null;
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
clearTimeout(_shiftTimer);
|
||||
_shiftTimer = null;
|
||||
document.body.classList.remove("impr-mode-shift");
|
||||
}
|
||||
}
|
||||
@@ -245,23 +268,27 @@ export function initImprovementModal({ apiBase = "http://127.0.0.1:8000", domain
|
||||
window.addEventListener("keyup", _updateMode);
|
||||
window.addEventListener("mousemove", _updateMode);
|
||||
// Clear on blur in case Shift is held when the window loses focus
|
||||
window.addEventListener("blur", () => document.body.classList.remove("impr-mode-shift"));
|
||||
window.addEventListener("blur", () => {
|
||||
clearTimeout(_shiftTimer);
|
||||
_shiftTimer = null;
|
||||
document.body.classList.remove("impr-mode-shift");
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!e.shiftKey) return;
|
||||
const inSidebar = !!e.target.closest("#observablehq-sidebar");
|
||||
// Block shift-clicks on form controls; allow sidebar links (preventDefault stops navigation)
|
||||
if (!inSidebar && e.target.matches("input, textarea, select, a, button")) return;
|
||||
if (inSidebar && e.target.matches("input, textarea, select")) return;
|
||||
const inToc = !!e.target.closest("#observablehq-toc");
|
||||
// Block shift-clicks on form controls; allow nav/toc links (preventDefault stops navigation)
|
||||
if (!inSidebar && !inToc && e.target.matches("input, textarea, select, a, button")) return;
|
||||
if ((inSidebar || inToc) && e.target.matches("input, textarea, select")) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const widgetName = _inferWidgetName(e.target);
|
||||
const pageName = inSidebar
|
||||
? "Navigation"
|
||||
: (document.title
|
||||
? document.title.replace(" – Custodian State Hub", "").trim()
|
||||
: (location.pathname.replace(/^\//, "") || "Overview"));
|
||||
const currentPage = document.title
|
||||
? document.title.replace(" – Custodian State Hub", "").trim()
|
||||
: (location.pathname.replace(/^\//, "") || "Overview");
|
||||
const pageName = inSidebar ? "Navigation" : currentPage;
|
||||
|
||||
// Remove any open modal
|
||||
document.getElementById("_impr-root")?.remove();
|
||||
|
||||
Reference in New Issue
Block a user