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.
Files changed (550) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +366 -233
  3. package/addons/auvrynt_bridge/auvrynt_bridge.gd +500 -0
  4. package/addons/auvrynt_bridge/plugin.cfg +7 -0
  5. package/bin/auvrynt-launcher.cjs +77 -0
  6. package/dist/artifact-registry.js +1 -0
  7. package/dist/aseprite-tools.js +1 -0
  8. package/dist/auth/connection-registry.js +145 -0
  9. package/dist/auth/connection-status.js +176 -0
  10. package/dist/auth/oauth-provider.js +617 -0
  11. package/dist/auth/room-registry.js +131 -0
  12. package/dist/auth/session-registry.js +531 -0
  13. package/dist/background-lifecycle.js +1 -0
  14. package/dist/blender-client.js +1 -109
  15. package/dist/blender-tools.js +1 -930
  16. package/dist/browser-stability.js +1 -0
  17. package/dist/cli/commands/help-command.js +10 -0
  18. package/dist/cli/commands/init-command.js +190 -0
  19. package/dist/cli/commands/integration-commands.js +88 -0
  20. package/dist/cli/commands/serve-command.js +83 -0
  21. package/dist/cli/commands/setup-command.js +81 -0
  22. package/dist/cli/commands/status-commands.js +166 -0
  23. package/dist/cli/commands/tunnel-command.js +127 -0
  24. package/dist/cli/commands/user-config-commands.js +105 -0
  25. package/dist/cli/dashboard-actions.js +516 -0
  26. package/dist/cli/dashboard-native-actions.js +282 -0
  27. package/dist/cli/foreground-server.js +270 -0
  28. package/dist/cli/instance-control.js +21 -0
  29. package/dist/cli/instance-lock.js +123 -0
  30. package/dist/cli/integration-bootstrap.js +206 -0
  31. package/dist/cli/lifecycle-manager.js +493 -0
  32. package/dist/cli/runtime-support.js +74 -0
  33. package/dist/cli/serena-detected-default.js +27 -0
  34. package/dist/cli/tunnel-health-monitor.js +182 -0
  35. package/dist/cli.js +82 -739
  36. package/dist/config.js +1 -243
  37. package/dist/connection-registry.js +1 -73
  38. package/dist/connection-status.js +1 -139
  39. package/dist/control-retry.js +1 -0
  40. package/dist/crash-recovery.js +1 -0
  41. package/dist/db/client.js +16 -3
  42. package/dist/db/schema.js +30 -0
  43. package/dist/dotnet-tools.js +1 -223
  44. package/dist/git-worktrees.js +1 -134
  45. package/dist/git.js +1 -41
  46. package/dist/godot-animation-tilemap.js +1 -82
  47. package/dist/godot-csharp-build.js +1 -93
  48. package/dist/godot-csharp-project.js +1 -157
  49. package/dist/godot-csharp-runner.js +1 -146
  50. package/dist/godot-csharp-semantic.js +1 -161
  51. package/dist/godot-csharp-validate.js +1 -71
  52. package/dist/godot-dotnet-env.js +1 -169
  53. package/dist/godot-editor-bridge.js +1 -140
  54. package/dist/godot-gdscript.js +1 -824
  55. package/dist/godot-project-settings.js +1 -223
  56. package/dist/godot-runtime-testing.js +1 -249
  57. package/dist/godot-tools.js +1 -183
  58. package/dist/http-rate-limit.js +1 -0
  59. package/dist/http-server-hardening.js +1 -0
  60. package/dist/image-tools.js +1 -157
  61. package/dist/infrastructure/command-reference.js +32 -0
  62. package/dist/infrastructure/config.js +424 -0
  63. package/dist/infrastructure/context7-auth.js +55 -0
  64. package/dist/infrastructure/http-rate-limit.js +42 -0
  65. package/dist/infrastructure/http-server-hardening.js +23 -0
  66. package/dist/infrastructure/integration-queue.js +46 -0
  67. package/dist/infrastructure/logger.js +129 -0
  68. package/dist/infrastructure/mcp-event-store.js +103 -0
  69. package/dist/infrastructure/ngrok-auth-pool.js +208 -0
  70. package/dist/infrastructure/request-context.js +14 -0
  71. package/dist/infrastructure/shutdown.js +37 -0
  72. package/dist/infrastructure/sse-heartbeat.js +47 -0
  73. package/dist/infrastructure/tool-capabilities.js +67 -0
  74. package/dist/infrastructure/tool-result-budget.js +175 -0
  75. package/dist/infrastructure/user-config.js +167 -0
  76. package/dist/integration-discovery.js +1 -98
  77. package/dist/integration-queue.js +1 -0
  78. package/dist/integrations/agent-reach/agent-reach-tools.js +196 -0
  79. package/dist/integrations/agent-reach/native-channels.js +468 -0
  80. package/dist/integrations/aseprite/aseprite-analysis-tools.js +368 -0
  81. package/dist/integrations/aseprite/aseprite-editing-tools.js +647 -0
  82. package/dist/integrations/aseprite/aseprite-live-tools.js +297 -0
  83. package/dist/integrations/aseprite/aseprite-safety-tools.js +594 -0
  84. package/dist/integrations/aseprite/aseprite-specialized-tools.js +289 -0
  85. package/dist/integrations/aseprite/aseprite-tools.js +837 -0
  86. package/dist/integrations/blender/blender-catalog-mesh.js +593 -0
  87. package/dist/integrations/blender/blender-catalog-object.js +906 -0
  88. package/dist/integrations/blender/blender-catalog-scene.js +266 -0
  89. package/dist/integrations/blender/blender-catalog-select.js +581 -0
  90. package/dist/integrations/blender/blender-catalog-stubs.js +7512 -0
  91. package/dist/integrations/blender/blender-catalog.js +165 -0
  92. package/dist/integrations/blender/blender-client.js +105 -0
  93. package/dist/integrations/blender/blender-tools.js +2243 -0
  94. package/dist/integrations/context7/context7-tools.js +106 -0
  95. package/dist/integrations/dotnet/dotnet-tools.js +195 -0
  96. package/dist/integrations/godot/godot-animation-tilemap.js +74 -0
  97. package/dist/integrations/godot/godot-csharp-build.js +91 -0
  98. package/dist/integrations/godot/godot-csharp-project.js +155 -0
  99. package/dist/integrations/godot/godot-csharp-runner.js +118 -0
  100. package/dist/integrations/godot/godot-csharp-semantic.js +146 -0
  101. package/dist/integrations/godot/godot-csharp-validate.js +71 -0
  102. package/dist/integrations/godot/godot-dotnet-env.js +134 -0
  103. package/dist/integrations/godot/godot-editor-bridge.js +328 -0
  104. package/dist/integrations/godot/godot-gdscript.js +536 -0
  105. package/dist/integrations/godot/godot-project-settings.js +178 -0
  106. package/dist/integrations/godot/godot-runtime-testing.js +263 -0
  107. package/dist/integrations/godot/godot-tools.js +193 -0
  108. package/dist/integrations/images/image-tools.js +414 -0
  109. package/dist/integrations/images/png-codec.js +251 -0
  110. package/dist/integrations/images/view-image.js +99 -0
  111. package/dist/integrations/images/window-capture.js +148 -0
  112. package/dist/integrations/integration-discovery.js +185 -0
  113. package/dist/integrations/obsidian/obsidian-tools.js +205 -0
  114. package/dist/integrations/playwright/browser-session.js +516 -0
  115. package/dist/integrations/playwright/browser-stability.js +36 -0
  116. package/dist/integrations/playwright/playwright-runtime.js +105 -0
  117. package/dist/integrations/playwright/web-tools.js +504 -0
  118. package/dist/integrations/roblox/roblox-helpers.js +381 -0
  119. package/dist/integrations/roblox/roblox-studio-client.js +112 -0
  120. package/dist/integrations/roblox/roblox-tools.js +106 -0
  121. package/dist/integrations/serena/serena-manager.js +464 -0
  122. package/dist/integrations/serena/serena-tools.js +411 -0
  123. package/dist/logger.js +1 -76
  124. package/dist/mcp-event-store.js +1 -0
  125. package/dist/oauth-provider.js +1 -377
  126. package/dist/pi-tools.js +1 -81
  127. package/dist/playwright-runtime.js +1 -0
  128. package/dist/process/background-lifecycle.js +189 -0
  129. package/dist/process/control-retry.js +42 -0
  130. package/dist/process/crash-recovery.js +23 -0
  131. package/dist/process/processes.js +456 -0
  132. package/dist/processes.js +1 -218
  133. package/dist/request-context.js +1 -0
  134. package/dist/review-checkpoints.js +1 -142
  135. package/dist/room-registry.js +1 -0
  136. package/dist/roots.js +1 -53
  137. package/dist/search-discovery.js +1 -271
  138. package/dist/serena-manager.js +1 -443
  139. package/dist/serena-tools.js +1 -411
  140. package/dist/server/dashboard-page-styles.js +331 -0
  141. package/dist/server/dashboard-page.js +1509 -0
  142. package/dist/server/dashboard-routes.js +46 -0
  143. package/dist/server/dashboard.js +240 -0
  144. package/dist/server/http-helpers.js +30 -0
  145. package/dist/server/integration-profile-update.js +37 -0
  146. package/dist/server/mcp-policy.js +169 -0
  147. package/dist/server/mcp-server-factory.js +127 -0
  148. package/dist/server/mcp-tool-registrar.js +182 -0
  149. package/dist/server/openai-session-hint.js +14 -0
  150. package/dist/server/tool-groups.js +29 -0
  151. package/dist/server/tool-logging.js +33 -0
  152. package/dist/server/tool-registration-shared.js +59 -0
  153. package/dist/server/tools/aseprite-advanced-tools-registration.js +547 -0
  154. package/dist/server/tools/aseprite-tools-registration.js +213 -0
  155. package/dist/server/tools/blender-tools-registration.js +690 -0
  156. package/dist/server/tools/core-file-tools-registration.js +459 -0
  157. package/dist/server/tools/dotnet-godot-window-tools-registration.js +120 -0
  158. package/dist/server/tools/godot-csharp-tools-registration.js +252 -0
  159. package/dist/server/tools/godot-gdscript-tools-registration.js +189 -0
  160. package/dist/server/tools/process-project-tools-registration.js +155 -0
  161. package/dist/server/tools/roblox-tools-registration.js +514 -0
  162. package/dist/server/tools/web-image-tools-registration.js +326 -0
  163. package/dist/server/tools/workspace-lifecycle-tools-registration.js +130 -0
  164. package/dist/server/ui-assets.js +97 -0
  165. package/dist/server/workspace-analytics.js +92 -0
  166. package/dist/server.js +453 -2713
  167. package/dist/session-registry.js +1 -0
  168. package/dist/shutdown.js +1 -0
  169. package/dist/skills.js +1 -45
  170. package/dist/sse-heartbeat.js +1 -0
  171. package/dist/tool-capabilities.js +1 -0
  172. package/dist/tool-result-budget.js +1 -0
  173. package/dist/tools/pi-tools.js +81 -0
  174. package/dist/tunnels/cloudflare-tunnel.js +217 -0
  175. package/dist/tunnels/cloudflared-service.js +174 -0
  176. package/dist/tunnels/ngrok-tunnel.js +220 -0
  177. package/dist/tunnels/tunnel-manager.js +186 -0
  178. package/dist/tunnels/tunnel-types.js +1 -0
  179. package/dist/tunnels/tunnel-utils.js +62 -0
  180. package/dist/ui/.vite/manifest.json +9 -2487
  181. package/dist/ui/assets/heavy-payload-D42A9ceL.js +1 -0
  182. package/dist/ui/assets/lightweight-diff-Cv4g9Hdr.js +13 -0
  183. package/dist/ui/assets/review-payload-DKIg1cKF.js +1 -0
  184. package/dist/ui/assets/workspace-app-CeK3NhmB.css +1 -0
  185. package/dist/ui/assets/{workspace-app-C5QsE314.js → workspace-app-CkUXnpl4.js} +6 -6
  186. package/dist/ui/brand-theme.js +23 -0
  187. package/dist/ui/workspace-app.html +15 -15
  188. package/dist/user-config.js +1 -57
  189. package/dist/view-image.js +1 -104
  190. package/dist/web-tools.js +1 -200
  191. package/dist/window-capture.js +1 -74
  192. package/dist/workspace/artifact-registry.js +69 -0
  193. package/dist/workspace/git-worktrees.js +141 -0
  194. package/dist/workspace/git.js +42 -0
  195. package/dist/workspace/review-checkpoints.js +142 -0
  196. package/dist/workspace/roots.js +56 -0
  197. package/dist/workspace/search-discovery.js +264 -0
  198. package/dist/workspace/skills.js +45 -0
  199. package/dist/workspace/workspace-store.js +219 -0
  200. package/dist/workspace/workspaces.js +314 -0
  201. package/dist/workspace-store.js +1 -125
  202. package/dist/workspaces.js +1 -226
  203. package/docs/CREATOR_STORE_FREE_ASSETS.md +162 -0
  204. package/docs/DEVELOPMENT_TOOLS.md +136 -93
  205. package/docs/assets/auvrynt-icon.png +0 -0
  206. package/docs/assets/auvrynt-icon.svg +20 -0
  207. package/docs/chatgpt-coding-workflow.md +143 -143
  208. package/docs/configuration.md +156 -124
  209. package/docs/diagnostics.md +79 -0
  210. package/docs/gotchas.md +244 -202
  211. package/docs/multi-agent-and-long-projects.md +91 -0
  212. package/docs/security.md +138 -100
  213. package/docs/setup.md +171 -131
  214. package/docs/source-layout.md +60 -0
  215. package/extensions/auvrynt_bridge/auvrynt_bridge.lua +594 -0
  216. package/extensions/auvrynt_bridge/live_analysis.lua +181 -0
  217. package/extensions/auvrynt_bridge/live_animation.lua +134 -0
  218. package/extensions/auvrynt_bridge/live_commands.lua +24 -0
  219. package/extensions/auvrynt_bridge/live_common.lua +130 -0
  220. package/extensions/auvrynt_bridge/live_document.lua +78 -0
  221. package/extensions/auvrynt_bridge/live_drawing.lua +125 -0
  222. package/extensions/auvrynt_bridge/live_effects.lua +91 -0
  223. package/extensions/auvrynt_bridge/live_palette_template.lua +167 -0
  224. package/extensions/auvrynt_bridge/live_selection.lua +110 -0
  225. package/extensions/auvrynt_bridge/live_structure.lua +129 -0
  226. package/extensions/auvrynt_bridge/package.json +17 -0
  227. package/guides/ASEPRITE_AGENT_GUIDE.md +133 -0
  228. package/guides/BLENDER_AGENT_GUIDE.md +472 -0
  229. package/guides/GODOT_AGENT_GUIDE.md +298 -0
  230. package/guides/PROJECT_ROUTER.md +165 -0
  231. package/guides/ROBLOX_AGENT_GUIDE.md +75 -0
  232. package/guides/SOFTWARE_AGENT_GUIDE.md +104 -0
  233. package/guides/WEB_AGENT_GUIDE.md +287 -0
  234. package/package.json +78 -70
  235. package/scripts/dev-server.mjs +121 -120
  236. package/scripts/live-tunnel-smoke.mjs +538 -0
  237. package/scripts/repo-hygiene.mjs +74 -0
  238. package/scripts/restart-after-crash.mjs +49 -0
  239. package/scripts/run-tests.mjs +166 -0
  240. package/dist/ui/assets/abap-CLvhMVsD.js +0 -1
  241. package/dist/ui/assets/actionscript-3--17pq3dv.js +0 -1
  242. package/dist/ui/assets/ada-C5qYipkI.js +0 -1
  243. package/dist/ui/assets/andromeeda-vGVdxbeo.js +0 -1
  244. package/dist/ui/assets/angular-html-BJqQFYI6.js +0 -1
  245. package/dist/ui/assets/angular-ts-CMJECO0Y.js +0 -1
  246. package/dist/ui/assets/apache-U0d_L8uA.js +0 -1
  247. package/dist/ui/assets/apex-CGTLDQj6.js +0 -1
  248. package/dist/ui/assets/apl-DWeXpBBr.js +0 -1
  249. package/dist/ui/assets/applescript-CCn79oCD.js +0 -1
  250. package/dist/ui/assets/ara-4CJ0cIlV.js +0 -1
  251. package/dist/ui/assets/asciidoc-DE70LPWp.js +0 -1
  252. package/dist/ui/assets/asm-Cmm7eHzH.js +0 -1
  253. package/dist/ui/assets/astro-CLbttLOq.js +0 -1
  254. package/dist/ui/assets/aurora-x-CDeNXAV0.js +0 -1
  255. package/dist/ui/assets/awk-BWXHIvNe.js +0 -1
  256. package/dist/ui/assets/ayu-dark-DluEY0Gj.js +0 -1
  257. package/dist/ui/assets/ayu-light-C3h-C4tm.js +0 -1
  258. package/dist/ui/assets/ayu-mirage-Bqwy1Gya.js +0 -1
  259. package/dist/ui/assets/ballerina-B7ZEbQpA.js +0 -1
  260. package/dist/ui/assets/bat-Bo4NYOV-.js +0 -1
  261. package/dist/ui/assets/beancount-D-usSTwE.js +0 -1
  262. package/dist/ui/assets/berry-DKpUyyne.js +0 -1
  263. package/dist/ui/assets/bibtex-Ci_nEsc7.js +0 -1
  264. package/dist/ui/assets/bicep-CUHmPFLl.js +0 -1
  265. package/dist/ui/assets/bird2-C2hNVINV.js +0 -1
  266. package/dist/ui/assets/blade-BOzY0P2V.js +0 -1
  267. package/dist/ui/assets/bsl-BkkzgIyY.js +0 -1
  268. package/dist/ui/assets/c-BG-8uJvh.js +0 -1
  269. package/dist/ui/assets/c3-CnJL0r0V.js +0 -1
  270. package/dist/ui/assets/cadence-CQ2zXKGN.js +0 -1
  271. package/dist/ui/assets/cairo-DLTphjLi.js +0 -1
  272. package/dist/ui/assets/catppuccin-frappe-3VR1Za6u.js +0 -1
  273. package/dist/ui/assets/catppuccin-latte-DwIHMF0Q.js +0 -1
  274. package/dist/ui/assets/catppuccin-macchiato-DYnBP6_5.js +0 -1
  275. package/dist/ui/assets/catppuccin-mocha-DYhrFGRu.js +0 -1
  276. package/dist/ui/assets/clarity-SemFz856.js +0 -1
  277. package/dist/ui/assets/clojure-DqKBuwfJ.js +0 -1
  278. package/dist/ui/assets/cmake-Bj61d0ZC.js +0 -1
  279. package/dist/ui/assets/cobol-I_17IuGe.js +0 -1
  280. package/dist/ui/assets/codeowners-C8r90Shi.js +0 -1
  281. package/dist/ui/assets/codeql-oeQT6MSM.js +0 -1
  282. package/dist/ui/assets/coffee-C0B8hmok.js +0 -1
  283. package/dist/ui/assets/common-lisp-Cv5bFMCO.js +0 -1
  284. package/dist/ui/assets/coq-BrsZFFmf.js +0 -1
  285. package/dist/ui/assets/cpp-DDJXt6Vs.js +0 -1
  286. package/dist/ui/assets/crystal-Bexqace6.js +0 -1
  287. package/dist/ui/assets/csharp-Ct8U2NOr.js +0 -1
  288. package/dist/ui/assets/css-DSQhw_9R.js +0 -1
  289. package/dist/ui/assets/csv-Dx-8-gkx.js +0 -1
  290. package/dist/ui/assets/cue-CE9AQfxI.js +0 -1
  291. package/dist/ui/assets/cypher-ClKdZ_lG.js +0 -1
  292. package/dist/ui/assets/d-qD-0Kul2.js +0 -1
  293. package/dist/ui/assets/dark-plus-Cs2F2srj.js +0 -1
  294. package/dist/ui/assets/dart-DkHntEIa.js +0 -1
  295. package/dist/ui/assets/dax-BkyTk9wS.js +0 -1
  296. package/dist/ui/assets/desktop-Dlh5hvp9.js +0 -1
  297. package/dist/ui/assets/diff-woXpYk--.js +0 -1
  298. package/dist/ui/assets/docker-IyjqRm3v.js +0 -1
  299. package/dist/ui/assets/dotenv-_5a1GRtc.js +0 -1
  300. package/dist/ui/assets/dracula-BHWKrbxM.js +0 -1
  301. package/dist/ui/assets/dracula-soft-5eyTD99u.js +0 -1
  302. package/dist/ui/assets/dream-maker-DW3nJb8Q.js +0 -1
  303. package/dist/ui/assets/edge-Z8YYkHOr.js +0 -1
  304. package/dist/ui/assets/elixir-CG9uKtAF.js +0 -1
  305. package/dist/ui/assets/elm-GfWG2IfT.js +0 -1
  306. package/dist/ui/assets/emacs-lisp-C9PiwqqW.js +0 -1
  307. package/dist/ui/assets/erb-xe89e73J.js +0 -1
  308. package/dist/ui/assets/erlang-Cphh6RMH.js +0 -1
  309. package/dist/ui/assets/everforest-dark-sB-x3p7T.js +0 -1
  310. package/dist/ui/assets/everforest-light-Df2xbC6M.js +0 -1
  311. package/dist/ui/assets/fennel-DQxkIbk2.js +0 -1
  312. package/dist/ui/assets/fish-BJitypiv.js +0 -1
  313. package/dist/ui/assets/fluent-C03EYrpw.js +0 -1
  314. package/dist/ui/assets/fortran-fixed-form-DEKoE2YW.js +0 -1
  315. package/dist/ui/assets/fortran-free-form-CYNrtFtB.js +0 -1
  316. package/dist/ui/assets/fsharp-D13ZGOAj.js +0 -1
  317. package/dist/ui/assets/gdresource-C0sCabJj.js +0 -1
  318. package/dist/ui/assets/gdscript-Cp2uCuqX.js +0 -1
  319. package/dist/ui/assets/gdshader-CBce3t8t.js +0 -1
  320. package/dist/ui/assets/genie-CV2tkWYe.js +0 -1
  321. package/dist/ui/assets/gherkin-DExj1W_8.js +0 -1
  322. package/dist/ui/assets/git-commit-BSykSTBG.js +0 -1
  323. package/dist/ui/assets/git-rebase-CoTL_uSE.js +0 -1
  324. package/dist/ui/assets/github-dark-C-LZuMrd.js +0 -1
  325. package/dist/ui/assets/github-dark-default-DXG-b-1a.js +0 -1
  326. package/dist/ui/assets/github-dark-dimmed-Bx1FflLF.js +0 -1
  327. package/dist/ui/assets/github-dark-high-contrast-B_tTalzw.js +0 -1
  328. package/dist/ui/assets/github-light-EUqPIrTm.js +0 -1
  329. package/dist/ui/assets/github-light-default-BXViO-2h.js +0 -1
  330. package/dist/ui/assets/github-light-high-contrast-B68TUdTA.js +0 -1
  331. package/dist/ui/assets/gleam-CSRkHgEL.js +0 -1
  332. package/dist/ui/assets/glimmer-js-DAn4UCSu.js +0 -1
  333. package/dist/ui/assets/glimmer-ts-Tjyi6Gh5.js +0 -1
  334. package/dist/ui/assets/glsl-CtTDZoqR.js +0 -1
  335. package/dist/ui/assets/gn-ilITqXS6.js +0 -1
  336. package/dist/ui/assets/gnuplot-7GGW24-e.js +0 -1
  337. package/dist/ui/assets/go-BJwz_mda.js +0 -1
  338. package/dist/ui/assets/graphql-D5gcC_Jk.js +0 -1
  339. package/dist/ui/assets/groovy-CacY0gHj.js +0 -1
  340. package/dist/ui/assets/gruvbox-dark-hard-C820rvS2.js +0 -1
  341. package/dist/ui/assets/gruvbox-dark-medium-BPjhmG05.js +0 -1
  342. package/dist/ui/assets/gruvbox-dark-soft-MrdJrrXF.js +0 -1
  343. package/dist/ui/assets/gruvbox-light-hard-BC_s9l72.js +0 -1
  344. package/dist/ui/assets/gruvbox-light-medium-BAWPOn9u.js +0 -1
  345. package/dist/ui/assets/gruvbox-light-soft-BSMLrYjP.js +0 -1
  346. package/dist/ui/assets/hack-BJXY27dt.js +0 -1
  347. package/dist/ui/assets/haml-BLfFoZf7.js +0 -1
  348. package/dist/ui/assets/handlebars-Dw6CWkvN.js +0 -1
  349. package/dist/ui/assets/haskell-D8IpX4py.js +0 -1
  350. package/dist/ui/assets/haxe-OTjmBuCE.js +0 -1
  351. package/dist/ui/assets/hcl-Dh228itO.js +0 -1
  352. package/dist/ui/assets/heavy-payload-Dcg0-F-S.js +0 -4
  353. package/dist/ui/assets/hjson-CxZEssPk.js +0 -1
  354. package/dist/ui/assets/hlsl-Cvrh5tZx.js +0 -1
  355. package/dist/ui/assets/horizon-CE9ld1lL.js +0 -1
  356. package/dist/ui/assets/horizon-bright-Br1oVSNq.js +0 -1
  357. package/dist/ui/assets/houston-CsvMBhTu.js +0 -1
  358. package/dist/ui/assets/html-CAcAWvoN.js +0 -1
  359. package/dist/ui/assets/html-derivative-8aSW2E_7.js +0 -1
  360. package/dist/ui/assets/http-B2YR_3V1.js +0 -1
  361. package/dist/ui/assets/hurl-BnDJ9Zhf.js +0 -1
  362. package/dist/ui/assets/hxml-B0Qn7Nwc.js +0 -1
  363. package/dist/ui/assets/hy-CZbG8q4J.js +0 -1
  364. package/dist/ui/assets/imba-DsUTQ-LC.js +0 -1
  365. package/dist/ui/assets/ini-B5eOa1yu.js +0 -1
  366. package/dist/ui/assets/java-Doxi4W8u.js +0 -1
  367. package/dist/ui/assets/javascript-BbRdEqti.js +0 -1
  368. package/dist/ui/assets/jinja-CS3Tpv_Q.js +0 -1
  369. package/dist/ui/assets/jison-BkPGYVHR.js +0 -1
  370. package/dist/ui/assets/json-BMt1m3m8.js +0 -1
  371. package/dist/ui/assets/json5-BR5RXkoi.js +0 -1
  372. package/dist/ui/assets/jsonc-CYpm1nAK.js +0 -1
  373. package/dist/ui/assets/jsonl-CmCQp5Yx.js +0 -1
  374. package/dist/ui/assets/jsonnet-CJTPZ8u_.js +0 -1
  375. package/dist/ui/assets/jssm-DXw9l8Rf.js +0 -1
  376. package/dist/ui/assets/jsx-CQKrJXXY.js +0 -1
  377. package/dist/ui/assets/julia-BuN7i2du.js +0 -1
  378. package/dist/ui/assets/just-BswEPFPu.js +0 -1
  379. package/dist/ui/assets/kanagawa-dragon-CXtmUGW6.js +0 -1
  380. package/dist/ui/assets/kanagawa-lotus-BN08jTvb.js +0 -1
  381. package/dist/ui/assets/kanagawa-wave-CTweb8Dz.js +0 -1
  382. package/dist/ui/assets/kdl-CsD5j6eV.js +0 -1
  383. package/dist/ui/assets/kotlin-DhhofPvG.js +0 -1
  384. package/dist/ui/assets/kusto-BUv0MjJC.js +0 -1
  385. package/dist/ui/assets/laserwave-C_8bwKvT.js +0 -1
  386. package/dist/ui/assets/latex-DU2VyFfA.js +0 -1
  387. package/dist/ui/assets/lean-CewbzKMR.js +0 -1
  388. package/dist/ui/assets/less-DVTAwKKz.js +0 -1
  389. package/dist/ui/assets/light-plus-DVQuIRkW.js +0 -1
  390. package/dist/ui/assets/liquid-Cmz1L3dq.js +0 -1
  391. package/dist/ui/assets/llvm-Cm23YOpf.js +0 -1
  392. package/dist/ui/assets/log-BNLmms1o.js +0 -1
  393. package/dist/ui/assets/logo-Cluzi2Zq.js +0 -1
  394. package/dist/ui/assets/lua-CfVXhkaJ.js +0 -1
  395. package/dist/ui/assets/luau-CNKltnaQ.js +0 -1
  396. package/dist/ui/assets/make-Dixweg8N.js +0 -1
  397. package/dist/ui/assets/markdown-BYOwaDjH.js +0 -1
  398. package/dist/ui/assets/marko-C5bfqGiA.js +0 -1
  399. package/dist/ui/assets/material-theme-Bm3Qr25_.js +0 -1
  400. package/dist/ui/assets/material-theme-darker-2IIEA8gg.js +0 -1
  401. package/dist/ui/assets/material-theme-lighter-uhdI0v04.js +0 -1
  402. package/dist/ui/assets/material-theme-ocean-CHQ94UKr.js +0 -1
  403. package/dist/ui/assets/material-theme-palenight-B5W6OYN7.js +0 -1
  404. package/dist/ui/assets/matlab-D7qyCx1q.js +0 -1
  405. package/dist/ui/assets/mdc-CVBXYfyq.js +0 -1
  406. package/dist/ui/assets/mdx-DQZ5AkYe.js +0 -1
  407. package/dist/ui/assets/mermaid-Bk4SNUv9.js +0 -1
  408. package/dist/ui/assets/min-dark-BSWPekZh.js +0 -1
  409. package/dist/ui/assets/min-light-DDpmG2fV.js +0 -1
  410. package/dist/ui/assets/mipsasm-BMqwQI7S.js +0 -1
  411. package/dist/ui/assets/mojo-BgCJLMeH.js +0 -1
  412. package/dist/ui/assets/monokai-CdkpiU2Y.js +0 -1
  413. package/dist/ui/assets/moonbit-CaWjb8XO.js +0 -1
  414. package/dist/ui/assets/move-B1IS1UjX.js +0 -1
  415. package/dist/ui/assets/narrat-_X_XdTYD.js +0 -1
  416. package/dist/ui/assets/nextflow-Bbiyy34d.js +0 -1
  417. package/dist/ui/assets/nextflow-groovy-Dc_ddanL.js +0 -1
  418. package/dist/ui/assets/nginx-CzJafxLa.js +0 -1
  419. package/dist/ui/assets/night-owl-DhmEMT88.js +0 -1
  420. package/dist/ui/assets/night-owl-light-eJ-hLW7d.js +0 -1
  421. package/dist/ui/assets/nim-DABKtHtM.js +0 -1
  422. package/dist/ui/assets/nix-IvuFDN5E.js +0 -1
  423. package/dist/ui/assets/nord-Cb4Vim4T.js +0 -1
  424. package/dist/ui/assets/nushell-DcLAeLz5.js +0 -1
  425. package/dist/ui/assets/objective-c-D1A_Heim.js +0 -1
  426. package/dist/ui/assets/objective-cpp-BsSzOQcm.js +0 -1
  427. package/dist/ui/assets/ocaml-O90oeIOV.js +0 -1
  428. package/dist/ui/assets/odin-B1RWQWA5.js +0 -1
  429. package/dist/ui/assets/one-dark-pro-CLwyXe_n.js +0 -1
  430. package/dist/ui/assets/one-light-D7Lr4KcI.js +0 -1
  431. package/dist/ui/assets/openscad-BUDT5pXO.js +0 -1
  432. package/dist/ui/assets/pascal-4ZHwLPI5.js +0 -1
  433. package/dist/ui/assets/perl-CBh7L3lZ.js +0 -1
  434. package/dist/ui/assets/php-DFbI9q5p.js +0 -1
  435. package/dist/ui/assets/pierre-dark-sU4Zdns8.js +0 -1
  436. package/dist/ui/assets/pierre-dark-soft-HXgun5vs.js +0 -1
  437. package/dist/ui/assets/pierre-light-DWBk51d8.js +0 -1
  438. package/dist/ui/assets/pierre-light-soft-CodEzPxf.js +0 -1
  439. package/dist/ui/assets/pkl-ot-7Btpt.js +0 -1
  440. package/dist/ui/assets/plastic-DQwYfKfQ.js +0 -1
  441. package/dist/ui/assets/plsql-DGHpHOYJ.js +0 -1
  442. package/dist/ui/assets/po-BiJDBrnU.js +0 -1
  443. package/dist/ui/assets/poimandres-DRFjx7u4.js +0 -1
  444. package/dist/ui/assets/polar-C7UOKdEL.js +0 -1
  445. package/dist/ui/assets/postcss-BXeXVLqQ.js +0 -1
  446. package/dist/ui/assets/powerquery-DNMTfnFr.js +0 -1
  447. package/dist/ui/assets/powershell-DshXNtvi.js +0 -1
  448. package/dist/ui/assets/prisma-BsRQq5mF.js +0 -1
  449. package/dist/ui/assets/prolog-iXnhIJG7.js +0 -1
  450. package/dist/ui/assets/proto-DB4EqR-F.js +0 -1
  451. package/dist/ui/assets/pug-C_PxC7gO.js +0 -1
  452. package/dist/ui/assets/puppet-CDv2pdJW.js +0 -1
  453. package/dist/ui/assets/purescript-9MfHhQsQ.js +0 -1
  454. package/dist/ui/assets/python-gzcpVVnB.js +0 -1
  455. package/dist/ui/assets/qml-DtngoFFK.js +0 -1
  456. package/dist/ui/assets/qmldir-DCQb3MpD.js +0 -1
  457. package/dist/ui/assets/qss-Fe1Jh2GI.js +0 -1
  458. package/dist/ui/assets/r-DohkcevW.js +0 -1
  459. package/dist/ui/assets/racket-DcIDlBhZ.js +0 -1
  460. package/dist/ui/assets/raku-B3gFvitq.js +0 -1
  461. package/dist/ui/assets/razor-C4t8J0_E.js +0 -1
  462. package/dist/ui/assets/red-CJ3rzSJv.js +0 -1
  463. package/dist/ui/assets/reg-CRGYupPL.js +0 -1
  464. package/dist/ui/assets/regexp-OGbTbjgY.js +0 -1
  465. package/dist/ui/assets/rel-BtDbiS_P.js +0 -1
  466. package/dist/ui/assets/review-payload-C5Qz60Kq.js +0 -1
  467. package/dist/ui/assets/riscv-Ckw8ddFX.js +0 -1
  468. package/dist/ui/assets/ron-VUp2lXgN.js +0 -1
  469. package/dist/ui/assets/rose-pine-BthvhNj6.js +0 -1
  470. package/dist/ui/assets/rose-pine-dawn-Dg85fqjY.js +0 -1
  471. package/dist/ui/assets/rose-pine-moon-hon4tzzS.js +0 -1
  472. package/dist/ui/assets/rosmsg-CAekHB0j.js +0 -1
  473. package/dist/ui/assets/rst-CfBbbcvC.js +0 -1
  474. package/dist/ui/assets/ruby-Bw0mpxHY.js +0 -1
  475. package/dist/ui/assets/rust-Cfkwpbl8.js +0 -1
  476. package/dist/ui/assets/sas-Cs3s9JOf.js +0 -1
  477. package/dist/ui/assets/sass-DXrisJhu.js +0 -1
  478. package/dist/ui/assets/scala-DKOlJaKm.js +0 -1
  479. package/dist/ui/assets/scheme-DQCgrYNe.js +0 -1
  480. package/dist/ui/assets/scss-sRxNJS54.js +0 -1
  481. package/dist/ui/assets/sdbl-bTVj8UrX.js +0 -1
  482. package/dist/ui/assets/shaderlab-TOUzSsQk.js +0 -1
  483. package/dist/ui/assets/shellscript-B0X6wh0R.js +0 -1
  484. package/dist/ui/assets/shellsession-D2FdOioR.js +0 -1
  485. package/dist/ui/assets/slack-dark-DnToyrRv.js +0 -1
  486. package/dist/ui/assets/slack-ochin-B2OO5cIa.js +0 -1
  487. package/dist/ui/assets/smalltalk-B16xEiuN.js +0 -1
  488. package/dist/ui/assets/snazzy-light-4G7pJPwS.js +0 -1
  489. package/dist/ui/assets/solarized-dark-DV17i1UV.js +0 -1
  490. package/dist/ui/assets/solarized-light-DSh2HLQt.js +0 -1
  491. package/dist/ui/assets/solidity-CKzVLygQ.js +0 -1
  492. package/dist/ui/assets/soy-s6g9OjJ1.js +0 -1
  493. package/dist/ui/assets/sparql-D_iOobhT.js +0 -1
  494. package/dist/ui/assets/splunk-BC2Px7Mm.js +0 -1
  495. package/dist/ui/assets/sql-DVTnh5V-.js +0 -1
  496. package/dist/ui/assets/ssh-config-BgfXC-Er.js +0 -1
  497. package/dist/ui/assets/stata-DFIDiXF9.js +0 -1
  498. package/dist/ui/assets/stylus-B6D30XZt.js +0 -1
  499. package/dist/ui/assets/surrealql-DVfrsLyg.js +0 -1
  500. package/dist/ui/assets/svelte-D5GsxESq.js +0 -1
  501. package/dist/ui/assets/swift-DonLKvLd.js +0 -1
  502. package/dist/ui/assets/synthwave-84-nFMaYfgc.js +0 -1
  503. package/dist/ui/assets/system-verilog-DJ5XKQeo.js +0 -1
  504. package/dist/ui/assets/systemd-BxMlprV5.js +0 -1
  505. package/dist/ui/assets/talonscript-CohzipZa.js +0 -1
  506. package/dist/ui/assets/tasl-DMoTqEGO.js +0 -1
  507. package/dist/ui/assets/tcl-CZd0xW_V.js +0 -1
  508. package/dist/ui/assets/templ-DI-Zyqzd.js +0 -1
  509. package/dist/ui/assets/terraform-DswuEJGm.js +0 -1
  510. package/dist/ui/assets/tex-WHHmlbJI.js +0 -1
  511. package/dist/ui/assets/tokyo-night-oM2G3aXe.js +0 -1
  512. package/dist/ui/assets/toml-CcmNWLt0.js +0 -1
  513. package/dist/ui/assets/ts-tags-Dwpo2RFJ.js +0 -1
  514. package/dist/ui/assets/tsv-sltzmVWM.js +0 -1
  515. package/dist/ui/assets/tsx-ph_idaAW.js +0 -1
  516. package/dist/ui/assets/turtle-ByJddavk.js +0 -1
  517. package/dist/ui/assets/twig-YO17jFjR.js +0 -1
  518. package/dist/ui/assets/typescript-CbAxjhkl.js +0 -1
  519. package/dist/ui/assets/typespec-B88KGewJ.js +0 -1
  520. package/dist/ui/assets/typst-DI99ib-x.js +0 -1
  521. package/dist/ui/assets/useFileDiffInstance-B2DLX3Vg.js +0 -244
  522. package/dist/ui/assets/v-DETTlOr0.js +0 -1
  523. package/dist/ui/assets/vala-zf12oZj6.js +0 -1
  524. package/dist/ui/assets/vb-Djn5o6TS.js +0 -1
  525. package/dist/ui/assets/verilog-CiiDBU1e.js +0 -1
  526. package/dist/ui/assets/vesper-D5bVUKB1.js +0 -1
  527. package/dist/ui/assets/vhdl-BroJfC0k.js +0 -1
  528. package/dist/ui/assets/viml-DvXPmvsu.js +0 -1
  529. package/dist/ui/assets/vitesse-black-fwtXNY1n.js +0 -1
  530. package/dist/ui/assets/vitesse-dark-BZCL-v6S.js +0 -1
  531. package/dist/ui/assets/vitesse-light-VbXTXTou.js +0 -1
  532. package/dist/ui/assets/vue-CraHL-Ps.js +0 -1
  533. package/dist/ui/assets/vue-html-e9tyZNtS.js +0 -1
  534. package/dist/ui/assets/vue-vine-CofmxaUb.js +0 -1
  535. package/dist/ui/assets/vyper-CgoNMtux.js +0 -1
  536. package/dist/ui/assets/wasm-BnjxR4X6.js +0 -1
  537. package/dist/ui/assets/wasm-ByWQv1Qj.js +0 -1
  538. package/dist/ui/assets/wenyan-C8pVoKbM.js +0 -1
  539. package/dist/ui/assets/wgsl-BsKzXJz4.js +0 -1
  540. package/dist/ui/assets/wikitext-ClFFjSW2.js +0 -1
  541. package/dist/ui/assets/wit-DdvCle-K.js +0 -1
  542. package/dist/ui/assets/wolfram-DLL8P-h_.js +0 -1
  543. package/dist/ui/assets/workspace-app-8--oTbCd.css +0 -1
  544. package/dist/ui/assets/xml-BWSdPZHm.js +0 -1
  545. package/dist/ui/assets/xsl-BtCt7Hx8.js +0 -1
  546. package/dist/ui/assets/yaml-BZiTw7cQ.js +0 -1
  547. package/dist/ui/assets/zenscript-BnlCZFoB.js +0 -1
  548. package/dist/ui/assets/zig-CMLA9XwU.js +0 -1
  549. package/docs/assets/auvrynt-logo-light.png +0 -0
  550. package/docs/assets/auvrynt-screenshot.png +0 -0
