sprite-animator 0.1.0__tar.gz
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.
- sprite_animator-0.1.0/.github/workflows/ci.yml +67 -0
- sprite_animator-0.1.0/.gitignore +13 -0
- sprite_animator-0.1.0/LICENSE +21 -0
- sprite_animator-0.1.0/PKG-INFO +210 -0
- sprite_animator-0.1.0/README.md +186 -0
- sprite_animator-0.1.0/banner.png +0 -0
- sprite_animator-0.1.0/pyproject.toml +47 -0
- sprite_animator-0.1.0/sprite_animator/__init__.py +3 -0
- sprite_animator-0.1.0/sprite_animator/cli.py +304 -0
- sprite_animator-0.1.0/sprite_animator/template.py +85 -0
- sprite_animator-0.1.0/uv.lock +287 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ['v*']
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v4
|
|
22
|
+
|
|
23
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
24
|
+
run: uv python install ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: uv sync --all-extras --dev
|
|
28
|
+
|
|
29
|
+
- name: Lint with ruff
|
|
30
|
+
run: uv run ruff check .
|
|
31
|
+
continue-on-error: true
|
|
32
|
+
|
|
33
|
+
- name: Run tests
|
|
34
|
+
run: uv run pytest -m "not integration"
|
|
35
|
+
continue-on-error: true
|
|
36
|
+
|
|
37
|
+
publish:
|
|
38
|
+
needs: test
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
|
|
45
|
+
- name: Install uv
|
|
46
|
+
uses: astral-sh/setup-uv@v4
|
|
47
|
+
|
|
48
|
+
- name: Build package
|
|
49
|
+
run: uv build
|
|
50
|
+
|
|
51
|
+
- name: Publish to PyPI
|
|
52
|
+
env:
|
|
53
|
+
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
|
54
|
+
run: uv publish --token $UV_PUBLISH_TOKEN
|
|
55
|
+
continue-on-error: true
|
|
56
|
+
|
|
57
|
+
- name: Install ClawHub CLI
|
|
58
|
+
run: npm install -g clawhub
|
|
59
|
+
|
|
60
|
+
- name: Publish to ClawHub
|
|
61
|
+
env:
|
|
62
|
+
CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }}
|
|
63
|
+
run: |
|
|
64
|
+
VERSION=${GITHUB_REF#refs/tags/v}
|
|
65
|
+
clawhub login --token "$CLAWHUB_TOKEN" --no-browser
|
|
66
|
+
clawhub publish . --slug sprite-animator --name "Sprite Animator" --version "$VERSION" --changelog "Release $VERSION"
|
|
67
|
+
continue-on-error: true
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Olafs-World
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sprite-animator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate animated pixel art sprites from any image using AI
|
|
5
|
+
Project-URL: Homepage, https://github.com/Olafs-World/sprite-animator
|
|
6
|
+
Project-URL: Repository, https://github.com/Olafs-World/sprite-animator
|
|
7
|
+
Project-URL: Issues, https://github.com/Olafs-World/sprite-animator/issues
|
|
8
|
+
Author-email: Aaron Levin <awlevin@comcast.net>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai,animation,game-dev,gif,pixel-art,sprite
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Games/Entertainment
|
|
20
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: pillow>=10.0.0
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
# sprite-animator 🎮
|
|
28
|
+
|
|
29
|
+
[](https://github.com/Olafs-World/sprite-animator/actions/workflows/ci.yml)
|
|
30
|
+
[](https://pypi.org/project/sprite-animator/)
|
|
31
|
+
[](https://www.python.org/downloads/)
|
|
32
|
+
[](https://opensource.org/licenses/MIT)
|
|
33
|
+
|
|
34
|
+
**Turn any image into an animated pixel art sprite.** Send a photo, get a 16-frame animated GIF. Powered by AI image generation.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
$ sprite-animator -i cat.png -o cat-idle.gif
|
|
38
|
+
|
|
39
|
+
🎮 sprite-animator
|
|
40
|
+
input: cat.png
|
|
41
|
+
animation: idle (16 frames, 4x4 grid)
|
|
42
|
+
output: cat-idle.gif
|
|
43
|
+
|
|
44
|
+
📐 creating sprite sheet template...
|
|
45
|
+
🎨 generating sprite sheet (single request)...
|
|
46
|
+
✓ sprite sheet generated
|
|
47
|
+
✂️ extracting 16 frames...
|
|
48
|
+
✓ extracted 16 frames
|
|
49
|
+
🔄 assembling animated GIF...
|
|
50
|
+
|
|
51
|
+
✨ done! saved: cat-idle.gif
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## How It Works
|
|
55
|
+
|
|
56
|
+
1. **Template** — A labeled 4×4 grid is generated to guide the AI
|
|
57
|
+
2. **Generation** — The grid template + your source image are sent to [Gemini](https://ai.google.dev/) in a single request
|
|
58
|
+
3. **Extraction** — The returned sprite sheet is sliced into 16 individual frames
|
|
59
|
+
4. **Assembly** — Frames are compiled into a looping animated GIF
|
|
60
|
+
|
|
61
|
+
One API call. Consistent character across all frames. No frame-by-frame generation drift.
|
|
62
|
+
|
|
63
|
+
## Animation Types
|
|
64
|
+
|
|
65
|
+
| Type | Description |
|
|
66
|
+
|------|-------------|
|
|
67
|
+
| `idle` | Gentle breathing + blink cycle (default) |
|
|
68
|
+
| `wave` | Arm raise → wave → return |
|
|
69
|
+
| `bounce` | Crouch → jump → land → recover |
|
|
70
|
+
| `dance` | Lean, spin, jump — full party mode |
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
### Quick run (no install)
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
uvx sprite-animator -i photo.png -o sprite.gif
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Install as CLI tool
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
uv tool install sprite-animator
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Install as OpenClaw skill
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
clawhub install sprite-animator
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Add to a project
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
uv add sprite-animator
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or with pip:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install sprite-animator
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Requirements
|
|
105
|
+
|
|
106
|
+
- Python 3.10+
|
|
107
|
+
- A Google AI API key (for Gemini image generation)
|
|
108
|
+
- [nano-banana-pro](https://clawhub.com/skills/nano-banana-pro) skill installed (provides the generation backend)
|
|
109
|
+
|
|
110
|
+
## Usage
|
|
111
|
+
|
|
112
|
+
### Command Line
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Default idle animation
|
|
116
|
+
sprite-animator -i photo.png -o idle.gif
|
|
117
|
+
|
|
118
|
+
# Wave animation, larger sprites
|
|
119
|
+
sprite-animator -i avatar.png -o wave.gif -a wave -s 256
|
|
120
|
+
|
|
121
|
+
# Bouncy animation, slower playback
|
|
122
|
+
sprite-animator -i pet.jpg -o bounce.gif -a bounce -d 150
|
|
123
|
+
|
|
124
|
+
# Dance animation, keep the raw sprite sheet
|
|
125
|
+
sprite-animator -i character.png -o dance.gif -a dance --keep-sheet
|
|
126
|
+
|
|
127
|
+
# Higher resolution generation
|
|
128
|
+
sprite-animator -i hero.png -o hero.gif -r 2K
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Python API
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from pathlib import Path
|
|
135
|
+
from sprite_animator.cli import ANIMATION_PRESETS, generate_sprite_sheet, create_gif
|
|
136
|
+
from sprite_animator.template import create_template, extract_frames
|
|
137
|
+
from PIL import Image
|
|
138
|
+
|
|
139
|
+
# Pick an animation
|
|
140
|
+
preset = ANIMATION_PRESETS["wave"]
|
|
141
|
+
|
|
142
|
+
# Create the template grid
|
|
143
|
+
template = create_template(
|
|
144
|
+
cols=preset["cols"],
|
|
145
|
+
rows=preset["rows"],
|
|
146
|
+
labels=preset["labels"],
|
|
147
|
+
)
|
|
148
|
+
template.save("template.png")
|
|
149
|
+
|
|
150
|
+
# Generate sprite sheet (requires nano-banana-pro)
|
|
151
|
+
generate_sprite_sheet(
|
|
152
|
+
input_image=Path("photo.png"),
|
|
153
|
+
template_path=Path("template.png"),
|
|
154
|
+
output_path=Path("sheet.png"),
|
|
155
|
+
prompt=preset["prompt"],
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Extract frames and build GIF
|
|
159
|
+
sheet = Image.open("sheet.png")
|
|
160
|
+
frames = extract_frames(sheet, cols=4, rows=4)
|
|
161
|
+
create_gif(frames, Path("output.gif"), frame_duration=100, size=128)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## CLI Reference
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
sprite-animator [OPTIONS]
|
|
168
|
+
|
|
169
|
+
Options:
|
|
170
|
+
-i, --input PATH Input image (required)
|
|
171
|
+
-o, --output PATH Output GIF path (required)
|
|
172
|
+
-a, --animation TYPE Animation type: idle, wave, bounce, dance (default: idle)
|
|
173
|
+
-d, --duration MS Frame duration in milliseconds (default: 100)
|
|
174
|
+
-s, --size PX Output sprite size in pixels (default: 128)
|
|
175
|
+
-r, --resolution RES Generation resolution: 1K or 2K (default: 1K)
|
|
176
|
+
--keep-sheet Save the raw sprite sheet alongside the GIF
|
|
177
|
+
--keep-frames Save individual frame PNGs
|
|
178
|
+
-v, --verbose Verbose output
|
|
179
|
+
--help Show help
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
git clone https://github.com/Olafs-World/sprite-animator.git
|
|
186
|
+
cd sprite-animator
|
|
187
|
+
uv sync
|
|
188
|
+
|
|
189
|
+
# Run tests
|
|
190
|
+
uv run pytest -m "not integration"
|
|
191
|
+
|
|
192
|
+
# Lint
|
|
193
|
+
uv run ruff check .
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Links
|
|
197
|
+
|
|
198
|
+
- [PyPI](https://pypi.org/project/sprite-animator/)
|
|
199
|
+
- [GitHub](https://github.com/Olafs-World/sprite-animator)
|
|
200
|
+
- [ClawHub Skill](https://clawhub.com/skills/sprite-animator)
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT © [Olaf](https://olafs-world.vercel.app)
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
<p align="center">
|
|
209
|
+
<i>Built by an AI who wanted to see things wiggle 🕺</i>
|
|
210
|
+
</p>
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# sprite-animator 🎮
|
|
4
|
+
|
|
5
|
+
[](https://github.com/Olafs-World/sprite-animator/actions/workflows/ci.yml)
|
|
6
|
+
[](https://pypi.org/project/sprite-animator/)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
**Turn any image into an animated pixel art sprite.** Send a photo, get a 16-frame animated GIF. Powered by AI image generation.
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
$ sprite-animator -i cat.png -o cat-idle.gif
|
|
14
|
+
|
|
15
|
+
🎮 sprite-animator
|
|
16
|
+
input: cat.png
|
|
17
|
+
animation: idle (16 frames, 4x4 grid)
|
|
18
|
+
output: cat-idle.gif
|
|
19
|
+
|
|
20
|
+
📐 creating sprite sheet template...
|
|
21
|
+
🎨 generating sprite sheet (single request)...
|
|
22
|
+
✓ sprite sheet generated
|
|
23
|
+
✂️ extracting 16 frames...
|
|
24
|
+
✓ extracted 16 frames
|
|
25
|
+
🔄 assembling animated GIF...
|
|
26
|
+
|
|
27
|
+
✨ done! saved: cat-idle.gif
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## How It Works
|
|
31
|
+
|
|
32
|
+
1. **Template** — A labeled 4×4 grid is generated to guide the AI
|
|
33
|
+
2. **Generation** — The grid template + your source image are sent to [Gemini](https://ai.google.dev/) in a single request
|
|
34
|
+
3. **Extraction** — The returned sprite sheet is sliced into 16 individual frames
|
|
35
|
+
4. **Assembly** — Frames are compiled into a looping animated GIF
|
|
36
|
+
|
|
37
|
+
One API call. Consistent character across all frames. No frame-by-frame generation drift.
|
|
38
|
+
|
|
39
|
+
## Animation Types
|
|
40
|
+
|
|
41
|
+
| Type | Description |
|
|
42
|
+
|------|-------------|
|
|
43
|
+
| `idle` | Gentle breathing + blink cycle (default) |
|
|
44
|
+
| `wave` | Arm raise → wave → return |
|
|
45
|
+
| `bounce` | Crouch → jump → land → recover |
|
|
46
|
+
| `dance` | Lean, spin, jump — full party mode |
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
### Quick run (no install)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uvx sprite-animator -i photo.png -o sprite.gif
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Install as CLI tool
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv tool install sprite-animator
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Install as OpenClaw skill
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
clawhub install sprite-animator
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Add to a project
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
uv add sprite-animator
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Or with pip:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install sprite-animator
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Requirements
|
|
81
|
+
|
|
82
|
+
- Python 3.10+
|
|
83
|
+
- A Google AI API key (for Gemini image generation)
|
|
84
|
+
- [nano-banana-pro](https://clawhub.com/skills/nano-banana-pro) skill installed (provides the generation backend)
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
### Command Line
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Default idle animation
|
|
92
|
+
sprite-animator -i photo.png -o idle.gif
|
|
93
|
+
|
|
94
|
+
# Wave animation, larger sprites
|
|
95
|
+
sprite-animator -i avatar.png -o wave.gif -a wave -s 256
|
|
96
|
+
|
|
97
|
+
# Bouncy animation, slower playback
|
|
98
|
+
sprite-animator -i pet.jpg -o bounce.gif -a bounce -d 150
|
|
99
|
+
|
|
100
|
+
# Dance animation, keep the raw sprite sheet
|
|
101
|
+
sprite-animator -i character.png -o dance.gif -a dance --keep-sheet
|
|
102
|
+
|
|
103
|
+
# Higher resolution generation
|
|
104
|
+
sprite-animator -i hero.png -o hero.gif -r 2K
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Python API
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from pathlib import Path
|
|
111
|
+
from sprite_animator.cli import ANIMATION_PRESETS, generate_sprite_sheet, create_gif
|
|
112
|
+
from sprite_animator.template import create_template, extract_frames
|
|
113
|
+
from PIL import Image
|
|
114
|
+
|
|
115
|
+
# Pick an animation
|
|
116
|
+
preset = ANIMATION_PRESETS["wave"]
|
|
117
|
+
|
|
118
|
+
# Create the template grid
|
|
119
|
+
template = create_template(
|
|
120
|
+
cols=preset["cols"],
|
|
121
|
+
rows=preset["rows"],
|
|
122
|
+
labels=preset["labels"],
|
|
123
|
+
)
|
|
124
|
+
template.save("template.png")
|
|
125
|
+
|
|
126
|
+
# Generate sprite sheet (requires nano-banana-pro)
|
|
127
|
+
generate_sprite_sheet(
|
|
128
|
+
input_image=Path("photo.png"),
|
|
129
|
+
template_path=Path("template.png"),
|
|
130
|
+
output_path=Path("sheet.png"),
|
|
131
|
+
prompt=preset["prompt"],
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Extract frames and build GIF
|
|
135
|
+
sheet = Image.open("sheet.png")
|
|
136
|
+
frames = extract_frames(sheet, cols=4, rows=4)
|
|
137
|
+
create_gif(frames, Path("output.gif"), frame_duration=100, size=128)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## CLI Reference
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
sprite-animator [OPTIONS]
|
|
144
|
+
|
|
145
|
+
Options:
|
|
146
|
+
-i, --input PATH Input image (required)
|
|
147
|
+
-o, --output PATH Output GIF path (required)
|
|
148
|
+
-a, --animation TYPE Animation type: idle, wave, bounce, dance (default: idle)
|
|
149
|
+
-d, --duration MS Frame duration in milliseconds (default: 100)
|
|
150
|
+
-s, --size PX Output sprite size in pixels (default: 128)
|
|
151
|
+
-r, --resolution RES Generation resolution: 1K or 2K (default: 1K)
|
|
152
|
+
--keep-sheet Save the raw sprite sheet alongside the GIF
|
|
153
|
+
--keep-frames Save individual frame PNGs
|
|
154
|
+
-v, --verbose Verbose output
|
|
155
|
+
--help Show help
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
git clone https://github.com/Olafs-World/sprite-animator.git
|
|
162
|
+
cd sprite-animator
|
|
163
|
+
uv sync
|
|
164
|
+
|
|
165
|
+
# Run tests
|
|
166
|
+
uv run pytest -m "not integration"
|
|
167
|
+
|
|
168
|
+
# Lint
|
|
169
|
+
uv run ruff check .
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Links
|
|
173
|
+
|
|
174
|
+
- [PyPI](https://pypi.org/project/sprite-animator/)
|
|
175
|
+
- [GitHub](https://github.com/Olafs-World/sprite-animator)
|
|
176
|
+
- [ClawHub Skill](https://clawhub.com/skills/sprite-animator)
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT © [Olaf](https://olafs-world.vercel.app)
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
<p align="center">
|
|
185
|
+
<i>Built by an AI who wanted to see things wiggle 🕺</i>
|
|
186
|
+
</p>
|
|
Binary file
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sprite-animator"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Generate animated pixel art sprites from any image using AI"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Aaron Levin", email = "awlevin@comcast.net" },
|
|
10
|
+
]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Topic :: Multimedia :: Graphics",
|
|
20
|
+
"Topic :: Games/Entertainment",
|
|
21
|
+
]
|
|
22
|
+
keywords = ["sprite", "pixel-art", "animation", "gif", "ai", "game-dev"]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"pillow>=10.0.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/Olafs-World/sprite-animator"
|
|
29
|
+
Repository = "https://github.com/Olafs-World/sprite-animator"
|
|
30
|
+
Issues = "https://github.com/Olafs-World/sprite-animator/issues"
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
sprite-animator = "sprite_animator.cli:main"
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["hatchling"]
|
|
37
|
+
build-backend = "hatchling.build"
|
|
38
|
+
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
target-version = "py310"
|
|
41
|
+
line-length = 120
|
|
42
|
+
|
|
43
|
+
[dependency-groups]
|
|
44
|
+
dev = [
|
|
45
|
+
"pytest>=7.0",
|
|
46
|
+
"ruff>=0.1.0",
|
|
47
|
+
]
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Sprite Animator CLI - Generate animated pixel art sprites from any image.
|
|
4
|
+
|
|
5
|
+
Uses a template-based sprite sheet approach: sends a grid template + source image
|
|
6
|
+
to nano-banana-pro in a SINGLE request, then splits the result into frames for a GIF.
|
|
7
|
+
This ensures visual consistency across all animation frames.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import tempfile
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from sprite_animator.template import create_template, extract_frames
|
|
17
|
+
|
|
18
|
+
# nano-banana-pro script path
|
|
19
|
+
NANO_BANANA_SCRIPT = Path(
|
|
20
|
+
"/home/ec2-user/.npm-global/lib/node_modules/openclaw/skills/nano-banana-pro/scripts/generate_image.py"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Animation presets: (labels, prompt_suffix)
|
|
24
|
+
ANIMATION_PRESETS = {
|
|
25
|
+
"idle": {
|
|
26
|
+
"cols": 4, "rows": 4,
|
|
27
|
+
"labels": [
|
|
28
|
+
"1:stand", "2:breathe↑", "3:breathe↑↑", "4:breathe↑",
|
|
29
|
+
"5:stand", "6:blink¼", "7:blink½", "8:blink¾",
|
|
30
|
+
"9:eyes shut", "10:blink¾", "11:blink½", "12:blink¼",
|
|
31
|
+
"13:stand", "14:breathe↓", "15:breathe↓↓", "16:breathe↓",
|
|
32
|
+
],
|
|
33
|
+
"prompt": (
|
|
34
|
+
"Fill this 4x4 sprite sheet grid (16 cells, read left-to-right, top-to-bottom) "
|
|
35
|
+
"with a cute pixel art version of the character from the reference image. "
|
|
36
|
+
"32x32 pixel art style, retro game aesthetic, clean chunky pixels. "
|
|
37
|
+
"This is an IDLE animation loop with smooth transitions. Each cell is one frame: "
|
|
38
|
+
"Row 1: standing → gentle breathe up (body rises 1px each frame) → back to center. "
|
|
39
|
+
"Row 2: standing → slow eye blink (eyes gradually close over 4 frames). "
|
|
40
|
+
"Row 3: eyes fully shut → slow eye open (eyes gradually open over 4 frames). "
|
|
41
|
+
"Row 4: standing → gentle breathe down (body lowers 1px each frame) → back to center. "
|
|
42
|
+
"CRITICAL: Keep the character IDENTICAL across all 16 frames — same colors, proportions, "
|
|
43
|
+
"size, position. Only the specified micro-movement should change. "
|
|
44
|
+
"Solid flat color background (same in all cells)."
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
"wave": {
|
|
48
|
+
"cols": 4, "rows": 4,
|
|
49
|
+
"labels": [
|
|
50
|
+
"1:stand", "2:arm↑¼", "3:arm↑½", "4:arm↑¾",
|
|
51
|
+
"5:arm up", "6:wave R", "7:wave L", "8:wave R",
|
|
52
|
+
"9:wave L", "10:wave R", "11:arm↓¾", "12:arm↓½",
|
|
53
|
+
"13:arm↓¼", "14:stand", "15:smile", "16:stand",
|
|
54
|
+
],
|
|
55
|
+
"prompt": (
|
|
56
|
+
"Fill this 4x4 sprite sheet grid (16 cells, read left-to-right, top-to-bottom) "
|
|
57
|
+
"with a cute pixel art version of the character from the reference image. "
|
|
58
|
+
"32x32 pixel art style, retro game aesthetic, clean chunky pixels. "
|
|
59
|
+
"This is a WAVE animation loop with smooth transitions. Each cell is one frame: "
|
|
60
|
+
"Row 1: standing still → arm gradually raising up (4 incremental positions). "
|
|
61
|
+
"Row 2: arm fully up → waving side to side (arm tilts right, left, right). "
|
|
62
|
+
"Row 3: still waving (left, right) → arm gradually lowering (2 frames). "
|
|
63
|
+
"Row 4: arm coming down → back to standing → happy smile → standing. "
|
|
64
|
+
"CRITICAL: Keep the character IDENTICAL across all 16 frames — same colors, proportions, "
|
|
65
|
+
"size, position. Only the arm position and expression should change. "
|
|
66
|
+
"Solid flat color background (same in all cells)."
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
"bounce": {
|
|
70
|
+
"cols": 4, "rows": 4,
|
|
71
|
+
"labels": [
|
|
72
|
+
"1:stand", "2:crouch¼", "3:crouch½", "4:crouch full",
|
|
73
|
+
"5:launch", "6:rise", "7:peak", "8:peak+happy",
|
|
74
|
+
"9:fall start", "10:falling", "11:land", "12:squish",
|
|
75
|
+
"13:recover¼", "14:recover½", "15:recover¾", "16:stand",
|
|
76
|
+
],
|
|
77
|
+
"prompt": (
|
|
78
|
+
"Fill this 4x4 sprite sheet grid (16 cells, read left-to-right, top-to-bottom) "
|
|
79
|
+
"with a cute pixel art version of the character from the reference image. "
|
|
80
|
+
"32x32 pixel art style, retro game aesthetic, clean chunky pixels. "
|
|
81
|
+
"This is a BOUNCE animation loop with smooth transitions. Each cell is one frame: "
|
|
82
|
+
"Row 1: standing → gradually crouching down (getting squished/compressed). "
|
|
83
|
+
"Row 2: launching upward → rising → at peak of jump (stretched tall) → happy face at peak. "
|
|
84
|
+
"Row 3: starting to fall → falling fast → landing impact → squished on landing. "
|
|
85
|
+
"Row 4: gradually recovering from squish back to standing position. "
|
|
86
|
+
"CRITICAL: Keep the character IDENTICAL across all 16 frames — same colors, proportions. "
|
|
87
|
+
"Only the vertical position and squish/stretch should change. "
|
|
88
|
+
"Solid flat color background (same in all cells)."
|
|
89
|
+
),
|
|
90
|
+
},
|
|
91
|
+
"dance": {
|
|
92
|
+
"cols": 4, "rows": 4,
|
|
93
|
+
"labels": [
|
|
94
|
+
"1:center", "2:lean L", "3:arms L", "4:lean L+",
|
|
95
|
+
"5:center", "6:lean R", "7:arms R", "8:lean R+",
|
|
96
|
+
"9:center", "10:arms up", "11:spin¼", "12:spin½",
|
|
97
|
+
"13:spin¾", "14:arms up", "15:jump", "16:land",
|
|
98
|
+
],
|
|
99
|
+
"prompt": (
|
|
100
|
+
"Fill this 4x4 sprite sheet grid (16 cells, read left-to-right, top-to-bottom) "
|
|
101
|
+
"with a cute pixel art version of the character from the reference image. "
|
|
102
|
+
"32x32 pixel art style, retro game aesthetic, clean chunky pixels. "
|
|
103
|
+
"This is a fun DANCE animation loop with smooth transitions. Each cell is one frame: "
|
|
104
|
+
"Row 1: center pose → leaning left → arms out left → full left lean. "
|
|
105
|
+
"Row 2: back to center → leaning right → arms out right → full right lean. "
|
|
106
|
+
"Row 3: center → arms up high → spinning (4 rotation frames). "
|
|
107
|
+
"Row 4: finish spin → arms up → jump → land back in center. "
|
|
108
|
+
"CRITICAL: Keep the character IDENTICAL across all 16 frames — same colors, proportions. "
|
|
109
|
+
"Only the pose/position should change. Make it look fun and energetic! "
|
|
110
|
+
"Solid flat color background (same in all cells)."
|
|
111
|
+
),
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def generate_sprite_sheet(
|
|
117
|
+
input_image: Path,
|
|
118
|
+
template_path: Path,
|
|
119
|
+
output_path: Path,
|
|
120
|
+
prompt: str,
|
|
121
|
+
resolution: str = "1K",
|
|
122
|
+
) -> bool:
|
|
123
|
+
"""Generate a sprite sheet using nano-banana-pro with template + reference image."""
|
|
124
|
+
cmd = [
|
|
125
|
+
"uv", "run", str(NANO_BANANA_SCRIPT),
|
|
126
|
+
"--prompt", prompt,
|
|
127
|
+
"--filename", str(output_path),
|
|
128
|
+
"-i", str(template_path),
|
|
129
|
+
"-i", str(input_image),
|
|
130
|
+
"--resolution", resolution,
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
result = subprocess.run(
|
|
135
|
+
cmd,
|
|
136
|
+
capture_output=True,
|
|
137
|
+
text=True,
|
|
138
|
+
timeout=180,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if result.returncode != 0:
|
|
142
|
+
print(f" Error: {result.stderr.strip()}", file=sys.stderr, flush=True)
|
|
143
|
+
return False
|
|
144
|
+
|
|
145
|
+
if not output_path.exists():
|
|
146
|
+
print(f" Sheet not created: {output_path}", file=sys.stderr, flush=True)
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
except subprocess.TimeoutExpired:
|
|
152
|
+
print(" Timeout generating sprite sheet", file=sys.stderr, flush=True)
|
|
153
|
+
return False
|
|
154
|
+
except Exception as e:
|
|
155
|
+
print(f" Exception: {e}", file=sys.stderr, flush=True)
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def create_gif(
|
|
160
|
+
frames: list,
|
|
161
|
+
output_path: Path,
|
|
162
|
+
frame_duration: int = 200,
|
|
163
|
+
size: int | None = None,
|
|
164
|
+
) -> bool:
|
|
165
|
+
"""Assemble PIL Image frames into an animated GIF."""
|
|
166
|
+
from PIL import Image
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
processed = []
|
|
170
|
+
for frame in frames:
|
|
171
|
+
if size:
|
|
172
|
+
frame = frame.resize((size, size), Image.Resampling.NEAREST)
|
|
173
|
+
if frame.mode != "RGBA":
|
|
174
|
+
frame = frame.convert("RGBA")
|
|
175
|
+
bg = Image.new("RGBA", frame.size, (255, 255, 255, 255))
|
|
176
|
+
composite = Image.alpha_composite(bg, frame)
|
|
177
|
+
processed.append(composite.convert("P", palette=Image.Palette.ADAPTIVE))
|
|
178
|
+
|
|
179
|
+
if not processed:
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
183
|
+
processed[0].save(
|
|
184
|
+
output_path,
|
|
185
|
+
save_all=True,
|
|
186
|
+
append_images=processed[1:],
|
|
187
|
+
duration=frame_duration,
|
|
188
|
+
loop=0,
|
|
189
|
+
optimize=True,
|
|
190
|
+
)
|
|
191
|
+
return True
|
|
192
|
+
except Exception as e:
|
|
193
|
+
print(f"Error creating GIF: {e}", file=sys.stderr, flush=True)
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def main():
|
|
198
|
+
parser = argparse.ArgumentParser(
|
|
199
|
+
description="Generate animated pixel art sprites from any image",
|
|
200
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
201
|
+
epilog="""
|
|
202
|
+
Examples:
|
|
203
|
+
sprite-animator -i photo.png -o sprite.gif
|
|
204
|
+
sprite-animator -i avatar.png -o wave.gif -a wave
|
|
205
|
+
sprite-animator -i pet.jpg -o bounce.gif -a bounce -s 256
|
|
206
|
+
""",
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
parser.add_argument("-i", "--input", required=True, type=Path, help="Input image")
|
|
210
|
+
parser.add_argument("-o", "--output", required=True, type=Path, help="Output GIF path")
|
|
211
|
+
parser.add_argument("-a", "--animation", choices=list(ANIMATION_PRESETS.keys()), default="idle", help="Animation type (default: idle)")
|
|
212
|
+
parser.add_argument("-d", "--duration", type=int, default=100, help="Frame duration in ms (default: 100)")
|
|
213
|
+
parser.add_argument("-s", "--size", type=int, default=128, help="Output sprite size in px (default: 128)")
|
|
214
|
+
parser.add_argument("-r", "--resolution", choices=["1K", "2K"], default="1K", help="Generation resolution")
|
|
215
|
+
parser.add_argument("--keep-sheet", action="store_true", help="Keep the raw sprite sheet")
|
|
216
|
+
parser.add_argument("--keep-frames", action="store_true", help="Keep individual frame files")
|
|
217
|
+
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
|
|
218
|
+
|
|
219
|
+
args = parser.parse_args()
|
|
220
|
+
|
|
221
|
+
if not args.input.exists():
|
|
222
|
+
print(f"Error: Input not found: {args.input}", file=sys.stderr, flush=True)
|
|
223
|
+
sys.exit(1)
|
|
224
|
+
|
|
225
|
+
if not NANO_BANANA_SCRIPT.exists():
|
|
226
|
+
print(f"Error: nano-banana-pro not found: {NANO_BANANA_SCRIPT}", file=sys.stderr, flush=True)
|
|
227
|
+
sys.exit(1)
|
|
228
|
+
|
|
229
|
+
preset = ANIMATION_PRESETS[args.animation]
|
|
230
|
+
cols = preset["cols"]
|
|
231
|
+
rows = preset["rows"]
|
|
232
|
+
total_frames = cols * rows
|
|
233
|
+
|
|
234
|
+
print(f"🎮 sprite-animator", flush=True)
|
|
235
|
+
print(f" input: {args.input}", flush=True)
|
|
236
|
+
print(f" animation: {args.animation} ({total_frames} frames, {cols}x{rows} grid)", flush=True)
|
|
237
|
+
print(f" output: {args.output}", flush=True)
|
|
238
|
+
|
|
239
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
240
|
+
tmpdir = Path(tmpdir)
|
|
241
|
+
|
|
242
|
+
# Step 1: Create template
|
|
243
|
+
print(f"\n📐 creating sprite sheet template...", flush=True)
|
|
244
|
+
template_img = create_template(cols=cols, rows=rows, labels=preset["labels"])
|
|
245
|
+
template_path = tmpdir / "template.png"
|
|
246
|
+
template_img.save(template_path)
|
|
247
|
+
if args.verbose:
|
|
248
|
+
print(f" template: {template_img.size}", flush=True)
|
|
249
|
+
|
|
250
|
+
# Step 2: Generate sprite sheet (single request!)
|
|
251
|
+
print(f"🎨 generating sprite sheet (single request)...", flush=True)
|
|
252
|
+
sheet_path = tmpdir / "sprite_sheet.png"
|
|
253
|
+
|
|
254
|
+
success = generate_sprite_sheet(
|
|
255
|
+
args.input,
|
|
256
|
+
template_path,
|
|
257
|
+
sheet_path,
|
|
258
|
+
preset["prompt"],
|
|
259
|
+
args.resolution,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
if not success:
|
|
263
|
+
print("❌ failed to generate sprite sheet", file=sys.stderr, flush=True)
|
|
264
|
+
sys.exit(1)
|
|
265
|
+
|
|
266
|
+
print(f" ✓ sprite sheet generated", flush=True)
|
|
267
|
+
|
|
268
|
+
# Step 3: Extract frames
|
|
269
|
+
print(f"✂️ extracting {cols} frames...", flush=True)
|
|
270
|
+
from PIL import Image
|
|
271
|
+
sheet = Image.open(sheet_path)
|
|
272
|
+
if args.verbose:
|
|
273
|
+
print(f" sheet size: {sheet.size}", flush=True)
|
|
274
|
+
|
|
275
|
+
frames = extract_frames(sheet, cols=cols, rows=rows)
|
|
276
|
+
print(f" ✓ extracted {len(frames)} frames", flush=True)
|
|
277
|
+
|
|
278
|
+
# Step 4: Assemble GIF
|
|
279
|
+
print(f"🔄 assembling animated GIF...", flush=True)
|
|
280
|
+
success = create_gif(frames, args.output, frame_duration=args.duration, size=args.size)
|
|
281
|
+
|
|
282
|
+
if not success:
|
|
283
|
+
print("❌ failed to create GIF", file=sys.stderr, flush=True)
|
|
284
|
+
sys.exit(1)
|
|
285
|
+
|
|
286
|
+
print(f"\n✨ done! saved: {args.output.resolve()}", flush=True)
|
|
287
|
+
|
|
288
|
+
# Optionally save sheet and frames
|
|
289
|
+
if args.keep_sheet:
|
|
290
|
+
sheet_out = args.output.parent / f"{args.output.stem}_sheet.png"
|
|
291
|
+
import shutil
|
|
292
|
+
shutil.copy2(sheet_path, sheet_out)
|
|
293
|
+
print(f" sheet: {sheet_out}", flush=True)
|
|
294
|
+
|
|
295
|
+
if args.keep_frames:
|
|
296
|
+
frames_dir = args.output.parent / f"{args.output.stem}_frames"
|
|
297
|
+
frames_dir.mkdir(exist_ok=True)
|
|
298
|
+
for idx, frame in enumerate(frames):
|
|
299
|
+
frame.save(frames_dir / f"frame_{idx:02d}.png")
|
|
300
|
+
print(f" frames: {frames_dir}/", flush=True)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
if __name__ == "__main__":
|
|
304
|
+
main()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Generate sprite sheet templates and parse output sheets into frames."""
|
|
2
|
+
|
|
3
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def create_template(
|
|
8
|
+
cols: int = 4,
|
|
9
|
+
rows: int = 1,
|
|
10
|
+
cell_size: int = 256,
|
|
11
|
+
labels: list[str] | None = None,
|
|
12
|
+
) -> Image.Image:
|
|
13
|
+
"""Create a sprite sheet template with labeled grid cells.
|
|
14
|
+
|
|
15
|
+
Returns a template image with numbered/labeled cells that guides
|
|
16
|
+
the AI model to place each animation frame in the right spot.
|
|
17
|
+
"""
|
|
18
|
+
width = cols * cell_size
|
|
19
|
+
height = rows * cell_size
|
|
20
|
+
img = Image.new("RGB", (width, height), (200, 200, 200))
|
|
21
|
+
draw = ImageDraw.Draw(img)
|
|
22
|
+
|
|
23
|
+
# Draw grid lines
|
|
24
|
+
for c in range(cols + 1):
|
|
25
|
+
x = c * cell_size
|
|
26
|
+
draw.line([(x, 0), (x, height)], fill=(100, 100, 100), width=3)
|
|
27
|
+
for r in range(rows + 1):
|
|
28
|
+
y = r * cell_size
|
|
29
|
+
draw.line([(0, y), (width, y)], fill=(100, 100, 100), width=3)
|
|
30
|
+
|
|
31
|
+
# Label each cell
|
|
32
|
+
for r in range(rows):
|
|
33
|
+
for c in range(cols):
|
|
34
|
+
idx = r * cols + c
|
|
35
|
+
if labels and idx < len(labels):
|
|
36
|
+
label = labels[idx]
|
|
37
|
+
else:
|
|
38
|
+
label = f"Frame {idx + 1}"
|
|
39
|
+
|
|
40
|
+
cx = c * cell_size + cell_size // 2
|
|
41
|
+
cy = r * cell_size + cell_size // 2
|
|
42
|
+
|
|
43
|
+
# Draw label text
|
|
44
|
+
try:
|
|
45
|
+
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
|
|
46
|
+
except (OSError, IOError):
|
|
47
|
+
font = ImageFont.load_default()
|
|
48
|
+
|
|
49
|
+
bbox = draw.textbbox((0, 0), label, font=font)
|
|
50
|
+
tw = bbox[2] - bbox[0]
|
|
51
|
+
th = bbox[3] - bbox[1]
|
|
52
|
+
draw.text((cx - tw // 2, cy - th // 2), label, fill=(80, 80, 80), font=font)
|
|
53
|
+
|
|
54
|
+
return img
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def extract_frames(
|
|
58
|
+
sheet: Image.Image,
|
|
59
|
+
cols: int = 4,
|
|
60
|
+
rows: int = 1,
|
|
61
|
+
) -> list[Image.Image]:
|
|
62
|
+
"""Split a sprite sheet into individual frames based on grid layout."""
|
|
63
|
+
w, h = sheet.size
|
|
64
|
+
cell_w = w // cols
|
|
65
|
+
cell_h = h // rows
|
|
66
|
+
|
|
67
|
+
frames = []
|
|
68
|
+
for r in range(rows):
|
|
69
|
+
for c in range(cols):
|
|
70
|
+
left = c * cell_w
|
|
71
|
+
top = r * cell_h
|
|
72
|
+
right = left + cell_w
|
|
73
|
+
bottom = top + cell_h
|
|
74
|
+
frame = sheet.crop((left, top, right, bottom))
|
|
75
|
+
frames.append(frame)
|
|
76
|
+
|
|
77
|
+
return frames
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
# Quick test
|
|
82
|
+
labels = ["Stand", "Arm up", "Wave right", "Arm down"]
|
|
83
|
+
tpl = create_template(cols=4, rows=1, labels=labels)
|
|
84
|
+
tpl.save("/tmp/sprite_template_test.png")
|
|
85
|
+
print(f"Template saved: /tmp/sprite_template_test.png ({tpl.size})")
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 3
|
|
3
|
+
requires-python = ">=3.10"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "colorama"
|
|
7
|
+
version = "0.4.6"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
|
10
|
+
wheels = [
|
|
11
|
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "exceptiongroup"
|
|
16
|
+
version = "1.3.1"
|
|
17
|
+
source = { registry = "https://pypi.org/simple" }
|
|
18
|
+
dependencies = [
|
|
19
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
|
20
|
+
]
|
|
21
|
+
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
|
22
|
+
wheels = [
|
|
23
|
+
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "iniconfig"
|
|
28
|
+
version = "2.3.0"
|
|
29
|
+
source = { registry = "https://pypi.org/simple" }
|
|
30
|
+
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
|
31
|
+
wheels = [
|
|
32
|
+
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[[package]]
|
|
36
|
+
name = "packaging"
|
|
37
|
+
version = "26.0"
|
|
38
|
+
source = { registry = "https://pypi.org/simple" }
|
|
39
|
+
sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
|
|
40
|
+
wheels = [
|
|
41
|
+
{ url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "pillow"
|
|
46
|
+
version = "12.1.0"
|
|
47
|
+
source = { registry = "https://pypi.org/simple" }
|
|
48
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d0/02/d52c733a2452ef1ffcc123b68e6606d07276b0e358db70eabad7e40042b7/pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9", size = 46977283, upload-time = "2026-01-02T09:13:29.892Z" }
|
|
49
|
+
wheels = [
|
|
50
|
+
{ url = "https://files.pythonhosted.org/packages/fe/41/f73d92b6b883a579e79600d391f2e21cb0df767b2714ecbd2952315dfeef/pillow-12.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:fb125d860738a09d363a88daa0f59c4533529a90e564785e20fe875b200b6dbd", size = 5304089, upload-time = "2026-01-02T09:10:24.953Z" },
|
|
51
|
+
{ url = "https://files.pythonhosted.org/packages/94/55/7aca2891560188656e4a91ed9adba305e914a4496800da6b5c0a15f09edf/pillow-12.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cad302dc10fac357d3467a74a9561c90609768a6f73a1923b0fd851b6486f8b0", size = 4657815, upload-time = "2026-01-02T09:10:27.063Z" },
|
|
52
|
+
{ url = "https://files.pythonhosted.org/packages/e9/d2/b28221abaa7b4c40b7dba948f0f6a708bd7342c4d47ce342f0ea39643974/pillow-12.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a40905599d8079e09f25027423aed94f2823adaf2868940de991e53a449e14a8", size = 6222593, upload-time = "2026-01-02T09:10:29.115Z" },
|
|
53
|
+
{ url = "https://files.pythonhosted.org/packages/71/b8/7a61fb234df6a9b0b479f69e66901209d89ff72a435b49933f9122f94cac/pillow-12.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:92a7fe4225365c5e3a8e598982269c6d6698d3e783b3b1ae979e7819f9cd55c1", size = 8027579, upload-time = "2026-01-02T09:10:31.182Z" },
|
|
54
|
+
{ url = "https://files.pythonhosted.org/packages/ea/51/55c751a57cc524a15a0e3db20e5cde517582359508d62305a627e77fd295/pillow-12.1.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f10c98f49227ed8383d28174ee95155a675c4ed7f85e2e573b04414f7e371bda", size = 6335760, upload-time = "2026-01-02T09:10:33.02Z" },
|
|
55
|
+
{ url = "https://files.pythonhosted.org/packages/dc/7c/60e3e6f5e5891a1a06b4c910f742ac862377a6fe842f7184df4a274ce7bf/pillow-12.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8637e29d13f478bc4f153d8daa9ffb16455f0a6cb287da1b432fdad2bfbd66c7", size = 7027127, upload-time = "2026-01-02T09:10:35.009Z" },
|
|
56
|
+
{ url = "https://files.pythonhosted.org/packages/06/37/49d47266ba50b00c27ba63a7c898f1bb41a29627ced8c09e25f19ebec0ff/pillow-12.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:21e686a21078b0f9cb8c8a961d99e6a4ddb88e0fc5ea6e130172ddddc2e5221a", size = 6449896, upload-time = "2026-01-02T09:10:36.793Z" },
|
|
57
|
+
{ url = "https://files.pythonhosted.org/packages/f9/e5/67fd87d2913902462cd9b79c6211c25bfe95fcf5783d06e1367d6d9a741f/pillow-12.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2415373395a831f53933c23ce051021e79c8cd7979822d8cc478547a3f4da8ef", size = 7151345, upload-time = "2026-01-02T09:10:39.064Z" },
|
|
58
|
+
{ url = "https://files.pythonhosted.org/packages/bd/15/f8c7abf82af68b29f50d77c227e7a1f87ce02fdc66ded9bf603bc3b41180/pillow-12.1.0-cp310-cp310-win32.whl", hash = "sha256:e75d3dba8fc1ddfec0cd752108f93b83b4f8d6ab40e524a95d35f016b9683b09", size = 6325568, upload-time = "2026-01-02T09:10:41.035Z" },
|
|
59
|
+
{ url = "https://files.pythonhosted.org/packages/d4/24/7d1c0e160b6b5ac2605ef7d8be537e28753c0db5363d035948073f5513d7/pillow-12.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:64efdf00c09e31efd754448a383ea241f55a994fd079866b92d2bbff598aad91", size = 7032367, upload-time = "2026-01-02T09:10:43.09Z" },
|
|
60
|
+
{ url = "https://files.pythonhosted.org/packages/f4/03/41c038f0d7a06099254c60f618d0ec7be11e79620fc23b8e85e5b31d9a44/pillow-12.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:f188028b5af6b8fb2e9a76ac0f841a575bd1bd396e46ef0840d9b88a48fdbcea", size = 2452345, upload-time = "2026-01-02T09:10:44.795Z" },
|
|
61
|
+
{ url = "https://files.pythonhosted.org/packages/43/c4/bf8328039de6cc22182c3ef007a2abfbbdab153661c0a9aa78af8d706391/pillow-12.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:a83e0850cb8f5ac975291ebfc4170ba481f41a28065277f7f735c202cd8e0af3", size = 5304057, upload-time = "2026-01-02T09:10:46.627Z" },
|
|
62
|
+
{ url = "https://files.pythonhosted.org/packages/43/06/7264c0597e676104cc22ca73ee48f752767cd4b1fe084662620b17e10120/pillow-12.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6e53e82ec2db0717eabb276aa56cf4e500c9a7cec2c2e189b55c24f65a3e8c0", size = 4657811, upload-time = "2026-01-02T09:10:49.548Z" },
|
|
63
|
+
{ url = "https://files.pythonhosted.org/packages/72/64/f9189e44474610daf83da31145fa56710b627b5c4c0b9c235e34058f6b31/pillow-12.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40a8e3b9e8773876d6e30daed22f016509e3987bab61b3b7fe309d7019a87451", size = 6232243, upload-time = "2026-01-02T09:10:51.62Z" },
|
|
64
|
+
{ url = "https://files.pythonhosted.org/packages/ef/30/0df458009be6a4caca4ca2c52975e6275c387d4e5c95544e34138b41dc86/pillow-12.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:800429ac32c9b72909c671aaf17ecd13110f823ddb7db4dfef412a5587c2c24e", size = 8037872, upload-time = "2026-01-02T09:10:53.446Z" },
|
|
65
|
+
{ url = "https://files.pythonhosted.org/packages/e4/86/95845d4eda4f4f9557e25381d70876aa213560243ac1a6d619c46caaedd9/pillow-12.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b022eaaf709541b391ee069f0022ee5b36c709df71986e3f7be312e46f42c84", size = 6345398, upload-time = "2026-01-02T09:10:55.426Z" },
|
|
66
|
+
{ url = "https://files.pythonhosted.org/packages/5c/1f/8e66ab9be3aaf1435bc03edd1ebdf58ffcd17f7349c1d970cafe87af27d9/pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f345e7bc9d7f368887c712aa5054558bad44d2a301ddf9248599f4161abc7c0", size = 7034667, upload-time = "2026-01-02T09:10:57.11Z" },
|
|
67
|
+
{ url = "https://files.pythonhosted.org/packages/f9/f6/683b83cb9b1db1fb52b87951b1c0b99bdcfceaa75febf11406c19f82cb5e/pillow-12.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d70347c8a5b7ccd803ec0c85c8709f036e6348f1e6a5bf048ecd9c64d3550b8b", size = 6458743, upload-time = "2026-01-02T09:10:59.331Z" },
|
|
68
|
+
{ url = "https://files.pythonhosted.org/packages/9a/7d/de833d63622538c1d58ce5395e7c6cb7e7dce80decdd8bde4a484e095d9f/pillow-12.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1fcc52d86ce7a34fd17cb04e87cfdb164648a3662a6f20565910a99653d66c18", size = 7159342, upload-time = "2026-01-02T09:11:01.82Z" },
|
|
69
|
+
{ url = "https://files.pythonhosted.org/packages/8c/40/50d86571c9e5868c42b81fe7da0c76ca26373f3b95a8dd675425f4a92ec1/pillow-12.1.0-cp311-cp311-win32.whl", hash = "sha256:3ffaa2f0659e2f740473bcf03c702c39a8d4b2b7ffc629052028764324842c64", size = 6328655, upload-time = "2026-01-02T09:11:04.556Z" },
|
|
70
|
+
{ url = "https://files.pythonhosted.org/packages/6c/af/b1d7e301c4cd26cd45d4af884d9ee9b6fab893b0ad2450d4746d74a6968c/pillow-12.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:806f3987ffe10e867bab0ddad45df1148a2b98221798457fa097ad85d6e8bc75", size = 7031469, upload-time = "2026-01-02T09:11:06.538Z" },
|
|
71
|
+
{ url = "https://files.pythonhosted.org/packages/48/36/d5716586d887fb2a810a4a61518a327a1e21c8b7134c89283af272efe84b/pillow-12.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:9f5fefaca968e700ad1a4a9de98bf0869a94e397fe3524c4c9450c1445252304", size = 2452515, upload-time = "2026-01-02T09:11:08.226Z" },
|
|
72
|
+
{ url = "https://files.pythonhosted.org/packages/20/31/dc53fe21a2f2996e1b7d92bf671cdb157079385183ef7c1ae08b485db510/pillow-12.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a332ac4ccb84b6dde65dbace8431f3af08874bf9770719d32a635c4ef411b18b", size = 5262642, upload-time = "2026-01-02T09:11:10.138Z" },
|
|
73
|
+
{ url = "https://files.pythonhosted.org/packages/ab/c1/10e45ac9cc79419cedf5121b42dcca5a50ad2b601fa080f58c22fb27626e/pillow-12.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:907bfa8a9cb790748a9aa4513e37c88c59660da3bcfffbd24a7d9e6abf224551", size = 4657464, upload-time = "2026-01-02T09:11:12.319Z" },
|
|
74
|
+
{ url = "https://files.pythonhosted.org/packages/ad/26/7b82c0ab7ef40ebede7a97c72d473bda5950f609f8e0c77b04af574a0ddb/pillow-12.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efdc140e7b63b8f739d09a99033aa430accce485ff78e6d311973a67b6bf3208", size = 6234878, upload-time = "2026-01-02T09:11:14.096Z" },
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/76/25/27abc9792615b5e886ca9411ba6637b675f1b77af3104710ac7353fe5605/pillow-12.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bef9768cab184e7ae6e559c032e95ba8d07b3023c289f79a2bd36e8bf85605a5", size = 8044868, upload-time = "2026-01-02T09:11:15.903Z" },
|
|
76
|
+
{ url = "https://files.pythonhosted.org/packages/0a/ea/f200a4c36d836100e7bc738fc48cd963d3ba6372ebc8298a889e0cfc3359/pillow-12.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:742aea052cf5ab5034a53c3846165bc3ce88d7c38e954120db0ab867ca242661", size = 6349468, upload-time = "2026-01-02T09:11:17.631Z" },
|
|
77
|
+
{ url = "https://files.pythonhosted.org/packages/11/8f/48d0b77ab2200374c66d344459b8958c86693be99526450e7aee714e03e4/pillow-12.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6dfc2af5b082b635af6e08e0d1f9f1c4e04d17d4e2ca0ef96131e85eda6eb17", size = 7041518, upload-time = "2026-01-02T09:11:19.389Z" },
|
|
78
|
+
{ url = "https://files.pythonhosted.org/packages/1d/23/c281182eb986b5d31f0a76d2a2c8cd41722d6fb8ed07521e802f9bba52de/pillow-12.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:609e89d9f90b581c8d16358c9087df76024cf058fa693dd3e1e1620823f39670", size = 6462829, upload-time = "2026-01-02T09:11:21.28Z" },
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/25/ef/7018273e0faac099d7b00982abdcc39142ae6f3bd9ceb06de09779c4a9d6/pillow-12.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43b4899cfd091a9693a1278c4982f3e50f7fb7cff5153b05174b4afc9593b616", size = 7166756, upload-time = "2026-01-02T09:11:23.559Z" },
|
|
80
|
+
{ url = "https://files.pythonhosted.org/packages/8f/c8/993d4b7ab2e341fe02ceef9576afcf5830cdec640be2ac5bee1820d693d4/pillow-12.1.0-cp312-cp312-win32.whl", hash = "sha256:aa0c9cc0b82b14766a99fbe6084409972266e82f459821cd26997a488a7261a7", size = 6328770, upload-time = "2026-01-02T09:11:25.661Z" },
|
|
81
|
+
{ url = "https://files.pythonhosted.org/packages/a7/87/90b358775a3f02765d87655237229ba64a997b87efa8ccaca7dd3e36e7a7/pillow-12.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:d70534cea9e7966169ad29a903b99fc507e932069a881d0965a1a84bb57f6c6d", size = 7033406, upload-time = "2026-01-02T09:11:27.474Z" },
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/5d/cf/881b457eccacac9e5b2ddd97d5071fb6d668307c57cbf4e3b5278e06e536/pillow-12.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:65b80c1ee7e14a87d6a068dd3b0aea268ffcabfe0498d38661b00c5b4b22e74c", size = 2452612, upload-time = "2026-01-02T09:11:29.309Z" },
|
|
83
|
+
{ url = "https://files.pythonhosted.org/packages/dd/c7/2530a4aa28248623e9d7f27316b42e27c32ec410f695929696f2e0e4a778/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1", size = 4062543, upload-time = "2026-01-02T09:11:31.566Z" },
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/8f/1f/40b8eae823dc1519b87d53c30ed9ef085506b05281d313031755c1705f73/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179", size = 4138373, upload-time = "2026-01-02T09:11:33.367Z" },
|
|
85
|
+
{ url = "https://files.pythonhosted.org/packages/d4/77/6fa60634cf06e52139fd0e89e5bbf055e8166c691c42fb162818b7fda31d/pillow-12.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0", size = 3601241, upload-time = "2026-01-02T09:11:35.011Z" },
|
|
86
|
+
{ url = "https://files.pythonhosted.org/packages/4f/bf/28ab865de622e14b747f0cd7877510848252d950e43002e224fb1c9ababf/pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587", size = 5262410, upload-time = "2026-01-02T09:11:36.682Z" },
|
|
87
|
+
{ url = "https://files.pythonhosted.org/packages/1c/34/583420a1b55e715937a85bd48c5c0991598247a1fd2eb5423188e765ea02/pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac", size = 4657312, upload-time = "2026-01-02T09:11:38.535Z" },
|
|
88
|
+
{ url = "https://files.pythonhosted.org/packages/1d/fd/f5a0896839762885b3376ff04878f86ab2b097c2f9a9cdccf4eda8ba8dc0/pillow-12.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b", size = 6232605, upload-time = "2026-01-02T09:11:40.602Z" },
|
|
89
|
+
{ url = "https://files.pythonhosted.org/packages/98/aa/938a09d127ac1e70e6ed467bd03834350b33ef646b31edb7452d5de43792/pillow-12.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea", size = 8041617, upload-time = "2026-01-02T09:11:42.721Z" },
|
|
90
|
+
{ url = "https://files.pythonhosted.org/packages/17/e8/538b24cb426ac0186e03f80f78bc8dc7246c667f58b540bdd57c71c9f79d/pillow-12.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c", size = 6346509, upload-time = "2026-01-02T09:11:44.955Z" },
|
|
91
|
+
{ url = "https://files.pythonhosted.org/packages/01/9a/632e58ec89a32738cabfd9ec418f0e9898a2b4719afc581f07c04a05e3c9/pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc", size = 7038117, upload-time = "2026-01-02T09:11:46.736Z" },
|
|
92
|
+
{ url = "https://files.pythonhosted.org/packages/c7/a2/d40308cf86eada842ca1f3ffa45d0ca0df7e4ab33c83f81e73f5eaed136d/pillow-12.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644", size = 6460151, upload-time = "2026-01-02T09:11:48.625Z" },
|
|
93
|
+
{ url = "https://files.pythonhosted.org/packages/f1/88/f5b058ad6453a085c5266660a1417bdad590199da1b32fb4efcff9d33b05/pillow-12.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c", size = 7164534, upload-time = "2026-01-02T09:11:50.445Z" },
|
|
94
|
+
{ url = "https://files.pythonhosted.org/packages/19/ce/c17334caea1db789163b5d855a5735e47995b0b5dc8745e9a3605d5f24c0/pillow-12.1.0-cp313-cp313-win32.whl", hash = "sha256:a786bf667724d84aa29b5db1c61b7bfdde380202aaca12c3461afd6b71743171", size = 6332551, upload-time = "2026-01-02T09:11:52.234Z" },
|
|
95
|
+
{ url = "https://files.pythonhosted.org/packages/e5/07/74a9d941fa45c90a0d9465098fe1ec85de3e2afbdc15cc4766622d516056/pillow-12.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a", size = 7040087, upload-time = "2026-01-02T09:11:54.822Z" },
|
|
96
|
+
{ url = "https://files.pythonhosted.org/packages/88/09/c99950c075a0e9053d8e880595926302575bc742b1b47fe1bbcc8d388d50/pillow-12.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:9212d6b86917a2300669511ed094a9406888362e085f2431a7da985a6b124f45", size = 2452470, upload-time = "2026-01-02T09:11:56.522Z" },
|
|
97
|
+
{ url = "https://files.pythonhosted.org/packages/b5/ba/970b7d85ba01f348dee4d65412476321d40ee04dcb51cd3735b9dc94eb58/pillow-12.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d", size = 5264816, upload-time = "2026-01-02T09:11:58.227Z" },
|
|
98
|
+
{ url = "https://files.pythonhosted.org/packages/10/60/650f2fb55fdba7a510d836202aa52f0baac633e50ab1cf18415d332188fb/pillow-12.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0", size = 4660472, upload-time = "2026-01-02T09:12:00.798Z" },
|
|
99
|
+
{ url = "https://files.pythonhosted.org/packages/2b/c0/5273a99478956a099d533c4f46cbaa19fd69d606624f4334b85e50987a08/pillow-12.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554", size = 6268974, upload-time = "2026-01-02T09:12:02.572Z" },
|
|
100
|
+
{ url = "https://files.pythonhosted.org/packages/b4/26/0bf714bc2e73d5267887d47931d53c4ceeceea6978148ed2ab2a4e6463c4/pillow-12.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e", size = 8073070, upload-time = "2026-01-02T09:12:04.75Z" },
|
|
101
|
+
{ url = "https://files.pythonhosted.org/packages/43/cf/1ea826200de111a9d65724c54f927f3111dc5ae297f294b370a670c17786/pillow-12.1.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82", size = 6380176, upload-time = "2026-01-02T09:12:06.626Z" },
|
|
102
|
+
{ url = "https://files.pythonhosted.org/packages/03/e0/7938dd2b2013373fd85d96e0f38d62b7a5a262af21ac274250c7ca7847c9/pillow-12.1.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4", size = 7067061, upload-time = "2026-01-02T09:12:08.624Z" },
|
|
103
|
+
{ url = "https://files.pythonhosted.org/packages/86/ad/a2aa97d37272a929a98437a8c0ac37b3cf012f4f8721e1bd5154699b2518/pillow-12.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0", size = 6491824, upload-time = "2026-01-02T09:12:10.488Z" },
|
|
104
|
+
{ url = "https://files.pythonhosted.org/packages/a4/44/80e46611b288d51b115826f136fb3465653c28f491068a72d3da49b54cd4/pillow-12.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b", size = 7190911, upload-time = "2026-01-02T09:12:12.772Z" },
|
|
105
|
+
{ url = "https://files.pythonhosted.org/packages/86/77/eacc62356b4cf81abe99ff9dbc7402750044aed02cfd6a503f7c6fc11f3e/pillow-12.1.0-cp313-cp313t-win32.whl", hash = "sha256:7315f9137087c4e0ee73a761b163fc9aa3b19f5f606a7fc08d83fd3e4379af65", size = 6336445, upload-time = "2026-01-02T09:12:14.775Z" },
|
|
106
|
+
{ url = "https://files.pythonhosted.org/packages/e7/3c/57d81d0b74d218706dafccb87a87ea44262c43eef98eb3b164fd000e0491/pillow-12.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:0ddedfaa8b5f0b4ffbc2fa87b556dc59f6bb4ecb14a53b33f9189713ae8053c0", size = 7045354, upload-time = "2026-01-02T09:12:16.599Z" },
|
|
107
|
+
{ url = "https://files.pythonhosted.org/packages/ac/82/8b9b97bba2e3576a340f93b044a3a3a09841170ab4c1eb0d5c93469fd32f/pillow-12.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:80941e6d573197a0c28f394753de529bb436b1ca990ed6e765cf42426abc39f8", size = 2454547, upload-time = "2026-01-02T09:12:18.704Z" },
|
|
108
|
+
{ url = "https://files.pythonhosted.org/packages/8c/87/bdf971d8bbcf80a348cc3bacfcb239f5882100fe80534b0ce67a784181d8/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91", size = 4062533, upload-time = "2026-01-02T09:12:20.791Z" },
|
|
109
|
+
{ url = "https://files.pythonhosted.org/packages/ff/4f/5eb37a681c68d605eb7034c004875c81f86ec9ef51f5be4a63eadd58859a/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796", size = 4138546, upload-time = "2026-01-02T09:12:23.664Z" },
|
|
110
|
+
{ url = "https://files.pythonhosted.org/packages/11/6d/19a95acb2edbace40dcd582d077b991646b7083c41b98da4ed7555b59733/pillow-12.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd", size = 3601163, upload-time = "2026-01-02T09:12:26.338Z" },
|
|
111
|
+
{ url = "https://files.pythonhosted.org/packages/fc/36/2b8138e51cb42e4cc39c3297713455548be855a50558c3ac2beebdc251dd/pillow-12.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13", size = 5266086, upload-time = "2026-01-02T09:12:28.782Z" },
|
|
112
|
+
{ url = "https://files.pythonhosted.org/packages/53/4b/649056e4d22e1caa90816bf99cef0884aed607ed38075bd75f091a607a38/pillow-12.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e", size = 4657344, upload-time = "2026-01-02T09:12:31.117Z" },
|
|
113
|
+
{ url = "https://files.pythonhosted.org/packages/6c/6b/c5742cea0f1ade0cd61485dc3d81f05261fc2276f537fbdc00802de56779/pillow-12.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643", size = 6232114, upload-time = "2026-01-02T09:12:32.936Z" },
|
|
114
|
+
{ url = "https://files.pythonhosted.org/packages/bf/8f/9f521268ce22d63991601aafd3d48d5ff7280a246a1ef62d626d67b44064/pillow-12.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5", size = 8042708, upload-time = "2026-01-02T09:12:34.78Z" },
|
|
115
|
+
{ url = "https://files.pythonhosted.org/packages/1a/eb/257f38542893f021502a1bbe0c2e883c90b5cff26cc33b1584a841a06d30/pillow-12.1.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de", size = 6347762, upload-time = "2026-01-02T09:12:36.748Z" },
|
|
116
|
+
{ url = "https://files.pythonhosted.org/packages/c4/5a/8ba375025701c09b309e8d5163c5a4ce0102fa86bbf8800eb0d7ac87bc51/pillow-12.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9", size = 7039265, upload-time = "2026-01-02T09:12:39.082Z" },
|
|
117
|
+
{ url = "https://files.pythonhosted.org/packages/cf/dc/cf5e4cdb3db533f539e88a7bbf9f190c64ab8a08a9bc7a4ccf55067872e4/pillow-12.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a", size = 6462341, upload-time = "2026-01-02T09:12:40.946Z" },
|
|
118
|
+
{ url = "https://files.pythonhosted.org/packages/d0/47/0291a25ac9550677e22eda48510cfc4fa4b2ef0396448b7fbdc0a6946309/pillow-12.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a", size = 7165395, upload-time = "2026-01-02T09:12:42.706Z" },
|
|
119
|
+
{ url = "https://files.pythonhosted.org/packages/4f/4c/e005a59393ec4d9416be06e6b45820403bb946a778e39ecec62f5b2b991e/pillow-12.1.0-cp314-cp314-win32.whl", hash = "sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030", size = 6431413, upload-time = "2026-01-02T09:12:44.944Z" },
|
|
120
|
+
{ url = "https://files.pythonhosted.org/packages/1c/af/f23697f587ac5f9095d67e31b81c95c0249cd461a9798a061ed6709b09b5/pillow-12.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94", size = 7176779, upload-time = "2026-01-02T09:12:46.727Z" },
|
|
121
|
+
{ url = "https://files.pythonhosted.org/packages/b3/36/6a51abf8599232f3e9afbd16d52829376a68909fe14efe29084445db4b73/pillow-12.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4", size = 2543105, upload-time = "2026-01-02T09:12:49.243Z" },
|
|
122
|
+
{ url = "https://files.pythonhosted.org/packages/82/54/2e1dd20c8749ff225080d6ba465a0cab4387f5db0d1c5fb1439e2d99923f/pillow-12.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2", size = 5268571, upload-time = "2026-01-02T09:12:51.11Z" },
|
|
123
|
+
{ url = "https://files.pythonhosted.org/packages/57/61/571163a5ef86ec0cf30d265ac2a70ae6fc9e28413d1dc94fa37fae6bda89/pillow-12.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61", size = 4660426, upload-time = "2026-01-02T09:12:52.865Z" },
|
|
124
|
+
{ url = "https://files.pythonhosted.org/packages/5e/e1/53ee5163f794aef1bf84243f755ee6897a92c708505350dd1923f4afec48/pillow-12.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51", size = 6269908, upload-time = "2026-01-02T09:12:54.884Z" },
|
|
125
|
+
{ url = "https://files.pythonhosted.org/packages/bc/0b/b4b4106ff0ee1afa1dc599fde6ab230417f800279745124f6c50bcffed8e/pillow-12.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc", size = 8074733, upload-time = "2026-01-02T09:12:56.802Z" },
|
|
126
|
+
{ url = "https://files.pythonhosted.org/packages/19/9f/80b411cbac4a732439e629a26ad3ef11907a8c7fc5377b7602f04f6fe4e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14", size = 6381431, upload-time = "2026-01-02T09:12:58.823Z" },
|
|
127
|
+
{ url = "https://files.pythonhosted.org/packages/8f/b7/d65c45db463b66ecb6abc17c6ba6917a911202a07662247e1355ce1789e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8", size = 7068529, upload-time = "2026-01-02T09:13:00.885Z" },
|
|
128
|
+
{ url = "https://files.pythonhosted.org/packages/50/96/dfd4cd726b4a45ae6e3c669fc9e49deb2241312605d33aba50499e9d9bd1/pillow-12.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924", size = 6492981, upload-time = "2026-01-02T09:13:03.314Z" },
|
|
129
|
+
{ url = "https://files.pythonhosted.org/packages/4d/1c/b5dc52cf713ae46033359c5ca920444f18a6359ce1020dd3e9c553ea5bc6/pillow-12.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef", size = 7191878, upload-time = "2026-01-02T09:13:05.276Z" },
|
|
130
|
+
{ url = "https://files.pythonhosted.org/packages/53/26/c4188248bd5edaf543864fe4834aebe9c9cb4968b6f573ce014cc42d0720/pillow-12.1.0-cp314-cp314t-win32.whl", hash = "sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988", size = 6438703, upload-time = "2026-01-02T09:13:07.491Z" },
|
|
131
|
+
{ url = "https://files.pythonhosted.org/packages/b8/0e/69ed296de8ea05cb03ee139cee600f424ca166e632567b2d66727f08c7ed/pillow-12.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6", size = 7182927, upload-time = "2026-01-02T09:13:09.841Z" },
|
|
132
|
+
{ url = "https://files.pythonhosted.org/packages/fc/f5/68334c015eed9b5cff77814258717dec591ded209ab5b6fb70e2ae873d1d/pillow-12.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831", size = 2545104, upload-time = "2026-01-02T09:13:12.068Z" },
|
|
133
|
+
{ url = "https://files.pythonhosted.org/packages/8b/bc/224b1d98cffd7164b14707c91aac83c07b047fbd8f58eba4066a3e53746a/pillow-12.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ca94b6aac0d7af2a10ba08c0f888b3d5114439b6b3ef39968378723622fed377", size = 5228605, upload-time = "2026-01-02T09:13:14.084Z" },
|
|
134
|
+
{ url = "https://files.pythonhosted.org/packages/0c/ca/49ca7769c4550107de049ed85208240ba0f330b3f2e316f24534795702ce/pillow-12.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:351889afef0f485b84078ea40fe33727a0492b9af3904661b0abbafee0355b72", size = 4622245, upload-time = "2026-01-02T09:13:15.964Z" },
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/73/48/fac807ce82e5955bcc2718642b94b1bd22a82a6d452aea31cbb678cddf12/pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb0984b30e973f7e2884362b7d23d0a348c7143ee559f38ef3eaab640144204c", size = 5247593, upload-time = "2026-01-02T09:13:17.913Z" },
|
|
136
|
+
{ url = "https://files.pythonhosted.org/packages/d2/95/3e0742fe358c4664aed4fd05d5f5373dcdad0b27af52aa0972568541e3f4/pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84cabc7095dd535ca934d57e9ce2a72ffd216e435a84acb06b2277b1de2689bd", size = 6989008, upload-time = "2026-01-02T09:13:20.083Z" },
|
|
137
|
+
{ url = "https://files.pythonhosted.org/packages/5a/74/fe2ac378e4e202e56d50540d92e1ef4ff34ed687f3c60f6a121bcf99437e/pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53d8b764726d3af1a138dd353116f774e3862ec7e3794e0c8781e30db0f35dfc", size = 5313824, upload-time = "2026-01-02T09:13:22.405Z" },
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/f3/77/2a60dee1adee4e2655ac328dd05c02a955c1cd683b9f1b82ec3feb44727c/pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5da841d81b1a05ef940a8567da92decaa15bc4d7dedb540a8c219ad83d91808a", size = 5963278, upload-time = "2026-01-02T09:13:24.706Z" },
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/2d/71/64e9b1c7f04ae0027f788a248e6297d7fcc29571371fe7d45495a78172c0/pillow-12.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:75af0b4c229ac519b155028fa1be632d812a519abba9b46b20e50c6caa184f19", size = 7029809, upload-time = "2026-01-02T09:13:26.541Z" },
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
[[package]]
|
|
143
|
+
name = "pluggy"
|
|
144
|
+
version = "1.6.0"
|
|
145
|
+
source = { registry = "https://pypi.org/simple" }
|
|
146
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
|
147
|
+
wheels = [
|
|
148
|
+
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
[[package]]
|
|
152
|
+
name = "pygments"
|
|
153
|
+
version = "2.19.2"
|
|
154
|
+
source = { registry = "https://pypi.org/simple" }
|
|
155
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
|
|
156
|
+
wheels = [
|
|
157
|
+
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
[[package]]
|
|
161
|
+
name = "pytest"
|
|
162
|
+
version = "9.0.2"
|
|
163
|
+
source = { registry = "https://pypi.org/simple" }
|
|
164
|
+
dependencies = [
|
|
165
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
166
|
+
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
|
167
|
+
{ name = "iniconfig" },
|
|
168
|
+
{ name = "packaging" },
|
|
169
|
+
{ name = "pluggy" },
|
|
170
|
+
{ name = "pygments" },
|
|
171
|
+
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
172
|
+
]
|
|
173
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
|
174
|
+
wheels = [
|
|
175
|
+
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
[[package]]
|
|
179
|
+
name = "ruff"
|
|
180
|
+
version = "0.15.0"
|
|
181
|
+
source = { registry = "https://pypi.org/simple" }
|
|
182
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893, upload-time = "2026-02-03T17:53:35.357Z" }
|
|
183
|
+
wheels = [
|
|
184
|
+
{ url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332, upload-time = "2026-02-03T17:52:54.892Z" },
|
|
185
|
+
{ url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189, upload-time = "2026-02-03T17:53:19.778Z" },
|
|
186
|
+
{ url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384, upload-time = "2026-02-03T17:53:29.241Z" },
|
|
187
|
+
{ url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363, upload-time = "2026-02-03T17:52:43.332Z" },
|
|
188
|
+
{ url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736, upload-time = "2026-02-03T17:53:00.522Z" },
|
|
189
|
+
{ url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415, upload-time = "2026-02-03T17:53:15.705Z" },
|
|
190
|
+
{ url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643, upload-time = "2026-02-03T17:53:23.031Z" },
|
|
191
|
+
{ url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787, upload-time = "2026-02-03T17:52:46.432Z" },
|
|
192
|
+
{ url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797, upload-time = "2026-02-03T17:52:49.274Z" },
|
|
193
|
+
{ url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133, upload-time = "2026-02-03T17:53:33.105Z" },
|
|
194
|
+
{ url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646, upload-time = "2026-02-03T17:53:06.278Z" },
|
|
195
|
+
{ url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750, upload-time = "2026-02-03T17:53:26.084Z" },
|
|
196
|
+
{ url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120, upload-time = "2026-02-03T17:53:09.363Z" },
|
|
197
|
+
{ url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636, upload-time = "2026-02-03T17:52:57.281Z" },
|
|
198
|
+
{ url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945, upload-time = "2026-02-03T17:53:12.591Z" },
|
|
199
|
+
{ url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657, upload-time = "2026-02-03T17:52:51.893Z" },
|
|
200
|
+
{ url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" },
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
[[package]]
|
|
204
|
+
name = "sprite-animator"
|
|
205
|
+
version = "0.1.0"
|
|
206
|
+
source = { editable = "." }
|
|
207
|
+
dependencies = [
|
|
208
|
+
{ name = "pillow" },
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
[package.dev-dependencies]
|
|
212
|
+
dev = [
|
|
213
|
+
{ name = "pytest" },
|
|
214
|
+
{ name = "ruff" },
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
[package.metadata]
|
|
218
|
+
requires-dist = [{ name = "pillow", specifier = ">=10.0.0" }]
|
|
219
|
+
|
|
220
|
+
[package.metadata.requires-dev]
|
|
221
|
+
dev = [
|
|
222
|
+
{ name = "pytest", specifier = ">=7.0" },
|
|
223
|
+
{ name = "ruff", specifier = ">=0.1.0" },
|
|
224
|
+
]
|
|
225
|
+
|
|
226
|
+
[[package]]
|
|
227
|
+
name = "tomli"
|
|
228
|
+
version = "2.4.0"
|
|
229
|
+
source = { registry = "https://pypi.org/simple" }
|
|
230
|
+
sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" }
|
|
231
|
+
wheels = [
|
|
232
|
+
{ url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" },
|
|
233
|
+
{ url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" },
|
|
234
|
+
{ url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" },
|
|
235
|
+
{ url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" },
|
|
236
|
+
{ url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" },
|
|
237
|
+
{ url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" },
|
|
238
|
+
{ url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" },
|
|
239
|
+
{ url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" },
|
|
240
|
+
{ url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" },
|
|
241
|
+
{ url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" },
|
|
242
|
+
{ url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" },
|
|
243
|
+
{ url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" },
|
|
244
|
+
{ url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" },
|
|
245
|
+
{ url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" },
|
|
246
|
+
{ url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" },
|
|
247
|
+
{ url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" },
|
|
248
|
+
{ url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" },
|
|
249
|
+
{ url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" },
|
|
250
|
+
{ url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" },
|
|
251
|
+
{ url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" },
|
|
252
|
+
{ url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" },
|
|
253
|
+
{ url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" },
|
|
254
|
+
{ url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" },
|
|
255
|
+
{ url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" },
|
|
256
|
+
{ url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" },
|
|
257
|
+
{ url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" },
|
|
258
|
+
{ url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" },
|
|
259
|
+
{ url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" },
|
|
260
|
+
{ url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" },
|
|
261
|
+
{ url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" },
|
|
262
|
+
{ url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" },
|
|
263
|
+
{ url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" },
|
|
264
|
+
{ url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" },
|
|
265
|
+
{ url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" },
|
|
266
|
+
{ url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" },
|
|
267
|
+
{ url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" },
|
|
268
|
+
{ url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" },
|
|
269
|
+
{ url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" },
|
|
270
|
+
{ url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" },
|
|
271
|
+
{ url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" },
|
|
272
|
+
{ url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" },
|
|
273
|
+
{ url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" },
|
|
274
|
+
{ url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" },
|
|
275
|
+
{ url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" },
|
|
276
|
+
{ url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" },
|
|
277
|
+
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
|
|
278
|
+
]
|
|
279
|
+
|
|
280
|
+
[[package]]
|
|
281
|
+
name = "typing-extensions"
|
|
282
|
+
version = "4.15.0"
|
|
283
|
+
source = { registry = "https://pypi.org/simple" }
|
|
284
|
+
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
|
285
|
+
wheels = [
|
|
286
|
+
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
|
287
|
+
]
|