Pixel art texture editor for Minecraft resource packs, built with Tauri v2.
- Rust >= 1.77 (
rustup update stable) - Node.js >= 20 LTS
- npm (bundled with Node.js)
- Visual Studio C++ Build Tools (Windows) / Xcode CLI (macOS) /
build-essential(Linux)
npm install
npm run tauri devTexLab follows Clean Architecture with strict inward-pointing dependencies. The backend is split into 5 layers, each with a clear responsibility.
src-tauri/src/
├── domain/ # Pure business logic (entities, value objects, domain rules)
├── use_cases/ # Application orchestration (imports only from domain/)
├── infrastructure/ # I/O adapters (PNG, ZIP, JSON, external services)
├── commands/ # Tauri IPC command wrappers (thin, delegates to use_cases)
├── mcp/ # Embedded MCP server for AI agent integration
├── error.rs # Unified AppError type (serde::Serialize for IPC)
├── state.rs # Mutex<AppState> — single source of truth
├── lib.rs # Tauri builder and module registration
└── main.rs # Desktop entry point (thin, calls lib)
Dependency rules:
domain/MUST NOT import from any external crate (no tauri, serde, image)use_cases/MUST only import fromdomain/infrastructure/implements adapters using external crates, depends ondomain/commands/are thin wrappers — delegate touse_cases/, handle serializationmcp/accessesMutex<AppState>directly for AI agent operations
src/
├── api/ # Typed invoke() wrappers for Tauri IPC
├── components/ # UI components organized by feature
├── hooks/ # React hooks
├── store/ # Zustand stores (cache of Rust state, NOT independent source)
├── App.tsx # Root component
└── main.tsx # React entry point
Zustand stores mirror Rust state — they are a cache for the frontend, not an independent source of truth.
- Files/modules:
snake_case(texture_editor.rs) - Types/enums:
PascalCase(AppState,AppError) - Functions:
snake_case(get_texture) - Constants:
SCREAMING_SNAKE_CASE(MAX_LAYERS) - Domain types: No
Serialize/Deserializederives — use separate DTOs incommands/
- Files:
PascalCasefor components (CanvasPanel.tsx),camelCasefor utilities (useEditor.ts) - Types/interfaces:
PascalCase(EditorState,TextureData) - Functions/variables:
camelCase(getTexture,editorState) - Constants:
SCREAMING_SNAKE_CASEorcamelCasedepending on scope
- Specify: Run
/speckit.specifyfrom the GitHub issue - Plan: Run
/speckit.planto generate the technical design - Tasks: Run
/speckit.tasksto generate the implementation tasks - Implement: Follow the task list, respecting dependency order
- Verify: Run
/speckit.verifyto validate against the spec
- Define domain types in
domain/ - Write use case logic in
use_cases/ - Create a
#[tauri::command]wrapper incommands/ - Register the command in
generate_handler![]inlib.rs - Add the corresponding capability permission if needed
Use Conventional Commits:
feat:— new featurefix:— bug fixrefactor:— code restructuring without behavior changedocs:— documentation onlytest:— adding or updating testschore:— tooling, CI, dependencies
Reference issue numbers in commits (e.g., feat: add layer panel (#12)).
| Command | Description |
|---|---|
npm run tauri dev |
Start app in dev mode (Vite HMR + Rust rebuild) |
npm run tauri build |
Build production binaries |
npm run dev |
Frontend dev server only |
npm run build |
Frontend production build |
cargo build |
Rust backend only (from src-tauri/) |
cargo test |
Rust unit tests (from src-tauri/) |