From 3568fa70ba9d5ecfdaf4864b9b594bee4851a19e Mon Sep 17 00:00:00 2001 From: Munan Date: Mon, 10 Aug 2026 14:42:30 +0800 Subject: [PATCH 1/3] Add keyboard shortcuts help overlay (press ?) --- packages/core/src/editor.js | 1 + packages/core/src/ui.js | 70 +++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/packages/core/src/editor.js b/packages/core/src/editor.js index 6a29560..7a58362 100644 --- a/packages/core/src/editor.js +++ b/packages/core/src/editor.js @@ -1262,6 +1262,7 @@ export class Editor { else this.setTool('select') return } + if (k === '?') { e.preventDefault(); this.emit('help'); return } // wipe the board — two modifiers deep, and undoable like any other edit if (meta && e.shiftKey && (k === 'delete' || k === 'backspace')) { e.preventDefault() diff --git a/packages/core/src/ui.js b/packages/core/src/ui.js index 3878623..160b7b9 100644 --- a/packages/core/src/ui.js +++ b/packages/core/src/ui.js @@ -419,6 +419,75 @@ export function buildUI(editor, { hidden = false, onSave, themeToggle = true, gr a.click() setTimeout(() => URL.revokeObjectURL(a.href), 5000) } + // ---- keyboard help overlay (press ?) ----------------------------------- + const SHORTCUTS = [ + { label: 'Tools', rows: [ + ['Select', 'V / 1'], ['Hand (hold Space)', 'H'], ['Draw', 'D / P / B'], + ['Highlight', 'I'], ['Eraser', 'E'], ['Laser', 'K'], + ['Arrow', 'A'], ['Line', 'L'], ['Shape', 'G'], + ['Rectangle', 'R'], ['Ellipse', 'O'], ['Text', 'T'], ['Sticky note', 'N'], + ]}, + { label: 'Edit', rows: [ + ['Undo', '⌘Z'], ['Redo', '⇧⌘Z'], ['Select all', '⌘A'], + ['Copy', '⌘C'], ['Cut', '⌘X'], ['Paste', '⌘V'], ['Duplicate', '⌘D'], + ]}, + { label: 'Arrange', rows: [ + ['Bring to front', ']'], ['Send to back', '['], ['Nudge', 'Arrows (Shift = 8px)'], + ]}, + { label: 'View', rows: [ + ['Zoom to fit', '⇧1'], ['Reset zoom', '⇧0'], ['Zoom in / out', '⌘+ / ⌘−'], + ]}, + { label: 'Board', rows: [ + ['Delete selection', '⌫'], ['Clear board', '⇧⌘⌫'], + ['Edit / finish text', 'Enter'], ['Cancel / deselect', 'Esc'], + ]}, + { label: 'Help', rows: [['Keyboard shortcuts', '?']] }, + ] + const buildHelp = () => { + const backdrop = el('div', 'qd-help-backdrop') + const panel = el('div', 'qd-help-panel') + const head = el('div', 'qd-help-head') + const title = el('div', 'qd-help-title'); title.textContent = 'Keyboard shortcuts' + const close = el('button', 'qd-help-close') + close.innerHTML = '' + close.setAttribute('aria-label', 'Close') + head.appendChild(title); head.appendChild(close) + panel.appendChild(head) + for (const g of SHORTCUTS) { + const sec = el('div', 'qd-help-group') + const gl = el('div', 'qd-help-glabel'); gl.textContent = g.label + sec.appendChild(gl) + for (const [name, keys] of g.rows) { + const row = el('div', 'qd-help-row') + const nm = el('span', 'qd-help-name'); nm.textContent = name + const kc = el('span', 'qd-help-keys'); kc.textContent = keys + row.appendChild(nm); row.appendChild(kc) + sec.appendChild(row) + } + panel.appendChild(sec) + } + backdrop.appendChild(panel) + backdrop.addEventListener('pointerdown', (e) => { if (e.target === backdrop) closeHelp() }) + close.addEventListener('click', () => closeHelp()) + return backdrop + } + let helpEl = null + const closeHelp = () => { + if (!helpEl) return + helpEl.remove(); helpEl = null + document.removeEventListener('keydown', onHelpKey, true) + } + const onHelpKey = (e) => { + if (e.key === 'Escape' || e.key === '?') { e.preventDefault(); e.stopPropagation(); closeHelp() } + } + const toggleHelp = () => { + if (helpEl) { closeHelp(); return } + helpEl = buildHelp() + ui.appendChild(helpEl) + document.addEventListener('keydown', onHelpKey, true) + refresh() + } + editor.on('help', toggleHelp) // ---- responsive fit ------------------------------------------------------ // Instead of scaling down, the dock sheds tools into the "more" flyout as @@ -516,6 +585,7 @@ export function buildUI(editor, { hidden = false, onSave, themeToggle = true, gr offs.forEach((f) => f()) ro.disconnect() root.removeEventListener('pointerdown', closeOnCanvas, { capture: true }) + closeHelp() ui.remove() }, } From 29a82c49311bb29ff5ba954a4f52e4ff89b0ac05 Mon Sep 17 00:00:00 2001 From: Munan Date: Mon, 10 Aug 2026 14:46:09 +0800 Subject: [PATCH 2/3] Add keyboard shortcuts help overlay (press ?) --- packages/core/src/quickdraw.css | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/core/src/quickdraw.css b/packages/core/src/quickdraw.css index 6c4b93c..fd1f313 100644 --- a/packages/core/src/quickdraw.css +++ b/packages/core/src/quickdraw.css @@ -429,3 +429,41 @@ -webkit-user-select: text; user-select: text; } +/* keyboard help overlay (press ?) */ +.qd-help-backdrop { + position: absolute; inset: 0; z-index: 50; + display: flex; align-items: center; justify-content: center; + padding: 16px; background: rgba(0, 0, 0, 0.28); + pointer-events: auto; animation: qd-help-in 140ms ease; +} +.qd-help-panel { + width: min(440px, 100%); max-height: 82%; overflow: auto; + padding: 16px 18px 18px; border-radius: 14px; + background: var(--qd-pop-bg); color: var(--qd-ink); + border: 1px solid var(--qd-border); box-shadow: var(--qd-pop-shadow); + font: 500 13px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; +} +.qd-help-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } +.qd-help-title { font-size: 15px; font-weight: 600; color: var(--qd-ink-strong); } +.qd-help-close { + display: flex; width: 26px; height: 26px; padding: 0; + align-items: center; justify-content: center; cursor: pointer; + border: 1px solid var(--qd-border); border-radius: 7px; + background: var(--qd-bar-bg); color: var(--qd-ink); +} +.qd-help-close:hover { background: var(--qd-hover); } +.qd-help-close svg { width: 15px; height: 15px; } +.qd-help-group { margin-bottom: 14px; } +.qd-help-group:last-child { margin-bottom: 0; } +.qd-help-glabel { + font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; + color: var(--qd-ink-faint); margin-bottom: 6px; +} +.qd-help-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 3px 0; } +.qd-help-name { color: var(--qd-ink); } +.qd-help-keys { + font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + color: var(--qd-ink-strong); padding: 2px 7px; border-radius: 6px; + background: var(--qd-bar-bg); border: 1px solid var(--qd-border); white-space: nowrap; +} +@keyframes qd-help-in { from { opacity: 0 } to { opacity: 1 } } From ab5f2c63f322f61d722e0a740f7f6eddb28753c5 Mon Sep 17 00:00:00 2001 From: Munan Date: Mon, 10 Aug 2026 14:58:57 +0800 Subject: [PATCH 3/3] Add keyboard shortcuts help overlay (press ?) --- packages/core/src/editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/editor.js b/packages/core/src/editor.js index 7a58362..e0f78cf 100644 --- a/packages/core/src/editor.js +++ b/packages/core/src/editor.js @@ -1262,7 +1262,7 @@ export class Editor { else this.setTool('select') return } - if (k === '?') { e.preventDefault(); this.emit('help'); return } + if (k === '?') { e.preventDefault(); this.emit('help'); return } // wipe the board — two modifiers deep, and undoable like any other edit if (meta && e.shiftKey && (k === 'delete' || k === 'backspace')) { e.preventDefault()