diff --git a/components/wysiwyg/Canvas.nudge.test.tsx b/components/wysiwyg/Canvas.nudge.test.tsx
index 4d91bee..6a9f2f0 100644
--- a/components/wysiwyg/Canvas.nudge.test.tsx
+++ b/components/wysiwyg/Canvas.nudge.test.tsx
@@ -41,4 +41,31 @@ describe('WYSIWYG Canvas keyboard nudge with 8px snap', () => {
const obj = getByTestId('obj-o1')
expect(obj).toHaveStyle({ top: '96px' }) // 100 - 1 => 99 -> snap8 => 96
})
+
+ it('Shift+ArrowRight nudges by 1px without grid snap', () => {
+ const frame = makeFrame()
+ const { getByTestId } = render()
+ const canvas = getByTestId('wysiwyg-canvas')
+ fireEvent.keyDown(canvas, { key: 'ArrowRight', shiftKey: true })
+ const obj = getByTestId('obj-o1')
+ expect(obj).toHaveStyle({ left: '102px' }) // 101 + 1 => 102 (no snap)
+ })
+
+ it('Shift+ArrowUp nudges by 1px without grid snap', () => {
+ const frame = makeFrame()
+ const { getByTestId } = render()
+ const canvas = getByTestId('wysiwyg-canvas')
+ fireEvent.keyDown(canvas, { key: 'ArrowUp', shiftKey: true })
+ const obj = getByTestId('obj-o1')
+ expect(obj).toHaveStyle({ top: '99px' }) // 100 - 1 => 99 (no snap)
+ })
+
+ it('Alt+ArrowRight nudges by 16px and snaps to grid', () => {
+ const frame = makeFrame()
+ const { getByTestId } = render()
+ const canvas = getByTestId('wysiwyg-canvas')
+ fireEvent.keyDown(canvas, { key: 'ArrowRight', altKey: true })
+ const obj = getByTestId('obj-o1')
+ expect(obj).toHaveStyle({ left: '120px' }) // 101 + 16 = 117 -> snap8 => 120
+ })
})
diff --git a/components/wysiwyg/Canvas.tsx b/components/wysiwyg/Canvas.tsx
index fd97b35..32522e6 100644
--- a/components/wysiwyg/Canvas.tsx
+++ b/components/wysiwyg/Canvas.tsx
@@ -56,15 +56,23 @@ export function Canvas({ frame, onChange }: CanvasProps) {
const onKeyDown = useCallback((e: React.KeyboardEvent) => {
if (!selected) return
let dx = 0, dy = 0
- if (e.key === 'ArrowLeft') dx = -1
- else if (e.key === 'ArrowRight') dx = 1
- else if (e.key === 'ArrowUp') dy = -1
- else if (e.key === 'ArrowDown') dy = 1
+ let step = 1
+ let useSnap = true
+ // Shift: 미세 이동(1px, 스냅 없음), Alt: 가속 이동(16px, 스냅 적용)
+ if (e.shiftKey) { step = 1; useSnap = false }
+ else if (e.altKey) { step = 16; useSnap = true }
+
+ if (e.key === 'ArrowLeft') dx = -step
+ else if (e.key === 'ArrowRight') dx = step
+ else if (e.key === 'ArrowUp') dy = -step
+ else if (e.key === 'ArrowDown') dy = step
else return
e.preventDefault()
updateObject(selected.id, (o) => {
- const nx = snap8(o.x + dx)
- const ny = snap8(o.y + dy)
+ const tx = o.x + dx
+ const ty = o.y + dy
+ const nx = useSnap ? snap8(tx) : tx
+ const ny = useSnap ? snap8(ty) : ty
return { ...o, x: nx, y: ny }
})
}, [selected, updateObject])