@@ -0,0 +1,181 @@
1
+ local C = require("live_common")
2
+ local M = {}
3
+
4
+ local function render_frame(sprite, frame_number, composite, layer_name)
5
+ if frame_number < 1 or frame_number > #sprite.frames then C.fail("Frame is outside the sprite") end
6
+ local image = Image(sprite.spec)
7
+ image:clear()
8
+ if composite ~= false then
9
+ image:drawSprite(sprite, frame_number, Point(0, 0))
10
+ else
11
+ local layer = C.find_layer(sprite.layers, layer_name) or app.activeLayer
12
+ if not layer or layer.isGroup or layer.isTilemap then C.fail("A normal drawable layer is required") end
13
+ local cel = layer:cel(frame_number)
14
+ if cel then image:drawImage(cel.image, cel.position) end
15
+ end
16
+ return image
17
+ end
18
+
19
+ local function image_bounds(sprite, image)
20
+ local min_x, min_y, max_x, max_y = sprite.width, sprite.height, -1, -1
21
+ local count = 0
22
+ for y=0,sprite.height-1 do
23
+ for x=0,sprite.width-1 do
24
+ if C.pixel_alpha(sprite, image:getPixel(x, y)) > 0 then
25
+ count = count + 1
26
+ if x < min_x then min_x = x end
27
+ if y < min_y then min_y = y end
28
+ if x > max_x then max_x = x end
29
+ if y > max_y then max_y = y end
30
+ end
31
+ end
32
+ end
33
+ if count == 0 then return { empty=true, pixels=0 } end
34
+ return {
35
+ empty=false,
36
+ pixels=count,
37
+ x=min_x,
38
+ y=min_y,
39
+ width=max_x-min_x+1,
40
+ height=max_y-min_y+1,
41
+ centerX=(min_x+max_x)/2,
42
+ centerY=(min_y+max_y)/2,
43
+ }
44
+ end
45
+
46
+ local function get_sprite_bounds(sprite, params)
47
+ local frame = C.target_frame_number(sprite, params)
48
+ local image = render_frame(sprite, frame, params.composite ~= false, params.layer)
49
+ local bounds = image_bounds(sprite, image)
50
+ bounds.frame = frame
51
+ bounds.composite = params.composite ~= false
52
+ return bounds
53
+ end
54
+
55
+ local function get_color_stats(sprite, params)
56
+ local frame = C.target_frame_number(sprite, params)
57
+ local image = render_frame(sprite, frame, params.composite ~= false, params.layer)
58
+ local counts, opaque, transparent, semitransparent = {}, 0, 0, 0
59
+ for y=0,sprite.height-1 do
60
+ for x=0,sprite.width-1 do
61
+ local color = C.pixel_to_color(sprite, image:getPixel(x, y))
62
+ if color.alpha == 0 then
63
+ transparent = transparent + 1
64
+ else
65
+ opaque = opaque + 1
66
+ if color.alpha < 255 then semitransparent = semitransparent + 1 end
67
+ local hex = C.color_to_hex(color)
68
+ counts[hex] = (counts[hex] or 0) + 1
69
+ end
70
+ end
71
+ end
72
+ local colors = {}
73
+ for color, count in pairs(counts) do colors[#colors+1] = { color=color, count=count } end
74
+ table.sort(colors, function(a, b)
75
+ if a.count == b.count then return a.color < b.color end
76
+ return a.count > b.count
77
+ end)
78
+ local limit = C.clamp(C.integer(params.limit, 256), 1, 256)
79
+ while #colors > limit do table.remove(colors) end
80
+ local unique = 0
81
+ for _ in pairs(counts) do unique = unique + 1 end
82
+ return {
83
+ frame=frame,
84
+ uniqueColors=unique,
85
+ opaquePixels=opaque,
86
+ transparentPixels=transparent,
87
+ semitransparentPixels=semitransparent,
88
+ colors=colors,
89
+ }
90
+ end
91
+
92
+ local function compare_frames(sprite, params)
93
+ local first = C.clamp(C.integer(params.frameA, 1), 1, #sprite.frames)
94
+ local second = C.clamp(C.integer(params.frameB, math.min(2, #sprite.frames)), 1, #sprite.frames)
95
+ local image_a = render_frame(sprite, first, params.composite ~= false, params.layer)
96
+ local image_b = render_frame(sprite, second, params.composite ~= false, params.layer)
97
+ local changed, min_x, min_y, max_x, max_y = 0, sprite.width, sprite.height, -1, -1
98
+ for y=0,sprite.height-1 do
99
+ for x=0,sprite.width-1 do
100
+ if image_a:getPixel(x, y) ~= image_b:getPixel(x, y) then
101
+ changed = changed + 1
102
+ if x < min_x then min_x = x end
103
+ if y < min_y then min_y = y end
104
+ if x > max_x then max_x = x end
105
+ if y > max_y then max_y = y end
106
+ end
107
+ end
108
+ end
109
+ local total = sprite.width * sprite.height
110
+ return {
111
+ frameA=first,
112
+ frameB=second,
113
+ exact=changed == 0,
114
+ changedPixels=changed,
115
+ matchPercent=total == 0 and 100 or ((total-changed)/total)*100,
116
+ differenceBounds=changed == 0 and nil or { x=min_x, y=min_y, width=max_x-min_x+1, height=max_y-min_y+1 },
117
+ }
118
+ end
119
+
120
+ local function find_tag(sprite, name)
121
+ if name == nil then return nil end
122
+ for _, tag in ipairs(sprite.tags) do if tag.name == name then return tag end end
123
+ return nil
124
+ end
125
+
126
+ local function validate_animation(sprite, params)
127
+ local tag = find_tag(sprite, params.tag)
128
+ if params.tag and not tag then C.fail("Tag was not found") end
129
+ local from = tag and tag.fromFrame.frameNumber or C.clamp(C.integer(params.from, 1), 1, #sprite.frames)
130
+ local to = tag and tag.toFrame.frameNumber or C.clamp(C.integer(params.to, #sprite.frames), from, #sprite.frames)
131
+ local frames, issues = {}, {}
132
+ local previous_image = nil
133
+ local previous_bounds = nil
134
+ for frame=from,to do
135
+ local image = render_frame(sprite, frame, true)
136
+ local bounds = image_bounds(sprite, image)
137
+ local duration = math.floor(sprite.frames[frame].duration * 1000 + 0.5)
138
+ local duplicate = false
139
+ if previous_image then
140
+ duplicate = true
141
+ for y=0,sprite.height-1 do
142
+ for x=0,sprite.width-1 do
143
+ if image:getPixel(x, y) ~= previous_image:getPixel(x, y) then duplicate = false break end
144
+ end
145
+ if not duplicate then break end
146
+ end
147
+ end
148
+ local movement = nil
149
+ if previous_bounds and not previous_bounds.empty and not bounds.empty then
150
+ movement = {
151
+ dx=bounds.centerX-previous_bounds.centerX,
152
+ dy=bounds.centerY-previous_bounds.centerY,
153
+ }
154
+ end
155
+ frames[#frames+1] = { frame=frame, durationMs=duration, bounds=bounds, duplicatePrevious=duplicate, movement=movement }
156
+ if bounds.empty then issues[#issues+1] = { frame=frame, type="empty_frame" } end
157
+ if duplicate then issues[#issues+1] = { frame=frame, type="duplicate_previous" } end
158
+ if duration < 16 or duration > 2000 then issues[#issues+1] = { frame=frame, type="duration_outlier", durationMs=duration } end
159
+ previous_image, previous_bounds = image, bounds
160
+ end
161
+ local loop = compare_frames(sprite, { frameA=from, frameB=to, composite=true })
162
+ return {
163
+ tag=tag and tag.name or nil,
164
+ from=from,
165
+ to=to,
166
+ frameCount=to-from+1,
167
+ frames=frames,
168
+ issues=issues,
169
+ loopEndpointDifference=loop,
170
+ valid=#issues == 0,
171
+ }
172
+ end
173
+
174
+ M.handlers = {
175
+ get_sprite_bounds=get_sprite_bounds,
176
+ get_color_stats=get_color_stats,
177
+ compare_frames=compare_frames,
178
+ validate_animation=validate_animation,
179
+ }
180
+
181
+ return M
@@ -0,0 +1,134 @@
1
+ local C = require("live_common")
2
+ local M = {}
3
+
4
+ local function set_frame_range_duration(sprite, params)
5
+ local from = C.clamp(C.integer(params.from, 1), 1, #sprite.frames)
6
+ local to = C.clamp(C.integer(params.to, from), from, #sprite.frames)
7
+ local duration_ms = C.clamp(C.integer(params.durationMs, 100), 1, 60000)
8
+ app.transaction("Auvrynt Live Frame Durations", function()
9
+ for frame=from,to do sprite.frames[frame].duration = duration_ms / 1000 end
10
+ end)
11
+ return { from=from, to=to, durationMs=duration_ms }
12
+ end
13
+
14
+ local function duplicate_frame(sprite, params)
15
+ local source = C.target_frame_number(sprite, params)
16
+ local created
17
+ app.transaction("Auvrynt Live Duplicate Frame", function()
18
+ sprite:newFrame(source)
19
+ created = sprite.frames[source + 1]
20
+ if params.durationMs ~= nil then created.duration = C.clamp(C.integer(params.durationMs, 100), 1, 60000) / 1000 end
21
+ end)
22
+ app.activeFrame = created
23
+ return { source=source, frame=created.frameNumber, durationMs=math.floor(created.duration * 1000 + 0.5) }
24
+ end
25
+
26
+ local function reverse_frames(sprite, params)
27
+ local from = C.clamp(C.integer(params.from, 1), 1, #sprite.frames)
28
+ local to = C.clamp(C.integer(params.to, #sprite.frames), from, #sprite.frames)
29
+ local frames = {}
30
+ for frame=from,to do frames[#frames+1] = sprite.frames[frame] end
31
+ app.activeSprite = sprite
32
+ app.range.frames = frames
33
+ app.command.ReverseFrames()
34
+ return { from=from, to=to, count=to-from+1 }
35
+ end
36
+
37
+ local function shift_cel(sprite, params)
38
+ local layer, frame_number = C.active_target(sprite, params)
39
+ local dx, dy = C.integer(params.dx, 0), C.integer(params.dy, 0)
40
+ local cel = layer:cel(frame_number)
41
+ if not cel then C.fail("Cel does not exist") end
42
+ app.transaction("Auvrynt Live Shift Cel", function()
43
+ if params.wrap == true then
44
+ cel = C.get_canvas_cel(sprite, layer, frame_number, true)
45
+ local source = Image(cel.image)
46
+ local transparent = C.color_to_pixel(sprite, Color{r=0,g=0,b=0,a=0})
47
+ cel.image:clear(transparent)
48
+ for y=0,sprite.height-1 do
49
+ for x=0,sprite.width-1 do
50
+ local nx = (x + dx) % sprite.width
51
+ local ny = (y + dy) % sprite.height
52
+ cel.image:drawPixel(nx, ny, source:getPixel(x, y))
53
+ end
54
+ end
55
+ else
56
+ cel.position = Point(cel.position.x + dx, cel.position.y + dy)
57
+ end
58
+ end)
59
+ return { layer=layer.name, frame=frame_number, x=cel.position.x, y=cel.position.y, dx=dx, dy=dy, wrap=params.wrap == true }
60
+ end
61
+
62
+ local function flip_sprite(sprite, params)
63
+ local orientation = params.orientation == "vertical" and "vertical" or "horizontal"
64
+ app.activeSprite = sprite
65
+ app.command.Flip { orientation=orientation }
66
+ return { orientation=orientation, width=sprite.width, height=sprite.height }
67
+ end
68
+
69
+ local function rotate_sprite(sprite, params)
70
+ local angle = C.integer(params.angle, 90)
71
+ if angle ~= 90 and angle ~= 180 and angle ~= 270 and angle ~= -90 then C.fail("angle must be 90, 180, 270, or -90") end
72
+ app.activeSprite = sprite
73
+ app.command.Rotate { angle=angle, ui=false }
74
+ return { angle=angle, width=sprite.width, height=sprite.height }
75
+ end
76
+
77
+ local function resolve_open_sprite(params, prefix)
78
+ local id = params[prefix .. "Id"]
79
+ local filename = params[prefix .. "Filename"]
80
+ for _, sprite in ipairs(app.sprites) do
81
+ if id ~= nil and tostring(sprite.id) == tostring(id) then return sprite end
82
+ if filename ~= nil and sprite.filename == filename then return sprite end
83
+ end
84
+ C.fail(prefix .. " sprite was not found among open documents")
85
+ end
86
+
87
+ local function copy_between_sprites(_, params)
88
+ local source = resolve_open_sprite(params, "source")
89
+ local target = resolve_open_sprite(params, "target")
90
+ local source_layer = C.find_layer(source.layers, params.sourceLayer)
91
+ if not source_layer or source_layer.isGroup or source_layer.isTilemap then C.fail("Source drawable layer was not found") end
92
+ local source_frame = C.clamp(C.integer(params.sourceFrame, 1), 1, #source.frames)
93
+ local source_cel = source_layer:cel(source_frame)
94
+ if not source_cel then C.fail("Source cel does not exist") end
95
+ local target_layer = params.targetLayer and C.find_layer(target.layers, params.targetLayer) or nil
96
+ if not target_layer then
97
+ target_layer = target:newLayer()
98
+ target_layer.name = tostring(params.targetLayer or source_layer.name)
99
+ end
100
+ if target_layer.isGroup or target_layer.isTilemap then C.fail("Target must be a normal drawable layer") end
101
+ local target_frame = C.clamp(C.integer(params.targetFrame, 1), 1, #target.frames)
102
+ local created
103
+ app.transaction("Auvrynt Live Copy Between Sprites", function()
104
+ local existing = target_layer:cel(target_frame)
105
+ if existing then target:deleteCel(existing) end
106
+ created = target:newCel(
107
+ target_layer,
108
+ target_frame,
109
+ source_cel.image,
110
+ Point(source_cel.position.x + C.integer(params.offsetX, 0), source_cel.position.y + C.integer(params.offsetY, 0))
111
+ )
112
+ created.opacity = source_cel.opacity
113
+ created.zIndex = source_cel.zIndex
114
+ end)
115
+ app.activeSprite, app.activeLayer, app.activeFrame = target, target_layer, target.frames[target_frame]
116
+ return {
117
+ sourceId=tostring(source.id), targetId=tostring(target.id),
118
+ sourceLayer=source_layer.name, targetLayer=target_layer.name,
119
+ sourceFrame=source_frame, targetFrame=target_frame,
120
+ x=created.position.x, y=created.position.y,
121
+ }
122
+ end
123
+
124
+ M.handlers = {
125
+ set_frame_range_duration=set_frame_range_duration,
126
+ duplicate_frame=duplicate_frame,
127
+ reverse_frames=reverse_frames,
128
+ shift_cel=shift_cel,
129
+ flip_sprite=flip_sprite,
130
+ rotate_sprite=rotate_sprite,
131
+ copy_between_sprites=copy_between_sprites,
132
+ }
133
+
134
+ return M
@@ -0,0 +1,24 @@
1
+ local M = {}
2
+ local handlers = {}
3
+
4
+ for _, module_name in ipairs({ "live_drawing", "live_structure", "live_palette_template", "live_selection", "live_animation", "live_analysis", "live_document", "live_effects" }) do
5
+ local module = require(module_name)
6
+ for name, handler in pairs(module.handlers) do handlers[name] = handler end
7
+ end
8
+
9
+ function M.run(sprite, request)
10
+ local command = request.liveCommand
11
+ if type(command) ~= "string" or command == "" then error("liveCommand is required", 0) end
12
+ local handler = handlers[command]
13
+ if not handler then error("Unsupported live command: " .. command, 0) end
14
+ return handler(sprite, request.arguments or {})
15
+ end
16
+
17
+ function M.names()
18
+ local result = {}
19
+ for name in pairs(handlers) do result[#result+1] = name end
20
+ table.sort(result)
21
+ return result
22
+ end
23
+
24
+ return M
@@ -0,0 +1,130 @@
1
+ local M = {}
2
+ local pc = app.pixelColor
3
+
4
+ function M.fail(message) error(message, 0) end
5
+ function M.clamp(value, minimum, maximum) return math.max(minimum, math.min(maximum, value)) end
6
+ function M.integer(value, fallback) return math.floor(tonumber(value) or fallback or 0) end
7
+ function M.sequence_length(value)
8
+ if value == nil then return nil end
9
+ local ok, count = pcall(function() return #value end)
10
+ return ok and count or nil
11
+ end
12
+
13
+ function M.color_from_hex(value, fallback)
14
+ if type(value) ~= "string" then return fallback or Color() end
15
+ local hex = value:gsub("^#", "")
16
+ if #hex ~= 6 and #hex ~= 8 then M.fail("Colors must use #RRGGBB or #RRGGBBAA") end
17
+ local r, g, b = tonumber(hex:sub(1, 2), 16), tonumber(hex:sub(3, 4), 16), tonumber(hex:sub(5, 6), 16)
18
+ local a = #hex == 8 and tonumber(hex:sub(7, 8), 16) or 255
19
+ if not r or not g or not b or not a then M.fail("Invalid hexadecimal color") end
20
+ return Color { r=r, g=g, b=b, a=a }
21
+ end
22
+
23
+ function M.color_to_hex(color)
24
+ return string.format("#%02X%02X%02X%02X", color.red, color.green, color.blue, color.alpha)
25
+ end
26
+
27
+ function M.pixel_to_color(sprite, pixel)
28
+ if sprite.colorMode == ColorMode.RGB then
29
+ return Color { r=pc.rgbaR(pixel), g=pc.rgbaG(pixel), b=pc.rgbaB(pixel), a=pc.rgbaA(pixel) }
30
+ elseif sprite.colorMode == ColorMode.GRAY then
31
+ local value = pc.grayaV(pixel)
32
+ return Color { r=value, g=value, b=value, a=pc.grayaA(pixel) }
33
+ end
34
+ local palette = sprite.palettes[1]
35
+ local color = palette:getColor(pixel)
36
+ return Color { r=color.red, g=color.green, b=color.blue, a=pixel == sprite.transparentColor and 0 or color.alpha }
37
+ end
38
+
39
+ function M.color_to_pixel(sprite, color)
40
+ if sprite.colorMode == ColorMode.RGB then return pc.rgba(color.red, color.green, color.blue, color.alpha) end
41
+ if sprite.colorMode == ColorMode.GRAY then
42
+ local gray = math.floor((color.red*299 + color.green*587 + color.blue*114)/1000)
43
+ return pc.graya(gray, color.alpha)
44
+ end
45
+ if color.alpha == 0 then return sprite.transparentColor end
46
+ local palette, best, distance = sprite.palettes[1], 0, math.huge
47
+ for index=0,#palette-1 do
48
+ local candidate = palette:getColor(index)
49
+ local dr, dg, db, da = candidate.red-color.red, candidate.green-color.green, candidate.blue-color.blue, candidate.alpha-color.alpha
50
+ local current = dr*dr + dg*dg + db*db + da*da
51
+ if current < distance then best, distance = index, current end
52
+ end
53
+ return best
54
+ end
55
+
56
+ function M.pixel_alpha(sprite, pixel)
57
+ if sprite.colorMode == ColorMode.RGB then return pc.rgbaA(pixel) end
58
+ if sprite.colorMode == ColorMode.GRAY then return pc.grayaA(pixel) end
59
+ if pixel == sprite.transparentColor then return 0 end
60
+ return sprite.palettes[1]:getColor(pixel).alpha
61
+ end
62
+
63
+ function M.find_layer(items, name_or_id)
64
+ if name_or_id == nil then return nil end
65
+ for _, layer in ipairs(items) do
66
+ if layer.name == name_or_id or tostring(layer.id) == tostring(name_or_id) then return layer end
67
+ if layer.isGroup then
68
+ local nested = M.find_layer(layer.layers, name_or_id)
69
+ if nested then return nested end
70
+ end
71
+ end
72
+ return nil
73
+ end
74
+
75
+ function M.target_layer(sprite, params)
76
+ local layer = params.layer and M.find_layer(sprite.layers, params.layer) or app.activeLayer or sprite.layers[1]
77
+ if not layer then M.fail("Layer was not found") end
78
+ if layer.isGroup or layer.isTilemap then M.fail("A normal drawable layer is required") end
79
+ return layer
80
+ end
81
+
82
+ function M.target_frame_number(sprite, params)
83
+ local number = M.integer(params.frame, app.activeFrame and app.activeFrame.frameNumber or 1)
84
+ if number < 1 or number > #sprite.frames then M.fail("Frame is outside the sprite") end
85
+ return number
86
+ end
87
+
88
+ function M.get_canvas_cel(sprite, layer, frame_number, create)
89
+ local cel = layer:cel(frame_number)
90
+ if not cel and create then
91
+ local image = Image(sprite.spec)
92
+ image:clear()
93
+ cel = sprite:newCel(layer, frame_number, image, Point(0, 0))
94
+ end
95
+ if cel and create and (cel.position.x ~= 0 or cel.position.y ~= 0 or cel.image.width ~= sprite.width or cel.image.height ~= sprite.height) then
96
+ local image = Image(sprite.spec)
97
+ image:clear()
98
+ image:drawImage(cel.image, cel.position)
99
+ cel.image = image
100
+ cel.position = Point(0, 0)
101
+ end
102
+ return cel
103
+ end
104
+
105
+ function M.selection_allows(sprite, x, y)
106
+ local selection = sprite.selection
107
+ return selection == nil or selection.isEmpty or selection:contains(Point(x, y))
108
+ end
109
+
110
+ function M.active_target(sprite, params)
111
+ local layer, frame_number = M.target_layer(sprite, params), M.target_frame_number(sprite, params)
112
+ app.activeSprite, app.activeLayer, app.activeFrame = sprite, layer, sprite.frames[frame_number]
113
+ return layer, frame_number
114
+ end
115
+
116
+ function M.read_canvas(sprite, params)
117
+ local frame_number = M.target_frame_number(sprite, params)
118
+ local image = Image(sprite.spec)
119
+ image:clear()
120
+ if params.composite == true then
121
+ image:drawSprite(sprite, frame_number, Point(0, 0))
122
+ else
123
+ local layer = M.target_layer(sprite, params)
124
+ local cel = layer:cel(frame_number)
125
+ if cel then image:drawImage(cel.image, cel.position) end
126
+ end
127
+ return image, frame_number
128
+ end
129
+
130
+ return M
@@ -0,0 +1,78 @@
1
+ local C = require("live_common")
2
+ local M = {}
3
+
4
+ local function resize_sprite(sprite, params)
5
+ local width = C.clamp(C.integer(params.width, sprite.width), 1, 8192)
6
+ local height = C.clamp(C.integer(params.height, sprite.height), 1, 8192)
7
+ app.activeSprite = sprite
8
+ sprite:resize(width, height)
9
+ return { width=sprite.width, height=sprite.height }
10
+ end
11
+
12
+ local function crop_sprite(sprite, params)
13
+ local x = C.integer(params.x, 0)
14
+ local y = C.integer(params.y, 0)
15
+ local width = C.integer(params.width, sprite.width)
16
+ local height = C.integer(params.height, sprite.height)
17
+ if width < 1 or height < 1 then C.fail("width and height must be positive") end
18
+ sprite:crop(Rectangle(x, y, width, height))
19
+ return { width=sprite.width, height=sprite.height, source={x=x,y=y,width=width,height=height} }
20
+ end
21
+
22
+ local function flatten_sprite(sprite, _)
23
+ sprite:flatten()
24
+ app.activeLayer = sprite.layers[1]
25
+ return { layers=#sprite.layers, activeLayer=sprite.layers[1].name }
26
+ end
27
+
28
+ local function set_grid(sprite, params)
29
+ local x = C.integer(params.x, 0)
30
+ local y = C.integer(params.y, 0)
31
+ local width = C.clamp(C.integer(params.width, 8), 1, 8192)
32
+ local height = C.clamp(C.integer(params.height, 8), 1, 8192)
33
+ sprite.gridBounds = Rectangle(x, y, width, height)
34
+ return { x=x, y=y, width=width, height=height }
35
+ end
36
+
37
+ local function create_tileset_template(_, params)
38
+ local tile_size = C.clamp(C.integer(params.tileSize, 16), 1, 256)
39
+ local columns = C.clamp(C.integer(params.columns, 8), 1, 128)
40
+ local rows = C.clamp(C.integer(params.rows, 8), 1, 128)
41
+ local width, height = tile_size * columns, tile_size * rows
42
+ if width > 8192 or height > 8192 then C.fail("Tileset template exceeds Aseprite's 8192px bridge limit") end
43
+ local sprite = Sprite(width, height, ColorMode.RGB)
44
+ app.activeSprite = sprite
45
+ app.transaction("Auvrynt Tileset Template", function()
46
+ sprite.layers[1].name = "tiles"
47
+ sprite.gridBounds = Rectangle(0, 0, tile_size, tile_size)
48
+ local grid = sprite:newLayer()
49
+ grid.name = "grid-guide"
50
+ grid.opacity = C.clamp(C.integer(params.gridOpacity, 64), 0, 255)
51
+ local image = Image(sprite.spec)
52
+ image:clear()
53
+ local color = C.color_to_pixel(sprite, C.color_from_hex(params.gridColor or "#FFFFFFFF"))
54
+ for column=1,columns-1 do
55
+ local x = column * tile_size
56
+ for y=0,height-1 do image:drawPixel(x, y, color) end
57
+ end
58
+ for row=1,rows-1 do
59
+ local y = row * tile_size
60
+ for x=0,width-1 do image:drawPixel(x, y, color) end
61
+ end
62
+ sprite:newCel(grid, 1, image, Point(0, 0))
63
+ end)
64
+ return {
65
+ id=tostring(sprite.id), tileSize=tile_size, columns=columns, rows=rows,
66
+ width=width, height=height, layers={"tiles","grid-guide"},
67
+ }
68
+ end
69
+
70
+ M.handlers = {
71
+ resize_sprite=resize_sprite,
72
+ crop_sprite=crop_sprite,
73
+ flatten_sprite=flatten_sprite,
74
+ set_grid=set_grid,
75
+ create_tileset_template=create_tileset_template,
76
+ }
77
+
78
+ return M
@@ -0,0 +1,125 @@
1
+ local C = require("live_common")
2
+ local M = {}
3
+
4
+ local function get_pixel(sprite, params)
5
+ local x, y = C.integer(params.x, -1), C.integer(params.y, -1)
6
+ if x < 0 or y < 0 or x >= sprite.width or y >= sprite.height then C.fail("Pixel is outside the sprite") end
7
+ local image, frame_number = C.read_canvas(sprite, params)
8
+ local color = C.pixel_to_color(sprite, image:getPixel(x, y))
9
+ return { x=x, y=y, frame=frame_number, color=C.color_to_hex(color), r=color.red, g=color.green, b=color.blue, a=color.alpha }
10
+ end
11
+
12
+ local function get_image_data(sprite, params)
13
+ local image, frame_number = C.read_canvas(sprite, params)
14
+ local region = params.region or {}
15
+ local x = C.clamp(C.integer(region.x, 0), 0, sprite.width - 1)
16
+ local y = C.clamp(C.integer(region.y, 0), 0, sprite.height - 1)
17
+ local width = C.clamp(C.integer(region.width, sprite.width - x), 1, sprite.width - x)
18
+ local height = C.clamp(C.integer(region.height, sprite.height - y), 1, sprite.height - y)
19
+ if width * height > 16384 then C.fail("Live image readback is limited to 16384 pixels") end
20
+ local rows = {}
21
+ for row=0,height-1 do
22
+ local output = {}
23
+ for column=0,width-1 do output[column+1] = C.color_to_hex(C.pixel_to_color(sprite, image:getPixel(x+column, y+row))) end
24
+ rows[row+1] = output
25
+ end
26
+ return { frame=frame_number, x=x, y=y, width=width, height=height, composite=params.composite == true, rows=rows }
27
+ end
28
+
29
+ local function draw_tool(sprite, params, tool, point_count)
30
+ local layer, frame_number = C.active_target(sprite, params)
31
+ local points = { Point(C.integer(params.x, 0), C.integer(params.y, 0)) }
32
+ if point_count == 2 then
33
+ local x2 = params.x2 ~= nil and C.integer(params.x2, 0) or C.integer(params.x, 0) + C.integer(params.width, 1) - 1
34
+ local y2 = params.y2 ~= nil and C.integer(params.y2, 0) or C.integer(params.y, 0) + C.integer(params.height, 1) - 1
35
+ points[2] = Point(x2, y2)
36
+ end
37
+ app.transaction("Auvrynt Live " .. tool, function()
38
+ app.useTool {
39
+ tool=tool,
40
+ color=C.color_from_hex(params.color, app.fgColor),
41
+ opacity=C.clamp(C.integer(params.opacity, 255), 0, 255),
42
+ brush=Brush(C.clamp(C.integer(params.brushSize, 1), 1, 256)),
43
+ points=points,
44
+ layer=layer,
45
+ frame=sprite.frames[frame_number],
46
+ }
47
+ end)
48
+ return { tool=tool, frame=frame_number, layer=layer.name }
49
+ end
50
+
51
+ local function clear_image(sprite, params)
52
+ local layer, frame_number = C.active_target(sprite, params)
53
+ local fill = params.color and C.color_to_pixel(sprite, C.color_from_hex(params.color)) or C.color_to_pixel(sprite, Color{r=0,g=0,b=0,a=0})
54
+ local changed = 0
55
+ app.transaction("Auvrynt Live Clear", function()
56
+ local cel = C.get_canvas_cel(sprite, layer, frame_number, true)
57
+ for y=0,sprite.height-1 do
58
+ for x=0,sprite.width-1 do
59
+ if C.selection_allows(sprite, x, y) then cel.image:drawPixel(x, y, fill) changed = changed + 1 end
60
+ end
61
+ end
62
+ end)
63
+ return { changed=changed, frame=frame_number, layer=layer.name }
64
+ end
65
+
66
+ local function replace_color(sprite, params)
67
+ if type(params.fromColor) ~= "string" or type(params.toColor) ~= "string" then C.fail("fromColor and toColor are required") end
68
+ local layer, frame_number = C.active_target(sprite, params)
69
+ local from_pixel = C.color_to_pixel(sprite, C.color_from_hex(params.fromColor))
70
+ local to_pixel = C.color_to_pixel(sprite, C.color_from_hex(params.toColor))
71
+ local changed = 0
72
+ app.transaction("Auvrynt Live Replace Color", function()
73
+ local cel = C.get_canvas_cel(sprite, layer, frame_number, true)
74
+ for y=0,sprite.height-1 do
75
+ for x=0,sprite.width-1 do
76
+ if C.selection_allows(sprite, x, y) and cel.image:getPixel(x, y) == from_pixel then
77
+ cel.image:drawPixel(x, y, to_pixel)
78
+ changed = changed + 1
79
+ end
80
+ end
81
+ end
82
+ end)
83
+ return { changed=changed, frame=frame_number, layer=layer.name }
84
+ end
85
+
86
+ local function outline(sprite, params)
87
+ if type(params.color) ~= "string" then C.fail("color is required") end
88
+ local layer, frame_number = C.active_target(sprite, params)
89
+ local outline_pixel = C.color_to_pixel(sprite, C.color_from_hex(params.color))
90
+ local neighbors = params.diagonal == true
91
+ and { {-1,-1},{0,-1},{1,-1},{-1,0},{1,0},{-1,1},{0,1},{1,1} }
92
+ or { {0,-1},{-1,0},{1,0},{0,1} }
93
+ local changed = 0
94
+ app.transaction("Auvrynt Live Outline", function()
95
+ local cel = C.get_canvas_cel(sprite, layer, frame_number, true)
96
+ local source = Image(cel.image)
97
+ for y=0,sprite.height-1 do
98
+ for x=0,sprite.width-1 do
99
+ if C.selection_allows(sprite, x, y) and C.pixel_alpha(sprite, source:getPixel(x, y)) == 0 then
100
+ local adjacent = false
101
+ for _, offset in ipairs(neighbors) do
102
+ local nx, ny = x + offset[1], y + offset[2]
103
+ if nx >= 0 and ny >= 0 and nx < sprite.width and ny < sprite.height and C.pixel_alpha(sprite, source:getPixel(nx, ny)) > 0 then adjacent = true break end
104
+ end
105
+ if adjacent then cel.image:drawPixel(x, y, outline_pixel) changed = changed + 1 end
106
+ end
107
+ end
108
+ end
109
+ end)
110
+ return { changed=changed, frame=frame_number, layer=layer.name, diagonal=params.diagonal == true }
111
+ end
112
+
113
+ M.handlers = {
114
+ get_pixel=get_pixel,
115
+ get_image_data=get_image_data,
116
+ draw_line=function(sprite, params) return draw_tool(sprite, params, "line", 2) end,
117
+ draw_rect=function(sprite, params) return draw_tool(sprite, params, params.filled == false and "rectangle" or "filled_rectangle", 2) end,
118
+ draw_ellipse=function(sprite, params) return draw_tool(sprite, params, params.filled == false and "ellipse" or "filled_ellipse", 2) end,
119
+ flood_fill=function(sprite, params) return draw_tool(sprite, params, "paint_bucket", 1) end,
120
+ clear_image=clear_image,
121
+ replace_color=replace_color,
122
+ outline=outline,
123
+ }
124
+
125
+ return M