pixelweaver 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.development +1 -0
- package/.github/workflows/ci.yml +22 -0
- package/.github/workflows/publish.yml +18 -0
- package/.prettierignore +5 -0
- package/.prettierrc +16 -0
- package/.python-version +1 -0
- package/.rlsbl/bases/.github/workflows/ci.yml +21 -0
- package/.rlsbl/bases/.github/workflows/publish.yml +18 -0
- package/.rlsbl/bases/.rlsbl/changes/unreleased.jsonl +0 -0
- package/.rlsbl/bases/.rlsbl/hooks/post-release.sh +8 -0
- package/.rlsbl/bases/.rlsbl/hooks/pre-checks.sh +5 -0
- package/.rlsbl/bases/.rlsbl/hooks/pre-release.sh +8 -0
- package/.rlsbl/bases/.rlsbl/lint/go.toml +17 -0
- package/.rlsbl/bases/.rlsbl/lint/npm.toml +19 -0
- package/.rlsbl/bases/.rlsbl/lint/python.toml +25 -0
- package/.rlsbl/bases/CHANGELOG.md +5 -0
- package/.rlsbl/bases/LICENSE +21 -0
- package/.rlsbl/changes/.validated +1 -0
- package/.rlsbl/changes/0.1.0.jsonl +85 -0
- package/.rlsbl/changes/0.1.0.md +13 -0
- package/.rlsbl/changes/unreleased.jsonl +0 -0
- package/.rlsbl/config.json +6 -0
- package/.rlsbl/hashes.json +14 -0
- package/.rlsbl/hooks/post-release.sh +8 -0
- package/.rlsbl/hooks/pre-checks.sh +5 -0
- package/.rlsbl/hooks/pre-release.sh +8 -0
- package/.rlsbl/lint/go.toml +17 -0
- package/.rlsbl/lint/npm.toml +19 -0
- package/.rlsbl/lint/python.toml +25 -0
- package/.rlsbl/releases/unreleased.toml +0 -0
- package/.rlsbl/releases/v0.1.0.toml +3 -0
- package/.rlsbl/version +1 -0
- package/.selfdoc/hashes/hashes.json +146 -0
- package/.strictcli/schema.json +227 -0
- package/CHANGELOG.md +17 -0
- package/CLAUDE.md +100 -0
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/assets/icon.png +0 -0
- package/docs/_README.md +117 -0
- package/docs/cli-config.md +35 -0
- package/docs/cli-dev.md +21 -0
- package/docs/cli-diagnose.md +12 -0
- package/docs/cli-index.md +30 -0
- package/docs/cli-list.md +18 -0
- package/docs/cli-mcp.md +18 -0
- package/docs/cli-new.md +26 -0
- package/docs/cli-open.md +21 -0
- package/docs/cli-serve.md +21 -0
- package/docs/cli-stop.md +12 -0
- package/docs/gen-index.md +36 -0
- package/docs/index.md +13 -0
- package/docs/server-src-pixelweaver-__main__.md +12 -0
- package/docs/server-src-pixelweaver-autosave.md +12 -0
- package/docs/server-src-pixelweaver-bridge.md +12 -0
- package/docs/server-src-pixelweaver-cli.md +12 -0
- package/docs/server-src-pixelweaver-config.md +12 -0
- package/docs/server-src-pixelweaver-connections.md +12 -0
- package/docs/server-src-pixelweaver-main.md +12 -0
- package/docs/server-src-pixelweaver-mcp_bridge.md +12 -0
- package/docs/server-src-pixelweaver-mcp_drawing_tools.md +12 -0
- package/docs/server-src-pixelweaver-mcp_export_tools.md +12 -0
- package/docs/server-src-pixelweaver-mcp_frame_tools.md +12 -0
- package/docs/server-src-pixelweaver-mcp_history_tools.md +12 -0
- package/docs/server-src-pixelweaver-mcp_layer_tools.md +12 -0
- package/docs/server-src-pixelweaver-mcp_lock.md +12 -0
- package/docs/server-src-pixelweaver-mcp_project_tools.md +12 -0
- package/docs/server-src-pixelweaver-mcp_read_tools.md +12 -0
- package/docs/server-src-pixelweaver-mcp_registry.md +12 -0
- package/docs/server-src-pixelweaver-mcp_resources.md +12 -0
- package/docs/server-src-pixelweaver-mcp_server.md +12 -0
- package/docs/server-src-pixelweaver-protocol.md +12 -0
- package/docs/server-src-pixelweaver-state.md +12 -0
- package/docs/server-src-pixelweaver-storage.md +12 -0
- package/docs/server-src-pixelweaver-websocket.md +12 -0
- package/docs/server-src-pixelweaver.md +12 -0
- package/e2e/app-launch.test.ts +35 -0
- package/e2e/menus.test.ts +26 -0
- package/e2e/tools.test.ts +27 -0
- package/e2e/undo-redo.test.ts +11 -0
- package/eslint.config.js +62 -0
- package/index.html +13 -0
- package/package.json +48 -0
- package/playwright.config.ts +19 -0
- package/plugins/builtin/.gitkeep +0 -0
- package/plugins/builtin/advanced-fill-tool.ts +146 -0
- package/plugins/builtin/circle-tool.ts +186 -0
- package/plugins/builtin/diamond-tool.ts +182 -0
- package/plugins/builtin/dither-tool.ts +186 -0
- package/plugins/builtin/drawing-primitives-plugin.ts +362 -0
- package/plugins/builtin/drawing-utils.test.ts +495 -0
- package/plugins/builtin/drawing-utils.ts +431 -0
- package/plugins/builtin/effects/blur.ts +97 -0
- package/plugins/builtin/effects/color-effects.ts +278 -0
- package/plugins/builtin/effects/flip.ts +83 -0
- package/plugins/builtin/effects/glow.ts +118 -0
- package/plugins/builtin/effects/outline.ts +110 -0
- package/plugins/builtin/effects/rotate.ts +357 -0
- package/plugins/builtin/effects/scale.ts +258 -0
- package/plugins/builtin/effects/shadow.ts +111 -0
- package/plugins/builtin/effects/sharpen.ts +102 -0
- package/plugins/builtin/effects.test.ts +715 -0
- package/plugins/builtin/eraser-tool.ts +23 -0
- package/plugins/builtin/eyedropper-tool.ts +83 -0
- package/plugins/builtin/fill-tool.ts +93 -0
- package/plugins/builtin/gradient-tool.ts +204 -0
- package/plugins/builtin/gradient.test.ts +142 -0
- package/plugins/builtin/importers/aseprite-importer-plugin.ts +174 -0
- package/plugins/builtin/importers/aseprite-parser.ts +497 -0
- package/plugins/builtin/importers/piskel-importer-plugin.ts +222 -0
- package/plugins/builtin/importers/sky-spec-plugin.ts +409 -0
- package/plugins/builtin/line-tool.ts +267 -0
- package/plugins/builtin/make-stroke-tool.ts +271 -0
- package/plugins/builtin/noise-dither.test.ts +151 -0
- package/plugins/builtin/noise-tool.ts +131 -0
- package/plugins/builtin/pattern-stamp-tool.ts +162 -0
- package/plugins/builtin/pencil-tool.ts +25 -0
- package/plugins/builtin/rect-tool.ts +179 -0
- package/plugins/builtin/selection-tool.ts +388 -0
- package/plugins/builtin/selection.test.ts +195 -0
- package/plugins/builtin/tool-plugins.test.ts +529 -0
- package/public/favicon.svg +7 -0
- package/public/sw.js +91 -0
- package/pyproject.toml +49 -0
- package/scripts/eslint-wave-a-list.sh +24 -0
- package/scripts/eslint-wave-a-status.sh +25 -0
- package/scripts/fix-index-signature-access.py +127 -0
- package/scripts/fix-unchecked-index.py +128 -0
- package/scripts/fix-unchecked-vars.py +127 -0
- package/scripts/fix-wave-a-bangs.py +36 -0
- package/scripts/fix-wave-a-templates.py +108 -0
- package/scripts/fix-wave-b-void-expr.py +167 -0
- package/scripts/generate-command-params.py +540 -0
- package/scripts/migrate-single-frame-to-multi.py +171 -0
- package/scripts/smoke-test.sh +77 -0
- package/selfdoc.json +10 -0
- package/server/README.md +0 -0
- package/server/src/pixelweaver/__init__.py +1 -0
- package/server/src/pixelweaver/__main__.py +4 -0
- package/server/src/pixelweaver/autosave.py +114 -0
- package/server/src/pixelweaver/bridge.py +127 -0
- package/server/src/pixelweaver/cli.py +199 -0
- package/server/src/pixelweaver/config.py +24 -0
- package/server/src/pixelweaver/connections.py +54 -0
- package/server/src/pixelweaver/main.py +271 -0
- package/server/src/pixelweaver/mcp_bridge.py +189 -0
- package/server/src/pixelweaver/mcp_drawing_tools.py +178 -0
- package/server/src/pixelweaver/mcp_export_tools.py +291 -0
- package/server/src/pixelweaver/mcp_frame_tools.py +423 -0
- package/server/src/pixelweaver/mcp_history_tools.py +106 -0
- package/server/src/pixelweaver/mcp_layer_tools.py +64 -0
- package/server/src/pixelweaver/mcp_lock.py +37 -0
- package/server/src/pixelweaver/mcp_project_tools.py +302 -0
- package/server/src/pixelweaver/mcp_read_tools.py +163 -0
- package/server/src/pixelweaver/mcp_registry.py +247 -0
- package/server/src/pixelweaver/mcp_resources.py +312 -0
- package/server/src/pixelweaver/mcp_server.py +234 -0
- package/server/src/pixelweaver/protocol.py +219 -0
- package/server/src/pixelweaver/state.py +267 -0
- package/server/src/pixelweaver/storage.py +293 -0
- package/server/src/pixelweaver/websocket.py +282 -0
- package/server/tests/__init__.py +0 -0
- package/server/tests/conftest.py +17 -0
- package/server/tests/test_api.py +96 -0
- package/server/tests/test_bridge.py +161 -0
- package/server/tests/test_health.py +9 -0
- package/server/tests/test_integration.py +86 -0
- package/server/tests/test_mcp_bridge.py +293 -0
- package/server/tests/test_mcp_lock.py +34 -0
- package/server/tests/test_mcp_registry.py +679 -0
- package/server/tests/test_mcp_resources.py +648 -0
- package/server/tests/test_protocol.py +125 -0
- package/server/tests/test_state.py +87 -0
- package/server/tests/test_storage.py +306 -0
- package/server/tests/test_websocket.py +275 -0
- package/src/App.svelte +107 -0
- package/src/app.css +215 -0
- package/src/lib/animation/AnimationPreviewPanel.svelte +667 -0
- package/src/lib/animation/animation-commands.test.ts +228 -0
- package/src/lib/animation/animation-commands.ts +540 -0
- package/src/lib/animation/animation-preview-panel-plugin.ts +25 -0
- package/src/lib/animation/animation-preview.svelte.ts +151 -0
- package/src/lib/animation/clipboard.ts +134 -0
- package/src/lib/animation/frame-model.svelte.ts +437 -0
- package/src/lib/animation/frame-model.test.ts +314 -0
- package/src/lib/animation/frame-selection.svelte.ts +77 -0
- package/src/lib/animation/frame-tags.svelte.ts +238 -0
- package/src/lib/animation/frame-tags.test.ts +136 -0
- package/src/lib/animation/import.test.ts +141 -0
- package/src/lib/animation/import.ts +112 -0
- package/src/lib/animation/spritesheet-export.test.ts +239 -0
- package/src/lib/animation/spritesheet-export.ts +314 -0
- package/src/lib/canvas/CanvasViewport.svelte +216 -0
- package/src/lib/canvas/canvas-init-plugin.ts +178 -0
- package/src/lib/canvas/canvas-renderer.ts +408 -0
- package/src/lib/canvas/canvas-state.svelte.ts +232 -0
- package/src/lib/canvas/canvas-state.test.ts +139 -0
- package/src/lib/canvas/input-handler.ts +221 -0
- package/src/lib/canvas/input-plugin.ts +150 -0
- package/src/lib/canvas/onion-skin.ts +94 -0
- package/src/lib/canvas/pixel-buffer.test.ts +249 -0
- package/src/lib/canvas/pixel-buffer.ts +151 -0
- package/src/lib/canvas/render-state.svelte.ts +18 -0
- package/src/lib/canvas/shape-preview-state.svelte.ts +36 -0
- package/src/lib/canvas/tile-mode.test.ts +53 -0
- package/src/lib/canvas/tile-mode.ts +92 -0
- package/src/lib/canvas/viewport-utils.ts +24 -0
- package/src/lib/canvas/zoom-utils.ts +31 -0
- package/src/lib/color/.gitkeep +0 -0
- package/src/lib/color/color-commands.ts +87 -0
- package/src/lib/color/color-state.svelte.ts +98 -0
- package/src/lib/color/color-state.test.ts +91 -0
- package/src/lib/color/color-utils.test.ts +220 -0
- package/src/lib/color/color-utils.ts +243 -0
- package/src/lib/color/palette-state.svelte.ts +127 -0
- package/src/lib/color/palette-state.test.ts +154 -0
- package/src/lib/color/palette.ts +79 -0
- package/src/lib/core/bootstrap.ts +66 -0
- package/src/lib/core/command-params.generated.ts +1549 -0
- package/src/lib/core/command-params.ts +20 -0
- package/src/lib/core/command-runner.ts +79 -0
- package/src/lib/core/commands.ts +134 -0
- package/src/lib/core/dispatcher.test.ts +548 -0
- package/src/lib/core/dispatcher.ts +361 -0
- package/src/lib/core/index.test.ts +7 -0
- package/src/lib/core/notification-state.svelte.ts +119 -0
- package/src/lib/core/plugin-api.ts +210 -0
- package/src/lib/core/plugin-discovery.test.ts +53 -0
- package/src/lib/core/plugin-discovery.ts +65 -0
- package/src/lib/core/plugin-loader.test.ts +159 -0
- package/src/lib/core/plugin-loader.ts +240 -0
- package/src/lib/core/plugin-types.ts +286 -0
- package/src/lib/core/registries.svelte.ts +74 -0
- package/src/lib/core/tool-options-state.svelte.ts +61 -0
- package/src/lib/dock/DockLayout.svelte +375 -0
- package/src/lib/dock/TabAddMenu.svelte +90 -0
- package/src/lib/dock/dock-persistence.ts +46 -0
- package/src/lib/dock/dock-plugin.ts +49 -0
- package/src/lib/dock/dock-presets.ts +156 -0
- package/src/lib/dock/dock-state.svelte.ts +77 -0
- package/src/lib/dock/dock-types.ts +2 -0
- package/src/lib/dock/dockview-theme.css +226 -0
- package/src/lib/dock/dockview-theme.ts +17 -0
- package/src/lib/dock/header-action-renderer.ts +77 -0
- package/src/lib/edit/clipboard-state.ts +34 -0
- package/src/lib/export/download.ts +201 -0
- package/src/lib/export/png-metadata.ts +181 -0
- package/src/lib/history/ActionLogPanel.svelte +418 -0
- package/src/lib/history/action-log-panel-plugin.ts +24 -0
- package/src/lib/history/action-log.svelte.ts +172 -0
- package/src/lib/history/action-log.test.ts +168 -0
- package/src/lib/history/history-commands.ts +403 -0
- package/src/lib/history/macros.svelte.ts +320 -0
- package/src/lib/history/macros.test.ts +224 -0
- package/src/lib/history/replay-engine.test.ts +241 -0
- package/src/lib/history/replay-engine.ts +149 -0
- package/src/lib/history/spec-format.test.ts +250 -0
- package/src/lib/history/spec-format.ts +210 -0
- package/src/lib/iso/SeamCheckerPanel.svelte +251 -0
- package/src/lib/iso/iso-math.test.ts +77 -0
- package/src/lib/iso/iso-math.ts +77 -0
- package/src/lib/iso/seam-checker-panel-plugin.ts +25 -0
- package/src/lib/iso/seam-checker.test.ts +126 -0
- package/src/lib/iso/seam-checker.ts +93 -0
- package/src/lib/iso/tessellation.ts +67 -0
- package/src/lib/layers/compositor.test.ts +193 -0
- package/src/lib/layers/compositor.ts +175 -0
- package/src/lib/layers/layer-commands.test.ts +263 -0
- package/src/lib/layers/layer-commands.ts +429 -0
- package/src/lib/layers/layer-tree.svelte.ts +516 -0
- package/src/lib/layers/layer-tree.test.ts +383 -0
- package/src/lib/layers/layer-types.ts +56 -0
- package/src/lib/leveleditor/LevelEditorViewport.svelte +1808 -0
- package/src/lib/leveleditor/MapPropertiesPanel.svelte +266 -0
- package/src/lib/leveleditor/TilePickerPanel.svelte +324 -0
- package/src/lib/leveleditor/depth-sort.test.ts +70 -0
- package/src/lib/leveleditor/depth-sort.ts +39 -0
- package/src/lib/leveleditor/level-editor-commands.ts +353 -0
- package/src/lib/leveleditor/level-editor-viewport-plugin.ts +25 -0
- package/src/lib/leveleditor/map-properties-panel-plugin.ts +25 -0
- package/src/lib/leveleditor/map-state.svelte.ts +372 -0
- package/src/lib/leveleditor/map-state.test.ts +243 -0
- package/src/lib/leveleditor/tile-picker-panel-plugin.ts +25 -0
- package/src/lib/leveleditor/tile-source-registry.svelte.ts +91 -0
- package/src/lib/leveleditor/tiled-export.test.ts +144 -0
- package/src/lib/leveleditor/tiled-export.ts +374 -0
- package/src/lib/pywebview.d.ts +15 -0
- package/src/lib/recovery/.gitkeep +0 -0
- package/src/lib/recovery/idb-store.ts +118 -0
- package/src/lib/recovery/recovery-manager.test.ts +321 -0
- package/src/lib/recovery/recovery-manager.ts +115 -0
- package/src/lib/recovery/recovery-plugin.ts +55 -0
- package/src/lib/recovery/recovery-state.svelte.ts +21 -0
- package/src/lib/recovery/sw-plugin.ts +18 -0
- package/src/lib/recovery/sw-register.ts +17 -0
- package/src/lib/save/directory-format.ts +42 -0
- package/src/lib/save/project-snapshot.ts +139 -0
- package/src/lib/save/recent-projects.ts +56 -0
- package/src/lib/save/save-state.svelte.ts +35 -0
- package/src/lib/save/storage.ts +167 -0
- package/src/lib/save/zip-format.ts +45 -0
- package/src/lib/shortcuts/.gitkeep +0 -0
- package/src/lib/shortcuts/ShortcutEditorPanel.svelte +690 -0
- package/src/lib/shortcuts/default-bindings.ts +61 -0
- package/src/lib/shortcuts/shortcut-editor-panel-plugin.ts +25 -0
- package/src/lib/shortcuts/shortcut-init.ts +380 -0
- package/src/lib/shortcuts/shortcut-manager.test.ts +466 -0
- package/src/lib/shortcuts/shortcut-manager.ts +281 -0
- package/src/lib/shortcuts/shortcut-state.svelte.ts +78 -0
- package/src/lib/shortcuts/shortcuts-plugin.ts +17 -0
- package/src/lib/sync/patch-applicator.ts +300 -0
- package/src/lib/sync/patch-types.ts +65 -0
- package/src/lib/sync/project-manager.ts +108 -0
- package/src/lib/sync/sync-init.ts +152 -0
- package/src/lib/sync/sync-plugin.ts +19 -0
- package/src/lib/sync/sync-state.svelte.ts +56 -0
- package/src/lib/sync/ws-client.test.ts +604 -0
- package/src/lib/sync/ws-client.ts +574 -0
- package/src/lib/tools/.gitkeep +0 -0
- package/src/lib/ui/.gitkeep +0 -0
- package/src/lib/ui/AboutDialog.svelte +113 -0
- package/src/lib/ui/ColorPicker.svelte +761 -0
- package/src/lib/ui/ContextMenu.svelte +216 -0
- package/src/lib/ui/ExportDialog.svelte +747 -0
- package/src/lib/ui/FrameStrip.svelte +854 -0
- package/src/lib/ui/LayerPanel.svelte +810 -0
- package/src/lib/ui/MenuBar.svelte +590 -0
- package/src/lib/ui/NewProjectDialog.svelte +803 -0
- package/src/lib/ui/PluginManagerPanel.svelte +475 -0
- package/src/lib/ui/PromptDialog.svelte +252 -0
- package/src/lib/ui/RecoveryDialog.svelte +295 -0
- package/src/lib/ui/ResizeDialog.svelte +416 -0
- package/src/lib/ui/StatusBar.svelte +145 -0
- package/src/lib/ui/ToolbarPanel.svelte +488 -0
- package/src/lib/ui/animation-menu-commands.ts +194 -0
- package/src/lib/ui/command-palette/CommandPalette.svelte +232 -0
- package/src/lib/ui/command-palette/command-palette-plugin.ts +30 -0
- package/src/lib/ui/command-palette/command-palette-state.svelte.ts +190 -0
- package/src/lib/ui/command-palette/command-palette.test.ts +129 -0
- package/src/lib/ui/dialog-state.svelte.ts +70 -0
- package/src/lib/ui/edit-commands.ts +271 -0
- package/src/lib/ui/file-commands.ts +275 -0
- package/src/lib/ui/file-open.ts +99 -0
- package/src/lib/ui/help-commands.ts +93 -0
- package/src/lib/ui/image-commands.ts +181 -0
- package/src/lib/ui/layer-menu-commands.ts +420 -0
- package/src/lib/ui/menu-builder.ts +224 -0
- package/src/lib/ui/notifications/NotificationBanner.svelte +137 -0
- package/src/lib/ui/notifications/notification-plugin.ts +29 -0
- package/src/lib/ui/notifications/notification-state.svelte.ts +9 -0
- package/src/lib/ui/plugin-manager-panel-plugin.ts +26 -0
- package/src/lib/ui/plugin-state.svelte.ts +62 -0
- package/src/lib/ui/select-commands.ts +75 -0
- package/src/lib/ui/theme-plugin.ts +18 -0
- package/src/lib/ui/theme.svelte.ts +45 -0
- package/src/lib/ui/theme.test.ts +51 -0
- package/src/lib/ui/toolbar-config.ts +90 -0
- package/src/lib/ui/toolbar-plugin.ts +39 -0
- package/src/lib/ui/view-commands.ts +629 -0
- package/src/lib/variants/BisectionExportDialog.svelte +500 -0
- package/src/lib/variants/VariantPanel.svelte +822 -0
- package/src/lib/variants/bisection-export.test.ts +113 -0
- package/src/lib/variants/bisection-export.ts +148 -0
- package/src/lib/variants/palette-extraction.test.ts +111 -0
- package/src/lib/variants/palette-extraction.ts +84 -0
- package/src/lib/variants/palette-interpolation.test.ts +113 -0
- package/src/lib/variants/palette-interpolation.ts +87 -0
- package/src/lib/variants/palette-swap.test.ts +101 -0
- package/src/lib/variants/palette-swap.ts +114 -0
- package/src/lib/variants/variant-commands.ts +594 -0
- package/src/lib/variants/variant-panel-plugin.ts +27 -0
- package/src/lib/variants/variant-randomizer.ts +101 -0
- package/src/lib/variants/variant-state.svelte.ts +166 -0
- package/src/lib/variants/variant-state.test.ts +138 -0
- package/src/main.ts +14 -0
- package/src/vite-env.d.ts +3 -0
- package/svelte.config.js +2 -0
- package/todo/.done/audit-design-decisions.md +812 -0
- package/todo/.done/audit-implementation-plan.md +1235 -0
- package/todo/.done/happy-path-polish.md +177 -0
- package/todo/.done/pixelweaver-full-build.md +937 -0
- package/todo/.done/server-multi-frame-design.md +405 -0
- package/todo/.done/typed-dispatcher-design.md +435 -0
- package/todo/.done/unified-toolbar-and-action-system.md +323 -0
- package/todo/.obsolete/comprehensive-audit-obsolete-items.md +33 -0
- package/todo/.obsolete/tauri-desktop-bundle.md +424 -0
- package/todo/comprehensive-audit.md +1085 -0
- package/tsconfig.app.json +26 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +20 -0
- package/uv.lock +1167 -0
- package/vite.config.ts +32 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Level Editor Commands Plugin -- registers tile operations as undoable commands.
|
|
3
|
+
*
|
|
4
|
+
* Each command snapshots the previous tile at the target cell so that undo
|
|
5
|
+
* can restore it. The map-state module is imported directly (module-level
|
|
6
|
+
* singleton), following the same pattern as layer-commands.ts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { PluginModule } from '../core/plugin-loader.js';
|
|
10
|
+
import {
|
|
11
|
+
mapState,
|
|
12
|
+
type TilePlacement,
|
|
13
|
+
type EntityPlacement,
|
|
14
|
+
type CollisionShape,
|
|
15
|
+
} from './map-state.svelte.js';
|
|
16
|
+
|
|
17
|
+
// --- Helpers ---
|
|
18
|
+
|
|
19
|
+
/** Create the tile lookup key from column and row (mirrors map-state's internal tileKey). */
|
|
20
|
+
function tileKey(col: number, row: number): string {
|
|
21
|
+
return `${String(col)},${String(row)}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Read the current tile at (col, row) from a layer, or null if empty. */
|
|
25
|
+
function getTile(layerId: string, col: number, row: number): TilePlacement | null {
|
|
26
|
+
const layer = mapState.layers.find((l) => l.id === layerId);
|
|
27
|
+
if (!layer || layer.type !== 'tile' || !layer.tiles) return null;
|
|
28
|
+
return layer.tiles.get(tileKey(col, row)) ?? null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// --- Snapshot types ---
|
|
32
|
+
|
|
33
|
+
type PlaceTileSnapshot = { previous: TilePlacement | null };
|
|
34
|
+
type RemoveTileSnapshot = { previous: TilePlacement | null };
|
|
35
|
+
|
|
36
|
+
/** Stores the ID assigned by addEntity so undo can remove it. */
|
|
37
|
+
type PlaceEntitySnapshot = { entityId: string };
|
|
38
|
+
/** Stores the full entity data so undo can restore it. */
|
|
39
|
+
type RemoveEntitySnapshot = { entity: EntityPlacement };
|
|
40
|
+
/** Stores old position so undo can move it back. */
|
|
41
|
+
type MoveEntitySnapshot = { oldX: number; oldY: number };
|
|
42
|
+
/** Stores old properties so undo can restore them. */
|
|
43
|
+
type UpdateEntityPropsSnapshot = { oldProperties: Record<string, string> };
|
|
44
|
+
|
|
45
|
+
/** Stores the original fromIndex so undo can reverse the move. */
|
|
46
|
+
type MoveLayerSnapshot = { fromIndex: number; toIndex: number };
|
|
47
|
+
|
|
48
|
+
/** Stores the ID assigned by addCollisionShape so undo can remove it. */
|
|
49
|
+
type AddCollisionSnapshot = { shapeId: string };
|
|
50
|
+
/** Stores the full shape data so undo can restore it. */
|
|
51
|
+
type RemoveCollisionSnapshot = { shape: CollisionShape };
|
|
52
|
+
|
|
53
|
+
// --- Plugin ---
|
|
54
|
+
|
|
55
|
+
export const levelEditorCommandsPlugin: PluginModule = {
|
|
56
|
+
name: 'leveleditor/commands',
|
|
57
|
+
version: '1.0.0',
|
|
58
|
+
description: 'Undoable tile, entity, and collision commands for the level editor',
|
|
59
|
+
dependencies: [],
|
|
60
|
+
|
|
61
|
+
register(api) {
|
|
62
|
+
// --- place_tile ---
|
|
63
|
+
api.addCommand('place_tile', {
|
|
64
|
+
tier: 'project',
|
|
65
|
+
execute(params) {
|
|
66
|
+
const layerId = params['layerId'];
|
|
67
|
+
const col = params['col'];
|
|
68
|
+
const row = params['row'];
|
|
69
|
+
const tile = params['tile'] as TilePlacement;
|
|
70
|
+
|
|
71
|
+
// Snapshot whatever was at this cell before (null if empty).
|
|
72
|
+
const previous = getTile(layerId, col, row);
|
|
73
|
+
const snapshot: PlaceTileSnapshot = {
|
|
74
|
+
previous: previous ? { ...previous } : null,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
mapState.placeTile(layerId, col, row, tile);
|
|
78
|
+
return snapshot;
|
|
79
|
+
},
|
|
80
|
+
undo(params, _ctx, snapshot) {
|
|
81
|
+
const layerId = params['layerId'];
|
|
82
|
+
const col = params['col'];
|
|
83
|
+
const row = params['row'];
|
|
84
|
+
const typed = snapshot as PlaceTileSnapshot | undefined;
|
|
85
|
+
|
|
86
|
+
if (typed?.previous) {
|
|
87
|
+
mapState.placeTile(layerId, col, row, typed.previous);
|
|
88
|
+
} else {
|
|
89
|
+
mapState.removeTile(layerId, col, row);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
describe(params) {
|
|
93
|
+
return `Placed tile at (${String(params['col'])}, ${String(params['row'])})`;
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// --- remove_tile ---
|
|
98
|
+
api.addCommand('remove_tile', {
|
|
99
|
+
tier: 'project',
|
|
100
|
+
execute(params) {
|
|
101
|
+
const layerId = params['layerId'];
|
|
102
|
+
const col = params['col'];
|
|
103
|
+
const row = params['row'];
|
|
104
|
+
|
|
105
|
+
// Snapshot the tile being removed (null if already empty).
|
|
106
|
+
const previous = getTile(layerId, col, row);
|
|
107
|
+
const snapshot: RemoveTileSnapshot = {
|
|
108
|
+
previous: previous ? { ...previous } : null,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
mapState.removeTile(layerId, col, row);
|
|
112
|
+
return snapshot;
|
|
113
|
+
},
|
|
114
|
+
undo(params, _ctx, snapshot) {
|
|
115
|
+
const layerId = params['layerId'];
|
|
116
|
+
const col = params['col'];
|
|
117
|
+
const row = params['row'];
|
|
118
|
+
const typed = snapshot as RemoveTileSnapshot | undefined;
|
|
119
|
+
|
|
120
|
+
if (typed?.previous) {
|
|
121
|
+
mapState.placeTile(layerId, col, row, typed.previous);
|
|
122
|
+
}
|
|
123
|
+
// If previous was null, the cell was already empty -- nothing to restore.
|
|
124
|
+
},
|
|
125
|
+
describe(params) {
|
|
126
|
+
return `Removed tile at (${String(params['col'])}, ${String(params['row'])})`;
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// --- move_layer ---
|
|
131
|
+
api.addCommand('move_layer', {
|
|
132
|
+
tier: 'project',
|
|
133
|
+
execute(params) {
|
|
134
|
+
const fromIndex: number = params['fromIndex'];
|
|
135
|
+
const toIndex: number = params['toIndex'];
|
|
136
|
+
|
|
137
|
+
const snapshot: MoveLayerSnapshot = { fromIndex, toIndex };
|
|
138
|
+
mapState.moveLayer(fromIndex, toIndex);
|
|
139
|
+
return snapshot;
|
|
140
|
+
},
|
|
141
|
+
undo(_params, _ctx, snapshot) {
|
|
142
|
+
const typed = snapshot as MoveLayerSnapshot | undefined;
|
|
143
|
+
if (typed) {
|
|
144
|
+
// Reverse the move: what went from->to now goes to->from.
|
|
145
|
+
mapState.moveLayer(typed.toIndex, typed.fromIndex);
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
describe(params) {
|
|
149
|
+
return `Moved layer from index ${String(params['fromIndex'])} to ${String(params['toIndex'])}`;
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// --- place_entity ---
|
|
154
|
+
api.addCommand('place_entity', {
|
|
155
|
+
tier: 'project',
|
|
156
|
+
execute(params) {
|
|
157
|
+
const layerId = params['layerId'];
|
|
158
|
+
const projectRef = params['projectRef'];
|
|
159
|
+
const canvasRef = params['canvasRef'];
|
|
160
|
+
const x = params['x'];
|
|
161
|
+
const y = params['y'];
|
|
162
|
+
const height = params['height'] ?? 0;
|
|
163
|
+
const properties = params['properties'] ?? {};
|
|
164
|
+
|
|
165
|
+
const entity = mapState.addEntity(layerId, {
|
|
166
|
+
projectRef,
|
|
167
|
+
canvasRef,
|
|
168
|
+
x,
|
|
169
|
+
y,
|
|
170
|
+
height,
|
|
171
|
+
properties,
|
|
172
|
+
});
|
|
173
|
+
const snapshot: PlaceEntitySnapshot = { entityId: entity.id };
|
|
174
|
+
return snapshot;
|
|
175
|
+
},
|
|
176
|
+
undo(params, _ctx, snapshot) {
|
|
177
|
+
const layerId = params['layerId'];
|
|
178
|
+
const typed = snapshot as PlaceEntitySnapshot | undefined;
|
|
179
|
+
if (typed) {
|
|
180
|
+
mapState.removeEntity(layerId, typed.entityId);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
describe(params) {
|
|
184
|
+
return `Placed entity at (${String(params['x'])}, ${String(params['y'])})`;
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// --- remove_entity ---
|
|
189
|
+
api.addCommand('remove_entity', {
|
|
190
|
+
tier: 'project',
|
|
191
|
+
execute(params) {
|
|
192
|
+
const layerId = params['layerId'];
|
|
193
|
+
const entityId = params['entityId'];
|
|
194
|
+
|
|
195
|
+
const entity = mapState.getEntity(layerId, entityId);
|
|
196
|
+
if (!entity) {
|
|
197
|
+
throw new Error(`Entity "${entityId}" not found on layer "${layerId}"`);
|
|
198
|
+
}
|
|
199
|
+
// Deep-copy the entity so the snapshot survives removal.
|
|
200
|
+
const snapshot: RemoveEntitySnapshot = {
|
|
201
|
+
entity: { ...entity, properties: { ...entity.properties } },
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
mapState.removeEntity(layerId, entityId);
|
|
205
|
+
return snapshot;
|
|
206
|
+
},
|
|
207
|
+
undo(params, _ctx, snapshot) {
|
|
208
|
+
const layerId = params['layerId'];
|
|
209
|
+
const typed = snapshot as RemoveEntitySnapshot | undefined;
|
|
210
|
+
if (typed) {
|
|
211
|
+
// Restore the entity with its original ID intact.
|
|
212
|
+
mapState.restoreEntity(layerId, typed.entity);
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
describe() {
|
|
216
|
+
return 'Removed entity';
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// --- move_entity ---
|
|
221
|
+
api.addCommand('move_entity', {
|
|
222
|
+
tier: 'project',
|
|
223
|
+
execute(params) {
|
|
224
|
+
const layerId = params['layerId'];
|
|
225
|
+
const entityId = params['entityId'];
|
|
226
|
+
const x = params['x'];
|
|
227
|
+
const y = params['y'];
|
|
228
|
+
|
|
229
|
+
// Prefer oldX/oldY from params (set by viewport drag) because by the
|
|
230
|
+
// time execute runs, the entity's position has already been mutated for
|
|
231
|
+
// smooth visual feedback during drag. Fall back to entity state for
|
|
232
|
+
// non-drag callers.
|
|
233
|
+
let oldX = params['oldX'];
|
|
234
|
+
let oldY = params['oldY'];
|
|
235
|
+
if (oldX == null || oldY == null) {
|
|
236
|
+
const entity = mapState.getEntity(layerId, entityId);
|
|
237
|
+
if (!entity) {
|
|
238
|
+
throw new Error(`Entity "${entityId}" not found on layer "${layerId}"`);
|
|
239
|
+
}
|
|
240
|
+
oldX = entity.x;
|
|
241
|
+
oldY = entity.y;
|
|
242
|
+
}
|
|
243
|
+
const snapshot: MoveEntitySnapshot = { oldX, oldY };
|
|
244
|
+
|
|
245
|
+
mapState.moveEntity(layerId, entityId, x, y);
|
|
246
|
+
return snapshot;
|
|
247
|
+
},
|
|
248
|
+
undo(params, _ctx, snapshot) {
|
|
249
|
+
const layerId = params['layerId'];
|
|
250
|
+
const entityId = params['entityId'];
|
|
251
|
+
const typed = snapshot as MoveEntitySnapshot | undefined;
|
|
252
|
+
if (typed) {
|
|
253
|
+
mapState.moveEntity(layerId, entityId, typed.oldX, typed.oldY);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
describe(params) {
|
|
257
|
+
return `Moved entity to (${String(params['x'])}, ${String(params['y'])})`;
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// --- update_entity_properties ---
|
|
262
|
+
api.addCommand('update_entity_properties', {
|
|
263
|
+
tier: 'project',
|
|
264
|
+
execute(params) {
|
|
265
|
+
const layerId = params['layerId'];
|
|
266
|
+
const entityId = params['entityId'];
|
|
267
|
+
const properties = params['properties'];
|
|
268
|
+
|
|
269
|
+
const entity = mapState.getEntity(layerId, entityId);
|
|
270
|
+
if (!entity) {
|
|
271
|
+
throw new Error(`Entity "${entityId}" not found on layer "${layerId}"`);
|
|
272
|
+
}
|
|
273
|
+
const snapshot: UpdateEntityPropsSnapshot = {
|
|
274
|
+
oldProperties: { ...entity.properties },
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
mapState.updateEntityProperties(layerId, entityId, properties);
|
|
278
|
+
return snapshot;
|
|
279
|
+
},
|
|
280
|
+
undo(params, _ctx, snapshot) {
|
|
281
|
+
const layerId = params['layerId'];
|
|
282
|
+
const entityId = params['entityId'];
|
|
283
|
+
const typed = snapshot as UpdateEntityPropsSnapshot | undefined;
|
|
284
|
+
if (typed) {
|
|
285
|
+
// Full replacement: overwrite current properties with the snapshot.
|
|
286
|
+
const entity = mapState.getEntity(layerId, entityId);
|
|
287
|
+
if (entity) {
|
|
288
|
+
entity.properties = { ...typed.oldProperties };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
describe() {
|
|
293
|
+
return 'Updated entity properties';
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// --- add_collision_shape ---
|
|
298
|
+
api.addCommand('add_collision_shape', {
|
|
299
|
+
tier: 'project',
|
|
300
|
+
execute(params) {
|
|
301
|
+
const layerId = params['layerId'];
|
|
302
|
+
const type = params['type'];
|
|
303
|
+
const points = params['points'];
|
|
304
|
+
|
|
305
|
+
const shape = mapState.addCollisionShape(layerId, { type, points });
|
|
306
|
+
const snapshot: AddCollisionSnapshot = { shapeId: shape.id };
|
|
307
|
+
return snapshot;
|
|
308
|
+
},
|
|
309
|
+
undo(params, _ctx, snapshot) {
|
|
310
|
+
const layerId = params['layerId'];
|
|
311
|
+
const typed = snapshot as AddCollisionSnapshot | undefined;
|
|
312
|
+
if (typed) {
|
|
313
|
+
mapState.removeCollisionShape(layerId, typed.shapeId);
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
describe(params) {
|
|
317
|
+
return `Added collision ${String(params['type'])}`;
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// --- remove_collision_shape ---
|
|
322
|
+
api.addCommand('remove_collision_shape', {
|
|
323
|
+
tier: 'project',
|
|
324
|
+
execute(params) {
|
|
325
|
+
const layerId = params['layerId'];
|
|
326
|
+
const shapeId = params['shapeId'];
|
|
327
|
+
|
|
328
|
+
const shape = mapState.getCollisionShape(layerId, shapeId);
|
|
329
|
+
if (!shape) {
|
|
330
|
+
throw new Error(`Collision shape "${shapeId}" not found on layer "${layerId}"`);
|
|
331
|
+
}
|
|
332
|
+
// Deep-copy points array so snapshot survives removal.
|
|
333
|
+
const snapshot: RemoveCollisionSnapshot = {
|
|
334
|
+
shape: { ...shape, points: shape.points.map((p) => ({ ...p })) },
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
mapState.removeCollisionShape(layerId, shapeId);
|
|
338
|
+
return snapshot;
|
|
339
|
+
},
|
|
340
|
+
undo(params, _ctx, snapshot) {
|
|
341
|
+
const layerId = params['layerId'];
|
|
342
|
+
const typed = snapshot as RemoveCollisionSnapshot | undefined;
|
|
343
|
+
if (typed) {
|
|
344
|
+
// Restore the shape with its original ID intact.
|
|
345
|
+
mapState.restoreCollisionShape(layerId, typed.shape);
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
describe() {
|
|
349
|
+
return 'Removed collision shape';
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
},
|
|
353
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Level Editor Viewport plugin -- registers the tilemap editor panel with
|
|
3
|
+
* the central panel registry. Bootstrap auto-discovers this module via the
|
|
4
|
+
* `*-plugin.ts` glob in bootstrap.ts, so there's nothing else to wire up.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PluginModule } from '../core/plugin-loader.js';
|
|
8
|
+
import LevelEditorViewport from './LevelEditorViewport.svelte';
|
|
9
|
+
|
|
10
|
+
export const levelEditorViewportPlugin: PluginModule = {
|
|
11
|
+
name: 'leveleditor/viewport',
|
|
12
|
+
version: '1.0.0',
|
|
13
|
+
dependencies: [],
|
|
14
|
+
register(api) {
|
|
15
|
+
api.addPanel('level-editor', {
|
|
16
|
+
title: 'Level Editor',
|
|
17
|
+
component: LevelEditorViewport,
|
|
18
|
+
// Tabbed alongside the canvas in the center area. Users can drag it
|
|
19
|
+
// out to a side/bottom if they prefer a split view.
|
|
20
|
+
position: 'center',
|
|
21
|
+
minWidth: 200,
|
|
22
|
+
minHeight: 200,
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map Properties Panel Plugin -- registers MapPropertiesPanel as a dockable
|
|
3
|
+
* panel in the right sidebar. Allows editing grid dimensions, tile size,
|
|
4
|
+
* and grid projection mode for the level editor.
|
|
5
|
+
*
|
|
6
|
+
* Discovered by bootstrap via the `*-plugin.ts` glob.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { PluginModule } from '../core/plugin-loader.js';
|
|
10
|
+
import MapPropertiesPanel from './MapPropertiesPanel.svelte';
|
|
11
|
+
|
|
12
|
+
export const mapPropertiesPanelPlugin: PluginModule = {
|
|
13
|
+
name: 'leveleditor/map-properties-panel',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
dependencies: [],
|
|
16
|
+
register(api) {
|
|
17
|
+
api.addPanel('map-properties', {
|
|
18
|
+
title: 'Map Properties',
|
|
19
|
+
component: MapPropertiesPanel,
|
|
20
|
+
position: 'right',
|
|
21
|
+
minWidth: 160,
|
|
22
|
+
maxWidth: 300,
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
};
|