From 939bdbe45b9c64001022d0e70ce254f3e660f6d9 Mon Sep 17 00:00:00 2001 From: Till JS Date: Sun, 29 Mar 2026 09:05:30 +0200 Subject: [PATCH] feat(manavoxel): add in-game sprite editor for items and characters - SpriteEditor component: 16x32 pixel canvas at 1cm resolution - Tools: brush, eraser, flood fill, color picker (pipette) - 24-color palette + custom color picker - Mirror horizontal/vertical, clear all - Undo/redo stack (30 levels) - Pixel-perfect rendering with grid overlay - Live preview at 2x scale - Keyboard shortcuts (B/E/G/I, Ctrl+Z/Y) - Integrated as modal overlay via "+ Item" button in editor mode - Saves sprite data as RGBA Uint8Array Co-Authored-By: Claude Opus 4.6 (1M context) --- .../web/src/lib/editor/sprite-editor.svelte | 447 ++++++++++++++++++ .../apps/web/src/routes/+page.svelte | 27 ++ 2 files changed, 474 insertions(+) create mode 100644 apps/manavoxel/apps/web/src/lib/editor/sprite-editor.svelte diff --git a/apps/manavoxel/apps/web/src/lib/editor/sprite-editor.svelte b/apps/manavoxel/apps/web/src/lib/editor/sprite-editor.svelte new file mode 100644 index 000000000..d1d152217 --- /dev/null +++ b/apps/manavoxel/apps/web/src/lib/editor/sprite-editor.svelte @@ -0,0 +1,447 @@ + + +
+ +
+
+ e.preventDefault()} + > +
+
{width} x {height} px | Zoom: {zoom}x
+
+ + +
+ +
+
Preview
+ +
+ + +
+ {#each [{ id: 'brush', label: 'Brush', key: 'B' }, { id: 'eraser', label: 'Eraser', key: 'E' }, { id: 'fill', label: 'Fill', key: 'G' }, { id: 'pipette', label: 'Pick', key: 'I' }] as tool} + + {/each} +
+ + +
+ + + +
+ + +
+ + +
+ + +
+
Color
+
+
+ +
+
+ {#each palette as color} + + {/each} +
+
+ + +
+ + {#if onClose} + + {/if} +
+
+
diff --git a/apps/manavoxel/apps/web/src/routes/+page.svelte b/apps/manavoxel/apps/web/src/routes/+page.svelte index 89852d903..94eef7ea4 100644 --- a/apps/manavoxel/apps/web/src/routes/+page.svelte +++ b/apps/manavoxel/apps/web/src/routes/+page.svelte @@ -3,6 +3,8 @@ import { GameEngine } from '$lib/engine/game'; import { DEFAULT_MATERIALS, MATERIAL_AIR } from '@manavoxel/shared'; import type { ToolType } from '$lib/editor/tools'; + import SpriteEditor from '$lib/editor/sprite-editor.svelte'; + import type { SpriteData } from '$lib/editor/sprite-editor.svelte'; let canvasContainer: HTMLDivElement; let engine: GameEngine | null = $state(null); @@ -13,6 +15,8 @@ let areaName = $state(''); let currentFloor = $state(0); let totalFloors = $state(1); + let showSpriteEditor = $state(false); + let createdItems: SpriteData[] = $state([]); const tools: { id: ToolType; label: string; key: string }[] = [ { id: 'brush', label: 'Brush', key: 'B' }, @@ -113,6 +117,14 @@ {/if}
+ {#if isEditing} + + {/if}
+ {/if}