diff --git a/TODO.md b/TODO.md index 9205d38..0bb86b9 100644 --- a/TODO.md +++ b/TODO.md @@ -58,17 +58,17 @@ Expose only controls that apply to the active tool or current selection. Use one visual system across editor chrome, menus, dialogs, popovers, and selection UI. -- [ ] Keep one primary Inkfinite accent family across light and dark themes -- [ ] Tune accent luminance and contrast independently for each theme -- [ ] Audit `editor/styles.css` and remove or migrate legacy theme, typography, +- [x] Keep one primary Inkfinite accent family across light and dark themes +- [x] Tune accent luminance and contrast independently for each theme +- [x] Audit `editor/styles.css` and remove or migrate legacy theme, typography, reset, and token rules -- [ ] Use the shared `--ink-*` tokens for editor components instead of local +- [x] Use the shared `--ink-*` tokens for editor components instead of local color or spacing systems -- [ ] Standardize panel borders, elevation, radii, and focus treatment -- [ ] Reserve irregular radii and hard offset shadows for deliberate +- [x] Standardize panel borders, elevation, radii, and focus treatment +- [x] Reserve irregular radii and hard offset shadows for deliberate canvas-oriented accents rather than every control -- [ ] Remove duplicate and contradictory toolbar/menu CSS declarations -- [ ] Verify text, controls, selection states, and focus indicators meet +- [x] Remove duplicate and contradictory toolbar/menu CSS declarations +- [x] Verify text, controls, selection states, and focus indicators meet contrast requirements in both themes ### Color controls @@ -195,4 +195,4 @@ Apply the same behavior to equivalent controls throughout the editor. - [ ] Revisit skill organization after SVG and MCP workflows stabilize - [ ] Export SVG as copyable code - [ ] Export PNG to clipboard -- [ ] Add a move handle to the layer pane +- [x] Add a move handle to the layer pane diff --git a/apps/desktop/src/routes/+layout.svelte b/apps/desktop/src/routes/+layout.svelte index 73802b9..873f9a4 100644 --- a/apps/desktop/src/routes/+layout.svelte +++ b/apps/desktop/src/routes/+layout.svelte @@ -4,7 +4,6 @@ import { listen } from '@tauri-apps/api/event'; import { onMount } from 'svelte'; - import '$editor/styles.css'; import '@inkfinite/ui/styles.css'; let { children } = $props(); diff --git a/packages/ui/package.json b/packages/ui/package.json index 6ec23dd..a083dda 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -34,7 +34,6 @@ "default": "./dist/editor/index.js" }, "./editor/favicon.svg": "./dist/editor/assets/favicon.svg", - "./editor/styles.css": "./dist/editor/styles.css", "./styles.css": "./dist/styles/index.css" }, "files": [ diff --git a/packages/ui/src/lib/components/Button.svelte b/packages/ui/src/lib/components/Button.svelte index e933d8d..c61c655 100644 --- a/packages/ui/src/lib/components/Button.svelte +++ b/packages/ui/src/lib/components/Button.svelte @@ -58,34 +58,30 @@ gap: var(--ink-space-2); padding-inline: var(--ink-space-4); border: var(--ink-line-width) solid var(--ink-border-strong); - border-radius: var(--ink-radius-wobbly); + border-radius: var(--ink-radius-control); color: var(--ink-text); background: var(--ink-surface-raised); - box-shadow: var(--ink-shadow-offset) var(--ink-shadow-offset) 0 var(--ink-shadow-color); + box-shadow: var(--ink-shadow-control); font: 650 var(--ink-type-sm) / 1 var(--ink-font-body); letter-spacing: 0.01em; cursor: pointer; transition: - translate var(--ink-duration-fast) var(--ink-ease-out), - box-shadow var(--ink-duration-fast) var(--ink-ease-out), + transform var(--ink-duration-fast) var(--ink-ease-out), background-color var(--ink-duration-fast) var(--ink-ease-out); } .ink-button:hover:not(:disabled) { background: var(--ink-surface-hover); - translate: -1px -1px; - box-shadow: calc(var(--ink-shadow-offset) + 1px) calc(var(--ink-shadow-offset) + 1px) 0 - var(--ink-shadow-color); + transform: translateY(-1px); } .ink-button:active:not(:disabled) { - translate: var(--ink-shadow-offset) var(--ink-shadow-offset); - box-shadow: 0 0 0 var(--ink-shadow-color); + transform: scale(0.98); } .ink-button:focus-visible { - outline: 3px solid var(--ink-focus); - outline-offset: 3px; + outline: var(--ink-line-width-strong) solid var(--ink-focus); + outline-offset: 2px; } .ink-button:disabled { @@ -114,8 +110,12 @@ box-shadow: none; } + .ink-button[data-variant='ghost']:focus-visible { + outline-offset: 0; + } + .ink-button[data-variant='ghost']:hover:not(:disabled) { - translate: 0; + transform: none; box-shadow: none; } diff --git a/packages/ui/src/lib/components/ColorPicker.svelte b/packages/ui/src/lib/components/ColorPicker.svelte index 880d7b4..cab87d6 100644 --- a/packages/ui/src/lib/components/ColorPicker.svelte +++ b/packages/ui/src/lib/components/ColorPicker.svelte @@ -345,12 +345,10 @@ height: 2.5rem; padding: 0.25rem; border: var(--ink-line-width) solid var(--ink-border-strong); - border-radius: var(--ink-radius-wobbly-small); - box-shadow: var(--ink-shadow-offset) var(--ink-shadow-offset) 0 var(--ink-shadow-color); + border-radius: var(--ink-radius-control-small); + box-shadow: var(--ink-shadow-control); cursor: pointer; - transition: - translate var(--ink-duration-fast) var(--ink-ease-out), - box-shadow var(--ink-duration-fast) var(--ink-ease-out); + transition: transform var(--ink-duration-fast) var(--ink-ease-out); } .color-picker__trigger--mixed { @@ -364,22 +362,19 @@ } .color-picker__trigger:hover:not(:disabled) { - translate: -1px -1px; - box-shadow: calc(var(--ink-shadow-offset) + 1px) calc(var(--ink-shadow-offset) + 1px) 0 - var(--ink-shadow-color); + transform: translateY(-1px); } .color-picker__trigger:active:not(:disabled) { - translate: var(--ink-shadow-offset) var(--ink-shadow-offset); - box-shadow: 0 0 0 var(--ink-shadow-color); + transform: scale(0.98); } .color-picker__trigger:focus-visible, .color-picker__swatch:focus-visible, .color-picker__apply:focus-visible, .color-picker__hex-input:focus-visible { - outline: 3px solid var(--ink-focus); - outline-offset: 3px; + outline: var(--ink-line-width-strong) solid var(--ink-focus); + outline-offset: 2px; } .color-picker__trigger:disabled { @@ -496,7 +491,7 @@ min-height: var(--ink-control-height-sm); padding: 0 var(--ink-space-2); border: var(--ink-line-width) solid var(--ink-border); - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); color: var(--ink-text); background: var(--ink-surface); font: 600 var(--ink-type-sm) / 1 var(--ink-font-body); @@ -511,7 +506,7 @@ min-height: var(--ink-control-height-sm); padding-inline: var(--ink-space-2); border: var(--ink-line-width) solid var(--ink-border-strong); - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); color: var(--ink-text); background: var(--ink-surface); font-size: var(--ink-type-xs); diff --git a/packages/ui/src/lib/components/ContextMenu.svelte b/packages/ui/src/lib/components/ContextMenu.svelte index 8ce0ef3..05f6d52 100644 --- a/packages/ui/src/lib/components/ContextMenu.svelte +++ b/packages/ui/src/lib/components/ContextMenu.svelte @@ -187,8 +187,7 @@ background: color-mix(in srgb, var(--ink-surface-raised) 96%, transparent); box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink-border) 72%, transparent), - 0 12px 32px color-mix(in srgb, var(--ink-shadow-color) 30%, transparent), - 0 2px 7px color-mix(in srgb, var(--ink-shadow-color) 22%, transparent); + var(--ink-shadow-popover); backdrop-filter: blur(16px); } @@ -200,7 +199,7 @@ gap: var(--ink-space-2); padding: 0 var(--ink-space-2); border: 0; - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); color: inherit; background: transparent; font: 600 var(--ink-type-sm) / 1.2 var(--ink-font-body); diff --git a/packages/ui/src/lib/components/Dialog.svelte b/packages/ui/src/lib/components/Dialog.svelte index 835ba5b..5859308 100644 --- a/packages/ui/src/lib/components/Dialog.svelte +++ b/packages/ui/src/lib/components/Dialog.svelte @@ -109,19 +109,19 @@ align-items: center; justify-content: center; z-index: 1000; - animation: fadeIn 0.15s ease-out; + animation: fadeIn var(--ink-duration-overlay) var(--ink-ease-out); } .dialog__content { background-color: var(--ink-surface-raised); color: var(--ink-text); - border: 0.75px solid var(--ink-border-strong); + border: var(--ink-line-width) solid var(--ink-border); border-radius: var(--ink-dialog-radius, var(--ink-radius-panel)); - box-shadow: var(--ink-shadow-panel); + box-shadow: var(--ink-shadow-popover); max-width: 90vw; max-height: 90vh; overflow: auto; - animation: slideIn 0.2s ease-out; + animation: slideIn var(--ink-duration-overlay) var(--ink-ease-out); outline: none; } diff --git a/packages/ui/src/lib/components/IconButton.svelte b/packages/ui/src/lib/components/IconButton.svelte index 76fb29e..f4f5b7b 100644 --- a/packages/ui/src/lib/components/IconButton.svelte +++ b/packages/ui/src/lib/components/IconButton.svelte @@ -43,25 +43,30 @@ height: var(--ink-control-height); place-items: center; border: var(--ink-line-width) solid transparent; - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); color: var(--ink-text-muted); background: transparent; cursor: pointer; + transition: + color var(--ink-duration-fast) var(--ink-ease-out), + background-color var(--ink-duration-fast) var(--ink-ease-out), + border-color var(--ink-duration-fast) var(--ink-ease-out); } - .ink-icon-button:hover:not(:disabled), - .ink-icon-button[data-selected] { - border-color: var(--ink-border-strong); + .ink-icon-button:hover:not(:disabled) { + border-color: var(--ink-border); color: var(--ink-text); background: var(--ink-surface-hover); } .ink-icon-button[data-selected] { - box-shadow: 2px 2px 0 var(--ink-shadow-color); + border-color: var(--ink-accent); + color: var(--ink-accent-text); + background: color-mix(in srgb, var(--ink-accent) 14%, var(--ink-surface-raised)); } .ink-icon-button:focus-visible { - outline: 3px solid var(--ink-focus); + outline: var(--ink-line-width-strong) solid var(--ink-focus); outline-offset: 2px; } diff --git a/packages/ui/src/lib/components/Panel.svelte b/packages/ui/src/lib/components/Panel.svelte index 3eefffb..0eeb59e 100644 --- a/packages/ui/src/lib/components/Panel.svelte +++ b/packages/ui/src/lib/components/Panel.svelte @@ -46,7 +46,7 @@ gap: var(--ink-space-5); min-width: min(100%, 18rem); padding: var(--ink-space-5); - border: var(--ink-line-width) solid var(--ink-border-strong); + border: var(--ink-line-width) solid var(--ink-border); border-radius: var(--ink-radius-panel); color: var(--ink-text); background: var(--ink-surface-raised); diff --git a/packages/ui/src/lib/components/Sheet.svelte b/packages/ui/src/lib/components/Sheet.svelte index 5aa7ee6..7b80140 100644 --- a/packages/ui/src/lib/components/Sheet.svelte +++ b/packages/ui/src/lib/components/Sheet.svelte @@ -91,7 +91,7 @@ background-color: color-mix(in srgb, var(--ink-shadow-color) 52%, transparent); display: flex; z-index: 1000; - animation: fadeIn 0.15s ease-out; + animation: fadeIn var(--ink-duration-overlay) var(--ink-ease-out); } .sheet__content { @@ -100,8 +100,7 @@ border: 0; box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink-border) 58%, transparent), - 0 18px 48px color-mix(in srgb, var(--ink-shadow-color) 38%, transparent), - 0 2px 8px color-mix(in srgb, var(--ink-shadow-color) 28%, transparent); + var(--ink-shadow-popover); overflow: auto; outline: none; } diff --git a/packages/ui/src/lib/components/stories/ContextMenuStory.svelte b/packages/ui/src/lib/components/stories/ContextMenuStory.svelte index 549d825..c0b4b1b 100644 --- a/packages/ui/src/lib/components/stories/ContextMenuStory.svelte +++ b/packages/ui/src/lib/components/stories/ContextMenuStory.svelte @@ -64,7 +64,7 @@ min-height: var(--ink-control-height); padding-inline: var(--ink-space-4); border: var(--ink-line-width) solid var(--ink-border-strong); - border-radius: var(--ink-radius-wobbly); + border-radius: var(--ink-radius-control); color: var(--ink-text); background: var(--ink-surface-raised); font: 650 var(--ink-type-sm) / 1 var(--ink-font-body); diff --git a/packages/ui/src/lib/editor/canvas/Canvas.svelte b/packages/ui/src/lib/editor/canvas/Canvas.svelte index 5c29678..7944c43 100644 --- a/packages/ui/src/lib/editor/canvas/Canvas.svelte +++ b/packages/ui/src/lib/editor/canvas/Canvas.svelte @@ -945,7 +945,7 @@ display: block; touch-action: none; cursor: - url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 28 28'%3E%3Cpath d='M4 2.75 22.2 16.1l-8.05 1.15 4.2 7.25-4.2 2.4-4.05-7.2-5.35 6.1z' fill='%23171928' stroke='%2388edc4' stroke-width='2.25' stroke-linejoin='round'/%3E%3C/svg%3E") + url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 28 28'%3E%3Cpath d='M4 2.75 22.2 16.1l-8.05 1.15 4.2 7.25-4.2 2.4-4.05-7.2-5.35 6.1z' fill='%23171928' stroke='%23a78bfa' stroke-width='2.25' stroke-linejoin='round'/%3E%3C/svg%3E") 4 3, default; } @@ -1085,9 +1085,7 @@ line-height: 1.2; font-family: inherit; z-index: 2; - box-shadow: - 0 0 0 1px rgba(0, 0, 0, 0.05), - 0 8px 20px rgba(0, 0, 0, 0.15); + box-shadow: var(--ink-shadow-popover); } .canvas-arrow-label-editor { @@ -1101,10 +1099,8 @@ font-family: sans-serif; text-align: center; z-index: 2; - box-shadow: - 0 0 0 1px rgba(0, 0, 0, 0.05), - 0 8px 20px rgba(0, 0, 0, 0.15); - border-radius: 0.25rem; + box-shadow: var(--ink-shadow-popover); + border-radius: var(--ink-radius-control-small); } .canvas-markdown-editor { @@ -1119,18 +1115,23 @@ line-height: 1.4; font-family: monospace; z-index: 2; - box-shadow: - 0 0 0 1px rgba(0, 0, 0, 0.05), - 0 8px 20px rgba(0, 0, 0, 0.15); + box-shadow: var(--ink-shadow-popover); white-space: pre-wrap; overflow: auto; } + .canvas-text-editor:focus-visible, + .canvas-arrow-label-editor:focus-visible, + .canvas-markdown-editor:focus-visible { + outline: var(--ink-line-width-strong) solid var(--ink-focus); + outline-offset: 2px; + } + .canvas-marquee { position: absolute; - border: 1px solid rgba(136, 192, 208, 0.7); - background-color: rgba(136, 192, 208, 0.2); - box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2) inset; + border: var(--ink-line-width) solid color-mix(in srgb, var(--ink-focus) 72%, transparent); + background-color: color-mix(in srgb, var(--ink-focus) 18%, transparent); + box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink-surface-raised) 25%, transparent); pointer-events: none; z-index: 1; } diff --git a/packages/ui/src/lib/editor/canvas/NavigationControls.svelte b/packages/ui/src/lib/editor/canvas/NavigationControls.svelte index f6eafd7..bfe1e43 100644 --- a/packages/ui/src/lib/editor/canvas/NavigationControls.svelte +++ b/packages/ui/src/lib/editor/canvas/NavigationControls.svelte @@ -115,7 +115,7 @@ background: var(--ink-surface-raised); box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink-border) 58%, transparent), - var(--ink-shadow-toolbar); + var(--ink-shadow-control); } .navigation-controls button { @@ -126,7 +126,7 @@ height: var(--ink-control-height); padding: 0 var(--ink-space-2); border: 0; - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); background: transparent; color: var(--ink-text); cursor: pointer; @@ -179,7 +179,7 @@ background: var(--ink-surface-raised); box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink-border) 58%, transparent), - var(--ink-shadow-toolbar); + var(--ink-shadow-popover); } .navigation-controls__menu button { diff --git a/packages/ui/src/lib/editor/components/ArrowPopover.svelte b/packages/ui/src/lib/editor/components/ArrowPopover.svelte index 17279d6..dc5fe2d 100644 --- a/packages/ui/src/lib/editor/components/ArrowPopover.svelte +++ b/packages/ui/src/lib/editor/components/ArrowPopover.svelte @@ -399,7 +399,7 @@ .arrow-popover__routing-buttons button, .arrow-popover__connection button { border: 1px solid var(--ink-border); - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); color: var(--ink-text); background: var(--ink-canvas); font: 650 var(--ink-type-xs) / 1 var(--ink-font-body); @@ -492,7 +492,7 @@ min-height: var(--ink-control-height); padding: 0 var(--ink-space-2); border: 1px solid var(--ink-border); - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); color: var(--ink-text); background: var(--ink-canvas); font: 600 var(--ink-type-xs) / 1 var(--ink-font-body); diff --git a/packages/ui/src/lib/editor/components/HistoryViewer.svelte b/packages/ui/src/lib/editor/components/HistoryViewer.svelte index 25c2abb..bb2d747 100644 --- a/packages/ui/src/lib/editor/components/HistoryViewer.svelte +++ b/packages/ui/src/lib/editor/components/HistoryViewer.svelte @@ -131,10 +131,10 @@ min-height: var(--ink-control-height); padding: var(--ink-space-2) var(--ink-space-4); border: 1px solid var(--ink-border); - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); background: var(--ink-canvas); color: var(--ink-text); - box-shadow: 2px 2px 0 color-mix(in srgb, var(--ink-shadow-color) 82%, transparent); + box-shadow: var(--ink-shadow-control); cursor: pointer; font-family: inherit; font-size: var(--ink-type-sm); @@ -147,8 +147,8 @@ .history-actions button:hover:not(:disabled) { background: var(--ink-surface-hover); border-color: var(--ink-border-strong); - box-shadow: 3px 3px 0 color-mix(in srgb, var(--ink-shadow-color) 90%, transparent); - transform: translate(-1px, -1px); + box-shadow: var(--ink-shadow-panel); + transform: translateY(-1px); } .history-actions button:active:not(:disabled) { @@ -156,7 +156,7 @@ } .history-actions button:focus-visible { - outline: 2px solid var(--ink-focus); + outline: var(--ink-line-width-strong) solid var(--ink-focus); outline-offset: 2px; } diff --git a/packages/ui/src/lib/editor/components/KeyboardShortcuts.svelte b/packages/ui/src/lib/editor/components/KeyboardShortcuts.svelte index 879d694..9577964 100644 --- a/packages/ui/src/lib/editor/components/KeyboardShortcuts.svelte +++ b/packages/ui/src/lib/editor/components/KeyboardShortcuts.svelte @@ -98,7 +98,7 @@ width: 2.75rem; height: 2.75rem; border: 0; - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); color: var(--ink-text-muted); background: transparent; font-size: 1.5rem; @@ -128,7 +128,7 @@ min-height: 2.75rem; padding: 0 var(--ink-space-3); border: 1px solid var(--ink-border-strong); - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); color: var(--ink-text); background: var(--ink-canvas); font: 500 var(--ink-type-sm) / 1 var(--ink-font-body); @@ -151,7 +151,7 @@ gap: var(--ink-space-4); min-height: 2.75rem; padding: var(--ink-space-2) var(--ink-space-3); - border-radius: var(--ink-radius-wobbly-small); + border-radius: var(--ink-radius-control-small); background: color-mix(in srgb, var(--ink-canvas) 68%, transparent); } diff --git a/packages/ui/src/lib/editor/components/LayerPanel.svelte b/packages/ui/src/lib/editor/components/LayerPanel.svelte index 6637244..6035d22 100644 --- a/packages/ui/src/lib/editor/components/LayerPanel.svelte +++ b/packages/ui/src/lib/editor/components/LayerPanel.svelte @@ -31,6 +31,10 @@ let menuOpen = $state(false); let menuPoint = $state({ x: 0, y: 0 }); let menuTrigger = $state(null); + let panelEl = $state(null); + let panelPosition = $state<{ left: number; top: number } | null>(null); + let panelDragOffset = $state({ x: 0, y: 0 }); + let panelDragging = $state(false); let layers = $derived(getLayersOnCurrentPage(editorState)); let activeLayer = $derived( layers.find((layer) => layer.id === editorState.ui.activeLayerId) ?? null @@ -82,6 +86,87 @@ if (next !== editorState) onCommit(name, next); } + function panelParent(): HTMLElement | null { + return (panelEl?.offsetParent as HTMLElement | null) ?? panelEl?.parentElement ?? null; + } + + function clampPanelPosition(left: number, top: number) { + const parent = panelParent(); + if (!parent || !panelEl) return { left, top }; + + const gutter = 8; + return { + left: Math.min( + Math.max(gutter, left), + Math.max(gutter, parent.clientWidth - panelEl.offsetWidth - gutter) + ), + top: Math.min( + Math.max(gutter, top), + Math.max(gutter, parent.clientHeight - panelEl.offsetHeight - gutter) + ) + }; + } + + function currentPanelPosition() { + if (panelPosition) return panelPosition; + const parent = panelParent(); + if (!parent || !panelEl) return { left: 8, top: 8 }; + + const parentBounds = parent.getBoundingClientRect(); + const panelBounds = panelEl.getBoundingClientRect(); + return clampPanelPosition( + panelBounds.left - parentBounds.left, + panelBounds.top - parentBounds.top + ); + } + + function setPanelPosition(left: number, top: number) { + panelPosition = clampPanelPosition(left, top); + } + + function beginPanelDrag(event: PointerEvent) { + if (event.button !== 0 || !panelEl) return; + const bounds = panelEl.getBoundingClientRect(); + panelDragOffset = { x: event.clientX - bounds.left, y: event.clientY - bounds.top }; + panelPosition = currentPanelPosition(); + panelDragging = true; + event.preventDefault(); + (event.currentTarget as HTMLElement).setPointerCapture(event.pointerId); + } + + function movePanel(event: PointerEvent) { + if (!panelDragging || !panelEl) return; + const parent = panelParent(); + if (!parent) return; + const parentBounds = parent.getBoundingClientRect(); + setPanelPosition( + event.clientX - parentBounds.left - panelDragOffset.x, + event.clientY - parentBounds.top - panelDragOffset.y + ); + } + + function endPanelDrag(event: PointerEvent) { + if (!panelDragging) return; + panelDragging = false; + const handle = event.currentTarget as HTMLElement; + if (handle.hasPointerCapture(event.pointerId)) + handle.releasePointerCapture(event.pointerId); + } + + function handlePanelKeyDown(event: KeyboardEvent) { + const distance = event.shiftKey ? 32 : 8; + const position = currentPanelPosition(); + let { left, top } = position; + if (event.key === 'ArrowLeft') left -= distance; + else if (event.key === 'ArrowRight') left += distance; + else if (event.key === 'ArrowUp') top -= distance; + else if (event.key === 'ArrowDown') top += distance; + else return; + + event.preventDefault(); + setPanelPosition(left, top); + } + function selectLayer(layer: LayerRecord) { commit('Select Layer', activateLayer(editorState, layer.id)); } @@ -160,12 +245,29 @@