openpencil/src/composables/use-keyboard.ts
Danila Poyarkov 859afdce45 Add .fig file import (Kiwi codec → scene graph)
File parser:
- .fig files are ZIP archives with Kiwi-encoded canvas data
- Detects canvas blob by name (canvas.fig) or largest binary entry
- Decodes Kiwi Message → NodeChange[] → SceneGraph

Node conversion:
- Maps Figma types to OpenPencil types (COMPONENT → FRAME, etc.)
- Extracts position from transform matrix (m02, m12)
- Extracts rotation from transform matrix (atan2)
- Converts fillPaints/strokePaints to Fill[]/Stroke[]
- Converts effects (shadows, blurs)
- Handles independent corner radii
- Preserves text properties (font, size, alignment, spacing)
- Builds parent-child tree from parentIndex GUIDs
- Sorts children by parentIndex.position

Browser compatibility:
- fzstd for Zstd decompression (replaces Bun.zstd*)
- fflate for ZIP extraction
- Cmd+O opens file dialog for .fig files
- UndoManager.clear() for resetting on file open
2026-02-27 23:01:35 +03:00

60 lines
1.5 KiB
TypeScript

import { useEventListener } from '@vueuse/core'
import { TOOL_SHORTCUTS } from '../stores/editor'
import type { EditorStore } from '../stores/editor'
function openFileDialog(store: EditorStore) {
const input = document.createElement('input')
input.type = 'file'
input.accept = '.fig'
input.addEventListener('change', () => {
const file = input.files?.[0]
if (file) store.openFigFile(file)
})
input.click()
}
export function useKeyboard(store: EditorStore) {
useEventListener(window, 'keydown', (e: KeyboardEvent) => {
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return
const tool = TOOL_SHORTCUTS[e.key.toLowerCase()]
if (tool) {
store.setTool(tool)
return
}
if (e.metaKey || e.ctrlKey) {
if (e.key === 'z' && !e.shiftKey) {
e.preventDefault()
store.undoAction()
} else if ((e.key === 'z' && e.shiftKey) || e.key === 'y') {
e.preventDefault()
store.redoAction()
} else if (e.key === '0') {
e.preventDefault()
store.zoomToFit()
} else if (e.key === 'd') {
e.preventDefault()
store.duplicateSelected()
} else if (e.key === 'a') {
e.preventDefault()
store.selectAll()
} else if (e.key === 'o') {
e.preventDefault()
openFileDialog(store)
}
}
if (e.key === 'Backspace' || e.key === 'Delete') {
store.deleteSelected()
}
if (e.key === 'Escape') {
store.clearSelection()
store.setTool('SELECT')
}
})
}