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 @@
-
+
-
- Drag & drop sprite images here
or click to select files
-
-
+
Drag & drop sprite images here
or click to select files
+
\ 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 @@
-
-
-
-
-
Spritesheet
+
-
-
- {{ tooltipText }}
+
+
+ {{ tooltipText }}
+
\ 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 @@
-
-
-
+
Auto Arrange
-
+
Preview Animation
-
+
Download
-
+
Clear All
@@ -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 sprites uploaded yet
-
+ No sprites uploaded yet
-
-
![]()
-
- {{ index + 1 }}. {{ truncateName(sprite.name) }}
-
+
+
![]()
+
{{ index + 1 }}. {{ truncateName(sprite.name) }}
\ 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');