diff --git a/.prettierrc.json b/.prettierrc.json index 29a2402..1e94086 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,6 +1,11 @@ { - "$schema": "https://json.schemastore.org/prettierrc", - "semi": false, + "semi": true, "singleQuote": true, - "printWidth": 100 + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 300, + "bracketSpacing": true, + "arrowParens": "avoid", + "endOfLine": "lf", + "vueIndentScriptAndStyle": true } diff --git a/package-lock.json b/package-lock.json index 89e1b65..d2fe8c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,13 +13,13 @@ "vue": "^3.5.13" }, "devDependencies": { + "@ianvs/prettier-plugin-sort-imports": "^4.4.1", "@tsconfig/node22": "^22.0.1", "@types/node": "^22.13.14", "@vitejs/plugin-vue": "^5.2.3", "@vue/tsconfig": "^0.7.0", "autoprefixer": "^10.4.21", "npm-run-all2": "^7.0.2", - "postcss": "^8.5.3", "prettier": "3.5.3", "tailwindcss": "^4.1.1", "typescript": "~5.8.0", @@ -897,6 +897,42 @@ "node": ">=18" } }, + "node_modules/@ianvs/prettier-plugin-sort-imports": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@ianvs/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.4.1.tgz", + "integrity": "sha512-F0/Hrcfpy8WuxlQyAWJTEren/uxKhYonOGY4OyWmwRdeTvkh9mMSCxowZLjNkhwi/2ipqCgtXwwOk7tW0mWXkA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/generator": "^7.26.2", + "@babel/parser": "^7.26.2", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "semver": "^7.5.2" + }, + "peerDependencies": { + "@vue/compiler-sfc": "2.7.x || 3.x", + "prettier": "2 || 3" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + } + } + }, + "node_modules/@ianvs/prettier-plugin-sort-imports/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", diff --git a/package.json b/package.json index 8ca2680..71d8774 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "vue": "^3.5.13" }, "devDependencies": { + "@ianvs/prettier-plugin-sort-imports": "^4.4.1", "@tsconfig/node22": "^22.0.1", "@types/node": "^22.13.14", "@vitejs/plugin-vue": "^5.2.3", diff --git a/src/App.vue b/src/App.vue index fa8be07..a1d6e09 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,21 +12,17 @@ \ No newline at end of file + const showHelpModal = () => { + alert('Keyboard Shortcuts:\n\n' + 'Shift + Drag: Fine-tune sprite position\n' + 'Space: Play/Pause animation\n' + 'Esc: Close preview modal\n' + 'Arrow Keys: Navigate frames when paused'); + }; + diff --git a/src/assets/main.css b/src/assets/main.css index a461c50..d4b5078 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -1 +1 @@ -@import "tailwindcss"; \ No newline at end of file +@import 'tailwindcss'; diff --git a/src/components/AppHeader.vue b/src/components/AppHeader.vue index 3dd8941..f65ef00 100644 --- a/src/components/AppHeader.vue +++ b/src/components/AppHeader.vue @@ -5,11 +5,7 @@

Spritesheet Creator

