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,812 @@
|
|
|
1
|
+
# PixelWeaver Codebase Audit -- Architectural Design Decisions
|
|
2
|
+
|
|
3
|
+
This document captures 10 design decisions arising from the PixelWeaver codebase audit.
|
|
4
|
+
Each decision includes full context, the problem, the chosen solution, exact files to change,
|
|
5
|
+
dependencies on other decisions, and effort estimates. The recommended implementation order
|
|
6
|
+
at the bottom accounts for all inter-decision dependencies.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Phase 1: DRY Factories (Isolated, No Dependencies)](#phase-1-dry-factories)
|
|
13
|
+
- [Decision #24: Create makeStrokeTool Factory](#decision-24-create-makestroketool-factory)
|
|
14
|
+
- [Decision #25: Create makeSnapshotUndo Factory](#decision-25-create-makesnapshotundo-factory)
|
|
15
|
+
- [Phase 2: Type System Foundation](#phase-2-type-system-foundation)
|
|
16
|
+
- [Decision #20: Generic Command\<P\> + Separate Snapshot from Params](#decision-20-generic-commandp--separate-snapshot-from-params)
|
|
17
|
+
- [Phase 3: Undoable Flag](#phase-3-undoable-flag)
|
|
18
|
+
- [Decision #1: Add undoable Flag to CommandDefinition](#decision-1-add-undoable-flag-to-commanddefinition)
|
|
19
|
+
- [Phase 4: Dialog / UI Work](#phase-4-dialog--ui-work)
|
|
20
|
+
- [Decision #22: Native \<dialog\> Element for Modals](#decision-22-native-dialog-element-for-modals)
|
|
21
|
+
- [Decision #26: Build Reusable PromptDialog Component](#decision-26-build-reusable-promptdialog-component)
|
|
22
|
+
- [Phase 5: God File Split](#phase-5-god-file-split)
|
|
23
|
+
- [Decision #14: Full 8-File Domain Split of menu-commands-plugin.ts](#decision-14-full-8-file-domain-split-of-menu-commands-plugints)
|
|
24
|
+
- [Phase 6: Plugin API Expansion](#phase-6-plugin-api-expansion)
|
|
25
|
+
- [Decision #12: Add getSelection() to PluginAPI](#decision-12-add-getselection-to-pluginapi)
|
|
26
|
+
- [Decision #13: Add Mutator Methods to PluginAPI Incrementally](#decision-13-add-mutator-methods-to-pluginapi-incrementally)
|
|
27
|
+
- [Phase 7: Strict TypeScript Flags](#phase-7-strict-typescript-flags)
|
|
28
|
+
- [Decision #19: Enable All 5 Strict TypeScript Flags](#decision-19-enable-all-5-strict-typescript-flags)
|
|
29
|
+
- [Implementation Order Summary](#implementation-order-summary)
|
|
30
|
+
- [Effort Summary](#effort-summary)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Phase 1: DRY Factories
|
|
35
|
+
|
|
36
|
+
These two decisions are fully isolated from every other decision and from each other.
|
|
37
|
+
They can be implemented in parallel on day one.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
### Decision #24: Create makeStrokeTool Factory
|
|
42
|
+
|
|
43
|
+
**Effort:** Small
|
|
44
|
+
|
|
45
|
+
**Depends on:** Nothing
|
|
46
|
+
|
|
47
|
+
#### Problem
|
|
48
|
+
|
|
49
|
+
`pencil-tool.ts` and `eraser-tool.ts` are structurally identical (~100 lines each).
|
|
50
|
+
They differ only in:
|
|
51
|
+
|
|
52
|
+
- Command name / description verb
|
|
53
|
+
- Pixel color resolution (foreground color vs. transparent)
|
|
54
|
+
- Icon
|
|
55
|
+
- Toolbar order
|
|
56
|
+
|
|
57
|
+
This is a textbook DRY violation. Any behavioral change to stroke tools must be
|
|
58
|
+
applied twice, and they can silently drift.
|
|
59
|
+
|
|
60
|
+
#### Solution
|
|
61
|
+
|
|
62
|
+
Create a `makeStrokeTool(config)` factory function that produces an entire `PluginModule`.
|
|
63
|
+
|
|
64
|
+
**Config interface:**
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
interface StrokeToolConfig {
|
|
68
|
+
pluginName: string;
|
|
69
|
+
commandName: string;
|
|
70
|
+
toolId: string;
|
|
71
|
+
icon: any;
|
|
72
|
+
toolbarOrder: number;
|
|
73
|
+
describeVerb: string;
|
|
74
|
+
resolveColor: (ctx: ToolContext) => { r: number; g: number; b: number; a: number };
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Color resolution per tool:**
|
|
79
|
+
|
|
80
|
+
| Tool | resolveColor |
|
|
81
|
+
|------|-------------|
|
|
82
|
+
| Pencil | `(ctx) => hexToRgba(ctx.color)` |
|
|
83
|
+
| Eraser | `() => ({ r: 0, g: 0, b: 0, a: 0 })` |
|
|
84
|
+
|
|
85
|
+
Note: `pattern-stamp` stays separate -- it has a fundamentally different data flow
|
|
86
|
+
and should not be forced into this factory.
|
|
87
|
+
|
|
88
|
+
#### Files to Change
|
|
89
|
+
|
|
90
|
+
| File | Action |
|
|
91
|
+
|------|--------|
|
|
92
|
+
| `plugins/builtin/make-stroke-tool.ts` | **Create** -- the factory function |
|
|
93
|
+
| `plugins/builtin/pencil-tool.ts` | **Reduce** to ~15 lines calling the factory |
|
|
94
|
+
| `plugins/builtin/eraser-tool.ts` | **Reduce** to ~15 lines calling the factory |
|
|
95
|
+
|
|
96
|
+
#### Verification
|
|
97
|
+
|
|
98
|
+
- Draw with pencil: pixels appear in foreground color
|
|
99
|
+
- Draw with eraser: pixels become transparent
|
|
100
|
+
- Undo/redo works for both tools
|
|
101
|
+
- Both appear in toolbar at correct positions with correct icons
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Decision #25: Create makeSnapshotUndo Factory
|
|
106
|
+
|
|
107
|
+
**Effort:** Small (mechanical find-and-replace)
|
|
108
|
+
|
|
109
|
+
**Depends on:** Ideally done after #20 (if snapshot is separated from params, the undo
|
|
110
|
+
handler signature changes). Can be done before #20 with the current signature and
|
|
111
|
+
updated during #20.
|
|
112
|
+
|
|
113
|
+
#### Problem
|
|
114
|
+
|
|
115
|
+
32 out of 33 undo handlers across 23 files are byte-for-byte identical:
|
|
116
|
+
|
|
117
|
+
1. Get the active buffer from context
|
|
118
|
+
2. Bail if buffer is missing
|
|
119
|
+
3. Apply the snapshot pixels from params
|
|
120
|
+
|
|
121
|
+
This is massive duplication. A bug fix to the undo pattern would need 32 edits.
|
|
122
|
+
|
|
123
|
+
#### Solution
|
|
124
|
+
|
|
125
|
+
Add a `makeSnapshotUndo()` function to `drawing-utils.ts` that returns a reusable
|
|
126
|
+
undo handler closure.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
export function makeSnapshotUndo(): UndoHandler {
|
|
130
|
+
return (params, ctx) => {
|
|
131
|
+
const buffer = ctx.getActiveBuffer?.();
|
|
132
|
+
if (!buffer) return;
|
|
133
|
+
applyPixels(buffer, params._snapshot);
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The single exception (`move_selection`) stays manual because it has custom undo logic
|
|
139
|
+
beyond pixel restoration.
|
|
140
|
+
|
|
141
|
+
#### Files to Change
|
|
142
|
+
|
|
143
|
+
| File | Action | Occurrences |
|
|
144
|
+
|------|--------|-------------|
|
|
145
|
+
| `plugins/builtin/drawing-utils.ts` | **Add** `makeSnapshotUndo()` | 1 (definition) |
|
|
146
|
+
| `plugins/builtin/drawing-primitives-plugin.ts` | **Replace** inline undo handlers | 6 |
|
|
147
|
+
| `plugins/builtin/color-effects.ts` | **Replace** inline undo handlers | 5 |
|
|
148
|
+
| ~9 other tool/effect files in `plugins/builtin/` | **Replace** inline undo handlers | ~21 |
|
|
149
|
+
|
|
150
|
+
Total: 32 replacements across 15 files, plus 1 definition.
|
|
151
|
+
|
|
152
|
+
#### Verification
|
|
153
|
+
|
|
154
|
+
- Undo/redo works for all drawing tools and effects
|
|
155
|
+
- `move_selection` undo still works (should be untouched)
|
|
156
|
+
- No regressions in any tool's undo behavior
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Phase 2: Type System Foundation
|
|
161
|
+
|
|
162
|
+
This is the most impactful single decision. It eliminates ~263 unsafe casts and makes
|
|
163
|
+
the entire command/dispatch system type-safe. Must come before #1 and #19.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### Decision #20: Generic Command\<P\> + Separate Snapshot from Params
|
|
168
|
+
|
|
169
|
+
**Effort:** Large
|
|
170
|
+
|
|
171
|
+
**Depends on:** Nothing (foundational change, should be done early)
|
|
172
|
+
|
|
173
|
+
#### Problem (Three Sub-Problems)
|
|
174
|
+
|
|
175
|
+
**Sub-problem A -- Untyped params:** `Command.params` is `Record<string, unknown>`,
|
|
176
|
+
forcing every plugin to write `params.x as number`, `params.color as string`, etc.
|
|
177
|
+
This produces ~170 field casts across 23 plugin files. Any typo or type mismatch is
|
|
178
|
+
invisible to the compiler.
|
|
179
|
+
|
|
180
|
+
**Sub-problem B -- Snapshot smuggled into params:** The `_snapshot` field is shoved
|
|
181
|
+
into `params` as a mutable side-channel by `execute()`, then read back in `undo()`.
|
|
182
|
+
This makes params non-immutable and conflates invocation data with undo state.
|
|
183
|
+
21 files use this convention.
|
|
184
|
+
|
|
185
|
+
**Sub-problem C -- Untyped CommandContext:** `CommandContext` lacks a
|
|
186
|
+
`getActiveBuffer` definition, causing 69 casts of the form
|
|
187
|
+
`(ctx as { getActiveBuffer?: ... })` across plugin files.
|
|
188
|
+
|
|
189
|
+
#### Solution
|
|
190
|
+
|
|
191
|
+
**Part A -- Generic Command\<P\>:**
|
|
192
|
+
|
|
193
|
+
- Make `Command<P>` and `CommandDefinition<P>` generic over a params type `P`
|
|
194
|
+
- Each plugin declares a typed interface for its params:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
interface DrawRectParams {
|
|
198
|
+
x: number;
|
|
199
|
+
y: number;
|
|
200
|
+
width: number;
|
|
201
|
+
height: number;
|
|
202
|
+
color: string;
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- The dispatcher and registries store `Command<any>` at the storage boundary
|
|
207
|
+
(type erasure at the collection level is expected and acceptable)
|
|
208
|
+
- ~25 param interfaces to write; eliminates ~170 field casts
|
|
209
|
+
|
|
210
|
+
**Part B -- Separate snapshot from params:**
|
|
211
|
+
|
|
212
|
+
- Add a dedicated `snapshot: unknown` slot on the undo stack entry
|
|
213
|
+
- `execute()` return value (or a callback/setter) provides the snapshot data:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
execute(params: P, ctx: CommandContext): SnapshotData | void;
|
|
217
|
+
undo(params: P, ctx: CommandContext, snapshot: SnapshotData): void;
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
- Params become truly immutable invocation data
|
|
221
|
+
- The `_snapshot` convention disappears from all 21 files
|
|
222
|
+
|
|
223
|
+
**Part C -- Typed CommandContext:**
|
|
224
|
+
|
|
225
|
+
- Add `getActiveBuffer?: () => PixelBuffer | null` to the `CommandContext` interface
|
|
226
|
+
- Eliminates 69 casts of `(ctx as { getActiveBuffer?: ... })`
|
|
227
|
+
|
|
228
|
+
#### Files to Change
|
|
229
|
+
|
|
230
|
+
| File | Action |
|
|
231
|
+
|------|--------|
|
|
232
|
+
| `src/lib/core/commands.ts` | Make `Command<P>` and `CommandDefinition<P>` generic; add typed `CommandContext` with `getActiveBuffer` |
|
|
233
|
+
| `src/lib/core/dispatcher.ts` | Accept snapshot return from `execute()`, store on `UndoEntry`, pass to `undo()` |
|
|
234
|
+
| `src/lib/core/registries.svelte.ts` | Store `CommandDefinition<any>` in the registry |
|
|
235
|
+
| All 23 files in `plugins/builtin/` | Define param interfaces, remove `as` casts, remove `_snapshot` from params |
|
|
236
|
+
| `src/lib/ui/menu-commands-plugin.ts` (or split successors) | Remove `ctx` casts |
|
|
237
|
+
| `plugins/builtin/drawing-utils.ts` | Update `snapshotPixels`/`applyPixels` if the snapshot interface changes |
|
|
238
|
+
|
|
239
|
+
#### Cast Elimination Summary
|
|
240
|
+
|
|
241
|
+
| Cast Category | Count | Fix |
|
|
242
|
+
|--------------|-------|-----|
|
|
243
|
+
| `params.field as Type` | ~170 | Typed param interfaces |
|
|
244
|
+
| `(ctx as { getActiveBuffer? })` | 69 | Typed CommandContext |
|
|
245
|
+
| `_snapshot` mutations on params | ~24 | Dedicated snapshot slot |
|
|
246
|
+
| **Total** | **~263** | |
|
|
247
|
+
|
|
248
|
+
#### Verification
|
|
249
|
+
|
|
250
|
+
- `tsc --noEmit` passes with zero errors
|
|
251
|
+
- All tools, effects, and commands work as before
|
|
252
|
+
- Undo/redo functions correctly for all commands
|
|
253
|
+
- No `as unknown` or `as any` casts remain in plugin files (beyond the storage boundary)
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Phase 3: Undoable Flag
|
|
258
|
+
|
|
259
|
+
Requires typed commands from Phase 2 for proper `dispatch()` call construction.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
### Decision #1: Add undoable Flag to CommandDefinition
|
|
264
|
+
|
|
265
|
+
**Effort:** Medium
|
|
266
|
+
|
|
267
|
+
**Depends on:** #20 (Generic Command\<P\>) should ideally come first since the
|
|
268
|
+
dispatch call sites need proper `Command` construction. Can be done independently
|
|
269
|
+
with empty params/context as a stopgap.
|
|
270
|
+
|
|
271
|
+
#### Problem
|
|
272
|
+
|
|
273
|
+
All 4 UI entry points (menu bar, toolbar, context menu, command palette) call
|
|
274
|
+
`cmd.execute({}, {})` directly, bypassing the dispatcher. For UI-only commands
|
|
275
|
+
(zoom, theme, dialogs) this is fine and desirable. But destructive commands
|
|
276
|
+
(flatten_image, resize_canvas, merge_down, cut, paste) triggered from menus
|
|
277
|
+
are non-undoable because they never pass through the undo stack.
|
|
278
|
+
|
|
279
|
+
#### Solution
|
|
280
|
+
|
|
281
|
+
Add an `undoable?: boolean` field to `CommandDefinition`. UI entry points check the
|
|
282
|
+
flag at invocation time:
|
|
283
|
+
|
|
284
|
+
- `undoable: true` -- route through `dispatch()` (goes onto the undo stack)
|
|
285
|
+
- `undoable: false` or `undefined` -- call `execute()` directly (current behavior)
|
|
286
|
+
|
|
287
|
+
**Commands that need `undoable: true`** (~15 total):
|
|
288
|
+
|
|
289
|
+
- `flatten_image`
|
|
290
|
+
- `resize_canvas`
|
|
291
|
+
- `crop_to_selection`
|
|
292
|
+
- `merge_down`
|
|
293
|
+
- `cut`
|
|
294
|
+
- `paste`
|
|
295
|
+
- `select_all`
|
|
296
|
+
- `invert_selection`
|
|
297
|
+
- `select_by_color`
|
|
298
|
+
- Any other destructive or data-mutating command triggered from UI
|
|
299
|
+
|
|
300
|
+
**Commands that must NOT be marked undoable:**
|
|
301
|
+
|
|
302
|
+
- `undo` and `redo` (they call `undoLast`/`redoLast` directly -- marking them
|
|
303
|
+
undoable would create an infinite recursion)
|
|
304
|
+
- All view/zoom commands (non-destructive, no undo needed)
|
|
305
|
+
- Dialog-opening commands (no data mutation)
|
|
306
|
+
|
|
307
|
+
#### Files to Change
|
|
308
|
+
|
|
309
|
+
| File | Action |
|
|
310
|
+
|------|--------|
|
|
311
|
+
| `src/lib/core/commands.ts` | Add `undoable?: boolean` to `CommandDefinition` |
|
|
312
|
+
| `src/lib/ui/menu-builder.ts` | Check `undoable` flag; route through `dispatch()` if true |
|
|
313
|
+
| `src/lib/ui/ToolbarPanel.svelte` | Same routing logic |
|
|
314
|
+
| `src/lib/dock/TabAddMenu.svelte` | Same routing logic |
|
|
315
|
+
| `src/lib/ui/ContextMenu.svelte` | Same routing logic |
|
|
316
|
+
| `src/lib/ui/command-palette/command-palette-state.svelte.ts` | Same routing logic |
|
|
317
|
+
| `src/lib/ui/menu-commands-plugin.ts` | Mark ~15 destructive commands as `undoable: true` |
|
|
318
|
+
|
|
319
|
+
#### Verification
|
|
320
|
+
|
|
321
|
+
- Trigger `flatten_image` from the menu: it should appear on the undo stack
|
|
322
|
+
- Press Ctrl+Z: the flatten is undone
|
|
323
|
+
- Trigger `zoom_in` from the menu: it should NOT appear on the undo stack
|
|
324
|
+
- `undo`/`redo` commands still work as direct calls
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Phase 4: Dialog / UI Work
|
|
329
|
+
|
|
330
|
+
Independent of the command system. Can be done in parallel with Phase 2/3 if
|
|
331
|
+
different developers are available.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
### Decision #22: Native \<dialog\> Element for Modals
|
|
336
|
+
|
|
337
|
+
**Effort:** Medium (restyling `::backdrop`, testing in Tauri WebView)
|
|
338
|
+
|
|
339
|
+
**Depends on:** Nothing
|
|
340
|
+
|
|
341
|
+
#### Problem
|
|
342
|
+
|
|
343
|
+
All 3 modal dialogs (NewProject, Export, About) plus CommandPalette lack:
|
|
344
|
+
|
|
345
|
+
- Focus traps (Tab key escapes to elements behind the overlay)
|
|
346
|
+
- `role="dialog"` attribute
|
|
347
|
+
- `aria-modal="true"` attribute
|
|
348
|
+
- Proper Escape key handling (AboutDialog has none at all)
|
|
349
|
+
|
|
350
|
+
These are implemented as `<div class="modal-overlay">` with manual event handlers.
|
|
351
|
+
|
|
352
|
+
#### Solution
|
|
353
|
+
|
|
354
|
+
Replace `<div class="modal-overlay">` with the native `<dialog>` element using
|
|
355
|
+
`.showModal()`. The browser provides all of the following for free:
|
|
356
|
+
|
|
357
|
+
- Focus trap (Tab stays within the dialog)
|
|
358
|
+
- Escape key closes the dialog
|
|
359
|
+
- `::backdrop` pseudo-element for the overlay
|
|
360
|
+
- `role="dialog"` and `aria-modal="true"` automatically
|
|
361
|
+
- Top-layer rendering (no z-index wars)
|
|
362
|
+
|
|
363
|
+
This is ideal for Tauri's Chromium WebView which has full `<dialog>` support.
|
|
364
|
+
|
|
365
|
+
#### Files to Change
|
|
366
|
+
|
|
367
|
+
| File | Action |
|
|
368
|
+
|------|--------|
|
|
369
|
+
| `src/lib/ui/NewProjectDialog.svelte` | Replace `<div class="modal-overlay">` with `<dialog>`, restyle `::backdrop`, remove manual Escape handler, remove svelte-ignore comments |
|
|
370
|
+
| `src/lib/ui/ExportDialog.svelte` | Same transformation |
|
|
371
|
+
| `src/lib/ui/AboutDialog.svelte` | Same transformation; add Escape handling (currently missing entirely) |
|
|
372
|
+
| `src/lib/ui/command-palette/CommandPalette.svelte` | Same pattern |
|
|
373
|
+
| `src/lib/ui/dialog-state.svelte.ts` | May need to store dialog element refs for `.showModal()` / `.close()` |
|
|
374
|
+
|
|
375
|
+
#### Key Implementation Notes
|
|
376
|
+
|
|
377
|
+
- Use `dialog::backdrop` for overlay styling (replaces `.modal-overlay` CSS)
|
|
378
|
+
- The `close` event on `<dialog>` fires on Escape; use it for cleanup
|
|
379
|
+
- Call `.showModal()` in a Svelte `$effect` when the dialog state becomes open
|
|
380
|
+
- Call `.close()` when the state becomes closed
|
|
381
|
+
- Test in Tauri WebView specifically -- Chromium's `<dialog>` works but verify
|
|
382
|
+
backdrop click behavior
|
|
383
|
+
|
|
384
|
+
#### Verification
|
|
385
|
+
|
|
386
|
+
- Tab key stays within each dialog (focus trap works)
|
|
387
|
+
- Escape closes each dialog
|
|
388
|
+
- Backdrop click closes each dialog (if desired behavior)
|
|
389
|
+
- Screen readers announce the dialogs correctly
|
|
390
|
+
- Dialogs render correctly in Tauri desktop build
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
### Decision #26: Build Reusable PromptDialog Component
|
|
395
|
+
|
|
396
|
+
**Effort:** Small-Medium
|
|
397
|
+
|
|
398
|
+
**Depends on:** #22 (native \<dialog\>) -- the PromptDialog should use `<dialog>`
|
|
399
|
+
from the start
|
|
400
|
+
|
|
401
|
+
#### Problem
|
|
402
|
+
|
|
403
|
+
Two `prompt()` calls exist in `menu-commands-plugin.ts`:
|
|
404
|
+
|
|
405
|
+
1. Resize canvas (asks for new dimensions)
|
|
406
|
+
2. Set frame duration (asks for milliseconds)
|
|
407
|
+
|
|
408
|
+
`window.prompt()` has critical platform issues:
|
|
409
|
+
|
|
410
|
+
- Fails silently on macOS in Tauri (returns null without showing)
|
|
411
|
+
- Unreliable on Linux in Tauri
|
|
412
|
+
- Ugly, non-themeable, blocks the main thread
|
|
413
|
+
|
|
414
|
+
#### Solution
|
|
415
|
+
|
|
416
|
+
Create a generic `PromptDialog.svelte` component with:
|
|
417
|
+
|
|
418
|
+
- Title string
|
|
419
|
+
- Message / label string
|
|
420
|
+
- Default value
|
|
421
|
+
- Validation callback (returns error string or null)
|
|
422
|
+
- onConfirm / onCancel callbacks
|
|
423
|
+
- Uses native `<dialog>` per Decision #22
|
|
424
|
+
|
|
425
|
+
Wire through `dialogState` so any command can open a prompt:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
dialogState.openPrompt({
|
|
429
|
+
title: 'Resize Canvas',
|
|
430
|
+
message: 'Enter new width:',
|
|
431
|
+
defaultValue: '64',
|
|
432
|
+
validate: (v) => isNaN(Number(v)) ? 'Must be a number' : null,
|
|
433
|
+
onConfirm: (value) => { /* use value */ },
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
#### Files to Change
|
|
438
|
+
|
|
439
|
+
| File | Action |
|
|
440
|
+
|------|--------|
|
|
441
|
+
| `src/lib/ui/PromptDialog.svelte` | **Create** -- generic single-input modal using native `<dialog>` |
|
|
442
|
+
| `src/lib/ui/dialog-state.svelte.ts` | Add prompt dialog state: open/close, config object, callback storage |
|
|
443
|
+
| `src/App.svelte` | Render `<PromptDialog>` when prompt state is open |
|
|
444
|
+
| `src/lib/ui/menu-commands-plugin.ts` (or split successors) | Replace `prompt()` calls with `dialogState.openPrompt()` |
|
|
445
|
+
|
|
446
|
+
#### Verification
|
|
447
|
+
|
|
448
|
+
- Resize canvas command opens a themed dialog, not a browser prompt
|
|
449
|
+
- Entering a valid number resizes the canvas
|
|
450
|
+
- Entering invalid input shows a validation error
|
|
451
|
+
- Pressing Escape or Cancel closes without action
|
|
452
|
+
- Works on macOS Tauri, Linux Tauri, and web browser
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## Phase 5: God File Split
|
|
457
|
+
|
|
458
|
+
Easier after Phase 3 (#1, undoable flag) and Phase 4 (#26, PromptDialog replaces
|
|
459
|
+
`prompt()` calls). This is the largest single decision by line count but is mostly
|
|
460
|
+
mechanical.
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
### Decision #14: Full 8-File Domain Split of menu-commands-plugin.ts
|
|
465
|
+
|
|
466
|
+
**Effort:** Large (but mostly mechanical cut-and-paste)
|
|
467
|
+
|
|
468
|
+
**Depends on:**
|
|
469
|
+
- #1 (undoable flag) -- the split files need to mark commands correctly
|
|
470
|
+
- #26 (PromptDialog) -- should come first to eliminate `prompt()` calls during the split
|
|
471
|
+
|
|
472
|
+
#### Problem
|
|
473
|
+
|
|
474
|
+
`menu-commands-plugin.ts` is 1333 lines containing:
|
|
475
|
+
|
|
476
|
+
- 42 commands
|
|
477
|
+
- 55 menu items
|
|
478
|
+
- 9 toolbar items
|
|
479
|
+
- Duplicated zoom logic (also in `input-handler.ts` and `CanvasViewport.svelte`)
|
|
480
|
+
- Clipboard state management
|
|
481
|
+
- DOM queries for viewport center
|
|
482
|
+
- `prompt()` calls
|
|
483
|
+
|
|
484
|
+
This is a maintenance bottleneck. Any change to one domain (e.g., animation) requires
|
|
485
|
+
navigating a 1300-line file. Merge conflicts are frequent because every feature
|
|
486
|
+
touches this file.
|
|
487
|
+
|
|
488
|
+
#### Solution
|
|
489
|
+
|
|
490
|
+
Split into 8 domain files + 3 shared utilities. Delete the original file entirely.
|
|
491
|
+
|
|
492
|
+
**New domain files** (each is a `PluginModule` auto-discovered by `bootstrap.ts`
|
|
493
|
+
via the existing `import.meta.glob` pattern):
|
|
494
|
+
|
|
495
|
+
| File | Contents | ~Lines |
|
|
496
|
+
|------|----------|--------|
|
|
497
|
+
| `src/lib/ui/file-commands.ts` | `new_project_dialog`, `open_file_dialog`, `save_project`, `export_dialog` | 80 |
|
|
498
|
+
| `src/lib/ui/edit-commands.ts` | `undo`, `redo`, `cut`, `copy`, `paste`, `select_all` + deselect menu item | 150 |
|
|
499
|
+
| `src/lib/ui/image-commands.ts` | `resize_canvas`, `crop_to_selection`, `flatten_image`, `canvas_size` | 120 |
|
|
500
|
+
| `src/lib/ui/layer-menu-commands.ts` | `merge_down`, `move_layer_up`, `move_layer_down` + 3 menu-only items | 100 |
|
|
501
|
+
| `src/lib/ui/animation-menu-commands.ts` | `play_animation`, `set_frame_duration_dialog` + 3 menu-only items | 80 |
|
|
502
|
+
| `src/lib/ui/select-commands.ts` | `invert_selection`, `select_by_color` + shared menu items | 60 |
|
|
503
|
+
| `src/lib/ui/view-commands.ts` | All 17 view commands + 9 toolbar items | 350 |
|
|
504
|
+
| `src/lib/ui/help-commands.ts` | `about`, `keyboard_shortcuts`, `report_bug` | 70 |
|
|
505
|
+
|
|
506
|
+
Note: Files use `-menu-commands` suffix where they would collide with existing
|
|
507
|
+
domain command files (e.g., `layer-menu-commands.ts` avoids colliding with any
|
|
508
|
+
existing `layer-commands.ts`).
|
|
509
|
+
|
|
510
|
+
**Shared utilities to extract** (eliminates duplication beyond the god file):
|
|
511
|
+
|
|
512
|
+
| File | Contents | Deduplication Benefit |
|
|
513
|
+
|------|----------|---------------------|
|
|
514
|
+
| `src/lib/canvas/zoom-utils.ts` | `ZOOM_STEPS`, `zoomStepUp()`, `zoomStepDown()`, `DEFAULT_ZOOM` | Eliminates triplication across `input-handler.ts`, `CanvasViewport.svelte`, and the god file |
|
|
515
|
+
| `src/lib/canvas/viewport-utils.ts` | `viewportCenter()` function | Replaces `document.querySelector('.canvas-viewport')` DOM queries |
|
|
516
|
+
| `src/lib/edit/clipboard-state.ts` | `clipboardBuffer` type and reactive state | Isolates clipboard concern |
|
|
517
|
+
|
|
518
|
+
**The original `src/lib/ui/menu-commands-plugin.ts` is deleted entirely.**
|
|
519
|
+
|
|
520
|
+
#### Migration Strategy
|
|
521
|
+
|
|
522
|
+
1. Create the 3 shared utilities first
|
|
523
|
+
2. Create the 8 domain files one at a time, moving commands and menu items
|
|
524
|
+
3. After each file is created, verify `tsc --noEmit` passes and the commands work
|
|
525
|
+
4. Delete `menu-commands-plugin.ts` only after all commands are migrated
|
|
526
|
+
5. Verify `bootstrap.ts` discovers all new plugin modules
|
|
527
|
+
|
|
528
|
+
#### Files to Change
|
|
529
|
+
|
|
530
|
+
| File | Action |
|
|
531
|
+
|------|--------|
|
|
532
|
+
| `src/lib/canvas/zoom-utils.ts` | **Create** -- shared zoom constants and functions |
|
|
533
|
+
| `src/lib/canvas/viewport-utils.ts` | **Create** -- viewport center calculation |
|
|
534
|
+
| `src/lib/edit/clipboard-state.ts` | **Create** -- clipboard buffer state |
|
|
535
|
+
| `src/lib/ui/file-commands.ts` | **Create** |
|
|
536
|
+
| `src/lib/ui/edit-commands.ts` | **Create** |
|
|
537
|
+
| `src/lib/ui/image-commands.ts` | **Create** |
|
|
538
|
+
| `src/lib/ui/layer-menu-commands.ts` | **Create** |
|
|
539
|
+
| `src/lib/ui/animation-menu-commands.ts` | **Create** |
|
|
540
|
+
| `src/lib/ui/select-commands.ts` | **Create** |
|
|
541
|
+
| `src/lib/ui/view-commands.ts` | **Create** |
|
|
542
|
+
| `src/lib/ui/help-commands.ts` | **Create** |
|
|
543
|
+
| `src/lib/ui/menu-commands-plugin.ts` | **Delete** |
|
|
544
|
+
| `src/lib/canvas/input-handler.ts` | **Update** -- import from `zoom-utils.ts` instead of local constants |
|
|
545
|
+
| `src/lib/ui/CanvasViewport.svelte` | **Update** -- import from `zoom-utils.ts` instead of local constants |
|
|
546
|
+
|
|
547
|
+
#### Verification
|
|
548
|
+
|
|
549
|
+
- All 42 commands still appear in the command palette
|
|
550
|
+
- All 55 menu items still appear in correct menus
|
|
551
|
+
- All 9 toolbar items still appear in correct toolbars
|
|
552
|
+
- Zoom from keyboard, mouse wheel, and menu all use the same step logic
|
|
553
|
+
- `tsc --noEmit` passes
|
|
554
|
+
- `bootstrap.ts` logs discovery of all 8 new plugin modules
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## Phase 6: Plugin API Expansion
|
|
559
|
+
|
|
560
|
+
Best done after the god file split (#14) since `menu-commands-plugin.ts` will have
|
|
561
|
+
been reorganized into smaller files.
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
### Decision #12: Add getSelection() to PluginAPI
|
|
566
|
+
|
|
567
|
+
**Effort:** Small
|
|
568
|
+
|
|
569
|
+
**Depends on:** #14 (god file split) should happen first since `menu-commands-plugin.ts`
|
|
570
|
+
will be reorganized
|
|
571
|
+
|
|
572
|
+
#### Problem
|
|
573
|
+
|
|
574
|
+
`menu-commands-plugin.ts` directly imports selection state (`hasSelection`,
|
|
575
|
+
`getSelectedPixels`, `selectRect`, `deselect`) from
|
|
576
|
+
`plugins/builtin/selection-tool.ts`. This breaks the plugin boundary -- one plugin
|
|
577
|
+
reaches into another plugin's internal module.
|
|
578
|
+
|
|
579
|
+
#### Solution
|
|
580
|
+
|
|
581
|
+
Add a `getSelection()` method to `PluginAPI` returning a read-only selection interface:
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
interface SelectionView {
|
|
585
|
+
hasSelection(): boolean;
|
|
586
|
+
getSelectedPixels(): ReadonlySet<string>;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// On PluginAPI:
|
|
590
|
+
getSelection(): SelectionView;
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
For mutations (selecting, deselecting), replace direct function calls with
|
|
594
|
+
`dispatch()` calls to the existing `select_rect` and `deselect` commands. This
|
|
595
|
+
keeps the plugin boundary intact: reads go through the API, writes go through
|
|
596
|
+
the command system.
|
|
597
|
+
|
|
598
|
+
#### Files to Change
|
|
599
|
+
|
|
600
|
+
| File | Action |
|
|
601
|
+
|------|--------|
|
|
602
|
+
| `src/lib/core/plugin-types.ts` | Add `getSelection(): SelectionView` to `PluginAPI` interface |
|
|
603
|
+
| `src/lib/core/plugin-api.ts` | Implement `getSelection()` by importing from `selection-tool.ts` internally |
|
|
604
|
+
| `src/lib/ui/menu-commands-plugin.ts` (or split successors) | Replace direct imports with `api.getSelection()` reads and `api.dispatch()` mutations |
|
|
605
|
+
| `plugins/builtin/selection-tool.ts` | No changes needed (exports remain for internal use within that plugin) |
|
|
606
|
+
|
|
607
|
+
#### Verification
|
|
608
|
+
|
|
609
|
+
- `cut` command still works (reads selection via API)
|
|
610
|
+
- `crop_to_selection` still works
|
|
611
|
+
- `select_all` and `deselect` still work (via dispatch)
|
|
612
|
+
- No direct imports of `selection-tool.ts` remain in UI command files
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
### Decision #13: Add Mutator Methods to PluginAPI Incrementally
|
|
617
|
+
|
|
618
|
+
**Effort:** Medium-Large (but each step is independently shippable)
|
|
619
|
+
|
|
620
|
+
**Depends on:** Nothing (can be done incrementally at any time)
|
|
621
|
+
|
|
622
|
+
#### Problem
|
|
623
|
+
|
|
624
|
+
All 3 importers bypass the `PluginAPI` entirely to set canvas size, add layers/frames,
|
|
625
|
+
and write pixel data. The API currently only has read-only getters that return `unknown`.
|
|
626
|
+
Approximately 15 new methods are needed to let importers work through the API.
|
|
627
|
+
|
|
628
|
+
#### Solution
|
|
629
|
+
|
|
630
|
+
Add mutator methods in 6 incremental steps. Each step is independently shippable
|
|
631
|
+
and testable.
|
|
632
|
+
|
|
633
|
+
**Step 1 -- Canvas:**
|
|
634
|
+
|
|
635
|
+
| Method | Signature |
|
|
636
|
+
|--------|-----------|
|
|
637
|
+
| `setCanvasSize` | `(width: number, height: number) => void` |
|
|
638
|
+
|
|
639
|
+
**Step 2 -- Layers:**
|
|
640
|
+
|
|
641
|
+
| Method | Signature |
|
|
642
|
+
|--------|-----------|
|
|
643
|
+
| `addLayer` | `(name: string, opts?: LayerOptions) => LayerId` |
|
|
644
|
+
| `addGroup` | `(name: string, opts?: GroupOptions) => GroupId` |
|
|
645
|
+
| `setLayerVisibility` | `(id: LayerId, visible: boolean) => void` |
|
|
646
|
+
| `setLayerOpacity` | `(id: LayerId, opacity: number) => void` |
|
|
647
|
+
| `resetLayers` | `() => void` |
|
|
648
|
+
|
|
649
|
+
**Step 3 -- Frames:**
|
|
650
|
+
|
|
651
|
+
| Method | Signature |
|
|
652
|
+
|--------|-----------|
|
|
653
|
+
| `addFrame` | `(opts?: FrameOptions) => FrameIndex` |
|
|
654
|
+
| `getFrames` | `() => ReadonlyArray<FrameInfo>` |
|
|
655
|
+
| `setFrameDuration` | `(index: number, ms: number) => void` |
|
|
656
|
+
| `setCurrentFrame` | `(index: number) => void` |
|
|
657
|
+
| `setGlobalFps` | `(fps: number) => void` |
|
|
658
|
+
| `resetFrames` | `() => void` |
|
|
659
|
+
|
|
660
|
+
**Step 4 -- Pixel Data:**
|
|
661
|
+
|
|
662
|
+
| Method | Signature |
|
|
663
|
+
|--------|-----------|
|
|
664
|
+
| `createPixelBuffer` | `(width: number, height: number) => PixelBuffer` |
|
|
665
|
+
| Pixel data setters | (exact API depends on PixelBuffer interface) |
|
|
666
|
+
|
|
667
|
+
**Step 5 -- Palette:**
|
|
668
|
+
|
|
669
|
+
| Method | Signature |
|
|
670
|
+
|--------|-----------|
|
|
671
|
+
| `setProjectPalette` | `(palette: Color[]) => void` |
|
|
672
|
+
|
|
673
|
+
**Step 6 -- Composite:**
|
|
674
|
+
|
|
675
|
+
| Method | Signature |
|
|
676
|
+
|--------|-----------|
|
|
677
|
+
| `resetProject` | `() => void` (clears layers + frames + canvas to defaults) |
|
|
678
|
+
|
|
679
|
+
After each step, migrate one importer to use the new API methods. All implementations
|
|
680
|
+
are thin wrappers around existing module functions.
|
|
681
|
+
|
|
682
|
+
#### Files to Change
|
|
683
|
+
|
|
684
|
+
| File | Action |
|
|
685
|
+
|------|--------|
|
|
686
|
+
| `src/lib/core/plugin-types.ts` | Expand `PluginAPI` interface with each step's methods |
|
|
687
|
+
| `src/lib/core/plugin-api.ts` | Implement methods as thin wrappers around existing module functions |
|
|
688
|
+
| `plugins/builtin/importers/aseprite-importer-plugin.ts` | Migrate to API calls |
|
|
689
|
+
| `plugins/builtin/importers/piskel-importer-plugin.ts` | Migrate to API calls |
|
|
690
|
+
| `plugins/builtin/importers/sky-spec-plugin.ts` | Migrate to API calls |
|
|
691
|
+
|
|
692
|
+
#### Verification (per step)
|
|
693
|
+
|
|
694
|
+
- After each step: `tsc --noEmit` passes
|
|
695
|
+
- After migrating each importer: import a test file, verify layers/frames/pixels are correct
|
|
696
|
+
- After all migrations: no direct imports of internal state modules remain in importer files
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## Phase 7: Strict TypeScript Flags
|
|
701
|
+
|
|
702
|
+
Last phase. Benefits from all prior type improvements (especially #20). Touches
|
|
703
|
+
nearly every file in the codebase.
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
### Decision #19: Enable All 5 Strict TypeScript Flags
|
|
708
|
+
|
|
709
|
+
**Effort:** Large (multi-hour, but can be done incrementally flag by flag)
|
|
710
|
+
|
|
711
|
+
**Depends on:** #20 (Generic Command\<P\>) since the command params type changes
|
|
712
|
+
will interact with index access patterns
|
|
713
|
+
|
|
714
|
+
#### Problem
|
|
715
|
+
|
|
716
|
+
`tsconfig.app.json` does not enable several strict flags that would catch real bugs
|
|
717
|
+
at compile time. The current codebase has latent type safety issues that are invisible
|
|
718
|
+
to the compiler.
|
|
719
|
+
|
|
720
|
+
#### Solution
|
|
721
|
+
|
|
722
|
+
Enable all 5 flags in `tsconfig.app.json`, in order of ascending blast radius:
|
|
723
|
+
|
|
724
|
+
| Order | Flag | Current Errors | Fix Strategy | Estimated Time |
|
|
725
|
+
|-------|------|---------------|-------------|----------------|
|
|
726
|
+
| 1 | `noFallthroughCasesInSwitch` | 0 | Enable immediately, no fixes needed | 1 min |
|
|
727
|
+
| 2 | `noUnusedLocals` + `noUnusedParameters` | 7 | Delete dead imports and unused variables | 10 min |
|
|
728
|
+
| 3 | `exactOptionalPropertyTypes` | 11 | Narrow types before passing, or change the target type to accept `undefined` explicitly | 30-60 min |
|
|
729
|
+
| 4 | `noUncheckedIndexedAccess` | 470 | 257 in tests (add `!` assertions where values are known to exist), 213 in prod (case-by-case null checks). Surfaces real latent bugs. | 2-3 hours |
|
|
730
|
+
| 5 | `noPropertyAccessFromIndexSignature` | 698 | Force bracket notation on index signature access. Significant overlap with `noUncheckedIndexedAccess` fixes. | 2-3 hours |
|
|
731
|
+
|
|
732
|
+
**Total: ~470 errors in 54+ files for `noUncheckedIndexedAccess`, ~698 errors in 44+
|
|
733
|
+
files for `noPropertyAccessFromIndexSignature`.**
|
|
734
|
+
|
|
735
|
+
#### Recommended Approach
|
|
736
|
+
|
|
737
|
+
Enable one flag at a time. After each flag:
|
|
738
|
+
|
|
739
|
+
1. Add the flag to `tsconfig.app.json`
|
|
740
|
+
2. Run `tsc --noEmit` to get the error list
|
|
741
|
+
3. Fix all errors
|
|
742
|
+
4. Commit
|
|
743
|
+
5. Move to the next flag
|
|
744
|
+
|
|
745
|
+
This prevents a massive all-at-once PR and lets each flag's fixes be reviewed
|
|
746
|
+
independently.
|
|
747
|
+
|
|
748
|
+
#### Files to Change
|
|
749
|
+
|
|
750
|
+
| File | Action |
|
|
751
|
+
|------|--------|
|
|
752
|
+
| `tsconfig.app.json` | Add flags incrementally |
|
|
753
|
+
| 54+ files across the codebase | Fix `noUncheckedIndexedAccess` errors |
|
|
754
|
+
| 44+ files across the codebase | Fix `noPropertyAccessFromIndexSignature` errors |
|
|
755
|
+
| ~7 files | Fix unused locals/params |
|
|
756
|
+
| ~11 files | Fix `exactOptionalPropertyTypes` errors |
|
|
757
|
+
|
|
758
|
+
#### Important Notes
|
|
759
|
+
|
|
760
|
+
- `noUncheckedIndexedAccess` will surface real latent bugs (array access without
|
|
761
|
+
bounds checking, map access without existence checking). These are worth fixing
|
|
762
|
+
properly, not just suppressing with `!`.
|
|
763
|
+
- In test files, `!` assertions are acceptable since the test data is controlled.
|
|
764
|
+
- In production code, prefer proper null checks or early returns.
|
|
765
|
+
|
|
766
|
+
#### Verification
|
|
767
|
+
|
|
768
|
+
- `tsc --noEmit` passes with all 5 flags enabled
|
|
769
|
+
- No runtime regressions (the fixes should be compile-time only)
|
|
770
|
+
- No `as any` or `@ts-ignore` comments added to suppress errors
|
|
771
|
+
|
|
772
|
+
---
|
|
773
|
+
|
|
774
|
+
## Implementation Order Summary
|
|
775
|
+
|
|
776
|
+
| Phase | Decisions | Key Rationale | Parallel? |
|
|
777
|
+
|-------|-----------|---------------|-----------|
|
|
778
|
+
| 1 | #24 (makeStrokeTool) + #25 (makeSnapshotUndo) | DRY factories, fully isolated, no dependencies | Yes, both in parallel |
|
|
779
|
+
| 2 | #20 (Generic Command\<P\> + snapshot + CommandContext) | Type system foundation -- all later phases benefit | Single item |
|
|
780
|
+
| 3 | #1 (undoable flag) | Needs typed commands from #20 for proper dispatch | Single item |
|
|
781
|
+
| 4 | #22 (native \<dialog\>) + #26 (PromptDialog) | Dialog/UI work, independent of command system | Yes, #22 first, then #26 |
|
|
782
|
+
| 5 | #14 (god file split) | Easier after #1 (undoable flag) and #26 (PromptDialog) | Single item |
|
|
783
|
+
| 6 | #12 (getSelection API) + #13 (mutator methods) | Plugin API expansion, benefits from split files | Yes, both in parallel |
|
|
784
|
+
| 7 | #19 (strict TS flags) | Last -- touches everything, benefits from all prior type improvements | Single item, incremental |
|
|
785
|
+
|
|
786
|
+
**Dependency graph** (read as "X must come before Y"):
|
|
787
|
+
|
|
788
|
+
- #20 must come before #1 (undoable flag needs typed Command construction)
|
|
789
|
+
- #20 should come before #19 (strict flags interact with command param types)
|
|
790
|
+
- #22 must come before #26 (PromptDialog uses native \<dialog\>)
|
|
791
|
+
- #1 should come before #14 (split files need undoable markings)
|
|
792
|
+
- #26 should come before #14 (split eliminates prompt() calls)
|
|
793
|
+
- #14 should come before #12 (split files are the consumers of getSelection API)
|
|
794
|
+
- #25 may need updating after #20 (if snapshot signature changes)
|
|
795
|
+
|
|
796
|
+
---
|
|
797
|
+
|
|
798
|
+
## Effort Summary
|
|
799
|
+
|
|
800
|
+
| Decision | Effort | Approximate Duration |
|
|
801
|
+
|----------|--------|---------------------|
|
|
802
|
+
| #24 makeStrokeTool | Small | 1-2 hours |
|
|
803
|
+
| #25 makeSnapshotUndo | Small | 1-2 hours |
|
|
804
|
+
| #20 Generic Command\<P\> | Large | 6-10 hours |
|
|
805
|
+
| #1 Undoable flag | Medium | 3-4 hours |
|
|
806
|
+
| #22 Native \<dialog\> | Medium | 3-4 hours |
|
|
807
|
+
| #26 PromptDialog | Small-Medium | 2-3 hours |
|
|
808
|
+
| #14 God file split | Large | 6-8 hours |
|
|
809
|
+
| #12 getSelection API | Small | 1-2 hours |
|
|
810
|
+
| #13 Mutator methods | Medium-Large | 6-8 hours |
|
|
811
|
+
| #19 Strict TS flags | Large | 6-8 hours |
|
|
812
|
+
| **Total** | | **~35-51 hours** |
|