fix(dashboard): robust shift-mode tracking via mousemove + element highlights

- updateMode() now subscribes to keydown, keyup AND mousemove so the
  body class stays in sync regardless of where focus is (mirrors the
  pattern from the working modifier-click demo)
- cursor: copy replaces crosshair (matches copy-affordance semantics)
- figure, h2–h4 and [data-widget-name] elements get a dashed indigo
  outline + subtle background tint when shift is held, so the user
  can see which elements are annotatable before clicking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 00:18:56 +01:00
parent 46f4b0c25d
commit 4d0941b524

View File

@@ -140,9 +140,21 @@ function _ensureStyles() {
@keyframes _im-tin { from { opacity:0; transform:translateX(-50%) translateY(6px) } to { opacity:1; transform:translateX(-50%) translateY(0) } } @keyframes _im-tin { from { opacity:0; transform:translateX(-50%) translateY(6px) } to { opacity:1; transform:translateX(-50%) translateY(0) } }
@keyframes _im-tout { from { opacity:1 } to { opacity:0 } } @keyframes _im-tout { from { opacity:1 } to { opacity:0 } }
/* ── Shift-held cursor override (applied to <body>) ─────────────────────── */ /* ── Shift-held mode: cursor + element highlighting ─────────────────────── */
.impr-shift-mode, .impr-mode-shift,
.impr-shift-mode * { cursor: crosshair !important; } .impr-mode-shift * { cursor: copy !important; }
/* Highlight "widget" elements so the user sees what can be annotated */
.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] {
outline: 1px dashed rgba(99, 102, 241, 0.45);
background: rgba(99, 102, 241, 0.055) !important;
border-radius: 4px;
transition: background 0.1s, outline 0.1s;
}
`; `;
document.head.append(s); document.head.append(s);
} }
@@ -197,7 +209,7 @@ function _toast(msg) {
let _initialized = false; let _initialized = false;
/** /**
* Wire right-click → improvement modal on the current page. * Wire Shift+click → improvement modal on the current page.
* Safe to call multiple times — only the first call takes effect. * Safe to call multiple times — only the first call takes effect.
* *
* @param {object} opts * @param {object} opts
@@ -209,15 +221,20 @@ export function initImprovementModal({ apiBase = "http://127.0.0.1:8000", domain
_initialized = true; _initialized = true;
_ensureStyles(); _ensureStyles();
// Shift-held cursor indicator // Track modifier state via keydown, keyup, AND mousemove so mode
document.addEventListener("keydown", (e) => { // stays in sync even when focus changes between elements.
if (e.key === "Shift") document.body.classList.add("impr-shift-mode"); function _updateMode(e) {
}); if (e.shiftKey) {
document.addEventListener("keyup", (e) => { document.body.classList.add("impr-mode-shift");
if (e.key === "Shift") document.body.classList.remove("impr-shift-mode"); } else {
}); document.body.classList.remove("impr-mode-shift");
// Remove shift-mode if window loses focus while Shift is held }
window.addEventListener("blur", () => document.body.classList.remove("impr-shift-mode")); }
window.addEventListener("keydown", _updateMode);
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"));
document.addEventListener("click", (e) => { document.addEventListener("click", (e) => {
if (!e.shiftKey) return; if (!e.shiftKey) return;