-
@@ -17,7 +13,7 @@ \ No newline at end of file + const emit = defineEmits<{ + (e: 'toggleHelp'): void; + }>(); + diff --git a/src/components/DropZone.vue b/src/components/DropZone.vue index 1334b5c..e93b03d 100644 --- a/src/components/DropZone.vue +++ b/src/components/DropZone.vue @@ -1,120 +1,104 @@ \ No newline at end of file + reader.onerror = reject; + reader.readAsDataURL(file); + }); + }; + diff --git a/src/components/HelpButton.vue b/src/components/HelpButton.vue index 8035c9a..5f63105 100644 --- a/src/components/HelpButton.vue +++ b/src/components/HelpButton.vue @@ -1,14 +1,11 @@ \ No newline at end of file + const emit = defineEmits<{ + showHelp: []; + }>(); + diff --git a/src/components/MainContent.vue b/src/components/MainContent.vue index 9e972d7..054ae14 100644 --- a/src/components/MainContent.vue +++ b/src/components/MainContent.vue @@ -1,206 +1,194 @@ \ No newline at end of file + const handleMouseUp = () => { + store.draggedSprite.value = null; + }; + + const handleMouseOut = () => { + isTooltipVisible.value = false; + store.draggedSprite.value = null; + }; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Shift') { + store.isShiftPressed.value = true; + } + }; + + const handleKeyUp = (e: KeyboardEvent) => { + if (e.key === 'Shift') { + store.isShiftPressed.value = false; + } + }; + + const setupCanvasEvents = () => { + if (!canvasEl.value) return; + + canvasEl.value.addEventListener('mousedown', handleMouseDown); + canvasEl.value.addEventListener('mousemove', handleMouseMove); + canvasEl.value.addEventListener('mouseup', handleMouseUp); + canvasEl.value.addEventListener('mouseout', handleMouseOut); + }; + + onMounted(() => { + if (canvasEl.value) { + store.canvas.value = canvasEl.value; + store.ctx.value = canvasEl.value.getContext('2d'); + + // Initialize canvas size + canvasEl.value.width = 400; + canvasEl.value.height = 300; + + setupCheckerboardPattern(); + setupCanvasEvents(); + + // Setup keyboard events for modifiers + window.addEventListener('keydown', handleKeyDown); + window.addEventListener('keyup', handleKeyUp); + } + }); + + onBeforeUnmount(() => { + window.removeEventListener('keydown', handleKeyDown); + window.removeEventListener('keyup', handleKeyUp); + + if (canvasEl.value) { + canvasEl.value.removeEventListener('mousedown', handleMouseDown); + canvasEl.value.removeEventListener('mousemove', handleMouseMove); + canvasEl.value.removeEventListener('mouseup', handleMouseUp); + canvasEl.value.removeEventListener('mouseout', handleMouseOut); + } + }); + diff --git a/src/components/Notification.vue b/src/components/Notification.vue index 284c235..4ac35d5 100644 --- a/src/components/Notification.vue +++ b/src/components/Notification.vue @@ -5,35 +5,32 @@ 'translate-y-0 opacity-100': notification.isVisible, 'translate-y-24 opacity-0': !notification.isVisible, 'border-l-4 border-green-500': notification.type === 'success', - 'border-l-4 border-red-500': notification.type === 'error' + 'border-l-4 border-red-500': notification.type === 'error', }" > {{ notification.message }} - \ No newline at end of file + const closeNotification = () => { + store.notification.isVisible = false; + }; + diff --git a/src/components/PreviewModal.vue b/src/components/PreviewModal.vue index 45ac8c2..7c572dd 100644 --- a/src/components/PreviewModal.vue +++ b/src/components/PreviewModal.vue @@ -1,13 +1,6 @@ \ No newline at end of file + input[type='range']::-moz-range-thumb { + width: 15px; + height: 15px; + background: #0096ff; + border-radius: 50%; + cursor: pointer; + } + diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index a7cc6d3..9dda202 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -40,32 +40,16 @@
- - - -
@@ -90,39 +74,39 @@ \ No newline at end of file + // Expose store methods directly + const { autoArrangeSprites, downloadSpritesheet } = store; + diff --git a/src/components/SpriteList.vue b/src/components/SpriteList.vue index 3c8ff38..e20a843 100644 --- a/src/components/SpriteList.vue +++ b/src/components/SpriteList.vue @@ -1,39 +1,26 @@ \ No newline at end of file + const truncateName = (name: string) => { + return name.length > 10 ? `${name.substring(0, 10)}...` : name; + }; + diff --git a/src/composables/useSpritesheetStore.ts b/src/composables/useSpritesheetStore.ts index 1492631..cc89ccb 100644 --- a/src/composables/useSpritesheetStore.ts +++ b/src/composables/useSpritesheetStore.ts @@ -48,13 +48,13 @@ export function useSpritesheetStore() { lastFrameTime: 0, animationId: null, slider: null, - manualUpdate: false + manualUpdate: false, }); const notification = reactive({ isVisible: false, message: '', - type: 'success' as 'success' | 'error' + type: 'success' as 'success' | 'error', }); function addSprites(newSprites: Sprite[]) { @@ -120,11 +120,7 @@ export function useSpritesheetStore() { } sprites.value.forEach(sprite => { - ctx.value!.drawImage( - sprite.img, - sprite.x, - sprite.y - ); + ctx.value!.drawImage(sprite.img, sprite.x, sprite.y); }); } @@ -164,12 +160,7 @@ export function useSpritesheetStore() { // Briefly flash the cell ctx.value.save(); ctx.value.fillStyle = 'rgba(0, 150, 255, 0.3)'; - ctx.value.fillRect( - cellX * cellSize.width, - cellY * cellSize.height, - cellSize.width, - cellSize.height - ); + ctx.value.fillRect(cellX * cellSize.width, cellY * cellSize.height, cellSize.width, cellSize.height); ctx.value.restore(); // Reset after a short delay @@ -215,11 +206,7 @@ export function useSpritesheetStore() { tempCtx.clearRect(0, 0, tempCanvas.width, tempCanvas.height); sprites.value.forEach(sprite => { - tempCtx.drawImage( - sprite.img, - sprite.x, - sprite.y - ); + tempCtx.drawImage(sprite.img, sprite.x, sprite.y); }); const link = document.createElement('a'); @@ -257,8 +244,7 @@ export function useSpritesheetStore() { function renderAnimationFrame(frameIndex: number) { if (sprites.value.length === 0 || !animation.canvas || !animation.ctx) return; - if (animation.canvas.width !== cellSize.width || - animation.canvas.height !== cellSize.height) { + if (animation.canvas.width !== cellSize.width || animation.canvas.height !== cellSize.height) { animation.canvas.width = cellSize.width; animation.canvas.height = cellSize.height; } @@ -270,14 +256,10 @@ export function useSpritesheetStore() { const cellX = Math.floor(currentSprite.x / cellSize.width); const cellY = Math.floor(currentSprite.y / cellSize.height); - const offsetX = currentSprite.x - (cellX * cellSize.width); - const offsetY = currentSprite.y - (cellY * cellSize.height); + const offsetX = currentSprite.x - cellX * cellSize.width; + const offsetY = currentSprite.y - cellY * cellSize.height; - animation.ctx.drawImage( - currentSprite.img, - offsetX, - offsetY - ); + animation.ctx.drawImage(currentSprite.img, offsetX, offsetY); } function animationLoop(timestamp?: number) { @@ -338,6 +320,6 @@ export function useSpritesheetStore() { startAnimation, stopAnimation, renderAnimationFrame, - showNotification + showNotification, }; -} \ No newline at end of file +} diff --git a/src/main.ts b/src/main.ts index 48dad43..ac4a450 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,11 @@ -import './assets/main.css' +import './assets/main.css'; -import { createApp } from 'vue' -import { createPinia } from 'pinia' -import App from './App.vue' +import { createApp } from 'vue'; +import { createPinia } from 'pinia'; +import App from './App.vue'; -const app = createApp(App) +const app = createApp(App); -app.use(createPinia()) +app.use(createPinia()); -app.mount('#app') +app.mount('#app');