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,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Patch Types -- protocol for server-to-client state synchronization.
|
|
3
|
+
*
|
|
4
|
+
* When the server modifies state (via MCP or other operations), it broadcasts
|
|
5
|
+
* patches describing the pre-computed results. The frontend applies these
|
|
6
|
+
* directly without re-executing commands.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** A patch to pixel data on a specific layer/frame. */
|
|
10
|
+
export interface PixelPatch {
|
|
11
|
+
type: 'pixel';
|
|
12
|
+
layerId: string;
|
|
13
|
+
frameIndex: number;
|
|
14
|
+
/** Raw RGBA pixel data as base64 string or flat number array (legacy). */
|
|
15
|
+
data: string | number[];
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A patch to the layer tree structure. */
|
|
21
|
+
export interface LayerPatch {
|
|
22
|
+
type: 'layer';
|
|
23
|
+
action: 'add' | 'remove' | 'update';
|
|
24
|
+
/** Full serialized layer for add/update, just the id for remove. */
|
|
25
|
+
layer?: unknown;
|
|
26
|
+
layerId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A patch to canvas dimensions. */
|
|
30
|
+
export interface CanvasPatch {
|
|
31
|
+
type: 'canvas';
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A patch to frame structure (add/remove/reorder). */
|
|
37
|
+
export interface FramePatch {
|
|
38
|
+
type: 'frame';
|
|
39
|
+
action: 'add' | 'remove' | 'reorder' | 'set_duration';
|
|
40
|
+
frameIndex?: number;
|
|
41
|
+
/** Full frame data for add (pixel data per layer). */
|
|
42
|
+
frameData?: unknown;
|
|
43
|
+
fromIndex?: number;
|
|
44
|
+
toIndex?: number;
|
|
45
|
+
durationMs?: number | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A full state replacement (used for initial sync or large MCP operations). */
|
|
49
|
+
export interface FullStatePatch {
|
|
50
|
+
type: 'full_state';
|
|
51
|
+
/** Complete ProjectSnapshot (same format as save/load). */
|
|
52
|
+
snapshot: unknown;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Union of all patch types. */
|
|
56
|
+
export type StatePatch = PixelPatch | LayerPatch | CanvasPatch | FramePatch | FullStatePatch;
|
|
57
|
+
|
|
58
|
+
/** Wrapper message sent via WebSocket. */
|
|
59
|
+
export interface PatchMessage {
|
|
60
|
+
type: 'state_patch';
|
|
61
|
+
projectName: string;
|
|
62
|
+
patches: StatePatch[];
|
|
63
|
+
/** ID of the originating command (for correlation). */
|
|
64
|
+
commandId?: string;
|
|
65
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Manager -- high-level project operations via the REST API.
|
|
3
|
+
*
|
|
4
|
+
* Provides typed wrappers around the server's REST endpoints for creating,
|
|
5
|
+
* listing, inspecting, and deleting projects. The base URL is derived from
|
|
6
|
+
* the WebSocket URL in sync state (same host/port, HTTP scheme).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// --- Types ---
|
|
10
|
+
|
|
11
|
+
export interface CanvasInfo {
|
|
12
|
+
name: string;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ProjectInfo {
|
|
18
|
+
name: string;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
canvases: CanvasInfo[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// --- API base URL ---
|
|
25
|
+
|
|
26
|
+
// Default: same-origin (empty string = relative URLs).
|
|
27
|
+
// Override via VITE_SERVER_URL for dev mode (separate Vite + server ports).
|
|
28
|
+
let apiBase = (import.meta.env.VITE_SERVER_URL as string | undefined) || '';
|
|
29
|
+
|
|
30
|
+
/** Set the REST API base URL (no trailing slash). */
|
|
31
|
+
export function setApiBase(url: string): void {
|
|
32
|
+
apiBase = url.replace(/\/+$/, '');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Get the current REST API base URL. */
|
|
36
|
+
export function getApiBase(): string {
|
|
37
|
+
return apiBase;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// --- REST API wrappers ---
|
|
41
|
+
|
|
42
|
+
/** List all project names on the server. */
|
|
43
|
+
export async function listProjects(): Promise<string[]> {
|
|
44
|
+
const res = await fetch(`${apiBase}/api/projects`);
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
throw new Error(`Failed to list projects: ${String(res.status)} ${res.statusText}`);
|
|
47
|
+
}
|
|
48
|
+
const data = (await res.json()) as { projects: string[] };
|
|
49
|
+
return data.projects;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Create a new project with the given dimensions. */
|
|
53
|
+
export async function createProject(
|
|
54
|
+
name: string,
|
|
55
|
+
width: number,
|
|
56
|
+
height: number,
|
|
57
|
+
): Promise<void> {
|
|
58
|
+
const res = await fetch(`${apiBase}/api/projects`, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: { 'Content-Type': 'application/json' },
|
|
61
|
+
body: JSON.stringify({ name, width, height }),
|
|
62
|
+
});
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
const text = await res.text().catch(() => res.statusText);
|
|
65
|
+
throw new Error(`Failed to create project "${name}": ${text}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Delete a project by name. */
|
|
70
|
+
export async function deleteProject(name: string): Promise<void> {
|
|
71
|
+
const res = await fetch(`${apiBase}/api/projects/${encodeURIComponent(name)}`, {
|
|
72
|
+
method: 'DELETE',
|
|
73
|
+
});
|
|
74
|
+
if (!res.ok && res.status !== 204) {
|
|
75
|
+
const text = await res.text().catch(() => res.statusText);
|
|
76
|
+
throw new Error(`Failed to delete project "${name}": ${text}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Get detailed info about a single project. */
|
|
81
|
+
export async function getProjectInfo(name: string): Promise<ProjectInfo> {
|
|
82
|
+
const res = await fetch(`${apiBase}/api/projects/${encodeURIComponent(name)}`);
|
|
83
|
+
if (!res.ok) {
|
|
84
|
+
throw new Error(`Project "${name}" not found: ${String(res.status)} ${res.statusText}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Server returns: { name, width, height, canvases: { [name]: { name, width, height, layers } }, history }
|
|
88
|
+
const data = (await res.json()) as {
|
|
89
|
+
name: string;
|
|
90
|
+
width: number;
|
|
91
|
+
height: number;
|
|
92
|
+
canvases?: Record<string, { name: string; width: number; height: number }>;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Transform the server response into our typed ProjectInfo.
|
|
96
|
+
const canvases: CanvasInfo[] = data.canvases
|
|
97
|
+
? Object.values(data.canvases).map(
|
|
98
|
+
(c) => ({ name: c.name, width: c.width, height: c.height }),
|
|
99
|
+
)
|
|
100
|
+
: [];
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
name: data.name,
|
|
104
|
+
width: data.width,
|
|
105
|
+
height: data.height,
|
|
106
|
+
canvases,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Initialization -- wires the WebSocket client to the app's reactive state.
|
|
3
|
+
*
|
|
4
|
+
* This is the glue layer that connects the server-authoritative protocol to
|
|
5
|
+
* PixelWeaver's Svelte state (canvas, layers, dispatcher). Call initializeSync()
|
|
6
|
+
* once at app startup; call teardownSync() on app shutdown.
|
|
7
|
+
*
|
|
8
|
+
* Current integration level:
|
|
9
|
+
* - Connection status is reflected in syncState
|
|
10
|
+
* - State sync messages update canvas dimensions (layer/pixel restoration
|
|
11
|
+
* will be extended as the data model matures)
|
|
12
|
+
* - Command broadcasts are forwarded to registered listeners
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { WebSocketClient, type StateSnapshot } from './ws-client.js';
|
|
16
|
+
import { syncState } from './sync-state.svelte.js';
|
|
17
|
+
import { canvasState } from '../canvas/canvas-state.svelte.js';
|
|
18
|
+
import { setDispatchHook } from '../core/dispatcher.js';
|
|
19
|
+
import { applyPatches } from './patch-applicator.js';
|
|
20
|
+
import { notificationState } from '../core/notification-state.svelte.js';
|
|
21
|
+
|
|
22
|
+
// Module-level client instance; null when not initialized.
|
|
23
|
+
let client: WebSocketClient | null = null;
|
|
24
|
+
|
|
25
|
+
// Unsubscribe functions for event handlers
|
|
26
|
+
let unsubscribers: Array<() => void> = [];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialize the sync system: create a WebSocket client, register event
|
|
30
|
+
* handlers, and connect to the server.
|
|
31
|
+
*
|
|
32
|
+
* Safe to call multiple times -- tears down existing connection first.
|
|
33
|
+
*/
|
|
34
|
+
export async function initializeSync(): Promise<void> {
|
|
35
|
+
// Tear down any previous connection
|
|
36
|
+
teardownSync();
|
|
37
|
+
|
|
38
|
+
client = new WebSocketClient(syncState.serverUrl);
|
|
39
|
+
|
|
40
|
+
// --- Wire up event handlers ---
|
|
41
|
+
|
|
42
|
+
unsubscribers.push(
|
|
43
|
+
client.onConnectionChange((connected) => {
|
|
44
|
+
syncState.connected = connected;
|
|
45
|
+
syncState.connecting = false;
|
|
46
|
+
if (connected) {
|
|
47
|
+
syncState.reconnecting = false;
|
|
48
|
+
syncState.lastError = null;
|
|
49
|
+
} else {
|
|
50
|
+
// If we were connected and lost it, we're now reconnecting
|
|
51
|
+
// (the client handles reconnect internally)
|
|
52
|
+
syncState.reconnecting = true;
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
unsubscribers.push(
|
|
58
|
+
client.onStateSync((state: StateSnapshot) => {
|
|
59
|
+
applyStateSync(state);
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
unsubscribers.push(
|
|
64
|
+
client.onCommandBroadcast(() => {
|
|
65
|
+
// For now, command broadcasts from other clients are logged but not
|
|
66
|
+
// applied locally. Full multi-client command replay is a later phase.
|
|
67
|
+
// The state_sync mechanism handles full state restoration.
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
unsubscribers.push(
|
|
72
|
+
client.onStatePatch((message) => {
|
|
73
|
+
applyPatches(message);
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
unsubscribers.push(
|
|
78
|
+
client.onError((error) => {
|
|
79
|
+
syncState.lastError = error;
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// --- Dispatch hook: forward locally-dispatched commands to the server ---
|
|
84
|
+
|
|
85
|
+
setDispatchHook((command) => {
|
|
86
|
+
if (client && syncState.connected) {
|
|
87
|
+
client.forwardCommand(command);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// --- Notify the user when the server rejects a forwarded command ---
|
|
92
|
+
|
|
93
|
+
client.setOnForwardReject((_commandId, reason) => {
|
|
94
|
+
notificationState.push({
|
|
95
|
+
id: 'sync-forward-reject',
|
|
96
|
+
message: `Server rejected command: ${reason}`,
|
|
97
|
+
type: 'warning',
|
|
98
|
+
autoDismissMs: 5000,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// --- Connect ---
|
|
103
|
+
|
|
104
|
+
syncState.connecting = true;
|
|
105
|
+
try {
|
|
106
|
+
await client.connect();
|
|
107
|
+
} catch (err) {
|
|
108
|
+
syncState.connecting = false;
|
|
109
|
+
syncState.lastError = err instanceof Error ? err.message : String(err);
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Tear down the sync system: disconnect and clean up event handlers. */
|
|
115
|
+
export function teardownSync(): void {
|
|
116
|
+
setDispatchHook(null);
|
|
117
|
+
if (client) {
|
|
118
|
+
client.disconnect();
|
|
119
|
+
client = null;
|
|
120
|
+
}
|
|
121
|
+
for (const unsub of unsubscribers) {
|
|
122
|
+
unsub();
|
|
123
|
+
}
|
|
124
|
+
unsubscribers = [];
|
|
125
|
+
syncState.connected = false;
|
|
126
|
+
syncState.connecting = false;
|
|
127
|
+
syncState.reconnecting = false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Get the active WebSocket client, or null if not initialized. */
|
|
131
|
+
export function getClient(): WebSocketClient | null {
|
|
132
|
+
return client;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// --- Internal: apply server state to local Svelte state ---
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Apply a state_sync snapshot from the server to local state.
|
|
139
|
+
*
|
|
140
|
+
* Currently updates canvas dimensions from the project info. As the data
|
|
141
|
+
* model grows (pixel buffers, full layer trees from server), this function
|
|
142
|
+
* will be extended to restore those as well.
|
|
143
|
+
*/
|
|
144
|
+
function applyStateSync(state: StateSnapshot): void {
|
|
145
|
+
if (state.project) {
|
|
146
|
+
canvasState.canvasWidth = state.project.width;
|
|
147
|
+
canvasState.canvasHeight = state.project.height;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Layer and pixel data restoration will be implemented as the server's
|
|
151
|
+
// state model and the client's data structures are aligned in later phases.
|
|
152
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Plugin -- wraps WebSocket sync initialization as a plugin.
|
|
3
|
+
* Starts the connection in the background (fire-and-forget).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PluginModule } from '../core/plugin-loader.js';
|
|
7
|
+
import { initializeSync } from './sync-init.js';
|
|
8
|
+
|
|
9
|
+
export const syncPlugin: PluginModule = {
|
|
10
|
+
name: 'builtin/sync',
|
|
11
|
+
version: '1.0.0',
|
|
12
|
+
description: 'WebSocket sync client for multi-user collaboration',
|
|
13
|
+
|
|
14
|
+
register() {
|
|
15
|
+
// initializeSync is async but register is sync; the connection starts
|
|
16
|
+
// in the background and errors are handled internally by the sync system.
|
|
17
|
+
void initializeSync();
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync State -- reactive singleton for connection status and server URL.
|
|
3
|
+
*
|
|
4
|
+
* Uses Svelte 5 runes ($state) so that UI components can reactively display
|
|
5
|
+
* connection status, errors, and configure the server URL.
|
|
6
|
+
*
|
|
7
|
+
* Module-level singleton -- one sync state per app instance.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// --- Reactive state ---
|
|
11
|
+
|
|
12
|
+
/** Whether the WebSocket is currently connected and open. */
|
|
13
|
+
let connected = $state(false);
|
|
14
|
+
|
|
15
|
+
/** Whether a connection attempt is in progress (first connect, not reconnect). */
|
|
16
|
+
let connecting = $state(false);
|
|
17
|
+
|
|
18
|
+
/** Whether the client is attempting to reconnect after a disconnect. */
|
|
19
|
+
let reconnecting = $state(false);
|
|
20
|
+
|
|
21
|
+
/** The last error message, or null if no error. */
|
|
22
|
+
let lastError = $state<string | null>(null);
|
|
23
|
+
|
|
24
|
+
/** Derive a same-origin WebSocket URL. */
|
|
25
|
+
function defaultWsUrl(): string {
|
|
26
|
+
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
27
|
+
return `${proto}//${window.location.host}/ws`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The WebSocket server URL. */
|
|
31
|
+
let serverUrl = $state(import.meta.env.VITE_SERVER_URL
|
|
32
|
+
? `${(import.meta.env.VITE_SERVER_URL as string).replace(/^http/, 'ws')}/ws`
|
|
33
|
+
: defaultWsUrl());
|
|
34
|
+
|
|
35
|
+
// --- Public API ---
|
|
36
|
+
|
|
37
|
+
export const syncState = {
|
|
38
|
+
get connected() { return connected; },
|
|
39
|
+
set connected(v: boolean) { connected = v; },
|
|
40
|
+
|
|
41
|
+
get connecting() { return connecting; },
|
|
42
|
+
set connecting(v: boolean) { connecting = v; },
|
|
43
|
+
|
|
44
|
+
get reconnecting() { return reconnecting; },
|
|
45
|
+
set reconnecting(v: boolean) { reconnecting = v; },
|
|
46
|
+
|
|
47
|
+
get lastError() { return lastError; },
|
|
48
|
+
set lastError(v: string | null) { lastError = v; },
|
|
49
|
+
|
|
50
|
+
get serverUrl() { return serverUrl; },
|
|
51
|
+
|
|
52
|
+
/** Update the server URL. Does not trigger reconnect on its own. */
|
|
53
|
+
setServerUrl(url: string): void {
|
|
54
|
+
serverUrl = url;
|
|
55
|
+
},
|
|
56
|
+
};
|