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,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the command palette's fuzzy matching and relevance scoring.
|
|
3
|
+
*
|
|
4
|
+
* These tests exercise the pure-logic helpers directly, without requiring
|
|
5
|
+
* a DOM or Svelte runtime.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { fuzzyMatch, relevanceScore, type PaletteItem } from './command-palette-state.svelte.js';
|
|
10
|
+
|
|
11
|
+
// --- Helpers ---
|
|
12
|
+
|
|
13
|
+
/** Create a minimal PaletteItem for testing. */
|
|
14
|
+
function makeItem(name: string, description = ''): PaletteItem {
|
|
15
|
+
return {
|
|
16
|
+
id: `test:${name}`,
|
|
17
|
+
name,
|
|
18
|
+
description,
|
|
19
|
+
category: 'tool',
|
|
20
|
+
execute: () => {},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// --- fuzzyMatch ---
|
|
25
|
+
|
|
26
|
+
describe('fuzzyMatch', () => {
|
|
27
|
+
it('matches "pen" against "Pencil tool"', () => {
|
|
28
|
+
const item = makeItem('Pencil tool');
|
|
29
|
+
expect(fuzzyMatch(item, 'pen')).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('matches "era" against "Eraser tool"', () => {
|
|
33
|
+
const item = makeItem('Eraser tool');
|
|
34
|
+
expect(fuzzyMatch(item, 'era')).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('matches multiple words: "draw line" against "Draw Line tool"', () => {
|
|
38
|
+
const item = makeItem('Draw Line tool');
|
|
39
|
+
expect(fuzzyMatch(item, 'draw line')).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('does not match when a word is missing', () => {
|
|
43
|
+
const item = makeItem('Pencil tool');
|
|
44
|
+
expect(fuzzyMatch(item, 'eraser')).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('is case-insensitive', () => {
|
|
48
|
+
const item = makeItem('Pencil tool');
|
|
49
|
+
expect(fuzzyMatch(item, 'PENCIL')).toBe(true);
|
|
50
|
+
expect(fuzzyMatch(item, 'pEnCiL')).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('empty query matches everything', () => {
|
|
54
|
+
const item = makeItem('Anything');
|
|
55
|
+
expect(fuzzyMatch(item, '')).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('matches against description as well', () => {
|
|
59
|
+
const item = makeItem('Do stuff', 'This draws pixels');
|
|
60
|
+
expect(fuzzyMatch(item, 'pixels')).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('requires all words to be present', () => {
|
|
64
|
+
const item = makeItem('Pencil tool', 'Draw pixels');
|
|
65
|
+
expect(fuzzyMatch(item, 'pencil draw')).toBe(true);
|
|
66
|
+
expect(fuzzyMatch(item, 'pencil eraser')).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// --- relevanceScore ---
|
|
71
|
+
|
|
72
|
+
describe('relevanceScore', () => {
|
|
73
|
+
it('gives score 0 for exact prefix match on name', () => {
|
|
74
|
+
const item = makeItem('Pencil tool');
|
|
75
|
+
expect(relevanceScore(item, 'pen')).toBe(0);
|
|
76
|
+
expect(relevanceScore(item, 'pencil')).toBe(0);
|
|
77
|
+
expect(relevanceScore(item, 'Pencil')).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('gives score 1 for substring match in name (not prefix)', () => {
|
|
81
|
+
const item = makeItem('Pencil tool');
|
|
82
|
+
// "cil" is in "pencil tool" but not a prefix
|
|
83
|
+
expect(relevanceScore(item, 'cil')).toBe(1);
|
|
84
|
+
expect(relevanceScore(item, 'tool')).toBe(1);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('gives score 2 when only description matches', () => {
|
|
88
|
+
const item = makeItem('Do stuff', 'Activate the pencil');
|
|
89
|
+
expect(relevanceScore(item, 'pencil')).toBe(2);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('gives score 0 for empty query', () => {
|
|
93
|
+
const item = makeItem('Anything');
|
|
94
|
+
expect(relevanceScore(item, '')).toBe(0);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// --- Sorting by relevance ---
|
|
99
|
+
|
|
100
|
+
describe('sorting by relevance', () => {
|
|
101
|
+
it('sorts prefix matches before substring matches', () => {
|
|
102
|
+
const items = [
|
|
103
|
+
makeItem('Fill tool'),
|
|
104
|
+
makeItem('Pencil tool'),
|
|
105
|
+
makeItem('Pen tool'),
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
const sorted = items
|
|
109
|
+
.filter((item) => fuzzyMatch(item, 'pen'))
|
|
110
|
+
.sort((a, b) => relevanceScore(a, 'pen') - relevanceScore(b, 'pen'));
|
|
111
|
+
|
|
112
|
+
// "Pencil tool" and "Pen tool" start with "pen", so score 0.
|
|
113
|
+
// Among score-0 items, original order is preserved (stable sort).
|
|
114
|
+
expect(sorted[0]?.name).toBe('Pencil tool');
|
|
115
|
+
expect(sorted[1]?.name).toBe('Pen tool');
|
|
116
|
+
// "Fill tool" doesn't match at all, so it's filtered out
|
|
117
|
+
expect(sorted.length).toBe(2);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('empty query returns all items with equal score', () => {
|
|
121
|
+
const items = [makeItem('A'), makeItem('B'), makeItem('C')];
|
|
122
|
+
const filtered = items.filter((item) => fuzzyMatch(item, ''));
|
|
123
|
+
expect(filtered.length).toBe(3);
|
|
124
|
+
// All scores should be 0
|
|
125
|
+
for (const item of filtered) {
|
|
126
|
+
expect(relevanceScore(item, '')).toBe(0);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialog State -- reactive singleton for managing dialog visibility.
|
|
3
|
+
* Allows menu commands and other plugins to open/close dialogs
|
|
4
|
+
* without requiring access to local component state.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface PromptConfig {
|
|
8
|
+
title: string;
|
|
9
|
+
message: string;
|
|
10
|
+
defaultValue: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
validate?: (value: string) => string | null;
|
|
13
|
+
onConfirm: (value: string) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let newProjectOpen = $state(false);
|
|
17
|
+
let exportOpen = $state(false);
|
|
18
|
+
let promptOpen = $state(false);
|
|
19
|
+
let promptConfig = $state<PromptConfig | null>(null);
|
|
20
|
+
let resizeCanvasOpen = $state(false);
|
|
21
|
+
let bisectionExportOpen = $state(false);
|
|
22
|
+
let bisectionExportGroupId = $state<string | null>(null);
|
|
23
|
+
|
|
24
|
+
export const dialogState = {
|
|
25
|
+
get newProjectOpen() { return newProjectOpen; },
|
|
26
|
+
set newProjectOpen(v: boolean) { newProjectOpen = v; },
|
|
27
|
+
|
|
28
|
+
get exportOpen() { return exportOpen; },
|
|
29
|
+
set exportOpen(v: boolean) { exportOpen = v; },
|
|
30
|
+
|
|
31
|
+
get promptOpen() { return promptOpen; },
|
|
32
|
+
set promptOpen(v: boolean) { promptOpen = v; },
|
|
33
|
+
|
|
34
|
+
get promptConfig() { return promptConfig; },
|
|
35
|
+
|
|
36
|
+
get resizeCanvasOpen() { return resizeCanvasOpen; },
|
|
37
|
+
set resizeCanvasOpen(v: boolean) { resizeCanvasOpen = v; },
|
|
38
|
+
|
|
39
|
+
openNewProject() { newProjectOpen = true; },
|
|
40
|
+
closeNewProject() { newProjectOpen = false; },
|
|
41
|
+
|
|
42
|
+
openExport() { exportOpen = true; },
|
|
43
|
+
closeExport() { exportOpen = false; },
|
|
44
|
+
|
|
45
|
+
openResizeCanvas() { resizeCanvasOpen = true; },
|
|
46
|
+
closeResizeCanvas() { resizeCanvasOpen = false; },
|
|
47
|
+
|
|
48
|
+
get bisectionExportOpen() { return bisectionExportOpen; },
|
|
49
|
+
set bisectionExportOpen(v: boolean) { bisectionExportOpen = v; },
|
|
50
|
+
|
|
51
|
+
get bisectionExportGroupId() { return bisectionExportGroupId; },
|
|
52
|
+
|
|
53
|
+
openBisectionExport(groupId: string) {
|
|
54
|
+
bisectionExportGroupId = groupId;
|
|
55
|
+
bisectionExportOpen = true;
|
|
56
|
+
},
|
|
57
|
+
closeBisectionExport() {
|
|
58
|
+
bisectionExportOpen = false;
|
|
59
|
+
bisectionExportGroupId = null;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
openPrompt(config: PromptConfig) {
|
|
63
|
+
promptConfig = config;
|
|
64
|
+
promptOpen = true;
|
|
65
|
+
},
|
|
66
|
+
closePrompt() {
|
|
67
|
+
promptOpen = false;
|
|
68
|
+
promptConfig = null;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit Commands -- Undo/Redo, Cut/Copy/Paste, Select All, Deselect (menu ref).
|
|
3
|
+
*
|
|
4
|
+
* Undo and Redo delegate to the dispatcher's history stack; clipboard
|
|
5
|
+
* commands read/write the shared clipboard-state module. `deselect` is
|
|
6
|
+
* registered by the selection-tool plugin -- we only add the menu item.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { PluginModule } from '../core/plugin-loader.js';
|
|
10
|
+
import { undoLast, redoLast } from '../core/dispatcher.js';
|
|
11
|
+
import { canvasState } from '../canvas/canvas-state.svelte.js';
|
|
12
|
+
import {
|
|
13
|
+
getClipboard,
|
|
14
|
+
setClipboard,
|
|
15
|
+
hasClipboard,
|
|
16
|
+
type ClipboardPixel,
|
|
17
|
+
} from '../edit/clipboard-state.js';
|
|
18
|
+
|
|
19
|
+
// Pre-mutation pixel snapshot for cut/paste. Stores the RGBA values that
|
|
20
|
+
// were at each affected coordinate BEFORE the command ran, so undo can
|
|
21
|
+
// write them back verbatim via ctx.getActiveBuffer().
|
|
22
|
+
interface PixelRegionSnapshot {
|
|
23
|
+
pixels: ClipboardPixel[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Cut additionally records the prior clipboard so undo can restore it
|
|
27
|
+
// (otherwise undoing a cut would leak the cut region into the clipboard).
|
|
28
|
+
interface CutSnapshot extends PixelRegionSnapshot {
|
|
29
|
+
priorClipboard: ClipboardPixel[];
|
|
30
|
+
}
|
|
31
|
+
import Undo2 from '~icons/lucide/undo-2';
|
|
32
|
+
import Redo2 from '~icons/lucide/redo-2';
|
|
33
|
+
import Scissors from '~icons/lucide/scissors';
|
|
34
|
+
import Copy from '~icons/lucide/copy';
|
|
35
|
+
import ClipboardPaste from '~icons/lucide/clipboard-paste';
|
|
36
|
+
import BoxSelect from '~icons/lucide/box-select';
|
|
37
|
+
|
|
38
|
+
export const editCommandsPlugin: PluginModule = {
|
|
39
|
+
name: 'ui/edit-commands',
|
|
40
|
+
version: '1.0.0',
|
|
41
|
+
dependencies: [],
|
|
42
|
+
register(api) {
|
|
43
|
+
// Capture once: the returned object is stable and its methods read live
|
|
44
|
+
// selection state on each call (see PluginAPI.getSelection).
|
|
45
|
+
const selection = api.getSelection();
|
|
46
|
+
|
|
47
|
+
// --- Undo/Redo ---
|
|
48
|
+
|
|
49
|
+
api.addCommand('undo', {
|
|
50
|
+
tier: 'project',
|
|
51
|
+
execute() { undoLast(); },
|
|
52
|
+
undo() {},
|
|
53
|
+
describe() { return 'Undo'; },
|
|
54
|
+
label: 'Undo',
|
|
55
|
+
category: 'Edit',
|
|
56
|
+
defaultShortcut: 'Ctrl+Z',
|
|
57
|
+
icon: Undo2,
|
|
58
|
+
});
|
|
59
|
+
api.addMenuItem('menu:edit:undo', {
|
|
60
|
+
commandId: 'undo',
|
|
61
|
+
menuPath: 'edit',
|
|
62
|
+
group: 'undo',
|
|
63
|
+
order: 10,
|
|
64
|
+
label: 'Undo',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
api.addCommand('redo', {
|
|
68
|
+
tier: 'project',
|
|
69
|
+
execute() { redoLast(); },
|
|
70
|
+
undo() {},
|
|
71
|
+
describe() { return 'Redo'; },
|
|
72
|
+
label: 'Redo',
|
|
73
|
+
category: 'Edit',
|
|
74
|
+
defaultShortcut: 'Ctrl+Shift+Z',
|
|
75
|
+
icon: Redo2,
|
|
76
|
+
});
|
|
77
|
+
api.addMenuItem('menu:edit:redo', {
|
|
78
|
+
commandId: 'redo',
|
|
79
|
+
menuPath: 'edit',
|
|
80
|
+
group: 'undo',
|
|
81
|
+
order: 20,
|
|
82
|
+
label: 'Redo',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// --- Clipboard commands ---
|
|
86
|
+
|
|
87
|
+
api.addCommand('cut', {
|
|
88
|
+
tier: 'frame',
|
|
89
|
+
undoable: true,
|
|
90
|
+
execute(_params, ctx): CutSnapshot | undefined {
|
|
91
|
+
if (!selection.hasSelection()) {
|
|
92
|
+
console.warn('cut: no selection exists');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const buffer = ctx.getActiveBuffer?.();
|
|
96
|
+
if (!buffer) {
|
|
97
|
+
console.warn('cut: no active pixel buffer');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Capture the prior clipboard so undo can restore it. Clone each
|
|
101
|
+
// entry (getClipboard returns a readonly reference-typed array).
|
|
102
|
+
const priorClipboard: ClipboardPixel[] = getClipboard().map((p) => ({ ...p }));
|
|
103
|
+
// Copy selected pixels to clipboard, then clear them
|
|
104
|
+
const pixels: ClipboardPixel[] = [];
|
|
105
|
+
for (const key of selection.getSelectedPixels()) {
|
|
106
|
+
const [x, y] = key.split(',').map(Number) as [number, number];
|
|
107
|
+
const [r, g, b, a] = buffer.getPixel(x, y);
|
|
108
|
+
pixels.push({ x, y, r, g, b, a });
|
|
109
|
+
buffer.setPixel(x, y, 0, 0, 0, 0);
|
|
110
|
+
}
|
|
111
|
+
setClipboard(pixels);
|
|
112
|
+
// Snapshot carries the pre-cut pixel values AND prior clipboard.
|
|
113
|
+
// pixels is safe to re-use as the snapshot payload since setClipboard
|
|
114
|
+
// took ownership of the same array -- but undo doesn't mutate it.
|
|
115
|
+
return { pixels, priorClipboard };
|
|
116
|
+
},
|
|
117
|
+
undo(_params, ctx, snapshot) {
|
|
118
|
+
const snap = snapshot as CutSnapshot | undefined;
|
|
119
|
+
if (!snap) return;
|
|
120
|
+
const buffer = ctx.getActiveBuffer?.();
|
|
121
|
+
if (!buffer) return;
|
|
122
|
+
// Restore the original pixel values that were cleared by the cut.
|
|
123
|
+
for (const p of snap.pixels) {
|
|
124
|
+
buffer.setPixel(p.x, p.y, p.r, p.g, p.b, p.a);
|
|
125
|
+
}
|
|
126
|
+
// Restore the prior clipboard (clone to avoid aliasing snapshot array).
|
|
127
|
+
setClipboard(snap.priorClipboard.map((p) => ({ ...p })));
|
|
128
|
+
},
|
|
129
|
+
describe() { return 'Cut selection'; },
|
|
130
|
+
label: 'Cut',
|
|
131
|
+
category: 'Edit',
|
|
132
|
+
defaultShortcut: 'Ctrl+X',
|
|
133
|
+
icon: Scissors,
|
|
134
|
+
});
|
|
135
|
+
api.addMenuItem('menu:edit:cut', {
|
|
136
|
+
commandId: 'cut',
|
|
137
|
+
menuPath: 'edit',
|
|
138
|
+
group: 'clipboard',
|
|
139
|
+
order: 30,
|
|
140
|
+
label: 'Cut',
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
api.addCommand('copy', {
|
|
144
|
+
tier: 'frame',
|
|
145
|
+
execute(_params, ctx) {
|
|
146
|
+
if (!selection.hasSelection()) {
|
|
147
|
+
console.warn('copy: no selection exists');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const buffer = ctx.getActiveBuffer?.();
|
|
151
|
+
if (!buffer) {
|
|
152
|
+
console.warn('copy: no active pixel buffer');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Copy selected pixels to clipboard (non-destructive)
|
|
156
|
+
const pixels = [];
|
|
157
|
+
for (const key of selection.getSelectedPixels()) {
|
|
158
|
+
const [x, y] = key.split(',').map(Number) as [number, number];
|
|
159
|
+
const [r, g, b, a] = buffer.getPixel(x, y);
|
|
160
|
+
pixels.push({ x, y, r, g, b, a });
|
|
161
|
+
}
|
|
162
|
+
setClipboard(pixels);
|
|
163
|
+
},
|
|
164
|
+
undo() {},
|
|
165
|
+
describe() { return 'Copied selection'; },
|
|
166
|
+
label: 'Copy',
|
|
167
|
+
category: 'Edit',
|
|
168
|
+
defaultShortcut: 'Ctrl+C',
|
|
169
|
+
icon: Copy,
|
|
170
|
+
});
|
|
171
|
+
api.addMenuItem('menu:edit:copy', {
|
|
172
|
+
commandId: 'copy',
|
|
173
|
+
menuPath: 'edit',
|
|
174
|
+
group: 'clipboard',
|
|
175
|
+
order: 40,
|
|
176
|
+
label: 'Copy',
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
api.addCommand('paste', {
|
|
180
|
+
tier: 'frame',
|
|
181
|
+
undoable: true,
|
|
182
|
+
execute(_params, ctx): PixelRegionSnapshot | undefined {
|
|
183
|
+
if (!hasClipboard()) {
|
|
184
|
+
console.warn('paste: clipboard is empty');
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const buffer = ctx.getActiveBuffer?.();
|
|
188
|
+
if (!buffer) {
|
|
189
|
+
console.warn('paste: no active pixel buffer');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// Snapshot the pre-paste pixel values at each clipboard coordinate
|
|
193
|
+
// BEFORE writing, so undo can restore them exactly.
|
|
194
|
+
const pre: ClipboardPixel[] = [];
|
|
195
|
+
for (const px of getClipboard()) {
|
|
196
|
+
const [r, g, b, a] = buffer.getPixel(px.x, px.y);
|
|
197
|
+
pre.push({ x: px.x, y: px.y, r, g, b, a });
|
|
198
|
+
}
|
|
199
|
+
// Paste clipboard pixels at their original positions
|
|
200
|
+
for (const px of getClipboard()) {
|
|
201
|
+
buffer.setPixel(px.x, px.y, px.r, px.g, px.b, px.a);
|
|
202
|
+
}
|
|
203
|
+
return { pixels: pre };
|
|
204
|
+
},
|
|
205
|
+
undo(_params, ctx, snapshot) {
|
|
206
|
+
const snap = snapshot as PixelRegionSnapshot | undefined;
|
|
207
|
+
if (!snap) return;
|
|
208
|
+
const buffer = ctx.getActiveBuffer?.();
|
|
209
|
+
if (!buffer) return;
|
|
210
|
+
for (const p of snap.pixels) {
|
|
211
|
+
buffer.setPixel(p.x, p.y, p.r, p.g, p.b, p.a);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
describe() { return 'Pasted from clipboard'; },
|
|
215
|
+
label: 'Paste',
|
|
216
|
+
category: 'Edit',
|
|
217
|
+
defaultShortcut: 'Ctrl+V',
|
|
218
|
+
icon: ClipboardPaste,
|
|
219
|
+
});
|
|
220
|
+
api.addMenuItem('menu:edit:paste', {
|
|
221
|
+
commandId: 'paste',
|
|
222
|
+
menuPath: 'edit',
|
|
223
|
+
group: 'clipboard',
|
|
224
|
+
order: 50,
|
|
225
|
+
label: 'Paste',
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// --- Selection commands ---
|
|
229
|
+
|
|
230
|
+
api.addCommand('select_all', {
|
|
231
|
+
tier: 'frame',
|
|
232
|
+
execute() {
|
|
233
|
+
// Delegate to the selection plugin via dispatch -- select_rect's own
|
|
234
|
+
// execute/undo handle the selection snapshot bookkeeping.
|
|
235
|
+
api.dispatch({
|
|
236
|
+
type: 'select_rect',
|
|
237
|
+
plugin: 'builtin/selection',
|
|
238
|
+
version: '1.0.0',
|
|
239
|
+
params: {
|
|
240
|
+
x: 0,
|
|
241
|
+
y: 0,
|
|
242
|
+
w: canvasState.canvasWidth,
|
|
243
|
+
h: canvasState.canvasHeight,
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
},
|
|
247
|
+
undo() {},
|
|
248
|
+
describe() { return 'Selected all'; },
|
|
249
|
+
label: 'Select All',
|
|
250
|
+
category: 'Edit',
|
|
251
|
+
defaultShortcut: 'Ctrl+A',
|
|
252
|
+
icon: BoxSelect,
|
|
253
|
+
});
|
|
254
|
+
api.addMenuItem('menu:edit:select-all', {
|
|
255
|
+
commandId: 'select_all',
|
|
256
|
+
menuPath: 'edit',
|
|
257
|
+
group: 'selection',
|
|
258
|
+
order: 60,
|
|
259
|
+
label: 'Select All',
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// deselect command already registered by selection-tool plugin; just add menu item
|
|
263
|
+
api.addMenuItem('menu:edit:deselect', {
|
|
264
|
+
commandId: 'deselect',
|
|
265
|
+
menuPath: 'edit',
|
|
266
|
+
group: 'selection',
|
|
267
|
+
order: 70,
|
|
268
|
+
label: 'Deselect',
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
};
|