@pebbly/aseprite-ai-artist 0.1.3 → 0.1.4
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/CHANGELOG.md +28 -0
- package/README.md +12 -2
- package/extension/ai-artist.lua +60 -11
- package/extension/package.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,34 @@ All notable changes to this project are documented here. Format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning is
|
|
5
5
|
[semver](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.1.4] — 2026-09-08
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **GIF export waited for a click nobody was there to give.** Aseprite warns
|
|
12
|
+
once per session that GIF cannot hold everything a sprite can, and no save API
|
|
13
|
+
declines it. From outside it did not look like a prompt at all: a modal pumps
|
|
14
|
+
events while it waits, so every other command kept answering and only the
|
|
15
|
+
export appeared to hang. It is now suppressed for the duration of the export
|
|
16
|
+
and handed straight back, so File ▸ Save As keeps whatever the user chose.
|
|
17
|
+
- 0.1.3 tried to fix this with `SaveFileCopyAs{ui = false}`. That flag does not
|
|
18
|
+
govern this dialog. GIF export now converts a throwaway copy to indexed
|
|
19
|
+
instead of leaving the conversion to a prompt — and on a copy, because doing
|
|
20
|
+
it in place would silently change the colour mode of the document being
|
|
21
|
+
worked in.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- `export` op `gif` honours `scale`. A browser scaling a 128px GIF up smooths
|
|
26
|
+
it, and smoothed pixel art is ruined pixel art; exporting big keeps the pixels
|
|
27
|
+
square wherever the file is shown.
|
|
28
|
+
|
|
29
|
+
### Known
|
|
30
|
+
|
|
31
|
+
- The first GIF export in an Aseprite session costs 20-30 seconds — measured
|
|
32
|
+
20.6s, then 435ms for the identical export straight after. Something warms up
|
|
33
|
+
once. The 120s export budget from 0.1.3 covers it.
|
|
34
|
+
|
|
7
35
|
## [0.1.3] — 2026-09-08
|
|
8
36
|
|
|
9
37
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
+
<img src="docs/media/hero.gif" alt="A pixel robot paints a mushroom house, stroke by stroke" width="512">
|
|
4
|
+
|
|
3
5
|
# Aseprite AI Artist
|
|
4
6
|
|
|
5
7
|
**Let a coding agent draw pixel art in your open Aseprite window** — not in a
|
|
@@ -9,6 +11,10 @@ copy, not on disk, in the document you are looking at.
|
|
|
9
11
|
[](https://github.com/with-pebbly/aseprite-ai-artist/actions/workflows/ci.yml)
|
|
10
12
|
[](LICENSE)
|
|
11
13
|
|
|
14
|
+
*Forty frames, one 16-colour palette, drawn by Codex through this server in a
|
|
15
|
+
live Aseprite window — silhouette, flats, hue-shifted shading, then the lights
|
|
16
|
+
come on. Nobody touched a pixel by hand.*
|
|
17
|
+
|
|
12
18
|
</div>
|
|
13
19
|
|
|
14
20
|
---
|
|
@@ -110,8 +116,12 @@ that surprises people:
|
|
|
110
116
|
32×32 grid is a spatial problem: the model has to hold a coordinate frame in
|
|
111
117
|
its head, keep a silhouette readable at that size, and notice when a shape has
|
|
112
118
|
gone wrong. Turn reasoning up before you blame the tool.
|
|
113
|
-
- **Codex CLI on a high-reasoning setting is the best drawer we have used
|
|
114
|
-
|
|
119
|
+
- **Codex CLI on a high-reasoning setting is the best drawer we have used** — it
|
|
120
|
+
drew the animation at the top of this page and the mascot below, working from
|
|
121
|
+
a brief and the rulebook alone.
|
|
122
|
+
|
|
123
|
+
<img src="docs/media/mascot.png" alt="The mascot" width="96">
|
|
124
|
+
|
|
115
125
|
- **Claude Code is the better planner and critic.** Palette construction, rig
|
|
116
126
|
layout, animation timing and the review pass come out noticeably stronger;
|
|
117
127
|
raw pixel placement at small canvas sizes is weaker.
|
package/extension/ai-artist.lua
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
--------------------------------------------------------------------------------
|
|
16
16
|
|
|
17
17
|
local PROTOCOL_VERSION = 1
|
|
18
|
-
local EXTENSION_VERSION = "0.1.
|
|
18
|
+
local EXTENSION_VERSION = "0.1.4"
|
|
19
19
|
|
|
20
20
|
-- Optional capabilities. The wire version stays 1 across builds; new command
|
|
21
21
|
-- families are gated on these flags plus the loud unsupported_command reply,
|
|
@@ -2210,17 +2210,66 @@ H["export.run"] = function(args)
|
|
|
2210
2210
|
out:close()
|
|
2211
2211
|
files[#files + 1] = path
|
|
2212
2212
|
|
|
2213
|
-
elseif op == "
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
--
|
|
2219
|
-
--
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2213
|
+
elseif op == "aseprite" then
|
|
2214
|
+
s:saveCopyAs(path)
|
|
2215
|
+
files[#files + 1] = path
|
|
2216
|
+
|
|
2217
|
+
elseif op == "gif" then
|
|
2218
|
+
-- GIF holds 256 indexed colours, so writing an RGB sprite to one needs a
|
|
2219
|
+
-- quantisation — and Aseprite asks a human how to do it. Neither
|
|
2220
|
+
-- Sprite:saveCopyAs nor SaveFileCopyAs{ui = false} declines that dialog;
|
|
2221
|
+
-- the export simply waits, forever if nobody is watching the screen. It
|
|
2222
|
+
-- does not even look like a prompt from the outside: a modal dialog pumps
|
|
2223
|
+
-- events while it waits, so every other command keeps answering normally
|
|
2224
|
+
-- and only the export appears stuck.
|
|
2225
|
+
--
|
|
2226
|
+
-- So do the conversion here, on a throwaway copy, and hand Aseprite a
|
|
2227
|
+
-- sprite it has nothing left to ask about. The copy keeps the sprite's
|
|
2228
|
+
-- own palette, which is the point of a palette-first tool: the colours
|
|
2229
|
+
-- that come back are the colours the artist chose.
|
|
2230
|
+
--
|
|
2231
|
+
-- Headless tests cannot catch a regression here — `aseprite -b` has no
|
|
2232
|
+
-- dialogs at all, so the broken version passed them.
|
|
2233
|
+
-- `scale` matters more here than for a still: a browser scaling a 128px
|
|
2234
|
+
-- GIF up smooths it, and smoothed pixel art is ruined pixel art. Exporting
|
|
2235
|
+
-- big means the pixels stay square wherever the file is shown.
|
|
2236
|
+
local scale = args.scale or 1
|
|
2237
|
+
|
|
2238
|
+
-- Aseprite warns, once per session, that GIF cannot hold everything a
|
|
2239
|
+
-- sprite can — and waits for a click. Nothing in the save API declines
|
|
2240
|
+
-- it, and from the outside it does not even look like a prompt: a modal
|
|
2241
|
+
-- pumps events while it waits, so every other command keeps answering and
|
|
2242
|
+
-- only the export appears to hang, forever, on a machine nobody is
|
|
2243
|
+
-- watching. Silence it for the duration and hand it back, so the user's
|
|
2244
|
+
-- own File > Save As keeps whatever they chose.
|
|
2245
|
+
local restore_alert = nil
|
|
2246
|
+
pcall(function()
|
|
2247
|
+
restore_alert = app.preferences.gif.show_alert
|
|
2248
|
+
app.preferences.gif.show_alert = false
|
|
2223
2249
|
end)
|
|
2250
|
+
|
|
2251
|
+
local ok, err = pcall(function()
|
|
2252
|
+
if s.colorMode == ColorMode.INDEXED and scale == 1 then
|
|
2253
|
+
s:saveCopyAs(path)
|
|
2254
|
+
else
|
|
2255
|
+
preserving_site(function()
|
|
2256
|
+
local copy = Sprite(s)
|
|
2257
|
+
app.sprite = copy
|
|
2258
|
+
if scale > 1 then copy:resize(s.width * scale, s.height * scale) end
|
|
2259
|
+
if copy.colorMode ~= ColorMode.INDEXED then
|
|
2260
|
+
app.command.ChangePixelFormat{ ui = false, format = "indexed" }
|
|
2261
|
+
end
|
|
2262
|
+
copy:saveCopyAs(path)
|
|
2263
|
+
copy:close()
|
|
2264
|
+
end)
|
|
2265
|
+
end
|
|
2266
|
+
end)
|
|
2267
|
+
|
|
2268
|
+
if restore_alert ~= nil then
|
|
2269
|
+
pcall(function() app.preferences.gif.show_alert = restore_alert end)
|
|
2270
|
+
end
|
|
2271
|
+
if not ok then error(err, 0) end
|
|
2272
|
+
|
|
2224
2273
|
files[#files + 1] = path
|
|
2225
2274
|
|
|
2226
2275
|
elseif op == "frames" then
|
package/extension/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "aseprite-ai-artist",
|
|
3
3
|
"displayName": "Aseprite AI Artist",
|
|
4
4
|
"description": "Lets a coding agent draw in this Aseprite session over a local WebSocket link. Connects to 127.0.0.1 only; nothing leaves the machine.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.4",
|
|
6
6
|
"author": "with-pebbly",
|
|
7
7
|
"publisher": "with-pebbly",
|
|
8
8
|
"license": "MIT",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pebbly/aseprite-ai-artist",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Agent-native control layer for Aseprite. MCP server (2025-11-25 spec, designed forward to 2026-07-28) + cross-agent skills for Claude Code, Codex, Gemini CLI and Cursor.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|