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.
Files changed (392) hide show
  1. package/.env.development +1 -0
  2. package/.github/workflows/ci.yml +22 -0
  3. package/.github/workflows/publish.yml +18 -0
  4. package/.prettierignore +5 -0
  5. package/.prettierrc +16 -0
  6. package/.python-version +1 -0
  7. package/.rlsbl/bases/.github/workflows/ci.yml +21 -0
  8. package/.rlsbl/bases/.github/workflows/publish.yml +18 -0
  9. package/.rlsbl/bases/.rlsbl/changes/unreleased.jsonl +0 -0
  10. package/.rlsbl/bases/.rlsbl/hooks/post-release.sh +8 -0
  11. package/.rlsbl/bases/.rlsbl/hooks/pre-checks.sh +5 -0
  12. package/.rlsbl/bases/.rlsbl/hooks/pre-release.sh +8 -0
  13. package/.rlsbl/bases/.rlsbl/lint/go.toml +17 -0
  14. package/.rlsbl/bases/.rlsbl/lint/npm.toml +19 -0
  15. package/.rlsbl/bases/.rlsbl/lint/python.toml +25 -0
  16. package/.rlsbl/bases/CHANGELOG.md +5 -0
  17. package/.rlsbl/bases/LICENSE +21 -0
  18. package/.rlsbl/changes/.validated +1 -0
  19. package/.rlsbl/changes/0.1.0.jsonl +85 -0
  20. package/.rlsbl/changes/0.1.0.md +13 -0
  21. package/.rlsbl/changes/unreleased.jsonl +0 -0
  22. package/.rlsbl/config.json +6 -0
  23. package/.rlsbl/hashes.json +14 -0
  24. package/.rlsbl/hooks/post-release.sh +8 -0
  25. package/.rlsbl/hooks/pre-checks.sh +5 -0
  26. package/.rlsbl/hooks/pre-release.sh +8 -0
  27. package/.rlsbl/lint/go.toml +17 -0
  28. package/.rlsbl/lint/npm.toml +19 -0
  29. package/.rlsbl/lint/python.toml +25 -0
  30. package/.rlsbl/releases/unreleased.toml +0 -0
  31. package/.rlsbl/releases/v0.1.0.toml +3 -0
  32. package/.rlsbl/version +1 -0
  33. package/.selfdoc/hashes/hashes.json +146 -0
  34. package/.strictcli/schema.json +227 -0
  35. package/CHANGELOG.md +17 -0
  36. package/CLAUDE.md +100 -0
  37. package/LICENSE +21 -0
  38. package/README.md +116 -0
  39. package/assets/icon.png +0 -0
  40. package/docs/_README.md +117 -0
  41. package/docs/cli-config.md +35 -0
  42. package/docs/cli-dev.md +21 -0
  43. package/docs/cli-diagnose.md +12 -0
  44. package/docs/cli-index.md +30 -0
  45. package/docs/cli-list.md +18 -0
  46. package/docs/cli-mcp.md +18 -0
  47. package/docs/cli-new.md +26 -0
  48. package/docs/cli-open.md +21 -0
  49. package/docs/cli-serve.md +21 -0
  50. package/docs/cli-stop.md +12 -0
  51. package/docs/gen-index.md +36 -0
  52. package/docs/index.md +13 -0
  53. package/docs/server-src-pixelweaver-__main__.md +12 -0
  54. package/docs/server-src-pixelweaver-autosave.md +12 -0
  55. package/docs/server-src-pixelweaver-bridge.md +12 -0
  56. package/docs/server-src-pixelweaver-cli.md +12 -0
  57. package/docs/server-src-pixelweaver-config.md +12 -0
  58. package/docs/server-src-pixelweaver-connections.md +12 -0
  59. package/docs/server-src-pixelweaver-main.md +12 -0
  60. package/docs/server-src-pixelweaver-mcp_bridge.md +12 -0
  61. package/docs/server-src-pixelweaver-mcp_drawing_tools.md +12 -0
  62. package/docs/server-src-pixelweaver-mcp_export_tools.md +12 -0
  63. package/docs/server-src-pixelweaver-mcp_frame_tools.md +12 -0
  64. package/docs/server-src-pixelweaver-mcp_history_tools.md +12 -0
  65. package/docs/server-src-pixelweaver-mcp_layer_tools.md +12 -0
  66. package/docs/server-src-pixelweaver-mcp_lock.md +12 -0
  67. package/docs/server-src-pixelweaver-mcp_project_tools.md +12 -0
  68. package/docs/server-src-pixelweaver-mcp_read_tools.md +12 -0
  69. package/docs/server-src-pixelweaver-mcp_registry.md +12 -0
  70. package/docs/server-src-pixelweaver-mcp_resources.md +12 -0
  71. package/docs/server-src-pixelweaver-mcp_server.md +12 -0
  72. package/docs/server-src-pixelweaver-protocol.md +12 -0
  73. package/docs/server-src-pixelweaver-state.md +12 -0
  74. package/docs/server-src-pixelweaver-storage.md +12 -0
  75. package/docs/server-src-pixelweaver-websocket.md +12 -0
  76. package/docs/server-src-pixelweaver.md +12 -0
  77. package/e2e/app-launch.test.ts +35 -0
  78. package/e2e/menus.test.ts +26 -0
  79. package/e2e/tools.test.ts +27 -0
  80. package/e2e/undo-redo.test.ts +11 -0
  81. package/eslint.config.js +62 -0
  82. package/index.html +13 -0
  83. package/package.json +48 -0
  84. package/playwright.config.ts +19 -0
  85. package/plugins/builtin/.gitkeep +0 -0
  86. package/plugins/builtin/advanced-fill-tool.ts +146 -0
  87. package/plugins/builtin/circle-tool.ts +186 -0
  88. package/plugins/builtin/diamond-tool.ts +182 -0
  89. package/plugins/builtin/dither-tool.ts +186 -0
  90. package/plugins/builtin/drawing-primitives-plugin.ts +362 -0
  91. package/plugins/builtin/drawing-utils.test.ts +495 -0
  92. package/plugins/builtin/drawing-utils.ts +431 -0
  93. package/plugins/builtin/effects/blur.ts +97 -0
  94. package/plugins/builtin/effects/color-effects.ts +278 -0
  95. package/plugins/builtin/effects/flip.ts +83 -0
  96. package/plugins/builtin/effects/glow.ts +118 -0
  97. package/plugins/builtin/effects/outline.ts +110 -0
  98. package/plugins/builtin/effects/rotate.ts +357 -0
  99. package/plugins/builtin/effects/scale.ts +258 -0
  100. package/plugins/builtin/effects/shadow.ts +111 -0
  101. package/plugins/builtin/effects/sharpen.ts +102 -0
  102. package/plugins/builtin/effects.test.ts +715 -0
  103. package/plugins/builtin/eraser-tool.ts +23 -0
  104. package/plugins/builtin/eyedropper-tool.ts +83 -0
  105. package/plugins/builtin/fill-tool.ts +93 -0
  106. package/plugins/builtin/gradient-tool.ts +204 -0
  107. package/plugins/builtin/gradient.test.ts +142 -0
  108. package/plugins/builtin/importers/aseprite-importer-plugin.ts +174 -0
  109. package/plugins/builtin/importers/aseprite-parser.ts +497 -0
  110. package/plugins/builtin/importers/piskel-importer-plugin.ts +222 -0
  111. package/plugins/builtin/importers/sky-spec-plugin.ts +409 -0
  112. package/plugins/builtin/line-tool.ts +267 -0
  113. package/plugins/builtin/make-stroke-tool.ts +271 -0
  114. package/plugins/builtin/noise-dither.test.ts +151 -0
  115. package/plugins/builtin/noise-tool.ts +131 -0
  116. package/plugins/builtin/pattern-stamp-tool.ts +162 -0
  117. package/plugins/builtin/pencil-tool.ts +25 -0
  118. package/plugins/builtin/rect-tool.ts +179 -0
  119. package/plugins/builtin/selection-tool.ts +388 -0
  120. package/plugins/builtin/selection.test.ts +195 -0
  121. package/plugins/builtin/tool-plugins.test.ts +529 -0
  122. package/public/favicon.svg +7 -0
  123. package/public/sw.js +91 -0
  124. package/pyproject.toml +49 -0
  125. package/scripts/eslint-wave-a-list.sh +24 -0
  126. package/scripts/eslint-wave-a-status.sh +25 -0
  127. package/scripts/fix-index-signature-access.py +127 -0
  128. package/scripts/fix-unchecked-index.py +128 -0
  129. package/scripts/fix-unchecked-vars.py +127 -0
  130. package/scripts/fix-wave-a-bangs.py +36 -0
  131. package/scripts/fix-wave-a-templates.py +108 -0
  132. package/scripts/fix-wave-b-void-expr.py +167 -0
  133. package/scripts/generate-command-params.py +540 -0
  134. package/scripts/migrate-single-frame-to-multi.py +171 -0
  135. package/scripts/smoke-test.sh +77 -0
  136. package/selfdoc.json +10 -0
  137. package/server/README.md +0 -0
  138. package/server/src/pixelweaver/__init__.py +1 -0
  139. package/server/src/pixelweaver/__main__.py +4 -0
  140. package/server/src/pixelweaver/autosave.py +114 -0
  141. package/server/src/pixelweaver/bridge.py +127 -0
  142. package/server/src/pixelweaver/cli.py +199 -0
  143. package/server/src/pixelweaver/config.py +24 -0
  144. package/server/src/pixelweaver/connections.py +54 -0
  145. package/server/src/pixelweaver/main.py +271 -0
  146. package/server/src/pixelweaver/mcp_bridge.py +189 -0
  147. package/server/src/pixelweaver/mcp_drawing_tools.py +178 -0
  148. package/server/src/pixelweaver/mcp_export_tools.py +291 -0
  149. package/server/src/pixelweaver/mcp_frame_tools.py +423 -0
  150. package/server/src/pixelweaver/mcp_history_tools.py +106 -0
  151. package/server/src/pixelweaver/mcp_layer_tools.py +64 -0
  152. package/server/src/pixelweaver/mcp_lock.py +37 -0
  153. package/server/src/pixelweaver/mcp_project_tools.py +302 -0
  154. package/server/src/pixelweaver/mcp_read_tools.py +163 -0
  155. package/server/src/pixelweaver/mcp_registry.py +247 -0
  156. package/server/src/pixelweaver/mcp_resources.py +312 -0
  157. package/server/src/pixelweaver/mcp_server.py +234 -0
  158. package/server/src/pixelweaver/protocol.py +219 -0
  159. package/server/src/pixelweaver/state.py +267 -0
  160. package/server/src/pixelweaver/storage.py +293 -0
  161. package/server/src/pixelweaver/websocket.py +282 -0
  162. package/server/tests/__init__.py +0 -0
  163. package/server/tests/conftest.py +17 -0
  164. package/server/tests/test_api.py +96 -0
  165. package/server/tests/test_bridge.py +161 -0
  166. package/server/tests/test_health.py +9 -0
  167. package/server/tests/test_integration.py +86 -0
  168. package/server/tests/test_mcp_bridge.py +293 -0
  169. package/server/tests/test_mcp_lock.py +34 -0
  170. package/server/tests/test_mcp_registry.py +679 -0
  171. package/server/tests/test_mcp_resources.py +648 -0
  172. package/server/tests/test_protocol.py +125 -0
  173. package/server/tests/test_state.py +87 -0
  174. package/server/tests/test_storage.py +306 -0
  175. package/server/tests/test_websocket.py +275 -0
  176. package/src/App.svelte +107 -0
  177. package/src/app.css +215 -0
  178. package/src/lib/animation/AnimationPreviewPanel.svelte +667 -0
  179. package/src/lib/animation/animation-commands.test.ts +228 -0
  180. package/src/lib/animation/animation-commands.ts +540 -0
  181. package/src/lib/animation/animation-preview-panel-plugin.ts +25 -0
  182. package/src/lib/animation/animation-preview.svelte.ts +151 -0
  183. package/src/lib/animation/clipboard.ts +134 -0
  184. package/src/lib/animation/frame-model.svelte.ts +437 -0
  185. package/src/lib/animation/frame-model.test.ts +314 -0
  186. package/src/lib/animation/frame-selection.svelte.ts +77 -0
  187. package/src/lib/animation/frame-tags.svelte.ts +238 -0
  188. package/src/lib/animation/frame-tags.test.ts +136 -0
  189. package/src/lib/animation/import.test.ts +141 -0
  190. package/src/lib/animation/import.ts +112 -0
  191. package/src/lib/animation/spritesheet-export.test.ts +239 -0
  192. package/src/lib/animation/spritesheet-export.ts +314 -0
  193. package/src/lib/canvas/CanvasViewport.svelte +216 -0
  194. package/src/lib/canvas/canvas-init-plugin.ts +178 -0
  195. package/src/lib/canvas/canvas-renderer.ts +408 -0
  196. package/src/lib/canvas/canvas-state.svelte.ts +232 -0
  197. package/src/lib/canvas/canvas-state.test.ts +139 -0
  198. package/src/lib/canvas/input-handler.ts +221 -0
  199. package/src/lib/canvas/input-plugin.ts +150 -0
  200. package/src/lib/canvas/onion-skin.ts +94 -0
  201. package/src/lib/canvas/pixel-buffer.test.ts +249 -0
  202. package/src/lib/canvas/pixel-buffer.ts +151 -0
  203. package/src/lib/canvas/render-state.svelte.ts +18 -0
  204. package/src/lib/canvas/shape-preview-state.svelte.ts +36 -0
  205. package/src/lib/canvas/tile-mode.test.ts +53 -0
  206. package/src/lib/canvas/tile-mode.ts +92 -0
  207. package/src/lib/canvas/viewport-utils.ts +24 -0
  208. package/src/lib/canvas/zoom-utils.ts +31 -0
  209. package/src/lib/color/.gitkeep +0 -0
  210. package/src/lib/color/color-commands.ts +87 -0
  211. package/src/lib/color/color-state.svelte.ts +98 -0
  212. package/src/lib/color/color-state.test.ts +91 -0
  213. package/src/lib/color/color-utils.test.ts +220 -0
  214. package/src/lib/color/color-utils.ts +243 -0
  215. package/src/lib/color/palette-state.svelte.ts +127 -0
  216. package/src/lib/color/palette-state.test.ts +154 -0
  217. package/src/lib/color/palette.ts +79 -0
  218. package/src/lib/core/bootstrap.ts +66 -0
  219. package/src/lib/core/command-params.generated.ts +1549 -0
  220. package/src/lib/core/command-params.ts +20 -0
  221. package/src/lib/core/command-runner.ts +79 -0
  222. package/src/lib/core/commands.ts +134 -0
  223. package/src/lib/core/dispatcher.test.ts +548 -0
  224. package/src/lib/core/dispatcher.ts +361 -0
  225. package/src/lib/core/index.test.ts +7 -0
  226. package/src/lib/core/notification-state.svelte.ts +119 -0
  227. package/src/lib/core/plugin-api.ts +210 -0
  228. package/src/lib/core/plugin-discovery.test.ts +53 -0
  229. package/src/lib/core/plugin-discovery.ts +65 -0
  230. package/src/lib/core/plugin-loader.test.ts +159 -0
  231. package/src/lib/core/plugin-loader.ts +240 -0
  232. package/src/lib/core/plugin-types.ts +286 -0
  233. package/src/lib/core/registries.svelte.ts +74 -0
  234. package/src/lib/core/tool-options-state.svelte.ts +61 -0
  235. package/src/lib/dock/DockLayout.svelte +375 -0
  236. package/src/lib/dock/TabAddMenu.svelte +90 -0
  237. package/src/lib/dock/dock-persistence.ts +46 -0
  238. package/src/lib/dock/dock-plugin.ts +49 -0
  239. package/src/lib/dock/dock-presets.ts +156 -0
  240. package/src/lib/dock/dock-state.svelte.ts +77 -0
  241. package/src/lib/dock/dock-types.ts +2 -0
  242. package/src/lib/dock/dockview-theme.css +226 -0
  243. package/src/lib/dock/dockview-theme.ts +17 -0
  244. package/src/lib/dock/header-action-renderer.ts +77 -0
  245. package/src/lib/edit/clipboard-state.ts +34 -0
  246. package/src/lib/export/download.ts +201 -0
  247. package/src/lib/export/png-metadata.ts +181 -0
  248. package/src/lib/history/ActionLogPanel.svelte +418 -0
  249. package/src/lib/history/action-log-panel-plugin.ts +24 -0
  250. package/src/lib/history/action-log.svelte.ts +172 -0
  251. package/src/lib/history/action-log.test.ts +168 -0
  252. package/src/lib/history/history-commands.ts +403 -0
  253. package/src/lib/history/macros.svelte.ts +320 -0
  254. package/src/lib/history/macros.test.ts +224 -0
  255. package/src/lib/history/replay-engine.test.ts +241 -0
  256. package/src/lib/history/replay-engine.ts +149 -0
  257. package/src/lib/history/spec-format.test.ts +250 -0
  258. package/src/lib/history/spec-format.ts +210 -0
  259. package/src/lib/iso/SeamCheckerPanel.svelte +251 -0
  260. package/src/lib/iso/iso-math.test.ts +77 -0
  261. package/src/lib/iso/iso-math.ts +77 -0
  262. package/src/lib/iso/seam-checker-panel-plugin.ts +25 -0
  263. package/src/lib/iso/seam-checker.test.ts +126 -0
  264. package/src/lib/iso/seam-checker.ts +93 -0
  265. package/src/lib/iso/tessellation.ts +67 -0
  266. package/src/lib/layers/compositor.test.ts +193 -0
  267. package/src/lib/layers/compositor.ts +175 -0
  268. package/src/lib/layers/layer-commands.test.ts +263 -0
  269. package/src/lib/layers/layer-commands.ts +429 -0
  270. package/src/lib/layers/layer-tree.svelte.ts +516 -0
  271. package/src/lib/layers/layer-tree.test.ts +383 -0
  272. package/src/lib/layers/layer-types.ts +56 -0
  273. package/src/lib/leveleditor/LevelEditorViewport.svelte +1808 -0
  274. package/src/lib/leveleditor/MapPropertiesPanel.svelte +266 -0
  275. package/src/lib/leveleditor/TilePickerPanel.svelte +324 -0
  276. package/src/lib/leveleditor/depth-sort.test.ts +70 -0
  277. package/src/lib/leveleditor/depth-sort.ts +39 -0
  278. package/src/lib/leveleditor/level-editor-commands.ts +353 -0
  279. package/src/lib/leveleditor/level-editor-viewport-plugin.ts +25 -0
  280. package/src/lib/leveleditor/map-properties-panel-plugin.ts +25 -0
  281. package/src/lib/leveleditor/map-state.svelte.ts +372 -0
  282. package/src/lib/leveleditor/map-state.test.ts +243 -0
  283. package/src/lib/leveleditor/tile-picker-panel-plugin.ts +25 -0
  284. package/src/lib/leveleditor/tile-source-registry.svelte.ts +91 -0
  285. package/src/lib/leveleditor/tiled-export.test.ts +144 -0
  286. package/src/lib/leveleditor/tiled-export.ts +374 -0
  287. package/src/lib/pywebview.d.ts +15 -0
  288. package/src/lib/recovery/.gitkeep +0 -0
  289. package/src/lib/recovery/idb-store.ts +118 -0
  290. package/src/lib/recovery/recovery-manager.test.ts +321 -0
  291. package/src/lib/recovery/recovery-manager.ts +115 -0
  292. package/src/lib/recovery/recovery-plugin.ts +55 -0
  293. package/src/lib/recovery/recovery-state.svelte.ts +21 -0
  294. package/src/lib/recovery/sw-plugin.ts +18 -0
  295. package/src/lib/recovery/sw-register.ts +17 -0
  296. package/src/lib/save/directory-format.ts +42 -0
  297. package/src/lib/save/project-snapshot.ts +139 -0
  298. package/src/lib/save/recent-projects.ts +56 -0
  299. package/src/lib/save/save-state.svelte.ts +35 -0
  300. package/src/lib/save/storage.ts +167 -0
  301. package/src/lib/save/zip-format.ts +45 -0
  302. package/src/lib/shortcuts/.gitkeep +0 -0
  303. package/src/lib/shortcuts/ShortcutEditorPanel.svelte +690 -0
  304. package/src/lib/shortcuts/default-bindings.ts +61 -0
  305. package/src/lib/shortcuts/shortcut-editor-panel-plugin.ts +25 -0
  306. package/src/lib/shortcuts/shortcut-init.ts +380 -0
  307. package/src/lib/shortcuts/shortcut-manager.test.ts +466 -0
  308. package/src/lib/shortcuts/shortcut-manager.ts +281 -0
  309. package/src/lib/shortcuts/shortcut-state.svelte.ts +78 -0
  310. package/src/lib/shortcuts/shortcuts-plugin.ts +17 -0
  311. package/src/lib/sync/patch-applicator.ts +300 -0
  312. package/src/lib/sync/patch-types.ts +65 -0
  313. package/src/lib/sync/project-manager.ts +108 -0
  314. package/src/lib/sync/sync-init.ts +152 -0
  315. package/src/lib/sync/sync-plugin.ts +19 -0
  316. package/src/lib/sync/sync-state.svelte.ts +56 -0
  317. package/src/lib/sync/ws-client.test.ts +604 -0
  318. package/src/lib/sync/ws-client.ts +574 -0
  319. package/src/lib/tools/.gitkeep +0 -0
  320. package/src/lib/ui/.gitkeep +0 -0
  321. package/src/lib/ui/AboutDialog.svelte +113 -0
  322. package/src/lib/ui/ColorPicker.svelte +761 -0
  323. package/src/lib/ui/ContextMenu.svelte +216 -0
  324. package/src/lib/ui/ExportDialog.svelte +747 -0
  325. package/src/lib/ui/FrameStrip.svelte +854 -0
  326. package/src/lib/ui/LayerPanel.svelte +810 -0
  327. package/src/lib/ui/MenuBar.svelte +590 -0
  328. package/src/lib/ui/NewProjectDialog.svelte +803 -0
  329. package/src/lib/ui/PluginManagerPanel.svelte +475 -0
  330. package/src/lib/ui/PromptDialog.svelte +252 -0
  331. package/src/lib/ui/RecoveryDialog.svelte +295 -0
  332. package/src/lib/ui/ResizeDialog.svelte +416 -0
  333. package/src/lib/ui/StatusBar.svelte +145 -0
  334. package/src/lib/ui/ToolbarPanel.svelte +488 -0
  335. package/src/lib/ui/animation-menu-commands.ts +194 -0
  336. package/src/lib/ui/command-palette/CommandPalette.svelte +232 -0
  337. package/src/lib/ui/command-palette/command-palette-plugin.ts +30 -0
  338. package/src/lib/ui/command-palette/command-palette-state.svelte.ts +190 -0
  339. package/src/lib/ui/command-palette/command-palette.test.ts +129 -0
  340. package/src/lib/ui/dialog-state.svelte.ts +70 -0
  341. package/src/lib/ui/edit-commands.ts +271 -0
  342. package/src/lib/ui/file-commands.ts +275 -0
  343. package/src/lib/ui/file-open.ts +99 -0
  344. package/src/lib/ui/help-commands.ts +93 -0
  345. package/src/lib/ui/image-commands.ts +181 -0
  346. package/src/lib/ui/layer-menu-commands.ts +420 -0
  347. package/src/lib/ui/menu-builder.ts +224 -0
  348. package/src/lib/ui/notifications/NotificationBanner.svelte +137 -0
  349. package/src/lib/ui/notifications/notification-plugin.ts +29 -0
  350. package/src/lib/ui/notifications/notification-state.svelte.ts +9 -0
  351. package/src/lib/ui/plugin-manager-panel-plugin.ts +26 -0
  352. package/src/lib/ui/plugin-state.svelte.ts +62 -0
  353. package/src/lib/ui/select-commands.ts +75 -0
  354. package/src/lib/ui/theme-plugin.ts +18 -0
  355. package/src/lib/ui/theme.svelte.ts +45 -0
  356. package/src/lib/ui/theme.test.ts +51 -0
  357. package/src/lib/ui/toolbar-config.ts +90 -0
  358. package/src/lib/ui/toolbar-plugin.ts +39 -0
  359. package/src/lib/ui/view-commands.ts +629 -0
  360. package/src/lib/variants/BisectionExportDialog.svelte +500 -0
  361. package/src/lib/variants/VariantPanel.svelte +822 -0
  362. package/src/lib/variants/bisection-export.test.ts +113 -0
  363. package/src/lib/variants/bisection-export.ts +148 -0
  364. package/src/lib/variants/palette-extraction.test.ts +111 -0
  365. package/src/lib/variants/palette-extraction.ts +84 -0
  366. package/src/lib/variants/palette-interpolation.test.ts +113 -0
  367. package/src/lib/variants/palette-interpolation.ts +87 -0
  368. package/src/lib/variants/palette-swap.test.ts +101 -0
  369. package/src/lib/variants/palette-swap.ts +114 -0
  370. package/src/lib/variants/variant-commands.ts +594 -0
  371. package/src/lib/variants/variant-panel-plugin.ts +27 -0
  372. package/src/lib/variants/variant-randomizer.ts +101 -0
  373. package/src/lib/variants/variant-state.svelte.ts +166 -0
  374. package/src/lib/variants/variant-state.test.ts +138 -0
  375. package/src/main.ts +14 -0
  376. package/src/vite-env.d.ts +3 -0
  377. package/svelte.config.js +2 -0
  378. package/todo/.done/audit-design-decisions.md +812 -0
  379. package/todo/.done/audit-implementation-plan.md +1235 -0
  380. package/todo/.done/happy-path-polish.md +177 -0
  381. package/todo/.done/pixelweaver-full-build.md +937 -0
  382. package/todo/.done/server-multi-frame-design.md +405 -0
  383. package/todo/.done/typed-dispatcher-design.md +435 -0
  384. package/todo/.done/unified-toolbar-and-action-system.md +323 -0
  385. package/todo/.obsolete/comprehensive-audit-obsolete-items.md +33 -0
  386. package/todo/.obsolete/tauri-desktop-bundle.md +424 -0
  387. package/todo/comprehensive-audit.md +1085 -0
  388. package/tsconfig.app.json +26 -0
  389. package/tsconfig.json +7 -0
  390. package/tsconfig.node.json +20 -0
  391. package/uv.lock +1167 -0
  392. package/vite.config.ts +32 -0
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Variant Randomizer -- generates random palette variants via HSV shifts.
3
+ *
4
+ * Uses hue rotation, saturation shift, and value shift in HSV space for
5
+ * predictable and aesthetically coherent results. Optionally accepts a seed
6
+ * for reproducible random generation.
7
+ */
8
+
9
+ import { hexToRgb, rgbToHex, rgbToHsv, hsvToRgb } from '../color/color-utils.js';
10
+ import type { VariantPreset } from './variant-state.svelte.js';
11
+
12
+ export interface RandomVariantOptions {
13
+ /** Max hue shift in degrees (default: 360) */
14
+ hueRange?: number;
15
+ /** Max saturation shift as percentage points (default: 30) */
16
+ satRange?: number;
17
+ /** Max value shift as percentage points (default: 20) */
18
+ valRange?: number;
19
+ /** Seed for reproducible randomness */
20
+ seed?: number;
21
+ }
22
+
23
+ /**
24
+ * Simple seeded PRNG (xorshift32).
25
+ * Returns a function that produces floats in [0, 1) on each call.
26
+ */
27
+ function createRng(seed: number): () => number {
28
+ let state = seed | 0;
29
+ // Ensure non-zero state
30
+ if (state === 0) state = 1;
31
+ return () => {
32
+ state ^= state << 13;
33
+ state ^= state >> 17;
34
+ state ^= state << 5;
35
+ // Convert to unsigned 32-bit, then to [0, 1)
36
+ return (state >>> 0) / 4294967296;
37
+ };
38
+ }
39
+
40
+ /**
41
+ * Generate a random variant by shifting colors in each group.
42
+ *
43
+ * Each group gets a single uniform hue rotation (so all colors in a group
44
+ * shift together, preserving relative relationships). Saturation and value
45
+ * get per-color jitter within the specified range.
46
+ *
47
+ * @param groupPalettes - map of groupId -> extracted palette (hex strings)
48
+ * @param options - randomization parameters
49
+ * @returns a new VariantPreset with the randomized color overrides
50
+ */
51
+ export function generateRandomVariant(
52
+ groupPalettes: Map<string, string[]>,
53
+ options?: RandomVariantOptions,
54
+ ): VariantPreset {
55
+ const hueRange = options?.hueRange ?? 360;
56
+ const satRange = options?.satRange ?? 30;
57
+ const valRange = options?.valRange ?? 20;
58
+
59
+ const rng =
60
+ options?.seed !== undefined
61
+ ? createRng(options.seed)
62
+ : () => Math.random();
63
+
64
+ const groupOverrides = new Map<string, Map<string, string>>();
65
+
66
+ for (const [groupId, palette] of groupPalettes) {
67
+ if (palette.length === 0) continue;
68
+
69
+ // One uniform hue rotation per group
70
+ const hueShift = (rng() - 0.5) * 2 * hueRange;
71
+ const colorMap = new Map<string, string>();
72
+
73
+ for (const hex of palette) {
74
+ const rgb = hexToRgb(hex);
75
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
76
+
77
+ // Apply hue rotation (wraps around 0-360)
78
+ let newH = (hsv.h + hueShift) % 360;
79
+ if (newH < 0) newH += 360;
80
+
81
+ // Per-color saturation and value jitter
82
+ const satShift = (rng() - 0.5) * 2 * satRange;
83
+ const valShift = (rng() - 0.5) * 2 * valRange;
84
+ const newS = Math.max(0, Math.min(100, hsv.s + satShift));
85
+ const newV = Math.max(0, Math.min(100, hsv.v + valShift));
86
+
87
+ const newRgb = hsvToRgb(newH, newS, newV);
88
+ const newHex = rgbToHex(newRgb.r, newRgb.g, newRgb.b);
89
+
90
+ colorMap.set(hex, newHex);
91
+ }
92
+
93
+ groupOverrides.set(groupId, colorMap);
94
+ }
95
+
96
+ return {
97
+ id: crypto.randomUUID(),
98
+ name: 'Random Variant',
99
+ groupOverrides,
100
+ };
101
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Variant State -- reactive Svelte 5 state for variant presets.
3
+ *
4
+ * A variant preset defines per-group color overrides (palette swaps).
5
+ * Multiple presets can coexist; only one is "active" for preview at a time.
6
+ *
7
+ * Uses $state runes for reactivity within the Svelte app.
8
+ */
9
+
10
+ import { SvelteMap } from 'svelte/reactivity';
11
+
12
+ // --- Types ---
13
+
14
+ export interface VariantPreset {
15
+ id: string;
16
+ name: string;
17
+ /** Map of layer group ID -> color overrides for that group */
18
+ groupOverrides: Map<string, Map<string, string>>; // groupId -> (original hex -> replacement hex)
19
+ }
20
+
21
+ interface SerializedPreset {
22
+ id: string;
23
+ name: string;
24
+ groupOverrides: Record<string, Record<string, string>>;
25
+ }
26
+
27
+ interface SerializedState {
28
+ presets: SerializedPreset[];
29
+ activePresetId: string | null;
30
+ }
31
+
32
+ // --- Reactive state ---
33
+
34
+ let presets = $state<VariantPreset[]>([]);
35
+ let activePresetId = $state<string | null>(null);
36
+
37
+ // --- Queries ---
38
+
39
+ export function getPresets(): VariantPreset[] {
40
+ return presets;
41
+ }
42
+
43
+ export function getActivePresetId(): string | null {
44
+ return activePresetId;
45
+ }
46
+
47
+ export function getActivePreset(): VariantPreset | null {
48
+ if (activePresetId === null) return null;
49
+ return presets.find((p) => p.id === activePresetId) ?? null;
50
+ }
51
+
52
+ export function getPreset(id: string): VariantPreset | undefined {
53
+ return presets.find((p) => p.id === id);
54
+ }
55
+
56
+ // --- Mutations ---
57
+
58
+ export function addPreset(name: string): VariantPreset {
59
+ const preset: VariantPreset = {
60
+ id: crypto.randomUUID(),
61
+ name,
62
+ groupOverrides: new SvelteMap(),
63
+ };
64
+ presets.push(preset);
65
+ return preset;
66
+ }
67
+
68
+ export function removePreset(id: string): void {
69
+ const index = presets.findIndex((p) => p.id === id);
70
+ if (index === -1) return;
71
+ presets.splice(index, 1);
72
+ if (activePresetId === id) {
73
+ activePresetId = null;
74
+ }
75
+ }
76
+
77
+ export function renamePreset(id: string, name: string): void {
78
+ const preset = presets.find((p) => p.id === id);
79
+ if (!preset) throw new Error(`Preset "${id}" not found.`);
80
+ preset.name = name;
81
+ }
82
+
83
+ export function setColorOverride(
84
+ presetId: string,
85
+ groupId: string,
86
+ originalColor: string,
87
+ newColor: string,
88
+ ): void {
89
+ const preset = presets.find((p) => p.id === presetId);
90
+ if (!preset) throw new Error(`Preset "${presetId}" not found.`);
91
+
92
+ let groupMap = preset.groupOverrides.get(groupId);
93
+ if (!groupMap) {
94
+ groupMap = new SvelteMap();
95
+ preset.groupOverrides.set(groupId, groupMap);
96
+ }
97
+ groupMap.set(originalColor, newColor);
98
+ }
99
+
100
+ export function removeColorOverride(
101
+ presetId: string,
102
+ groupId: string,
103
+ originalColor: string,
104
+ ): void {
105
+ const preset = presets.find((p) => p.id === presetId);
106
+ if (!preset) throw new Error(`Preset "${presetId}" not found.`);
107
+
108
+ const groupMap = preset.groupOverrides.get(groupId);
109
+ if (!groupMap) return;
110
+ groupMap.delete(originalColor);
111
+
112
+ // Clean up empty group maps
113
+ if (groupMap.size === 0) {
114
+ preset.groupOverrides.delete(groupId);
115
+ }
116
+ }
117
+
118
+ export function setActivePreset(id: string | null): void {
119
+ if (id !== null) {
120
+ const exists = presets.some((p) => p.id === id);
121
+ if (!exists) throw new Error(`Preset "${id}" not found.`);
122
+ }
123
+ activePresetId = id;
124
+ }
125
+
126
+ // --- Serialization ---
127
+
128
+ export function serialize(): SerializedState {
129
+ return {
130
+ presets: presets.map((preset) => {
131
+ const groupOverrides: Record<string, Record<string, string>> = {};
132
+ for (const [groupId, colorMap] of preset.groupOverrides) {
133
+ groupOverrides[groupId] = Object.fromEntries(colorMap);
134
+ }
135
+ return {
136
+ id: preset.id,
137
+ name: preset.name,
138
+ groupOverrides,
139
+ };
140
+ }),
141
+ activePresetId,
142
+ };
143
+ }
144
+
145
+ export function deserialize(data: SerializedState): void {
146
+ presets = data.presets.map((sp) => {
147
+ const groupOverrides = new SvelteMap<string, Map<string, string>>();
148
+ for (const [groupId, overrides] of Object.entries(sp.groupOverrides)) {
149
+ groupOverrides.set(groupId, new SvelteMap(Object.entries(overrides)));
150
+ }
151
+ return {
152
+ id: sp.id,
153
+ name: sp.name,
154
+ groupOverrides,
155
+ };
156
+ });
157
+ activePresetId = data.activePresetId;
158
+ }
159
+
160
+ /**
161
+ * Reset all variant state. Intended for tests only.
162
+ */
163
+ export function _resetForTesting(): void {
164
+ presets = [];
165
+ activePresetId = null;
166
+ }
@@ -0,0 +1,138 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import * as vs from './variant-state.svelte.js';
3
+
4
+ beforeEach(() => {
5
+ vs._resetForTesting();
6
+ });
7
+
8
+ describe('addPreset', () => {
9
+ it('should add a preset with the given name', () => {
10
+ const preset = vs.addPreset('Fire');
11
+ expect(preset.name).toBe('Fire');
12
+ expect(preset.id).toBeTruthy();
13
+ expect(preset.groupOverrides.size).toBe(0);
14
+ expect(vs.getPresets()).toHaveLength(1);
15
+ });
16
+
17
+ it('should add multiple presets', () => {
18
+ vs.addPreset('Fire');
19
+ vs.addPreset('Ice');
20
+ expect(vs.getPresets()).toHaveLength(2);
21
+ });
22
+ });
23
+
24
+ describe('removePreset', () => {
25
+ it('should remove an existing preset', () => {
26
+ const preset = vs.addPreset('Fire');
27
+ vs.removePreset(preset.id);
28
+ expect(vs.getPresets()).toHaveLength(0);
29
+ });
30
+
31
+ it('should clear active preset if the removed preset was active', () => {
32
+ const preset = vs.addPreset('Fire');
33
+ vs.setActivePreset(preset.id);
34
+ expect(vs.getActivePresetId()).toBe(preset.id);
35
+ vs.removePreset(preset.id);
36
+ expect(vs.getActivePresetId()).toBeNull();
37
+ });
38
+
39
+ it('should be a no-op for non-existent ID', () => {
40
+ vs.addPreset('Fire');
41
+ vs.removePreset('nonexistent');
42
+ expect(vs.getPresets()).toHaveLength(1);
43
+ });
44
+ });
45
+
46
+ describe('renamePreset', () => {
47
+ it('should rename an existing preset', () => {
48
+ const preset = vs.addPreset('Fire');
49
+ vs.renamePreset(preset.id, 'Inferno');
50
+ expect(vs.getPreset(preset.id)?.name).toBe('Inferno');
51
+ });
52
+
53
+ it('should throw for non-existent preset', () => {
54
+ expect(() => { vs.renamePreset('bad-id', 'Oops'); }).toThrow();
55
+ });
56
+ });
57
+
58
+ describe('color overrides', () => {
59
+ it('should set and get a color override', () => {
60
+ const preset = vs.addPreset('Fire');
61
+ vs.setColorOverride(preset.id, 'g1', '#FF0000', '#FFAA00');
62
+ const groupMap = preset.groupOverrides.get('g1');
63
+ expect(groupMap).toBeDefined();
64
+ expect(groupMap?.get('#FF0000')).toBe('#FFAA00');
65
+ });
66
+
67
+ it('should remove a color override', () => {
68
+ const preset = vs.addPreset('Fire');
69
+ vs.setColorOverride(preset.id, 'g1', '#FF0000', '#FFAA00');
70
+ vs.removeColorOverride(preset.id, 'g1', '#FF0000');
71
+ // The group map should be cleaned up entirely
72
+ expect(preset.groupOverrides.has('g1')).toBe(false);
73
+ });
74
+
75
+ it('should handle removing a non-existent override gracefully', () => {
76
+ const preset = vs.addPreset('Fire');
77
+ // Should not throw
78
+ vs.removeColorOverride(preset.id, 'g1', '#FF0000');
79
+ expect(preset.groupOverrides.size).toBe(0);
80
+ });
81
+ });
82
+
83
+ describe('activePreset', () => {
84
+ it('should start with no active preset', () => {
85
+ expect(vs.getActivePresetId()).toBeNull();
86
+ expect(vs.getActivePreset()).toBeNull();
87
+ });
88
+
89
+ it('should set and get active preset', () => {
90
+ const preset = vs.addPreset('Fire');
91
+ vs.setActivePreset(preset.id);
92
+ expect(vs.getActivePresetId()).toBe(preset.id);
93
+ expect(vs.getActivePreset()).toStrictEqual(preset);
94
+ });
95
+
96
+ it('should clear active preset with null', () => {
97
+ const preset = vs.addPreset('Fire');
98
+ vs.setActivePreset(preset.id);
99
+ vs.setActivePreset(null);
100
+ expect(vs.getActivePresetId()).toBeNull();
101
+ });
102
+
103
+ it('should throw when setting non-existent preset as active', () => {
104
+ expect(() => { vs.setActivePreset('bad-id'); }).toThrow();
105
+ });
106
+ });
107
+
108
+ describe('serialization', () => {
109
+ it('should roundtrip serialize/deserialize', () => {
110
+ const preset = vs.addPreset('Fire');
111
+ vs.setColorOverride(preset.id, 'g1', '#FF0000', '#FFAA00');
112
+ vs.setColorOverride(preset.id, 'g1', '#00FF00', '#AAFF00');
113
+ vs.setColorOverride(preset.id, 'g2', '#0000FF', '#00AAFF');
114
+ vs.setActivePreset(preset.id);
115
+
116
+ const serialized = vs.serialize();
117
+ vs._resetForTesting();
118
+ expect(vs.getPresets()).toHaveLength(0);
119
+
120
+ vs.deserialize(serialized);
121
+
122
+ expect(vs.getPresets()).toHaveLength(1);
123
+ const restored = vs.getPresets()[0];
124
+ if (!restored) throw new Error('missing restored preset');
125
+ expect(restored.name).toBe('Fire');
126
+ expect(restored.id).toBe(preset.id);
127
+ expect(restored.groupOverrides.get('g1')?.get('#FF0000')).toBe('#FFAA00');
128
+ expect(restored.groupOverrides.get('g1')?.get('#00FF00')).toBe('#AAFF00');
129
+ expect(restored.groupOverrides.get('g2')?.get('#0000FF')).toBe('#00AAFF');
130
+ expect(vs.getActivePresetId()).toBe(preset.id);
131
+ });
132
+
133
+ it('should serialize empty state correctly', () => {
134
+ const serialized = vs.serialize();
135
+ expect(serialized.presets).toEqual([]);
136
+ expect(serialized.activePresetId).toBeNull();
137
+ });
138
+ });
package/src/main.ts ADDED
@@ -0,0 +1,14 @@
1
+ import './app.css';
2
+ import { mount } from 'svelte';
3
+ import App from './App.svelte';
4
+ import { bootstrap } from './lib/core/bootstrap.js';
5
+
6
+ // Initialize the plugin system -- discovers and loads all plugins,
7
+ // sets up tools, commands, shortcuts, sync, recovery, etc.
8
+ bootstrap();
9
+
10
+ const target = document.getElementById('app');
11
+ if (!target) throw new Error('Mount target #app not found in document');
12
+ const app = mount(App, { target });
13
+
14
+ export default app;
@@ -0,0 +1,3 @@
1
+ /// <reference types="svelte" />
2
+ /// <reference types="vite/client" />
3
+ /// <reference types="unplugin-icons/types/svelte" />
@@ -0,0 +1,2 @@
1
+ /** @type {import("@sveltejs/vite-plugin-svelte").SvelteConfig} */
2
+ export default {}