auvrynt 1.0.2 → 1.0.5
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/LICENSE +21 -21
- package/README.md +366 -233
- package/addons/auvrynt_bridge/auvrynt_bridge.gd +500 -0
- package/addons/auvrynt_bridge/plugin.cfg +7 -0
- package/bin/auvrynt-launcher.cjs +77 -0
- package/dist/artifact-registry.js +1 -0
- package/dist/aseprite-tools.js +1 -0
- package/dist/auth/connection-registry.js +145 -0
- package/dist/auth/connection-status.js +176 -0
- package/dist/auth/oauth-provider.js +617 -0
- package/dist/auth/room-registry.js +131 -0
- package/dist/auth/session-registry.js +531 -0
- package/dist/background-lifecycle.js +1 -0
- package/dist/blender-client.js +1 -109
- package/dist/blender-tools.js +1 -930
- package/dist/browser-stability.js +1 -0
- package/dist/cli/commands/help-command.js +10 -0
- package/dist/cli/commands/init-command.js +190 -0
- package/dist/cli/commands/integration-commands.js +88 -0
- package/dist/cli/commands/serve-command.js +83 -0
- package/dist/cli/commands/setup-command.js +81 -0
- package/dist/cli/commands/status-commands.js +166 -0
- package/dist/cli/commands/tunnel-command.js +127 -0
- package/dist/cli/commands/user-config-commands.js +105 -0
- package/dist/cli/dashboard-actions.js +516 -0
- package/dist/cli/dashboard-native-actions.js +282 -0
- package/dist/cli/foreground-server.js +270 -0
- package/dist/cli/instance-control.js +21 -0
- package/dist/cli/instance-lock.js +123 -0
- package/dist/cli/integration-bootstrap.js +206 -0
- package/dist/cli/lifecycle-manager.js +493 -0
- package/dist/cli/runtime-support.js +74 -0
- package/dist/cli/serena-detected-default.js +27 -0
- package/dist/cli/tunnel-health-monitor.js +182 -0
- package/dist/cli.js +82 -739
- package/dist/config.js +1 -243
- package/dist/connection-registry.js +1 -73
- package/dist/connection-status.js +1 -139
- package/dist/control-retry.js +1 -0
- package/dist/crash-recovery.js +1 -0
- package/dist/db/client.js +16 -3
- package/dist/db/schema.js +30 -0
- package/dist/dotnet-tools.js +1 -223
- package/dist/git-worktrees.js +1 -134
- package/dist/git.js +1 -41
- package/dist/godot-animation-tilemap.js +1 -82
- package/dist/godot-csharp-build.js +1 -93
- package/dist/godot-csharp-project.js +1 -157
- package/dist/godot-csharp-runner.js +1 -146
- package/dist/godot-csharp-semantic.js +1 -161
- package/dist/godot-csharp-validate.js +1 -71
- package/dist/godot-dotnet-env.js +1 -169
- package/dist/godot-editor-bridge.js +1 -140
- package/dist/godot-gdscript.js +1 -824
- package/dist/godot-project-settings.js +1 -223
- package/dist/godot-runtime-testing.js +1 -249
- package/dist/godot-tools.js +1 -183
- package/dist/http-rate-limit.js +1 -0
- package/dist/http-server-hardening.js +1 -0
- package/dist/image-tools.js +1 -157
- package/dist/infrastructure/command-reference.js +32 -0
- package/dist/infrastructure/config.js +424 -0
- package/dist/infrastructure/context7-auth.js +55 -0
- package/dist/infrastructure/http-rate-limit.js +42 -0
- package/dist/infrastructure/http-server-hardening.js +23 -0
- package/dist/infrastructure/integration-queue.js +46 -0
- package/dist/infrastructure/logger.js +129 -0
- package/dist/infrastructure/mcp-event-store.js +103 -0
- package/dist/infrastructure/ngrok-auth-pool.js +208 -0
- package/dist/infrastructure/request-context.js +14 -0
- package/dist/infrastructure/shutdown.js +37 -0
- package/dist/infrastructure/sse-heartbeat.js +47 -0
- package/dist/infrastructure/tool-capabilities.js +67 -0
- package/dist/infrastructure/tool-result-budget.js +175 -0
- package/dist/infrastructure/user-config.js +167 -0
- package/dist/integration-discovery.js +1 -98
- package/dist/integration-queue.js +1 -0
- package/dist/integrations/agent-reach/agent-reach-tools.js +196 -0
- package/dist/integrations/agent-reach/native-channels.js +468 -0
- package/dist/integrations/aseprite/aseprite-analysis-tools.js +368 -0
- package/dist/integrations/aseprite/aseprite-editing-tools.js +647 -0
- package/dist/integrations/aseprite/aseprite-live-tools.js +297 -0
- package/dist/integrations/aseprite/aseprite-safety-tools.js +594 -0
- package/dist/integrations/aseprite/aseprite-specialized-tools.js +289 -0
- package/dist/integrations/aseprite/aseprite-tools.js +837 -0
- package/dist/integrations/blender/blender-catalog-mesh.js +593 -0
- package/dist/integrations/blender/blender-catalog-object.js +906 -0
- package/dist/integrations/blender/blender-catalog-scene.js +266 -0
- package/dist/integrations/blender/blender-catalog-select.js +581 -0
- package/dist/integrations/blender/blender-catalog-stubs.js +7512 -0
- package/dist/integrations/blender/blender-catalog.js +165 -0
- package/dist/integrations/blender/blender-client.js +105 -0
- package/dist/integrations/blender/blender-tools.js +2243 -0
- package/dist/integrations/context7/context7-tools.js +106 -0
- package/dist/integrations/dotnet/dotnet-tools.js +195 -0
- package/dist/integrations/godot/godot-animation-tilemap.js +74 -0
- package/dist/integrations/godot/godot-csharp-build.js +91 -0
- package/dist/integrations/godot/godot-csharp-project.js +155 -0
- package/dist/integrations/godot/godot-csharp-runner.js +118 -0
- package/dist/integrations/godot/godot-csharp-semantic.js +146 -0
- package/dist/integrations/godot/godot-csharp-validate.js +71 -0
- package/dist/integrations/godot/godot-dotnet-env.js +134 -0
- package/dist/integrations/godot/godot-editor-bridge.js +328 -0
- package/dist/integrations/godot/godot-gdscript.js +536 -0
- package/dist/integrations/godot/godot-project-settings.js +178 -0
- package/dist/integrations/godot/godot-runtime-testing.js +263 -0
- package/dist/integrations/godot/godot-tools.js +193 -0
- package/dist/integrations/images/image-tools.js +414 -0
- package/dist/integrations/images/png-codec.js +251 -0
- package/dist/integrations/images/view-image.js +99 -0
- package/dist/integrations/images/window-capture.js +148 -0
- package/dist/integrations/integration-discovery.js +185 -0
- package/dist/integrations/obsidian/obsidian-tools.js +205 -0
- package/dist/integrations/playwright/browser-session.js +516 -0
- package/dist/integrations/playwright/browser-stability.js +36 -0
- package/dist/integrations/playwright/playwright-runtime.js +105 -0
- package/dist/integrations/playwright/web-tools.js +504 -0
- package/dist/integrations/roblox/roblox-helpers.js +381 -0
- package/dist/integrations/roblox/roblox-studio-client.js +112 -0
- package/dist/integrations/roblox/roblox-tools.js +106 -0
- package/dist/integrations/serena/serena-manager.js +464 -0
- package/dist/integrations/serena/serena-tools.js +411 -0
- package/dist/logger.js +1 -76
- package/dist/mcp-event-store.js +1 -0
- package/dist/oauth-provider.js +1 -377
- package/dist/pi-tools.js +1 -81
- package/dist/playwright-runtime.js +1 -0
- package/dist/process/background-lifecycle.js +189 -0
- package/dist/process/control-retry.js +42 -0
- package/dist/process/crash-recovery.js +23 -0
- package/dist/process/processes.js +456 -0
- package/dist/processes.js +1 -218
- package/dist/request-context.js +1 -0
- package/dist/review-checkpoints.js +1 -142
- package/dist/room-registry.js +1 -0
- package/dist/roots.js +1 -53
- package/dist/search-discovery.js +1 -271
- package/dist/serena-manager.js +1 -443
- package/dist/serena-tools.js +1 -411
- package/dist/server/dashboard-page-styles.js +331 -0
- package/dist/server/dashboard-page.js +1509 -0
- package/dist/server/dashboard-routes.js +46 -0
- package/dist/server/dashboard.js +240 -0
- package/dist/server/http-helpers.js +30 -0
- package/dist/server/integration-profile-update.js +37 -0
- package/dist/server/mcp-policy.js +169 -0
- package/dist/server/mcp-server-factory.js +127 -0
- package/dist/server/mcp-tool-registrar.js +182 -0
- package/dist/server/openai-session-hint.js +14 -0
- package/dist/server/tool-groups.js +29 -0
- package/dist/server/tool-logging.js +33 -0
- package/dist/server/tool-registration-shared.js +59 -0
- package/dist/server/tools/aseprite-advanced-tools-registration.js +547 -0
- package/dist/server/tools/aseprite-tools-registration.js +213 -0
- package/dist/server/tools/blender-tools-registration.js +690 -0
- package/dist/server/tools/core-file-tools-registration.js +459 -0
- package/dist/server/tools/dotnet-godot-window-tools-registration.js +120 -0
- package/dist/server/tools/godot-csharp-tools-registration.js +252 -0
- package/dist/server/tools/godot-gdscript-tools-registration.js +189 -0
- package/dist/server/tools/process-project-tools-registration.js +155 -0
- package/dist/server/tools/roblox-tools-registration.js +514 -0
- package/dist/server/tools/web-image-tools-registration.js +326 -0
- package/dist/server/tools/workspace-lifecycle-tools-registration.js +130 -0
- package/dist/server/ui-assets.js +97 -0
- package/dist/server/workspace-analytics.js +92 -0
- package/dist/server.js +453 -2713
- package/dist/session-registry.js +1 -0
- package/dist/shutdown.js +1 -0
- package/dist/skills.js +1 -45
- package/dist/sse-heartbeat.js +1 -0
- package/dist/tool-capabilities.js +1 -0
- package/dist/tool-result-budget.js +1 -0
- package/dist/tools/pi-tools.js +81 -0
- package/dist/tunnels/cloudflare-tunnel.js +217 -0
- package/dist/tunnels/cloudflared-service.js +174 -0
- package/dist/tunnels/ngrok-tunnel.js +220 -0
- package/dist/tunnels/tunnel-manager.js +186 -0
- package/dist/tunnels/tunnel-types.js +1 -0
- package/dist/tunnels/tunnel-utils.js +62 -0
- package/dist/ui/.vite/manifest.json +9 -2487
- package/dist/ui/assets/heavy-payload-D42A9ceL.js +1 -0
- package/dist/ui/assets/lightweight-diff-Cv4g9Hdr.js +13 -0
- package/dist/ui/assets/review-payload-DKIg1cKF.js +1 -0
- package/dist/ui/assets/workspace-app-CeK3NhmB.css +1 -0
- package/dist/ui/assets/{workspace-app-C5QsE314.js → workspace-app-CkUXnpl4.js} +6 -6
- package/dist/ui/brand-theme.js +23 -0
- package/dist/ui/workspace-app.html +15 -15
- package/dist/user-config.js +1 -57
- package/dist/view-image.js +1 -104
- package/dist/web-tools.js +1 -200
- package/dist/window-capture.js +1 -74
- package/dist/workspace/artifact-registry.js +69 -0
- package/dist/workspace/git-worktrees.js +141 -0
- package/dist/workspace/git.js +42 -0
- package/dist/workspace/review-checkpoints.js +142 -0
- package/dist/workspace/roots.js +56 -0
- package/dist/workspace/search-discovery.js +264 -0
- package/dist/workspace/skills.js +45 -0
- package/dist/workspace/workspace-store.js +219 -0
- package/dist/workspace/workspaces.js +314 -0
- package/dist/workspace-store.js +1 -125
- package/dist/workspaces.js +1 -226
- package/docs/CREATOR_STORE_FREE_ASSETS.md +162 -0
- package/docs/DEVELOPMENT_TOOLS.md +136 -93
- package/docs/assets/auvrynt-icon.png +0 -0
- package/docs/assets/auvrynt-icon.svg +20 -0
- package/docs/chatgpt-coding-workflow.md +143 -143
- package/docs/configuration.md +156 -124
- package/docs/diagnostics.md +79 -0
- package/docs/gotchas.md +244 -202
- package/docs/multi-agent-and-long-projects.md +91 -0
- package/docs/security.md +138 -100
- package/docs/setup.md +171 -131
- package/docs/source-layout.md +60 -0
- package/extensions/auvrynt_bridge/auvrynt_bridge.lua +594 -0
- package/extensions/auvrynt_bridge/live_analysis.lua +181 -0
- package/extensions/auvrynt_bridge/live_animation.lua +134 -0
- package/extensions/auvrynt_bridge/live_commands.lua +24 -0
- package/extensions/auvrynt_bridge/live_common.lua +130 -0
- package/extensions/auvrynt_bridge/live_document.lua +78 -0
- package/extensions/auvrynt_bridge/live_drawing.lua +125 -0
- package/extensions/auvrynt_bridge/live_effects.lua +91 -0
- package/extensions/auvrynt_bridge/live_palette_template.lua +167 -0
- package/extensions/auvrynt_bridge/live_selection.lua +110 -0
- package/extensions/auvrynt_bridge/live_structure.lua +129 -0
- package/extensions/auvrynt_bridge/package.json +17 -0
- package/guides/ASEPRITE_AGENT_GUIDE.md +133 -0
- package/guides/BLENDER_AGENT_GUIDE.md +472 -0
- package/guides/GODOT_AGENT_GUIDE.md +298 -0
- package/guides/PROJECT_ROUTER.md +165 -0
- package/guides/ROBLOX_AGENT_GUIDE.md +75 -0
- package/guides/SOFTWARE_AGENT_GUIDE.md +104 -0
- package/guides/WEB_AGENT_GUIDE.md +287 -0
- package/package.json +78 -70
- package/scripts/dev-server.mjs +121 -120
- package/scripts/live-tunnel-smoke.mjs +538 -0
- package/scripts/repo-hygiene.mjs +74 -0
- package/scripts/restart-after-crash.mjs +49 -0
- package/scripts/run-tests.mjs +166 -0
- package/dist/ui/assets/abap-CLvhMVsD.js +0 -1
- package/dist/ui/assets/actionscript-3--17pq3dv.js +0 -1
- package/dist/ui/assets/ada-C5qYipkI.js +0 -1
- package/dist/ui/assets/andromeeda-vGVdxbeo.js +0 -1
- package/dist/ui/assets/angular-html-BJqQFYI6.js +0 -1
- package/dist/ui/assets/angular-ts-CMJECO0Y.js +0 -1
- package/dist/ui/assets/apache-U0d_L8uA.js +0 -1
- package/dist/ui/assets/apex-CGTLDQj6.js +0 -1
- package/dist/ui/assets/apl-DWeXpBBr.js +0 -1
- package/dist/ui/assets/applescript-CCn79oCD.js +0 -1
- package/dist/ui/assets/ara-4CJ0cIlV.js +0 -1
- package/dist/ui/assets/asciidoc-DE70LPWp.js +0 -1
- package/dist/ui/assets/asm-Cmm7eHzH.js +0 -1
- package/dist/ui/assets/astro-CLbttLOq.js +0 -1
- package/dist/ui/assets/aurora-x-CDeNXAV0.js +0 -1
- package/dist/ui/assets/awk-BWXHIvNe.js +0 -1
- package/dist/ui/assets/ayu-dark-DluEY0Gj.js +0 -1
- package/dist/ui/assets/ayu-light-C3h-C4tm.js +0 -1
- package/dist/ui/assets/ayu-mirage-Bqwy1Gya.js +0 -1
- package/dist/ui/assets/ballerina-B7ZEbQpA.js +0 -1
- package/dist/ui/assets/bat-Bo4NYOV-.js +0 -1
- package/dist/ui/assets/beancount-D-usSTwE.js +0 -1
- package/dist/ui/assets/berry-DKpUyyne.js +0 -1
- package/dist/ui/assets/bibtex-Ci_nEsc7.js +0 -1
- package/dist/ui/assets/bicep-CUHmPFLl.js +0 -1
- package/dist/ui/assets/bird2-C2hNVINV.js +0 -1
- package/dist/ui/assets/blade-BOzY0P2V.js +0 -1
- package/dist/ui/assets/bsl-BkkzgIyY.js +0 -1
- package/dist/ui/assets/c-BG-8uJvh.js +0 -1
- package/dist/ui/assets/c3-CnJL0r0V.js +0 -1
- package/dist/ui/assets/cadence-CQ2zXKGN.js +0 -1
- package/dist/ui/assets/cairo-DLTphjLi.js +0 -1
- package/dist/ui/assets/catppuccin-frappe-3VR1Za6u.js +0 -1
- package/dist/ui/assets/catppuccin-latte-DwIHMF0Q.js +0 -1
- package/dist/ui/assets/catppuccin-macchiato-DYnBP6_5.js +0 -1
- package/dist/ui/assets/catppuccin-mocha-DYhrFGRu.js +0 -1
- package/dist/ui/assets/clarity-SemFz856.js +0 -1
- package/dist/ui/assets/clojure-DqKBuwfJ.js +0 -1
- package/dist/ui/assets/cmake-Bj61d0ZC.js +0 -1
- package/dist/ui/assets/cobol-I_17IuGe.js +0 -1
- package/dist/ui/assets/codeowners-C8r90Shi.js +0 -1
- package/dist/ui/assets/codeql-oeQT6MSM.js +0 -1
- package/dist/ui/assets/coffee-C0B8hmok.js +0 -1
- package/dist/ui/assets/common-lisp-Cv5bFMCO.js +0 -1
- package/dist/ui/assets/coq-BrsZFFmf.js +0 -1
- package/dist/ui/assets/cpp-DDJXt6Vs.js +0 -1
- package/dist/ui/assets/crystal-Bexqace6.js +0 -1
- package/dist/ui/assets/csharp-Ct8U2NOr.js +0 -1
- package/dist/ui/assets/css-DSQhw_9R.js +0 -1
- package/dist/ui/assets/csv-Dx-8-gkx.js +0 -1
- package/dist/ui/assets/cue-CE9AQfxI.js +0 -1
- package/dist/ui/assets/cypher-ClKdZ_lG.js +0 -1
- package/dist/ui/assets/d-qD-0Kul2.js +0 -1
- package/dist/ui/assets/dark-plus-Cs2F2srj.js +0 -1
- package/dist/ui/assets/dart-DkHntEIa.js +0 -1
- package/dist/ui/assets/dax-BkyTk9wS.js +0 -1
- package/dist/ui/assets/desktop-Dlh5hvp9.js +0 -1
- package/dist/ui/assets/diff-woXpYk--.js +0 -1
- package/dist/ui/assets/docker-IyjqRm3v.js +0 -1
- package/dist/ui/assets/dotenv-_5a1GRtc.js +0 -1
- package/dist/ui/assets/dracula-BHWKrbxM.js +0 -1
- package/dist/ui/assets/dracula-soft-5eyTD99u.js +0 -1
- package/dist/ui/assets/dream-maker-DW3nJb8Q.js +0 -1
- package/dist/ui/assets/edge-Z8YYkHOr.js +0 -1
- package/dist/ui/assets/elixir-CG9uKtAF.js +0 -1
- package/dist/ui/assets/elm-GfWG2IfT.js +0 -1
- package/dist/ui/assets/emacs-lisp-C9PiwqqW.js +0 -1
- package/dist/ui/assets/erb-xe89e73J.js +0 -1
- package/dist/ui/assets/erlang-Cphh6RMH.js +0 -1
- package/dist/ui/assets/everforest-dark-sB-x3p7T.js +0 -1
- package/dist/ui/assets/everforest-light-Df2xbC6M.js +0 -1
- package/dist/ui/assets/fennel-DQxkIbk2.js +0 -1
- package/dist/ui/assets/fish-BJitypiv.js +0 -1
- package/dist/ui/assets/fluent-C03EYrpw.js +0 -1
- package/dist/ui/assets/fortran-fixed-form-DEKoE2YW.js +0 -1
- package/dist/ui/assets/fortran-free-form-CYNrtFtB.js +0 -1
- package/dist/ui/assets/fsharp-D13ZGOAj.js +0 -1
- package/dist/ui/assets/gdresource-C0sCabJj.js +0 -1
- package/dist/ui/assets/gdscript-Cp2uCuqX.js +0 -1
- package/dist/ui/assets/gdshader-CBce3t8t.js +0 -1
- package/dist/ui/assets/genie-CV2tkWYe.js +0 -1
- package/dist/ui/assets/gherkin-DExj1W_8.js +0 -1
- package/dist/ui/assets/git-commit-BSykSTBG.js +0 -1
- package/dist/ui/assets/git-rebase-CoTL_uSE.js +0 -1
- package/dist/ui/assets/github-dark-C-LZuMrd.js +0 -1
- package/dist/ui/assets/github-dark-default-DXG-b-1a.js +0 -1
- package/dist/ui/assets/github-dark-dimmed-Bx1FflLF.js +0 -1
- package/dist/ui/assets/github-dark-high-contrast-B_tTalzw.js +0 -1
- package/dist/ui/assets/github-light-EUqPIrTm.js +0 -1
- package/dist/ui/assets/github-light-default-BXViO-2h.js +0 -1
- package/dist/ui/assets/github-light-high-contrast-B68TUdTA.js +0 -1
- package/dist/ui/assets/gleam-CSRkHgEL.js +0 -1
- package/dist/ui/assets/glimmer-js-DAn4UCSu.js +0 -1
- package/dist/ui/assets/glimmer-ts-Tjyi6Gh5.js +0 -1
- package/dist/ui/assets/glsl-CtTDZoqR.js +0 -1
- package/dist/ui/assets/gn-ilITqXS6.js +0 -1
- package/dist/ui/assets/gnuplot-7GGW24-e.js +0 -1
- package/dist/ui/assets/go-BJwz_mda.js +0 -1
- package/dist/ui/assets/graphql-D5gcC_Jk.js +0 -1
- package/dist/ui/assets/groovy-CacY0gHj.js +0 -1
- package/dist/ui/assets/gruvbox-dark-hard-C820rvS2.js +0 -1
- package/dist/ui/assets/gruvbox-dark-medium-BPjhmG05.js +0 -1
- package/dist/ui/assets/gruvbox-dark-soft-MrdJrrXF.js +0 -1
- package/dist/ui/assets/gruvbox-light-hard-BC_s9l72.js +0 -1
- package/dist/ui/assets/gruvbox-light-medium-BAWPOn9u.js +0 -1
- package/dist/ui/assets/gruvbox-light-soft-BSMLrYjP.js +0 -1
- package/dist/ui/assets/hack-BJXY27dt.js +0 -1
- package/dist/ui/assets/haml-BLfFoZf7.js +0 -1
- package/dist/ui/assets/handlebars-Dw6CWkvN.js +0 -1
- package/dist/ui/assets/haskell-D8IpX4py.js +0 -1
- package/dist/ui/assets/haxe-OTjmBuCE.js +0 -1
- package/dist/ui/assets/hcl-Dh228itO.js +0 -1
- package/dist/ui/assets/heavy-payload-Dcg0-F-S.js +0 -4
- package/dist/ui/assets/hjson-CxZEssPk.js +0 -1
- package/dist/ui/assets/hlsl-Cvrh5tZx.js +0 -1
- package/dist/ui/assets/horizon-CE9ld1lL.js +0 -1
- package/dist/ui/assets/horizon-bright-Br1oVSNq.js +0 -1
- package/dist/ui/assets/houston-CsvMBhTu.js +0 -1
- package/dist/ui/assets/html-CAcAWvoN.js +0 -1
- package/dist/ui/assets/html-derivative-8aSW2E_7.js +0 -1
- package/dist/ui/assets/http-B2YR_3V1.js +0 -1
- package/dist/ui/assets/hurl-BnDJ9Zhf.js +0 -1
- package/dist/ui/assets/hxml-B0Qn7Nwc.js +0 -1
- package/dist/ui/assets/hy-CZbG8q4J.js +0 -1
- package/dist/ui/assets/imba-DsUTQ-LC.js +0 -1
- package/dist/ui/assets/ini-B5eOa1yu.js +0 -1
- package/dist/ui/assets/java-Doxi4W8u.js +0 -1
- package/dist/ui/assets/javascript-BbRdEqti.js +0 -1
- package/dist/ui/assets/jinja-CS3Tpv_Q.js +0 -1
- package/dist/ui/assets/jison-BkPGYVHR.js +0 -1
- package/dist/ui/assets/json-BMt1m3m8.js +0 -1
- package/dist/ui/assets/json5-BR5RXkoi.js +0 -1
- package/dist/ui/assets/jsonc-CYpm1nAK.js +0 -1
- package/dist/ui/assets/jsonl-CmCQp5Yx.js +0 -1
- package/dist/ui/assets/jsonnet-CJTPZ8u_.js +0 -1
- package/dist/ui/assets/jssm-DXw9l8Rf.js +0 -1
- package/dist/ui/assets/jsx-CQKrJXXY.js +0 -1
- package/dist/ui/assets/julia-BuN7i2du.js +0 -1
- package/dist/ui/assets/just-BswEPFPu.js +0 -1
- package/dist/ui/assets/kanagawa-dragon-CXtmUGW6.js +0 -1
- package/dist/ui/assets/kanagawa-lotus-BN08jTvb.js +0 -1
- package/dist/ui/assets/kanagawa-wave-CTweb8Dz.js +0 -1
- package/dist/ui/assets/kdl-CsD5j6eV.js +0 -1
- package/dist/ui/assets/kotlin-DhhofPvG.js +0 -1
- package/dist/ui/assets/kusto-BUv0MjJC.js +0 -1
- package/dist/ui/assets/laserwave-C_8bwKvT.js +0 -1
- package/dist/ui/assets/latex-DU2VyFfA.js +0 -1
- package/dist/ui/assets/lean-CewbzKMR.js +0 -1
- package/dist/ui/assets/less-DVTAwKKz.js +0 -1
- package/dist/ui/assets/light-plus-DVQuIRkW.js +0 -1
- package/dist/ui/assets/liquid-Cmz1L3dq.js +0 -1
- package/dist/ui/assets/llvm-Cm23YOpf.js +0 -1
- package/dist/ui/assets/log-BNLmms1o.js +0 -1
- package/dist/ui/assets/logo-Cluzi2Zq.js +0 -1
- package/dist/ui/assets/lua-CfVXhkaJ.js +0 -1
- package/dist/ui/assets/luau-CNKltnaQ.js +0 -1
- package/dist/ui/assets/make-Dixweg8N.js +0 -1
- package/dist/ui/assets/markdown-BYOwaDjH.js +0 -1
- package/dist/ui/assets/marko-C5bfqGiA.js +0 -1
- package/dist/ui/assets/material-theme-Bm3Qr25_.js +0 -1
- package/dist/ui/assets/material-theme-darker-2IIEA8gg.js +0 -1
- package/dist/ui/assets/material-theme-lighter-uhdI0v04.js +0 -1
- package/dist/ui/assets/material-theme-ocean-CHQ94UKr.js +0 -1
- package/dist/ui/assets/material-theme-palenight-B5W6OYN7.js +0 -1
- package/dist/ui/assets/matlab-D7qyCx1q.js +0 -1
- package/dist/ui/assets/mdc-CVBXYfyq.js +0 -1
- package/dist/ui/assets/mdx-DQZ5AkYe.js +0 -1
- package/dist/ui/assets/mermaid-Bk4SNUv9.js +0 -1
- package/dist/ui/assets/min-dark-BSWPekZh.js +0 -1
- package/dist/ui/assets/min-light-DDpmG2fV.js +0 -1
- package/dist/ui/assets/mipsasm-BMqwQI7S.js +0 -1
- package/dist/ui/assets/mojo-BgCJLMeH.js +0 -1
- package/dist/ui/assets/monokai-CdkpiU2Y.js +0 -1
- package/dist/ui/assets/moonbit-CaWjb8XO.js +0 -1
- package/dist/ui/assets/move-B1IS1UjX.js +0 -1
- package/dist/ui/assets/narrat-_X_XdTYD.js +0 -1
- package/dist/ui/assets/nextflow-Bbiyy34d.js +0 -1
- package/dist/ui/assets/nextflow-groovy-Dc_ddanL.js +0 -1
- package/dist/ui/assets/nginx-CzJafxLa.js +0 -1
- package/dist/ui/assets/night-owl-DhmEMT88.js +0 -1
- package/dist/ui/assets/night-owl-light-eJ-hLW7d.js +0 -1
- package/dist/ui/assets/nim-DABKtHtM.js +0 -1
- package/dist/ui/assets/nix-IvuFDN5E.js +0 -1
- package/dist/ui/assets/nord-Cb4Vim4T.js +0 -1
- package/dist/ui/assets/nushell-DcLAeLz5.js +0 -1
- package/dist/ui/assets/objective-c-D1A_Heim.js +0 -1
- package/dist/ui/assets/objective-cpp-BsSzOQcm.js +0 -1
- package/dist/ui/assets/ocaml-O90oeIOV.js +0 -1
- package/dist/ui/assets/odin-B1RWQWA5.js +0 -1
- package/dist/ui/assets/one-dark-pro-CLwyXe_n.js +0 -1
- package/dist/ui/assets/one-light-D7Lr4KcI.js +0 -1
- package/dist/ui/assets/openscad-BUDT5pXO.js +0 -1
- package/dist/ui/assets/pascal-4ZHwLPI5.js +0 -1
- package/dist/ui/assets/perl-CBh7L3lZ.js +0 -1
- package/dist/ui/assets/php-DFbI9q5p.js +0 -1
- package/dist/ui/assets/pierre-dark-sU4Zdns8.js +0 -1
- package/dist/ui/assets/pierre-dark-soft-HXgun5vs.js +0 -1
- package/dist/ui/assets/pierre-light-DWBk51d8.js +0 -1
- package/dist/ui/assets/pierre-light-soft-CodEzPxf.js +0 -1
- package/dist/ui/assets/pkl-ot-7Btpt.js +0 -1
- package/dist/ui/assets/plastic-DQwYfKfQ.js +0 -1
- package/dist/ui/assets/plsql-DGHpHOYJ.js +0 -1
- package/dist/ui/assets/po-BiJDBrnU.js +0 -1
- package/dist/ui/assets/poimandres-DRFjx7u4.js +0 -1
- package/dist/ui/assets/polar-C7UOKdEL.js +0 -1
- package/dist/ui/assets/postcss-BXeXVLqQ.js +0 -1
- package/dist/ui/assets/powerquery-DNMTfnFr.js +0 -1
- package/dist/ui/assets/powershell-DshXNtvi.js +0 -1
- package/dist/ui/assets/prisma-BsRQq5mF.js +0 -1
- package/dist/ui/assets/prolog-iXnhIJG7.js +0 -1
- package/dist/ui/assets/proto-DB4EqR-F.js +0 -1
- package/dist/ui/assets/pug-C_PxC7gO.js +0 -1
- package/dist/ui/assets/puppet-CDv2pdJW.js +0 -1
- package/dist/ui/assets/purescript-9MfHhQsQ.js +0 -1
- package/dist/ui/assets/python-gzcpVVnB.js +0 -1
- package/dist/ui/assets/qml-DtngoFFK.js +0 -1
- package/dist/ui/assets/qmldir-DCQb3MpD.js +0 -1
- package/dist/ui/assets/qss-Fe1Jh2GI.js +0 -1
- package/dist/ui/assets/r-DohkcevW.js +0 -1
- package/dist/ui/assets/racket-DcIDlBhZ.js +0 -1
- package/dist/ui/assets/raku-B3gFvitq.js +0 -1
- package/dist/ui/assets/razor-C4t8J0_E.js +0 -1
- package/dist/ui/assets/red-CJ3rzSJv.js +0 -1
- package/dist/ui/assets/reg-CRGYupPL.js +0 -1
- package/dist/ui/assets/regexp-OGbTbjgY.js +0 -1
- package/dist/ui/assets/rel-BtDbiS_P.js +0 -1
- package/dist/ui/assets/review-payload-C5Qz60Kq.js +0 -1
- package/dist/ui/assets/riscv-Ckw8ddFX.js +0 -1
- package/dist/ui/assets/ron-VUp2lXgN.js +0 -1
- package/dist/ui/assets/rose-pine-BthvhNj6.js +0 -1
- package/dist/ui/assets/rose-pine-dawn-Dg85fqjY.js +0 -1
- package/dist/ui/assets/rose-pine-moon-hon4tzzS.js +0 -1
- package/dist/ui/assets/rosmsg-CAekHB0j.js +0 -1
- package/dist/ui/assets/rst-CfBbbcvC.js +0 -1
- package/dist/ui/assets/ruby-Bw0mpxHY.js +0 -1
- package/dist/ui/assets/rust-Cfkwpbl8.js +0 -1
- package/dist/ui/assets/sas-Cs3s9JOf.js +0 -1
- package/dist/ui/assets/sass-DXrisJhu.js +0 -1
- package/dist/ui/assets/scala-DKOlJaKm.js +0 -1
- package/dist/ui/assets/scheme-DQCgrYNe.js +0 -1
- package/dist/ui/assets/scss-sRxNJS54.js +0 -1
- package/dist/ui/assets/sdbl-bTVj8UrX.js +0 -1
- package/dist/ui/assets/shaderlab-TOUzSsQk.js +0 -1
- package/dist/ui/assets/shellscript-B0X6wh0R.js +0 -1
- package/dist/ui/assets/shellsession-D2FdOioR.js +0 -1
- package/dist/ui/assets/slack-dark-DnToyrRv.js +0 -1
- package/dist/ui/assets/slack-ochin-B2OO5cIa.js +0 -1
- package/dist/ui/assets/smalltalk-B16xEiuN.js +0 -1
- package/dist/ui/assets/snazzy-light-4G7pJPwS.js +0 -1
- package/dist/ui/assets/solarized-dark-DV17i1UV.js +0 -1
- package/dist/ui/assets/solarized-light-DSh2HLQt.js +0 -1
- package/dist/ui/assets/solidity-CKzVLygQ.js +0 -1
- package/dist/ui/assets/soy-s6g9OjJ1.js +0 -1
- package/dist/ui/assets/sparql-D_iOobhT.js +0 -1
- package/dist/ui/assets/splunk-BC2Px7Mm.js +0 -1
- package/dist/ui/assets/sql-DVTnh5V-.js +0 -1
- package/dist/ui/assets/ssh-config-BgfXC-Er.js +0 -1
- package/dist/ui/assets/stata-DFIDiXF9.js +0 -1
- package/dist/ui/assets/stylus-B6D30XZt.js +0 -1
- package/dist/ui/assets/surrealql-DVfrsLyg.js +0 -1
- package/dist/ui/assets/svelte-D5GsxESq.js +0 -1
- package/dist/ui/assets/swift-DonLKvLd.js +0 -1
- package/dist/ui/assets/synthwave-84-nFMaYfgc.js +0 -1
- package/dist/ui/assets/system-verilog-DJ5XKQeo.js +0 -1
- package/dist/ui/assets/systemd-BxMlprV5.js +0 -1
- package/dist/ui/assets/talonscript-CohzipZa.js +0 -1
- package/dist/ui/assets/tasl-DMoTqEGO.js +0 -1
- package/dist/ui/assets/tcl-CZd0xW_V.js +0 -1
- package/dist/ui/assets/templ-DI-Zyqzd.js +0 -1
- package/dist/ui/assets/terraform-DswuEJGm.js +0 -1
- package/dist/ui/assets/tex-WHHmlbJI.js +0 -1
- package/dist/ui/assets/tokyo-night-oM2G3aXe.js +0 -1
- package/dist/ui/assets/toml-CcmNWLt0.js +0 -1
- package/dist/ui/assets/ts-tags-Dwpo2RFJ.js +0 -1
- package/dist/ui/assets/tsv-sltzmVWM.js +0 -1
- package/dist/ui/assets/tsx-ph_idaAW.js +0 -1
- package/dist/ui/assets/turtle-ByJddavk.js +0 -1
- package/dist/ui/assets/twig-YO17jFjR.js +0 -1
- package/dist/ui/assets/typescript-CbAxjhkl.js +0 -1
- package/dist/ui/assets/typespec-B88KGewJ.js +0 -1
- package/dist/ui/assets/typst-DI99ib-x.js +0 -1
- package/dist/ui/assets/useFileDiffInstance-B2DLX3Vg.js +0 -244
- package/dist/ui/assets/v-DETTlOr0.js +0 -1
- package/dist/ui/assets/vala-zf12oZj6.js +0 -1
- package/dist/ui/assets/vb-Djn5o6TS.js +0 -1
- package/dist/ui/assets/verilog-CiiDBU1e.js +0 -1
- package/dist/ui/assets/vesper-D5bVUKB1.js +0 -1
- package/dist/ui/assets/vhdl-BroJfC0k.js +0 -1
- package/dist/ui/assets/viml-DvXPmvsu.js +0 -1
- package/dist/ui/assets/vitesse-black-fwtXNY1n.js +0 -1
- package/dist/ui/assets/vitesse-dark-BZCL-v6S.js +0 -1
- package/dist/ui/assets/vitesse-light-VbXTXTou.js +0 -1
- package/dist/ui/assets/vue-CraHL-Ps.js +0 -1
- package/dist/ui/assets/vue-html-e9tyZNtS.js +0 -1
- package/dist/ui/assets/vue-vine-CofmxaUb.js +0 -1
- package/dist/ui/assets/vyper-CgoNMtux.js +0 -1
- package/dist/ui/assets/wasm-BnjxR4X6.js +0 -1
- package/dist/ui/assets/wasm-ByWQv1Qj.js +0 -1
- package/dist/ui/assets/wenyan-C8pVoKbM.js +0 -1
- package/dist/ui/assets/wgsl-BsKzXJz4.js +0 -1
- package/dist/ui/assets/wikitext-ClFFjSW2.js +0 -1
- package/dist/ui/assets/wit-DdvCle-K.js +0 -1
- package/dist/ui/assets/wolfram-DLL8P-h_.js +0 -1
- package/dist/ui/assets/workspace-app-8--oTbCd.css +0 -1
- package/dist/ui/assets/xml-BWSdPZHm.js +0 -1
- package/dist/ui/assets/xsl-BtCt7Hx8.js +0 -1
- package/dist/ui/assets/yaml-BZiTw7cQ.js +0 -1
- package/dist/ui/assets/zenscript-BnlCZFoB.js +0 -1
- package/dist/ui/assets/zig-CMLA9XwU.js +0 -1
- package/docs/assets/auvrynt-logo-light.png +0 -0
- package/docs/assets/auvrynt-screenshot.png +0 -0
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { copyFile, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
5
|
+
import { dirname, join, relative } from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { discoverLocalIntegrations, processDetected } from "../integration-discovery.js";
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const RESULT_PREFIX = "AUVRYNT_RESULT:";
|
|
10
|
+
const ASEPRITE_TIMEOUT_MS = 60_000;
|
|
11
|
+
const MAX_PIXEL_EDITS = 8_192;
|
|
12
|
+
const MAX_SHAPES = 1_024;
|
|
13
|
+
const ASEPRITE_PALETTE_PRESETS = {
|
|
14
|
+
gameboy: ["#0F380F", "#306230", "#8BAC0F", "#9BBC0F"],
|
|
15
|
+
cga: ["#000000", "#55FFFF", "#FF55FF", "#FFFFFF"],
|
|
16
|
+
c64: ["#000000", "#FFFFFF", "#883932", "#67B6BD", "#8B3F96", "#55A049", "#40318D", "#BFCE72", "#8B5429", "#574200", "#B86962", "#505050", "#787878", "#94E089", "#7869C4", "#ABABAB"],
|
|
17
|
+
grayscale_4: ["#000000", "#555555", "#AAAAAA", "#FFFFFF"],
|
|
18
|
+
monochrome: ["#000000", "#FFFFFF"],
|
|
19
|
+
dawnbringer16: ["#140C1C", "#442434", "#30346D", "#4E4A4E", "#854C30", "#346524", "#D04648", "#757161", "#597DCE", "#D27D2C", "#8595A1", "#6DAA2C", "#D2AA99", "#6DC2CA", "#DAD45E", "#DEEED6"],
|
|
20
|
+
pico8: ["#000000", "#1D2B53", "#7E2553", "#008751", "#AB5236", "#5F574F", "#C2C3C7", "#FFF1E8", "#FF004D", "#FFA300", "#FFEC27", "#00E436", "#29ADFF", "#83769C", "#FF77A8", "#FFCCAA"],
|
|
21
|
+
};
|
|
22
|
+
export function getAsepritePalettePreset(name) {
|
|
23
|
+
return [...ASEPRITE_PALETTE_PRESETS[name]];
|
|
24
|
+
}
|
|
25
|
+
export function textResponse(value) {
|
|
26
|
+
return {
|
|
27
|
+
content: [{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: typeof value === "string" ? value : JSON.stringify(value, null, 2),
|
|
30
|
+
}],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function errorResponse(message) {
|
|
34
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
35
|
+
}
|
|
36
|
+
// Third-party Aseprite extensions (e.g. pixellab and other community scripts)
|
|
37
|
+
// print their own Lua tracebacks straight to stdout/stderr when they error,
|
|
38
|
+
// and those extensions run inside the same headless Aseprite process we
|
|
39
|
+
// shell out to. Left unfiltered, that noise bleeds into our own tool output
|
|
40
|
+
// on every call that captures stdout/stderr. Strip any line that looks like
|
|
41
|
+
// a third-party extension's Lua traceback before returning CLI output.
|
|
42
|
+
const EXTENSION_TRACEBACK_LINE = /extensions[\\/].*\.lua:\d+/i;
|
|
43
|
+
export function sanitizeCliOutput(output) {
|
|
44
|
+
if (!output)
|
|
45
|
+
return output;
|
|
46
|
+
return output
|
|
47
|
+
.split(/\r?\n/)
|
|
48
|
+
.filter((line) => !EXTENSION_TRACEBACK_LINE.test(line))
|
|
49
|
+
.join("\n")
|
|
50
|
+
.trim();
|
|
51
|
+
}
|
|
52
|
+
function cleanPath(value) {
|
|
53
|
+
const cleaned = value?.trim().replace(/^["']|["']$/g, "").trim();
|
|
54
|
+
return cleaned || undefined;
|
|
55
|
+
}
|
|
56
|
+
function sourceExecutableCandidates(sourcePath) {
|
|
57
|
+
if (!sourcePath)
|
|
58
|
+
return [];
|
|
59
|
+
return process.platform === "win32"
|
|
60
|
+
? [
|
|
61
|
+
join(sourcePath, "build", "bin", "aseprite.exe"),
|
|
62
|
+
join(sourcePath, "bin", "aseprite.exe"),
|
|
63
|
+
join(sourcePath, "aseprite.exe"),
|
|
64
|
+
]
|
|
65
|
+
: [
|
|
66
|
+
join(sourcePath, "build", "bin", "aseprite"),
|
|
67
|
+
join(sourcePath, "bin", "aseprite"),
|
|
68
|
+
join(sourcePath, "aseprite"),
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
async function findOnPath() {
|
|
72
|
+
try {
|
|
73
|
+
const command = process.platform === "win32" ? "where.exe" : "which";
|
|
74
|
+
const { stdout } = await execFileAsync(command, ["aseprite"], {
|
|
75
|
+
timeout: 5_000,
|
|
76
|
+
windowsHide: true,
|
|
77
|
+
});
|
|
78
|
+
return stdout.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export async function resolveAsepriteExecutable(config) {
|
|
85
|
+
const direct = cleanPath(config.executables.aseprite ?? process.env.AUVRYNT_ASEPRITE_PATH);
|
|
86
|
+
if (direct && existsSync(direct))
|
|
87
|
+
return direct;
|
|
88
|
+
const sourcePath = cleanPath(config.executables.asepriteSource ?? process.env.AUVRYNT_ASEPRITE_SOURCE_PATH);
|
|
89
|
+
for (const candidate of sourceExecutableCandidates(sourcePath)) {
|
|
90
|
+
if (existsSync(candidate))
|
|
91
|
+
return candidate;
|
|
92
|
+
}
|
|
93
|
+
const discovered = await findOnPath();
|
|
94
|
+
if (discovered && existsSync(discovered))
|
|
95
|
+
return discovered;
|
|
96
|
+
throw new Error("Aseprite executable was not found. Configure executables.aseprite, "
|
|
97
|
+
+ "executables.asepriteSource, AUVRYNT_ASEPRITE_PATH, or AUVRYNT_ASEPRITE_SOURCE_PATH.");
|
|
98
|
+
}
|
|
99
|
+
export function luaString(value) {
|
|
100
|
+
return `"${value
|
|
101
|
+
.replace(/\\/g, "\\\\")
|
|
102
|
+
.replace(/"/g, "\\\"")
|
|
103
|
+
.replace(/\r/g, "\\r")
|
|
104
|
+
.replace(/\n/g, "\\n")
|
|
105
|
+
.replace(/\t/g, "\\t")}"`;
|
|
106
|
+
}
|
|
107
|
+
export function parseColor(value) {
|
|
108
|
+
const normalized = value.trim().replace(/^#/, "");
|
|
109
|
+
if (!/^[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/.test(normalized)) {
|
|
110
|
+
throw new Error(`Invalid color ${value}. Use #RRGGBB or #RRGGBBAA.`);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
r: Number.parseInt(normalized.slice(0, 2), 16),
|
|
114
|
+
g: Number.parseInt(normalized.slice(2, 4), 16),
|
|
115
|
+
b: Number.parseInt(normalized.slice(4, 6), 16),
|
|
116
|
+
a: normalized.length === 8 ? Number.parseInt(normalized.slice(6, 8), 16) : 255,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function luaColor(value) {
|
|
120
|
+
const color = parseColor(value);
|
|
121
|
+
return `{r=${color.r},g=${color.g},b=${color.b},a=${color.a}}`;
|
|
122
|
+
}
|
|
123
|
+
export function assertInteger(value, label) {
|
|
124
|
+
if (!Number.isInteger(value))
|
|
125
|
+
throw new Error(`${label} must be an integer.`);
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
export function assertPositiveInteger(value, label) {
|
|
129
|
+
assertInteger(value, label);
|
|
130
|
+
if (value < 1)
|
|
131
|
+
throw new Error(`${label} must be at least 1.`);
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
export function luaCommonPrelude() {
|
|
135
|
+
return `
|
|
136
|
+
local function fail(message)
|
|
137
|
+
error(message, 0)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
local function color_mode_name(mode)
|
|
141
|
+
if mode == ColorMode.RGB then return "rgb" end
|
|
142
|
+
if mode == ColorMode.GRAY then return "grayscale" end
|
|
143
|
+
if mode == ColorMode.INDEXED then return "indexed" end
|
|
144
|
+
return tostring(mode)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
local function ani_dir_name(direction)
|
|
148
|
+
if direction == AniDir.REVERSE then return "reverse" end
|
|
149
|
+
if direction == AniDir.PING_PONG then return "ping_pong" end
|
|
150
|
+
return "forward"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
local function find_layer(layers, name)
|
|
154
|
+
for _, layer in ipairs(layers) do
|
|
155
|
+
if layer.name == name then return layer end
|
|
156
|
+
if layer.isGroup then
|
|
157
|
+
local nested = find_layer(layer.layers, name)
|
|
158
|
+
if nested then return nested end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
return nil
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
local function color_pixel(sprite, color)
|
|
165
|
+
if sprite.colorMode == ColorMode.RGB then
|
|
166
|
+
return app.pixelColor.rgba(color.r, color.g, color.b, color.a)
|
|
167
|
+
elseif sprite.colorMode == ColorMode.GRAY then
|
|
168
|
+
local gray = math.floor((color.r * 299 + color.g * 587 + color.b * 114) / 1000)
|
|
169
|
+
return app.pixelColor.graya(gray, color.a)
|
|
170
|
+
else
|
|
171
|
+
local palette = sprite.palettes[1]
|
|
172
|
+
local best_index = 0
|
|
173
|
+
local best_distance = math.huge
|
|
174
|
+
for index = 0, #palette - 1 do
|
|
175
|
+
local candidate = palette:getColor(index)
|
|
176
|
+
local dr = candidate.red - color.r
|
|
177
|
+
local dg = candidate.green - color.g
|
|
178
|
+
local db = candidate.blue - color.b
|
|
179
|
+
local da = candidate.alpha - color.a
|
|
180
|
+
local distance = dr*dr + dg*dg + db*db + da*da
|
|
181
|
+
if distance < best_distance then
|
|
182
|
+
best_distance = distance
|
|
183
|
+
best_index = index
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
return best_index
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
local function pixel_rgba(sprite, pixel)
|
|
191
|
+
if sprite.colorMode == ColorMode.RGB then
|
|
192
|
+
return {
|
|
193
|
+
r = app.pixelColor.rgbaR(pixel),
|
|
194
|
+
g = app.pixelColor.rgbaG(pixel),
|
|
195
|
+
b = app.pixelColor.rgbaB(pixel),
|
|
196
|
+
a = app.pixelColor.rgbaA(pixel),
|
|
197
|
+
index = nil,
|
|
198
|
+
}
|
|
199
|
+
elseif sprite.colorMode == ColorMode.GRAY then
|
|
200
|
+
local gray = app.pixelColor.grayaV(pixel)
|
|
201
|
+
return {
|
|
202
|
+
r = gray,
|
|
203
|
+
g = gray,
|
|
204
|
+
b = gray,
|
|
205
|
+
a = app.pixelColor.grayaA(pixel),
|
|
206
|
+
index = nil,
|
|
207
|
+
}
|
|
208
|
+
else
|
|
209
|
+
local palette = sprite.palettes[1]
|
|
210
|
+
local color = palette:getColor(pixel)
|
|
211
|
+
return {
|
|
212
|
+
r = color.red,
|
|
213
|
+
g = color.green,
|
|
214
|
+
b = color.blue,
|
|
215
|
+
a = pixel == sprite.transparentColor and 0 or color.alpha,
|
|
216
|
+
index = pixel,
|
|
217
|
+
}
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
local function rgba_hex(color)
|
|
222
|
+
return string.format("#%02X%02X%02X%02X", color.r, color.g, color.b, color.a)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
local function canvas_pixel(sprite, layer, frame_number, x, y)
|
|
226
|
+
local cel = layer:cel(frame_number)
|
|
227
|
+
if cel == nil then return pixel_rgba(sprite, sprite.colorMode == ColorMode.INDEXED and sprite.transparentColor or 0) end
|
|
228
|
+
local local_x = x - cel.position.x
|
|
229
|
+
local local_y = y - cel.position.y
|
|
230
|
+
if local_x < 0 or local_y < 0 or local_x >= cel.image.width or local_y >= cel.image.height then
|
|
231
|
+
return pixel_rgba(sprite, sprite.colorMode == ColorMode.INDEXED and sprite.transparentColor or 0)
|
|
232
|
+
end
|
|
233
|
+
return pixel_rgba(sprite, cel.image:getPixel(local_x, local_y))
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
local function ensure_canvas_cel(sprite, layer, frame_number)
|
|
237
|
+
local cel = layer:cel(frame_number)
|
|
238
|
+
if cel == nil then
|
|
239
|
+
local image = Image(sprite.spec)
|
|
240
|
+
image:clear()
|
|
241
|
+
cel = sprite:newCel(layer, frame_number, image, Point(0, 0))
|
|
242
|
+
elseif cel.position.x ~= 0 or cel.position.y ~= 0 or
|
|
243
|
+
cel.image.width ~= sprite.width or cel.image.height ~= sprite.height then
|
|
244
|
+
local image = Image(sprite.spec)
|
|
245
|
+
image:clear()
|
|
246
|
+
image:drawImage(cel.image, cel.position)
|
|
247
|
+
cel.image = image
|
|
248
|
+
cel.position = Point(0, 0)
|
|
249
|
+
end
|
|
250
|
+
return cel
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
local function sprite_summary(sprite)
|
|
254
|
+
local layers = {}
|
|
255
|
+
local function append_layers(items, parent)
|
|
256
|
+
for _, layer in ipairs(items) do
|
|
257
|
+
table.insert(layers, {
|
|
258
|
+
name = layer.name,
|
|
259
|
+
parent = parent,
|
|
260
|
+
is_group = layer.isGroup,
|
|
261
|
+
is_visible = layer.isVisible,
|
|
262
|
+
is_editable = layer.isEditable,
|
|
263
|
+
opacity = layer.opacity,
|
|
264
|
+
blend_mode = tostring(layer.blendMode),
|
|
265
|
+
cel_count = #layer.cels,
|
|
266
|
+
})
|
|
267
|
+
if layer.isGroup then append_layers(layer.layers, layer.name) end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
append_layers(sprite.layers, nil)
|
|
271
|
+
|
|
272
|
+
local frames = {}
|
|
273
|
+
for _, frame in ipairs(sprite.frames) do
|
|
274
|
+
table.insert(frames, {
|
|
275
|
+
frame = frame.frameNumber,
|
|
276
|
+
duration_ms = math.floor(frame.duration * 1000 + 0.5),
|
|
277
|
+
})
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
local tags = {}
|
|
281
|
+
for _, tag in ipairs(sprite.tags) do
|
|
282
|
+
table.insert(tags, {
|
|
283
|
+
name = tag.name,
|
|
284
|
+
from = tag.fromFrame.frameNumber,
|
|
285
|
+
to = tag.toFrame.frameNumber,
|
|
286
|
+
direction = ani_dir_name(tag.aniDir),
|
|
287
|
+
repeats = tag.repeats,
|
|
288
|
+
})
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
local slices = {}
|
|
292
|
+
for _, slice in ipairs(sprite.slices) do
|
|
293
|
+
table.insert(slices, { name = slice.name })
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
local palette_size = 0
|
|
297
|
+
if #sprite.palettes > 0 then palette_size = #sprite.palettes[1] end
|
|
298
|
+
return {
|
|
299
|
+
filename = sprite.filename,
|
|
300
|
+
width = sprite.width,
|
|
301
|
+
height = sprite.height,
|
|
302
|
+
color_mode = color_mode_name(sprite.colorMode),
|
|
303
|
+
frame_count = #sprite.frames,
|
|
304
|
+
layer_count = #layers,
|
|
305
|
+
palette_size = palette_size,
|
|
306
|
+
layers = layers,
|
|
307
|
+
frames = frames,
|
|
308
|
+
tags = tags,
|
|
309
|
+
slices = slices,
|
|
310
|
+
}
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
local function emit_result(value)
|
|
314
|
+
print(${luaString(RESULT_PREFIX)} .. json.encode(value))
|
|
315
|
+
end
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
export async function runLua(config, registry, workspaceId, scriptBody) {
|
|
319
|
+
const workspace = registry.getWorkspace(workspaceId);
|
|
320
|
+
const executable = await resolveAsepriteExecutable(config);
|
|
321
|
+
const unique = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
322
|
+
const scriptPath = registry.resolveArtifactPath(workspace, `scripts/${unique}.lua`, "aseprite");
|
|
323
|
+
await mkdir(dirname(scriptPath), { recursive: true });
|
|
324
|
+
await writeFile(scriptPath, `${luaCommonPrelude()}\n${scriptBody}\n`, "utf8");
|
|
325
|
+
try {
|
|
326
|
+
const { stdout, stderr } = await execFileAsync(executable, ["-b", "--script", scriptPath], {
|
|
327
|
+
cwd: workspace.root,
|
|
328
|
+
timeout: ASEPRITE_TIMEOUT_MS,
|
|
329
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
330
|
+
windowsHide: true,
|
|
331
|
+
});
|
|
332
|
+
const line = stdout.split(/\r?\n/).reverse().find((entry) => entry.startsWith(RESULT_PREFIX));
|
|
333
|
+
if (!line) {
|
|
334
|
+
const details = sanitizeCliOutput([stdout.trim(), stderr.trim()].filter(Boolean).join("\n")).slice(-4_000);
|
|
335
|
+
throw new Error(`Aseprite script returned no structured result.${details ? `\n${details}` : ""}`);
|
|
336
|
+
}
|
|
337
|
+
return JSON.parse(line.slice(RESULT_PREFIX.length));
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
const stdout = typeof error?.stdout === "string" ? error.stdout.trim() : "";
|
|
341
|
+
const stderr = typeof error?.stderr === "string" ? error.stderr.trim() : "";
|
|
342
|
+
const detail = sanitizeCliOutput([stdout, stderr, error?.message].filter(Boolean).join("\n")).slice(-6_000);
|
|
343
|
+
throw new Error(`Aseprite operation failed: ${detail || "unknown error"}`);
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
await unlink(scriptPath).catch(() => undefined);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
export function workspacePath(registry, workspaceId, inputPath) {
|
|
350
|
+
const workspace = registry.getWorkspace(workspaceId);
|
|
351
|
+
const absolute = registry.resolvePath(workspace, inputPath);
|
|
352
|
+
return { absolute, relative: relative(workspace.root, absolute).replace(/\\/g, "/") };
|
|
353
|
+
}
|
|
354
|
+
function safeCheckpointSegment(value) {
|
|
355
|
+
const normalized = value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
356
|
+
return normalized.slice(0, 120) || "sprite";
|
|
357
|
+
}
|
|
358
|
+
export async function prepareAsepriteSourceMutation(registry, workspaceId, filePath, expectedVersion, checkpoint, label) {
|
|
359
|
+
const workspace = registry.getWorkspace(workspaceId);
|
|
360
|
+
const file = workspacePath(registry, workspaceId, filePath);
|
|
361
|
+
const bytes = await readFile(file.absolute);
|
|
362
|
+
const version = createHash("sha256").update(bytes).digest("hex");
|
|
363
|
+
if (expectedVersion && expectedVersion !== version) {
|
|
364
|
+
throw new Error(`Aseprite file changed since it was inspected. Expected ${expectedVersion}, current ${version}.`);
|
|
365
|
+
}
|
|
366
|
+
if (checkpoint !== false) {
|
|
367
|
+
const root = registry.resolveArtifactPath(workspace, `checkpoints/${safeCheckpointSegment(file.relative)}`, "aseprite");
|
|
368
|
+
await mkdir(root, { recursive: true });
|
|
369
|
+
const id = `${new Date().toISOString().replace(/[:.]/g, "-")}-${version.slice(0, 12)}`;
|
|
370
|
+
const extension = file.absolute.match(/(\.[^./\\]+)$/)?.[1] ?? ".aseprite";
|
|
371
|
+
const copyName = `${id}${extension}`;
|
|
372
|
+
await copyFile(file.absolute, join(root, copyName));
|
|
373
|
+
await writeFile(join(root, `${id}.json`), JSON.stringify({
|
|
374
|
+
id,
|
|
375
|
+
file: file.relative,
|
|
376
|
+
version,
|
|
377
|
+
label,
|
|
378
|
+
createdAt: new Date().toISOString(),
|
|
379
|
+
copyFile: copyName,
|
|
380
|
+
}, null, 2), "utf8");
|
|
381
|
+
}
|
|
382
|
+
return version;
|
|
383
|
+
}
|
|
384
|
+
export function openSpriteScript(filePath) {
|
|
385
|
+
return `
|
|
386
|
+
local sprite = app.open(${luaString(filePath)})
|
|
387
|
+
if sprite == nil then fail("Unable to open sprite") end
|
|
388
|
+
app.activeSprite = sprite
|
|
389
|
+
`;
|
|
390
|
+
}
|
|
391
|
+
export function selectLayerAndFrameScript(layerName, frame) {
|
|
392
|
+
const frameNumber = frame === undefined ? 1 : assertPositiveInteger(frame, "frame");
|
|
393
|
+
return `
|
|
394
|
+
local layer = ${layerName ? `find_layer(sprite.layers, ${luaString(layerName)})` : "sprite.layers[1]"}
|
|
395
|
+
if layer == nil then fail("Layer not found") end
|
|
396
|
+
if layer.isGroup then fail("A group layer cannot contain pixels") end
|
|
397
|
+
if ${frameNumber} > #sprite.frames then fail("Frame is outside the sprite") end
|
|
398
|
+
local frame_number = ${frameNumber}
|
|
399
|
+
`;
|
|
400
|
+
}
|
|
401
|
+
export async function asepriteDetect(config, registry, input) {
|
|
402
|
+
try {
|
|
403
|
+
registry.getWorkspace(input.workspaceId);
|
|
404
|
+
const executable = await resolveAsepriteExecutable(config);
|
|
405
|
+
const { stdout, stderr } = await execFileAsync(executable, ["--version"], {
|
|
406
|
+
timeout: 10_000,
|
|
407
|
+
windowsHide: true,
|
|
408
|
+
});
|
|
409
|
+
const discovery = await discoverLocalIntegrations({ forceRefresh: true });
|
|
410
|
+
return textResponse({
|
|
411
|
+
detected: true,
|
|
412
|
+
executable,
|
|
413
|
+
sourcePath: cleanPath(config.executables.asepriteSource ?? process.env.AUVRYNT_ASEPRITE_SOURCE_PATH),
|
|
414
|
+
version: (stdout || stderr).split(/\r?\n/).map((line) => line.trim()).find((line) => /^Aseprite\s/i.test(line)) ?? (stdout || stderr).trim(),
|
|
415
|
+
running: processDetected(discovery, "aseprite"),
|
|
416
|
+
integration: "native-cli",
|
|
417
|
+
pluginRequired: false,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
return errorResponse(error instanceof Error ? error.message : String(error));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
export async function asepriteInspectFile(config, registry, input) {
|
|
425
|
+
try {
|
|
426
|
+
const file = workspacePath(registry, input.workspaceId, input.filePath);
|
|
427
|
+
const result = await runLua(config, registry, input.workspaceId, `
|
|
428
|
+
${openSpriteScript(file.absolute)}
|
|
429
|
+
local result = sprite_summary(sprite)
|
|
430
|
+
result.filename = ${luaString(file.relative)}
|
|
431
|
+
emit_result(result)
|
|
432
|
+
sprite:close()
|
|
433
|
+
`);
|
|
434
|
+
return textResponse(result);
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
return errorResponse(error instanceof Error ? error.message : String(error));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
export async function asepriteCreateSprite(config, registry, input) {
|
|
441
|
+
try {
|
|
442
|
+
const output = workspacePath(registry, input.workspaceId, input.outputPath);
|
|
443
|
+
await mkdir(dirname(output.absolute), { recursive: true });
|
|
444
|
+
const width = assertPositiveInteger(input.width, "width");
|
|
445
|
+
const height = assertPositiveInteger(input.height, "height");
|
|
446
|
+
if (width > 8192 || height > 8192)
|
|
447
|
+
throw new Error("Sprite dimensions cannot exceed 8192x8192.");
|
|
448
|
+
const mode = input.colorMode ?? "rgb";
|
|
449
|
+
const modeLua = mode === "indexed" ? "ColorMode.INDEXED" : mode === "grayscale" ? "ColorMode.GRAY" : "ColorMode.RGB";
|
|
450
|
+
const background = input.backgroundColor ? luaColor(input.backgroundColor) : undefined;
|
|
451
|
+
const result = await runLua(config, registry, input.workspaceId, `
|
|
452
|
+
local sprite = Sprite(${width}, ${height}, ${modeLua})
|
|
453
|
+
sprite.layers[1].name = ${luaString(input.layerName?.trim() || "Layer 1")}
|
|
454
|
+
${background ? `sprite.cels[1].image:clear(color_pixel(sprite, ${background}))` : "sprite.cels[1].image:clear()"}
|
|
455
|
+
if not sprite:saveAs(${luaString(output.absolute)}) then fail("Unable to save sprite") end
|
|
456
|
+
local result = sprite_summary(sprite)
|
|
457
|
+
result.filename = ${luaString(output.relative)}
|
|
458
|
+
emit_result(result)
|
|
459
|
+
sprite:close()
|
|
460
|
+
`);
|
|
461
|
+
return textResponse(result);
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
return errorResponse(error instanceof Error ? error.message : String(error));
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
export async function asepriteSetPixels(config, registry, input) {
|
|
468
|
+
try {
|
|
469
|
+
await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, "set-pixels");
|
|
470
|
+
if (!Array.isArray(input.pixels) || input.pixels.length === 0)
|
|
471
|
+
throw new Error("pixels must not be empty.");
|
|
472
|
+
if (input.pixels.length > MAX_PIXEL_EDITS)
|
|
473
|
+
throw new Error(`A maximum of ${MAX_PIXEL_EDITS} pixels can be changed per call.`);
|
|
474
|
+
const pixels = input.pixels.map((pixel) => ({
|
|
475
|
+
x: assertInteger(pixel.x, "pixel.x"),
|
|
476
|
+
y: assertInteger(pixel.y, "pixel.y"),
|
|
477
|
+
color: parseColor(pixel.color),
|
|
478
|
+
}));
|
|
479
|
+
const file = workspacePath(registry, input.workspaceId, input.filePath);
|
|
480
|
+
const pixelsLua = `{${pixels.map((pixel) => `{x=${pixel.x},y=${pixel.y},color={r=${pixel.color.r},g=${pixel.color.g},b=${pixel.color.b},a=${pixel.color.a}}}`).join(",")}}`;
|
|
481
|
+
const result = await runLua(config, registry, input.workspaceId, `
|
|
482
|
+
${openSpriteScript(file.absolute)}
|
|
483
|
+
${selectLayerAndFrameScript(input.layer, input.frame)}
|
|
484
|
+
local cel = ensure_canvas_cel(sprite, layer, frame_number)
|
|
485
|
+
local image = cel.image
|
|
486
|
+
local pixels = ${pixelsLua}
|
|
487
|
+
for _, pixel in ipairs(pixels) do
|
|
488
|
+
if pixel.x < 0 or pixel.x >= sprite.width or pixel.y < 0 or pixel.y >= sprite.height then
|
|
489
|
+
fail("Pixel coordinate is outside the sprite")
|
|
490
|
+
end
|
|
491
|
+
image:drawPixel(pixel.x, pixel.y, color_pixel(sprite, pixel.color))
|
|
492
|
+
end
|
|
493
|
+
if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
|
|
494
|
+
emit_result({ file = ${luaString(file.relative)}, changed_pixels = #pixels, layer = layer.name, frame = frame_number })
|
|
495
|
+
sprite:close()
|
|
496
|
+
`);
|
|
497
|
+
return textResponse(result);
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
return errorResponse(error instanceof Error ? error.message : String(error));
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
export async function asepriteDrawShapes(config, registry, input) {
|
|
504
|
+
try {
|
|
505
|
+
await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, "draw-shapes");
|
|
506
|
+
if (!Array.isArray(input.shapes) || input.shapes.length === 0)
|
|
507
|
+
throw new Error("shapes must not be empty.");
|
|
508
|
+
if (input.shapes.length > MAX_SHAPES)
|
|
509
|
+
throw new Error(`A maximum of ${MAX_SHAPES} shapes can be drawn per call.`);
|
|
510
|
+
const shapes = input.shapes.map((shape) => {
|
|
511
|
+
const color = parseColor(shape.color);
|
|
512
|
+
if (shape.type === "point")
|
|
513
|
+
return { ...shape, x: assertInteger(shape.x, "point.x"), y: assertInteger(shape.y, "point.y"), color };
|
|
514
|
+
if (shape.type === "line")
|
|
515
|
+
return {
|
|
516
|
+
...shape,
|
|
517
|
+
x1: assertInteger(shape.x1, "line.x1"), y1: assertInteger(shape.y1, "line.y1"),
|
|
518
|
+
x2: assertInteger(shape.x2, "line.x2"), y2: assertInteger(shape.y2, "line.y2"), color,
|
|
519
|
+
};
|
|
520
|
+
return {
|
|
521
|
+
...shape,
|
|
522
|
+
x: assertInteger(shape.x, "rect.x"), y: assertInteger(shape.y, "rect.y"),
|
|
523
|
+
width: assertPositiveInteger(shape.width, "rect.width"), height: assertPositiveInteger(shape.height, "rect.height"), color,
|
|
524
|
+
};
|
|
525
|
+
});
|
|
526
|
+
const file = workspacePath(registry, input.workspaceId, input.filePath);
|
|
527
|
+
const shapesLua = `{${shapes.map((shape) => {
|
|
528
|
+
const color = `{r=${shape.color.r},g=${shape.color.g},b=${shape.color.b},a=${shape.color.a}}`;
|
|
529
|
+
if (shape.type === "point")
|
|
530
|
+
return `{type="point",x=${shape.x},y=${shape.y},color=${color}}`;
|
|
531
|
+
if (shape.type === "line")
|
|
532
|
+
return `{type="line",x1=${shape.x1},y1=${shape.y1},x2=${shape.x2},y2=${shape.y2},color=${color}}`;
|
|
533
|
+
return `{type="rect",x=${shape.x},y=${shape.y},width=${shape.width},height=${shape.height},fill=${shape.fill === true ? "true" : "false"},color=${color}}`;
|
|
534
|
+
}).join(",")}}`;
|
|
535
|
+
const result = await runLua(config, registry, input.workspaceId, `
|
|
536
|
+
${openSpriteScript(file.absolute)}
|
|
537
|
+
${selectLayerAndFrameScript(input.layer, input.frame)}
|
|
538
|
+
local cel = ensure_canvas_cel(sprite, layer, frame_number)
|
|
539
|
+
local image = cel.image
|
|
540
|
+
local shapes = ${shapesLua}
|
|
541
|
+
local function put(x, y, pixel)
|
|
542
|
+
if x >= 0 and x < sprite.width and y >= 0 and y < sprite.height then image:drawPixel(x, y, pixel) end
|
|
543
|
+
end
|
|
544
|
+
local function line(x0, y0, x1, y1, pixel)
|
|
545
|
+
local dx = math.abs(x1 - x0)
|
|
546
|
+
local sx = x0 < x1 and 1 or -1
|
|
547
|
+
local dy = -math.abs(y1 - y0)
|
|
548
|
+
local sy = y0 < y1 and 1 or -1
|
|
549
|
+
local err = dx + dy
|
|
550
|
+
while true do
|
|
551
|
+
put(x0, y0, pixel)
|
|
552
|
+
if x0 == x1 and y0 == y1 then break end
|
|
553
|
+
local e2 = 2 * err
|
|
554
|
+
if e2 >= dy then err = err + dy; x0 = x0 + sx end
|
|
555
|
+
if e2 <= dx then err = err + dx; y0 = y0 + sy end
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
for _, shape in ipairs(shapes) do
|
|
559
|
+
local pixel = color_pixel(sprite, shape.color)
|
|
560
|
+
if shape.type == "point" then
|
|
561
|
+
put(shape.x, shape.y, pixel)
|
|
562
|
+
elseif shape.type == "line" then
|
|
563
|
+
line(shape.x1, shape.y1, shape.x2, shape.y2, pixel)
|
|
564
|
+
elseif shape.fill then
|
|
565
|
+
for y = shape.y, shape.y + shape.height - 1 do
|
|
566
|
+
for x = shape.x, shape.x + shape.width - 1 do put(x, y, pixel) end
|
|
567
|
+
end
|
|
568
|
+
else
|
|
569
|
+
line(shape.x, shape.y, shape.x + shape.width - 1, shape.y, pixel)
|
|
570
|
+
line(shape.x, shape.y + shape.height - 1, shape.x + shape.width - 1, shape.y + shape.height - 1, pixel)
|
|
571
|
+
line(shape.x, shape.y, shape.x, shape.y + shape.height - 1, pixel)
|
|
572
|
+
line(shape.x + shape.width - 1, shape.y, shape.x + shape.width - 1, shape.y + shape.height - 1, pixel)
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
|
|
576
|
+
emit_result({ file = ${luaString(file.relative)}, shapes = #shapes, layer = layer.name, frame = frame_number })
|
|
577
|
+
sprite:close()
|
|
578
|
+
`);
|
|
579
|
+
return textResponse(result);
|
|
580
|
+
}
|
|
581
|
+
catch (error) {
|
|
582
|
+
return errorResponse(error instanceof Error ? error.message : String(error));
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
export async function asepriteManageLayers(config, registry, input) {
|
|
586
|
+
try {
|
|
587
|
+
await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, `manage-layer-${input.action}`);
|
|
588
|
+
const file = workspacePath(registry, input.workspaceId, input.filePath);
|
|
589
|
+
if (["rename", "delete", "set_visibility", "set_opacity"].includes(input.action) && !input.layer?.trim()) {
|
|
590
|
+
throw new Error("layer is required for this action.");
|
|
591
|
+
}
|
|
592
|
+
if (["add", "rename"].includes(input.action) && !input.name?.trim())
|
|
593
|
+
throw new Error("name is required for this action.");
|
|
594
|
+
if (input.opacity !== undefined && (!Number.isInteger(input.opacity) || input.opacity < 0 || input.opacity > 255)) {
|
|
595
|
+
throw new Error("opacity must be an integer from 0 to 255.");
|
|
596
|
+
}
|
|
597
|
+
const result = await runLua(config, registry, input.workspaceId, `
|
|
598
|
+
${openSpriteScript(file.absolute)}
|
|
599
|
+
local action = ${luaString(input.action)}
|
|
600
|
+
local layer = ${input.layer ? `find_layer(sprite.layers, ${luaString(input.layer)})` : "nil"}
|
|
601
|
+
if action == "add" then
|
|
602
|
+
layer = sprite:newLayer()
|
|
603
|
+
layer.name = ${luaString(input.name?.trim() || "Layer")}
|
|
604
|
+
elseif layer == nil then
|
|
605
|
+
fail("Layer not found")
|
|
606
|
+
elseif action == "rename" then
|
|
607
|
+
layer.name = ${luaString(input.name?.trim() || "Layer")}
|
|
608
|
+
elseif action == "delete" then
|
|
609
|
+
sprite:deleteLayer(layer)
|
|
610
|
+
elseif action == "set_visibility" then
|
|
611
|
+
layer.isVisible = ${input.visible === false ? "false" : "true"}
|
|
612
|
+
elseif action == "set_opacity" then
|
|
613
|
+
layer.opacity = ${input.opacity ?? 255}
|
|
614
|
+
else
|
|
615
|
+
fail("Unsupported layer action")
|
|
616
|
+
end
|
|
617
|
+
if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
|
|
618
|
+
emit_result(sprite_summary(sprite))
|
|
619
|
+
sprite:close()
|
|
620
|
+
`);
|
|
621
|
+
return textResponse(result);
|
|
622
|
+
}
|
|
623
|
+
catch (error) {
|
|
624
|
+
return errorResponse(error instanceof Error ? error.message : String(error));
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
export async function asepriteManageFrames(config, registry, input) {
|
|
628
|
+
try {
|
|
629
|
+
await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, `manage-frame-${input.action}`);
|
|
630
|
+
const file = workspacePath(registry, input.workspaceId, input.filePath);
|
|
631
|
+
const frame = input.frame === undefined ? 1 : assertPositiveInteger(input.frame, "frame");
|
|
632
|
+
if (input.durationMs !== undefined && (!Number.isInteger(input.durationMs) || input.durationMs < 1 || input.durationMs > 60_000)) {
|
|
633
|
+
throw new Error("durationMs must be an integer from 1 to 60000.");
|
|
634
|
+
}
|
|
635
|
+
const result = await runLua(config, registry, input.workspaceId, `
|
|
636
|
+
${openSpriteScript(file.absolute)}
|
|
637
|
+
local action = ${luaString(input.action)}
|
|
638
|
+
local frame_number = ${frame}
|
|
639
|
+
if action == "add_empty" then
|
|
640
|
+
sprite:newEmptyFrame(math.min(frame_number, #sprite.frames + 1))
|
|
641
|
+
elseif frame_number > #sprite.frames then
|
|
642
|
+
fail("Frame is outside the sprite")
|
|
643
|
+
elseif action == "duplicate" then
|
|
644
|
+
sprite:newFrame(frame_number)
|
|
645
|
+
elseif action == "delete" then
|
|
646
|
+
if #sprite.frames <= 1 then fail("Cannot delete the only frame") end
|
|
647
|
+
sprite:deleteFrame(frame_number)
|
|
648
|
+
elseif action == "set_duration" then
|
|
649
|
+
sprite.frames[frame_number].duration = ${(input.durationMs ?? 100) / 1000}
|
|
650
|
+
else
|
|
651
|
+
fail("Unsupported frame action")
|
|
652
|
+
end
|
|
653
|
+
if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
|
|
654
|
+
emit_result(sprite_summary(sprite))
|
|
655
|
+
sprite:close()
|
|
656
|
+
`);
|
|
657
|
+
return textResponse(result);
|
|
658
|
+
}
|
|
659
|
+
catch (error) {
|
|
660
|
+
return errorResponse(error instanceof Error ? error.message : String(error));
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
export async function asepriteManageTags(config, registry, input) {
|
|
664
|
+
try {
|
|
665
|
+
await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, `manage-tag-${input.action}`);
|
|
666
|
+
const file = workspacePath(registry, input.workspaceId, input.filePath);
|
|
667
|
+
if (!input.name.trim())
|
|
668
|
+
throw new Error("name is required.");
|
|
669
|
+
const from = input.from === undefined ? 1 : assertPositiveInteger(input.from, "from");
|
|
670
|
+
const to = input.to === undefined ? from : assertPositiveInteger(input.to, "to");
|
|
671
|
+
if (from > to)
|
|
672
|
+
throw new Error("from cannot be greater than to.");
|
|
673
|
+
if (input.action === "rename" && !input.newName?.trim())
|
|
674
|
+
throw new Error("newName is required when renaming a tag.");
|
|
675
|
+
const direction = input.direction ?? "forward";
|
|
676
|
+
const directionLua = direction === "reverse" ? "AniDir.REVERSE" : direction === "ping_pong" ? "AniDir.PING_PONG" : "AniDir.FORWARD";
|
|
677
|
+
const result = await runLua(config, registry, input.workspaceId, `
|
|
678
|
+
${openSpriteScript(file.absolute)}
|
|
679
|
+
local action = ${luaString(input.action)}
|
|
680
|
+
local name = ${luaString(input.name.trim())}
|
|
681
|
+
local tag = nil
|
|
682
|
+
for _, candidate in ipairs(sprite.tags) do if candidate.name == name then tag = candidate; break end end
|
|
683
|
+
if action == "add" then
|
|
684
|
+
if ${to} > #sprite.frames then fail("Tag range is outside the sprite") end
|
|
685
|
+
if tag ~= nil then fail("Tag already exists") end
|
|
686
|
+
tag = sprite:newTag(${from}, ${to})
|
|
687
|
+
tag.name = name
|
|
688
|
+
tag.aniDir = ${directionLua}
|
|
689
|
+
elseif tag == nil then
|
|
690
|
+
fail("Tag not found")
|
|
691
|
+
elseif action == "rename" then
|
|
692
|
+
tag.name = ${luaString(input.newName?.trim() || input.name.trim())}
|
|
693
|
+
elseif action == "delete" then
|
|
694
|
+
sprite:deleteTag(tag)
|
|
695
|
+
else
|
|
696
|
+
fail("Unsupported tag action")
|
|
697
|
+
end
|
|
698
|
+
if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
|
|
699
|
+
emit_result(sprite_summary(sprite))
|
|
700
|
+
sprite:close()
|
|
701
|
+
`);
|
|
702
|
+
return textResponse(result);
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
return errorResponse(error instanceof Error ? error.message : String(error));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
export async function asepriteSetPalette(config, registry, input) {
|
|
709
|
+
try {
|
|
710
|
+
await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, "set-palette");
|
|
711
|
+
if (!Array.isArray(input.colors) || input.colors.length < 1 || input.colors.length > 256) {
|
|
712
|
+
throw new Error("colors must contain between 1 and 256 entries.");
|
|
713
|
+
}
|
|
714
|
+
const colors = input.colors.map(parseColor);
|
|
715
|
+
const file = workspacePath(registry, input.workspaceId, input.filePath);
|
|
716
|
+
const colorsLua = `{${colors.map((color) => `{r=${color.r},g=${color.g},b=${color.b},a=${color.a}}`).join(",")}}`;
|
|
717
|
+
const result = await runLua(config, registry, input.workspaceId, `
|
|
718
|
+
${openSpriteScript(file.absolute)}
|
|
719
|
+
local colors = ${colorsLua}
|
|
720
|
+
local palette = Palette(#colors)
|
|
721
|
+
for index, color in ipairs(colors) do palette:setColor(index - 1, Color(color)) end
|
|
722
|
+
sprite:setPalette(palette)
|
|
723
|
+
if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
|
|
724
|
+
emit_result({ file = ${luaString(file.relative)}, palette_size = #colors })
|
|
725
|
+
sprite:close()
|
|
726
|
+
`);
|
|
727
|
+
return textResponse(result);
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
return errorResponse(error instanceof Error ? error.message : String(error));
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
export async function asepriteExportSpriteSheet(config, registry, input) {
|
|
734
|
+
try {
|
|
735
|
+
const workspace = registry.getWorkspace(input.workspaceId);
|
|
736
|
+
const executable = await resolveAsepriteExecutable(config);
|
|
737
|
+
const source = workspacePath(registry, input.workspaceId, input.filePath);
|
|
738
|
+
const sheet = workspacePath(registry, input.workspaceId, input.sheetPath);
|
|
739
|
+
const data = input.dataPath ? workspacePath(registry, input.workspaceId, input.dataPath) : undefined;
|
|
740
|
+
await mkdir(dirname(sheet.absolute), { recursive: true });
|
|
741
|
+
if (data)
|
|
742
|
+
await mkdir(dirname(data.absolute), { recursive: true });
|
|
743
|
+
const args = ["-b", source.absolute, "--sheet", sheet.absolute, "--sheet-type", input.sheetType ?? "horizontal"];
|
|
744
|
+
if (data)
|
|
745
|
+
args.push("--data", data.absolute, "--format", "json-array");
|
|
746
|
+
if (input.columns !== undefined)
|
|
747
|
+
args.push("--sheet-columns", String(assertPositiveInteger(input.columns, "columns")));
|
|
748
|
+
if (input.rows !== undefined)
|
|
749
|
+
args.push("--sheet-rows", String(assertPositiveInteger(input.rows, "rows")));
|
|
750
|
+
if (input.tag)
|
|
751
|
+
args.push("--tag", input.tag);
|
|
752
|
+
if (input.layer)
|
|
753
|
+
args.push("--layer", input.layer);
|
|
754
|
+
if (input.trim)
|
|
755
|
+
args.push("--trim");
|
|
756
|
+
if (input.mergeDuplicates)
|
|
757
|
+
args.push("--merge-duplicates");
|
|
758
|
+
if (input.borderPadding !== undefined)
|
|
759
|
+
args.push("--border-padding", String(Math.max(0, assertInteger(input.borderPadding, "borderPadding"))));
|
|
760
|
+
if (input.shapePadding !== undefined)
|
|
761
|
+
args.push("--shape-padding", String(Math.max(0, assertInteger(input.shapePadding, "shapePadding"))));
|
|
762
|
+
if (input.innerPadding !== undefined)
|
|
763
|
+
args.push("--inner-padding", String(Math.max(0, assertInteger(input.innerPadding, "innerPadding"))));
|
|
764
|
+
const { stdout, stderr } = await execFileAsync(executable, args, {
|
|
765
|
+
cwd: workspace.root,
|
|
766
|
+
timeout: ASEPRITE_TIMEOUT_MS,
|
|
767
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
768
|
+
windowsHide: true,
|
|
769
|
+
});
|
|
770
|
+
return textResponse({
|
|
771
|
+
success: true,
|
|
772
|
+
source: source.relative,
|
|
773
|
+
sheet: sheet.relative,
|
|
774
|
+
data: data?.relative,
|
|
775
|
+
output: sanitizeCliOutput([stdout.trim(), stderr.trim()].filter(Boolean).join("\n")).slice(-2_000),
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
catch (error) {
|
|
779
|
+
return errorResponse(`Aseprite export failed: ${sanitizeCliOutput(String(error?.stderr || error?.message || String(error)))}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
export async function asepriteConvertFile(config, registry, input) {
|
|
783
|
+
try {
|
|
784
|
+
const workspace = registry.getWorkspace(input.workspaceId);
|
|
785
|
+
const executable = await resolveAsepriteExecutable(config);
|
|
786
|
+
const source = workspacePath(registry, input.workspaceId, input.filePath);
|
|
787
|
+
const output = workspacePath(registry, input.workspaceId, input.outputPath);
|
|
788
|
+
await mkdir(dirname(output.absolute), { recursive: true });
|
|
789
|
+
const args = ["-b", source.absolute];
|
|
790
|
+
if (input.scale !== undefined) {
|
|
791
|
+
if (!Number.isFinite(input.scale) || input.scale <= 0 || input.scale > 64)
|
|
792
|
+
throw new Error("scale must be greater than 0 and no more than 64.");
|
|
793
|
+
args.push("--scale", String(input.scale));
|
|
794
|
+
}
|
|
795
|
+
if (input.colorMode)
|
|
796
|
+
args.push("--color-mode", input.colorMode === "grayscale" ? "grayscale" : input.colorMode);
|
|
797
|
+
if (input.palettePath)
|
|
798
|
+
args.push("--palette", workspacePath(registry, input.workspaceId, input.palettePath).absolute);
|
|
799
|
+
if (input.trim)
|
|
800
|
+
args.push("--trim");
|
|
801
|
+
if (input.crop) {
|
|
802
|
+
args.push("--crop", [
|
|
803
|
+
assertInteger(input.crop.x, "crop.x"),
|
|
804
|
+
assertInteger(input.crop.y, "crop.y"),
|
|
805
|
+
assertPositiveInteger(input.crop.width, "crop.width"),
|
|
806
|
+
assertPositiveInteger(input.crop.height, "crop.height"),
|
|
807
|
+
].join(","));
|
|
808
|
+
}
|
|
809
|
+
if (input.frameRange) {
|
|
810
|
+
const from = assertPositiveInteger(input.frameRange.from, "frameRange.from");
|
|
811
|
+
const to = assertPositiveInteger(input.frameRange.to, "frameRange.to");
|
|
812
|
+
if (from > to)
|
|
813
|
+
throw new Error("frameRange.from cannot be greater than frameRange.to.");
|
|
814
|
+
args.push("--frame-range", `${from},${to}`);
|
|
815
|
+
}
|
|
816
|
+
if (input.tag)
|
|
817
|
+
args.push("--tag", input.tag);
|
|
818
|
+
if (input.layer)
|
|
819
|
+
args.push("--layer", input.layer);
|
|
820
|
+
args.push("--save-as", output.absolute);
|
|
821
|
+
const { stdout, stderr } = await execFileAsync(executable, args, {
|
|
822
|
+
cwd: workspace.root,
|
|
823
|
+
timeout: ASEPRITE_TIMEOUT_MS,
|
|
824
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
825
|
+
windowsHide: true,
|
|
826
|
+
});
|
|
827
|
+
return textResponse({
|
|
828
|
+
success: true,
|
|
829
|
+
source: source.relative,
|
|
830
|
+
output: output.relative,
|
|
831
|
+
details: sanitizeCliOutput([stdout.trim(), stderr.trim()].filter(Boolean).join("\n")).slice(-2_000),
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
catch (error) {
|
|
835
|
+
return errorResponse(`Aseprite conversion failed: ${sanitizeCliOutput(String(error?.stderr || error?.message || String(error)))}`);
|
|
836
|
+
}
|
|
837
|
+
}
|