artlab 1.0.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.
- artlab-1.0.0/LICENSE +21 -0
- artlab-1.0.0/PKG-INFO +151 -0
- artlab-1.0.0/README.md +121 -0
- artlab-1.0.0/artlab/__init__.py +3 -0
- artlab-1.0.0/artlab/__main__.py +4 -0
- artlab-1.0.0/artlab/cli.py +82 -0
- artlab-1.0.0/artlab/core.py +334 -0
- artlab-1.0.0/artlab.egg-info/PKG-INFO +151 -0
- artlab-1.0.0/artlab.egg-info/SOURCES.txt +13 -0
- artlab-1.0.0/artlab.egg-info/dependency_links.txt +1 -0
- artlab-1.0.0/artlab.egg-info/entry_points.txt +2 -0
- artlab-1.0.0/artlab.egg-info/top_level.txt +1 -0
- artlab-1.0.0/pyproject.toml +41 -0
- artlab-1.0.0/setup.cfg +4 -0
- artlab-1.0.0/tests/test_artlab.py +108 -0
artlab-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsk-dev-ai
|
|
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.
|
artlab-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: artlab
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: ASCII art in your terminal. Zero dependencies.
|
|
5
|
+
Author: dsk-dev-ai
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dsk-dev-ai/artlab
|
|
8
|
+
Project-URL: Source, https://github.com/dsk-dev-ai/artlab
|
|
9
|
+
Project-URL: Issues, https://github.com/dsk-dev-ai/artlab/issues
|
|
10
|
+
Project-URL: Funding, https://github.com/sponsors/dsk-dev-ai
|
|
11
|
+
Keywords: ascii,art,terminal,cli,fun,text,rainbow,ascii-art
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Artistic Software
|
|
24
|
+
Classifier: Topic :: Terminals
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
|
|
33
|
+
# 🎨 artlab
|
|
34
|
+
|
|
35
|
+
**ASCII art in your terminal.**
|
|
36
|
+
|
|
37
|
+
Bundled retro art, big block-letter text, rainbow colors, and animation.
|
|
38
|
+
Zero dependencies. Works in Python 3.8+.
|
|
39
|
+
|
|
40
|
+
[](https://pypi.org/project/artlab/)
|
|
41
|
+
[](#)
|
|
42
|
+
[](#)
|
|
43
|
+
[](https://github.com/dsk-dev-ai/artlab/pkgs/container/artlab)
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Why artlab?
|
|
50
|
+
|
|
51
|
+
Because the terminal deserves a little personality.
|
|
52
|
+
|
|
53
|
+
- **Bundled ASCII art** — robots, cats, dragons, unicorns, yoda, octopuses, hearts and more.
|
|
54
|
+
- **Block-letter text** — print any word big, `figlet`-style, with zero dependencies.
|
|
55
|
+
- **Rainbow & colors** — 256-color palettes, or dims down for your pipelines.
|
|
56
|
+
- **Animation** — reveal art with a slick frame-by-frame intro.
|
|
57
|
+
- **Zero dependencies** — pure Python stdlib. No install drama.
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install artlab
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or run without installing:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pipx run artlab --help
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Via Docker:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
docker run --rm ghcr.io/dsk-dev-ai/artlab --help
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Show a random piece
|
|
81
|
+
artlab
|
|
82
|
+
|
|
83
|
+
# Show a specific piece
|
|
84
|
+
artlab robot
|
|
85
|
+
artlab cat
|
|
86
|
+
artlab dragon
|
|
87
|
+
|
|
88
|
+
# List everything
|
|
89
|
+
artlab -l
|
|
90
|
+
|
|
91
|
+
# Print big block text
|
|
92
|
+
artlab --text "SHIP IT"
|
|
93
|
+
artlab -t "artlab" -c cyan
|
|
94
|
+
|
|
95
|
+
# Force a palette
|
|
96
|
+
artlab robot -c green
|
|
97
|
+
artlab dragon -c magenta
|
|
98
|
+
|
|
99
|
+
# Animate the reveal
|
|
100
|
+
artlab unicorn -a
|
|
101
|
+
|
|
102
|
+
# No color (for scripts / piping)
|
|
103
|
+
artlab robot --no-color
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Options
|
|
107
|
+
|
|
108
|
+
| Flag | Description |
|
|
109
|
+
|------|-------------|
|
|
110
|
+
| `-l, --list` | List all available pieces |
|
|
111
|
+
| `-r, --random` | Show a random piece |
|
|
112
|
+
| `-t, --text TEXT` | Render text as big block letters |
|
|
113
|
+
| `-c, --color` | Palette: `rainbow`, `green`, `cyan`, `magenta`, `none` |
|
|
114
|
+
| `-w, --width N` | Pad art to a fixed width |
|
|
115
|
+
| `--no-center` | Don't center the art |
|
|
116
|
+
| `-a, --animate` | Reveal the art with an animation |
|
|
117
|
+
| `--no-color` | Disable color output |
|
|
118
|
+
| `--version` | Print version |
|
|
119
|
+
|
|
120
|
+
## Gallery
|
|
121
|
+
|
|
122
|
+
Run `artlab` to see a random piece, or `artlab -l` to browse the full library.
|
|
123
|
+
Everything is bundled — no API, no network, no nonsense.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
/\_/\
|
|
127
|
+
( o.o )
|
|
128
|
+
> ^ <
|
|
129
|
+
/ \
|
|
130
|
+
\_____/ meow
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Tests
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
python -m pytest
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Tested on Python 3.8–3.12 via GitHub Actions.
|
|
140
|
+
|
|
141
|
+
## Sponsor
|
|
142
|
+
|
|
143
|
+
Like `artlab`? Keep the terminal artsy for free by [becoming a sponsor](https://github.com/sponsors/dsk-dev-ai) on GitHub 💜.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
<div align="center">
|
|
148
|
+
|
|
149
|
+
**artlab** · because the terminal deserves art.
|
|
150
|
+
|
|
151
|
+
</div>
|
artlab-1.0.0/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🎨 artlab
|
|
4
|
+
|
|
5
|
+
**ASCII art in your terminal.**
|
|
6
|
+
|
|
7
|
+
Bundled retro art, big block-letter text, rainbow colors, and animation.
|
|
8
|
+
Zero dependencies. Works in Python 3.8+.
|
|
9
|
+
|
|
10
|
+
[](https://pypi.org/project/artlab/)
|
|
11
|
+
[](#)
|
|
12
|
+
[](#)
|
|
13
|
+
[](https://github.com/dsk-dev-ai/artlab/pkgs/container/artlab)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Why artlab?
|
|
20
|
+
|
|
21
|
+
Because the terminal deserves a little personality.
|
|
22
|
+
|
|
23
|
+
- **Bundled ASCII art** — robots, cats, dragons, unicorns, yoda, octopuses, hearts and more.
|
|
24
|
+
- **Block-letter text** — print any word big, `figlet`-style, with zero dependencies.
|
|
25
|
+
- **Rainbow & colors** — 256-color palettes, or dims down for your pipelines.
|
|
26
|
+
- **Animation** — reveal art with a slick frame-by-frame intro.
|
|
27
|
+
- **Zero dependencies** — pure Python stdlib. No install drama.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install artlab
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or run without installing:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pipx run artlab --help
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Via Docker:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
docker run --rm ghcr.io/dsk-dev-ai/artlab --help
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Show a random piece
|
|
51
|
+
artlab
|
|
52
|
+
|
|
53
|
+
# Show a specific piece
|
|
54
|
+
artlab robot
|
|
55
|
+
artlab cat
|
|
56
|
+
artlab dragon
|
|
57
|
+
|
|
58
|
+
# List everything
|
|
59
|
+
artlab -l
|
|
60
|
+
|
|
61
|
+
# Print big block text
|
|
62
|
+
artlab --text "SHIP IT"
|
|
63
|
+
artlab -t "artlab" -c cyan
|
|
64
|
+
|
|
65
|
+
# Force a palette
|
|
66
|
+
artlab robot -c green
|
|
67
|
+
artlab dragon -c magenta
|
|
68
|
+
|
|
69
|
+
# Animate the reveal
|
|
70
|
+
artlab unicorn -a
|
|
71
|
+
|
|
72
|
+
# No color (for scripts / piping)
|
|
73
|
+
artlab robot --no-color
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Options
|
|
77
|
+
|
|
78
|
+
| Flag | Description |
|
|
79
|
+
|------|-------------|
|
|
80
|
+
| `-l, --list` | List all available pieces |
|
|
81
|
+
| `-r, --random` | Show a random piece |
|
|
82
|
+
| `-t, --text TEXT` | Render text as big block letters |
|
|
83
|
+
| `-c, --color` | Palette: `rainbow`, `green`, `cyan`, `magenta`, `none` |
|
|
84
|
+
| `-w, --width N` | Pad art to a fixed width |
|
|
85
|
+
| `--no-center` | Don't center the art |
|
|
86
|
+
| `-a, --animate` | Reveal the art with an animation |
|
|
87
|
+
| `--no-color` | Disable color output |
|
|
88
|
+
| `--version` | Print version |
|
|
89
|
+
|
|
90
|
+
## Gallery
|
|
91
|
+
|
|
92
|
+
Run `artlab` to see a random piece, or `artlab -l` to browse the full library.
|
|
93
|
+
Everything is bundled — no API, no network, no nonsense.
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
/\_/\
|
|
97
|
+
( o.o )
|
|
98
|
+
> ^ <
|
|
99
|
+
/ \
|
|
100
|
+
\_____/ meow
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Tests
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
python -m pytest
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Tested on Python 3.8–3.12 via GitHub Actions.
|
|
110
|
+
|
|
111
|
+
## Sponsor
|
|
112
|
+
|
|
113
|
+
Like `artlab`? Keep the terminal artsy for free by [becoming a sponsor](https://github.com/sponsors/dsk-dev-ai) on GitHub 💜.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
<div align="center">
|
|
118
|
+
|
|
119
|
+
**artlab** · because the terminal deserves art.
|
|
120
|
+
|
|
121
|
+
</div>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""artlab command-line interface."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import random
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from artlab import __version__
|
|
8
|
+
import artlab.core as core
|
|
9
|
+
from artlab.core import ART, list_pieces, show
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def build_parser():
|
|
13
|
+
p = argparse.ArgumentParser(
|
|
14
|
+
prog="artlab",
|
|
15
|
+
description="ASCII art in your terminal. Show bundled art, rainbow text, "
|
|
16
|
+
"or animate it. Zero dependencies.",
|
|
17
|
+
)
|
|
18
|
+
p.add_argument("piece", nargs="?", default=None,
|
|
19
|
+
help="name of a piece to show (see 'artlab list')")
|
|
20
|
+
p.add_argument("-t", "--text", default=None,
|
|
21
|
+
help="render text as block letters (big ASCII)")
|
|
22
|
+
p.add_argument("-l", "--list", action="store_true",
|
|
23
|
+
help="list all available pieces")
|
|
24
|
+
p.add_argument("-r", "--random", action="store_true",
|
|
25
|
+
help="show a random piece")
|
|
26
|
+
p.add_argument("-c", "--color", choices=["rainbow", "green", "cyan",
|
|
27
|
+
"magenta", "none"],
|
|
28
|
+
default="rainbow", help="color palette (default rainbow)")
|
|
29
|
+
p.add_argument("-w", "--width", type=int, default=None,
|
|
30
|
+
help="pad art to a fixed width")
|
|
31
|
+
p.add_argument("--no-center", action="store_true",
|
|
32
|
+
help="don't center the art")
|
|
33
|
+
p.add_argument("--no-color", action="store_true",
|
|
34
|
+
help="don't colorize output")
|
|
35
|
+
p.add_argument("-a", "--animate", action="store_true",
|
|
36
|
+
help="reveal the art with an animation")
|
|
37
|
+
p.add_argument("--version", action="version", version=f"artlab {__version__}")
|
|
38
|
+
return p
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main(argv=None):
|
|
42
|
+
args = build_parser().parse_args(argv)
|
|
43
|
+
|
|
44
|
+
if args.no_color:
|
|
45
|
+
core.NO_COLOR = True
|
|
46
|
+
|
|
47
|
+
if args.list:
|
|
48
|
+
for name in list_pieces():
|
|
49
|
+
print(f" {name}")
|
|
50
|
+
print(f"\n({len(ART)} pieces available)")
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
if args.text is not None:
|
|
54
|
+
pieces = args.text if args.text else None
|
|
55
|
+
show(text=args.text, color=args.color, width=args.width,
|
|
56
|
+
center=not args.no_center, animate=args.animate)
|
|
57
|
+
return 0
|
|
58
|
+
|
|
59
|
+
if args.random:
|
|
60
|
+
key, art = random.choice(list(ART.items()))
|
|
61
|
+
show(name=key, color=args.color, width=args.width,
|
|
62
|
+
center=not args.no_center, animate=args.animate)
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
if args.piece:
|
|
66
|
+
from artlab.core import pick_piece
|
|
67
|
+
if pick_piece(args.piece)[0] is None:
|
|
68
|
+
print(f"artlab: unknown piece '{args.piece}' (try 'artlab -l')",
|
|
69
|
+
file=sys.stderr)
|
|
70
|
+
return 1
|
|
71
|
+
show(name=args.piece, color=args.color, width=args.width,
|
|
72
|
+
center=not args.no_center, animate=args.animate)
|
|
73
|
+
return 0
|
|
74
|
+
|
|
75
|
+
# default: random piece
|
|
76
|
+
show(color=args.color, width=args.width, center=not args.no_center,
|
|
77
|
+
animate=args.animate)
|
|
78
|
+
return 0
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
sys.exit(main())
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""artlab - ASCII art in your terminal.
|
|
2
|
+
|
|
3
|
+
Bundled ASCII art library, a zero-dependency block-letter text renderer,
|
|
4
|
+
color/rainbow helpers, and simple frame animation. Zero dependencies.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import colorsys
|
|
8
|
+
import random
|
|
9
|
+
import shutil
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
# --------------------------------------------------------------------------
|
|
14
|
+
# Bundled ASCII art library
|
|
15
|
+
# --------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
ART = {
|
|
18
|
+
"robot": r""" .-""" + """""" + r""".
|
|
19
|
+
.'_________'.
|
|
20
|
+
/ _ _ \
|
|
21
|
+
| (o) (o) |
|
|
22
|
+
| ___ |
|
|
23
|
+
| \_______/ |
|
|
24
|
+
\ _ _ /
|
|
25
|
+
| [ o ] |
|
|
26
|
+
|___|_|___|
|
|
27
|
+
/ | | | \
|
|
28
|
+
| | | | |
|
|
29
|
+
~~~~~~~~~~~~~""",
|
|
30
|
+
"cat": r""" /\_/\
|
|
31
|
+
( o.o )
|
|
32
|
+
> ^ <
|
|
33
|
+
/ \
|
|
34
|
+
\_____/ meow""",
|
|
35
|
+
"dog": r""" / \__
|
|
36
|
+
( @\___
|
|
37
|
+
/ O
|
|
38
|
+
/ (_____/
|
|
39
|
+
/_____/ U
|
|
40
|
+
dog""",
|
|
41
|
+
"fox": r""" /\_/\
|
|
42
|
+
( o.o )
|
|
43
|
+
> ^ <
|
|
44
|
+
/ _ \
|
|
45
|
+
\__|__/ ring-ding-ding""",
|
|
46
|
+
"dragon": r""" __====-_ _-====___
|
|
47
|
+
_--^^^#####// \\#####^^^--_
|
|
48
|
+
-^##########/ ( ) \##########^-
|
|
49
|
+
############/ ( ) \############
|
|
50
|
+
###########/ ( ) \###########
|
|
51
|
+
#########/ ( ) ( ) \#########
|
|
52
|
+
########( ( ) ( ) )#######
|
|
53
|
+
#########\ ( ) /#########
|
|
54
|
+
#######\ ( ) /#######
|
|
55
|
+
####### \ ( ) /#######
|
|
56
|
+
####### \______/ #######
|
|
57
|
+
d r a g o n""",
|
|
58
|
+
"unicorn": r""" /
|
|
59
|
+
\ /
|
|
60
|
+
\ /____
|
|
61
|
+
( ____\_________
|
|
62
|
+
___/ \____\tttt___
|
|
63
|
+
/ \____( ' ' ) |\
|
|
64
|
+
( o' (o o) ) | )
|
|
65
|
+
\ \ (_,_)\___/ /
|
|
66
|
+
\ \____)___(____ )
|
|
67
|
+
\__(___________\__/
|
|
68
|
+
unicorn ~*~*~""",
|
|
69
|
+
"heart": r""" ****** ******
|
|
70
|
+
********* *********
|
|
71
|
+
*********** ***********
|
|
72
|
+
*********************
|
|
73
|
+
*******************
|
|
74
|
+
*****************
|
|
75
|
+
***************
|
|
76
|
+
*************
|
|
77
|
+
***********
|
|
78
|
+
*********
|
|
79
|
+
*******
|
|
80
|
+
*****
|
|
81
|
+
***
|
|
82
|
+
*""",
|
|
83
|
+
"yoda": r""" ..,
|
|
84
|
+
( o o )
|
|
85
|
+
( > < )
|
|
86
|
+
/A A\
|
|
87
|
+
|+. _ .+|
|
|
88
|
+
\_|_|_/
|
|
89
|
+
v v
|
|
90
|
+
use the source""",
|
|
91
|
+
"octopus": r""" _
|
|
92
|
+
.-( )-.
|
|
93
|
+
/ _ _ \
|
|
94
|
+
( (o)(o)(o) )
|
|
95
|
+
\ \___/ /
|
|
96
|
+
\_______/
|
|
97
|
+
| |
|
|
98
|
+
/| |\
|
|
99
|
+
/ |_____| \
|
|
100
|
+
~~~ ~~~
|
|
101
|
+
octopus""",
|
|
102
|
+
"ghost": r""" .-~~~~-.
|
|
103
|
+
/ ____ \
|
|
104
|
+
| / \ |
|
|
105
|
+
| | o o | |
|
|
106
|
+
| \____/ |
|
|
107
|
+
\ ____ /
|
|
108
|
+
| | | |
|
|
109
|
+
\|__|/
|
|
110
|
+
/ / \ \
|
|
111
|
+
~~ ~~
|
|
112
|
+
boo!""",
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# --------------------------------------------------------------------------
|
|
116
|
+
# Minimal block-letter text renderer (zero-dependency figlet-style)
|
|
117
|
+
# --------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
_FONT = {}
|
|
120
|
+
|
|
121
|
+
_Q = [" ##### ", "## ##", "## ##", "## ##", " ##### "]
|
|
122
|
+
_W = ["## ##", "## ##", "## ## ##", "#### ###", "## ##"]
|
|
123
|
+
_H = ["## ##", "## ##", "#######", "## ##", "## ##"]
|
|
124
|
+
_A = [" ##### ", "## ##", "#######", "## ##", "## ##"]
|
|
125
|
+
_R = [" ##### ", "## ##", "##### ", "## ## ", "## ##"]
|
|
126
|
+
_T = ["#######", " # ", " # ", " # ", " # "]
|
|
127
|
+
_P = ["###### ", "## ##", "###### ", "## ", "## "]
|
|
128
|
+
_L = ["## ", "## ", "## ", "## ", "#######"]
|
|
129
|
+
_I = ["###", " # ", " # ", " # ", "###"]
|
|
130
|
+
_E = ["#######", "## ", "##### ", "## ", "#######"]
|
|
131
|
+
_S = [" ######", "## ", " ##### ", " ##", "###### "]
|
|
132
|
+
_O = [" ##### ", "## ##", "## ##", "## ##", " ##### "]
|
|
133
|
+
_U = ["## ##", "## ##", "## ##", "## ##", " ##### "]
|
|
134
|
+
_N = ["## #", "### #", "## # #", "## # #", "## ##"]
|
|
135
|
+
_Y = ["## ##", "## ##", " ##### ", " ## ", " ## "]
|
|
136
|
+
_G = [" ##### ", "## ", "## ###", "## ##", " ##### "]
|
|
137
|
+
_D = ["###### ", "## ##", "## ##", "## ##", "###### "]
|
|
138
|
+
_K = ["## ## ", "## ## ", "#### ", "## ## ", "## ## "]
|
|
139
|
+
_X = ["## ##", " ## ## ", " ### ", " ## ## ", "## ##"]
|
|
140
|
+
_Z = ["#######", " ## ", " ## ", " ## ", "#######"]
|
|
141
|
+
_V = ["## ##", "## ##", "## ##", " ## ## ", " ### "]
|
|
142
|
+
_J = [" ###", " ##", " ##", "## ##", " ##### "]
|
|
143
|
+
_M = ["## ## ##", "### ### ##", "## # # ##", "## ##", "## ##"]
|
|
144
|
+
_B = ["###### ", "## ##", "###### ", "## ##", "###### "]
|
|
145
|
+
_F = ["#######", "## ", "##### ", "## ", "## "]
|
|
146
|
+
_C = [" ##### ", "## ", "## ", "## ", " ##### "]
|
|
147
|
+
|
|
148
|
+
_FONT = {
|
|
149
|
+
"0": _O, "1": [" ## ", " #### ", " ## ", " ## ", " ## "],
|
|
150
|
+
"2": [" #### ", " ##", " #### ", "## ", "##### "],
|
|
151
|
+
"3": [" #### ", " ##", " #### ", " ##", " #### "],
|
|
152
|
+
"4": ["## ##", "## ##", "#######", " ##", " ##"],
|
|
153
|
+
"5": ["#######", "## ", "##### ", " ##", "##### "],
|
|
154
|
+
"6": [" ##### ", "## ", "###### ", "## ##", " ##### "],
|
|
155
|
+
"7": ["#######", " ## ", " ## ", " ## ", " ## "],
|
|
156
|
+
"8": [" ##### ", "## ##", " ##### ", "## ##", " ##### "],
|
|
157
|
+
"9": [" ##### ", "## ##", " #######", " ##", " ##### "],
|
|
158
|
+
".": [" ", " ", " ", " ", " ## "],
|
|
159
|
+
" ": [" ", " ", " ", " ", " "],
|
|
160
|
+
"!": ["###", "###", "###", " ", " # "],
|
|
161
|
+
"?": ["#### ", " ##", " ## ", " ", " # "],
|
|
162
|
+
"-": [" ", " ", "#####", " ", " "],
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# Assign letters to their glyphs
|
|
166
|
+
for _char, _glyph in [
|
|
167
|
+
("A", _A), ("B", _B), ("C", _C), ("D", _D), ("E", _E), ("F", _F),
|
|
168
|
+
("G", _G), ("H", _H), ("I", _I), ("J", _J), ("K", _K), ("L", _L),
|
|
169
|
+
("M", _M), ("N", _N), ("O", _O), ("P", _P), ("Q", _Q), ("R", _R),
|
|
170
|
+
("S", _S), ("T", _T), ("U", _U), ("V", _V), ("W", _W), ("X", _X),
|
|
171
|
+
("Y", _Y), ("Z", _Z),
|
|
172
|
+
]:
|
|
173
|
+
_FONT[_char] = _glyph
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def render_text(text, font="block"):
|
|
177
|
+
"""Render text as block letters (zero dependencies). Returns list of lines."""
|
|
178
|
+
lines = ["" for _ in range(5)]
|
|
179
|
+
for ch in text.upper():
|
|
180
|
+
glyph = _FONT.get(ch, _FONT[" "])
|
|
181
|
+
for i in range(5):
|
|
182
|
+
lines[i] += glyph[i] + " "
|
|
183
|
+
return [line.rstrip() for line in lines]
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# --------------------------------------------------------------------------
|
|
187
|
+
# Color helpers
|
|
188
|
+
# --------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
NO_COLOR = False
|
|
191
|
+
FORCE_COLOR = False
|
|
192
|
+
|
|
193
|
+
RESET = "\033[0m"
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _should_colorize(palette, out):
|
|
197
|
+
if NO_COLOR or palette[0] == "":
|
|
198
|
+
return False
|
|
199
|
+
if FORCE_COLOR:
|
|
200
|
+
return True
|
|
201
|
+
return bool(getattr(out, "isatty", lambda: False)())
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _rainbow_codes(n):
|
|
205
|
+
codes = []
|
|
206
|
+
for i in range(n):
|
|
207
|
+
h = i / max(1, n) if n > 1 else 0.0
|
|
208
|
+
r, g, b = colorsys.hsv_to_rgb(h, 1.0, 1.0)
|
|
209
|
+
codes.append(f"\033[38;2;{int(r*255)};{int(g*255)};{int(b*255)}m")
|
|
210
|
+
return codes
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _color_lines(lines, palette, cycle=False, out=None):
|
|
214
|
+
if not _should_colorize(palette, out):
|
|
215
|
+
return lines
|
|
216
|
+
res = []
|
|
217
|
+
for idx, line in enumerate(lines):
|
|
218
|
+
code = palette[idx % len(palette)] if cycle else palette[0]
|
|
219
|
+
res.append(code + line + RESET)
|
|
220
|
+
return res
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
RAINBOW_PALETTE = [
|
|
224
|
+
"\033[38;5;196m", "\033[38;5;208m", "\033[38;5;226m", "\033[38;5;46m",
|
|
225
|
+
"\033[38;5;39m", "\033[38;5;93m", "\033[38;5;201m",
|
|
226
|
+
]
|
|
227
|
+
GREEN = "\033[38;5;82m"
|
|
228
|
+
CYAN = "\033[38;5;45m"
|
|
229
|
+
MAGENTA = "\033[38;5;201m"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _palette(kind):
|
|
233
|
+
return {
|
|
234
|
+
"rainbow": RAINBOW_PALETTE,
|
|
235
|
+
"green": [GREEN],
|
|
236
|
+
"cyan": [CYAN],
|
|
237
|
+
"magenta": [MAGENTA],
|
|
238
|
+
"none": [""],
|
|
239
|
+
}.get(kind, RAINBOW_PALETTE)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# --------------------------------------------------------------------------
|
|
243
|
+
# Art selection and display
|
|
244
|
+
# --------------------------------------------------------------------------
|
|
245
|
+
|
|
246
|
+
def list_pieces():
|
|
247
|
+
return sorted(ART.keys())
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def pick_piece(name=None):
|
|
251
|
+
if name:
|
|
252
|
+
name = name.lower()
|
|
253
|
+
if name in ART:
|
|
254
|
+
return name, ART[name]
|
|
255
|
+
for key in ART:
|
|
256
|
+
if key.startswith(name):
|
|
257
|
+
return key, ART[key]
|
|
258
|
+
return None, None
|
|
259
|
+
return None, None
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def show(name=None, text=None, color="rainbow", width=None, center=True,
|
|
263
|
+
animate=False, frames=20, delay=0.06, out=None, return_lines=False):
|
|
264
|
+
"""Render and (optionally) print art. Returns the rendered lines."""
|
|
265
|
+
if out is None:
|
|
266
|
+
out = sys.stdout
|
|
267
|
+
|
|
268
|
+
def build():
|
|
269
|
+
if text is not None:
|
|
270
|
+
return render_text(text)
|
|
271
|
+
if name:
|
|
272
|
+
key, art = pick_piece(name)
|
|
273
|
+
if not art:
|
|
274
|
+
return None
|
|
275
|
+
return art.splitlines()
|
|
276
|
+
key, art = random.choice(list(ART.items()))
|
|
277
|
+
return art.splitlines()
|
|
278
|
+
|
|
279
|
+
lines = build()
|
|
280
|
+
if lines is None:
|
|
281
|
+
return None
|
|
282
|
+
if return_lines:
|
|
283
|
+
return lines
|
|
284
|
+
|
|
285
|
+
lines = _fit_width(lines, width, center=center)
|
|
286
|
+
lines = _color_lines(lines, _palette(color), cycle=(color == "rainbow"), out=out)
|
|
287
|
+
if animate:
|
|
288
|
+
_animate(lines, out, frames, delay)
|
|
289
|
+
else:
|
|
290
|
+
for line in lines:
|
|
291
|
+
out.write(line + "\n")
|
|
292
|
+
out.flush()
|
|
293
|
+
return lines
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _fit_width(lines, width, center=True):
|
|
297
|
+
if not width:
|
|
298
|
+
maxlen = max((len(line) for line in lines), default=0)
|
|
299
|
+
width = maxlen
|
|
300
|
+
return [_center(line, width) if center else line for line in lines]
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _center(line, width):
|
|
304
|
+
pad = width - len(line)
|
|
305
|
+
if pad <= 0:
|
|
306
|
+
return line
|
|
307
|
+
left = pad // 2
|
|
308
|
+
return " " * left + line
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _animate(lines, out, frames, delay):
|
|
312
|
+
terminal = shutil.get_terminal_size((80, 24)).lines
|
|
313
|
+
height = len(lines)
|
|
314
|
+
for frame in range(frames):
|
|
315
|
+
upto = int((frame + 1) / frames * height)
|
|
316
|
+
view = lines[max(0, upto - min(terminal, height)) : upto]
|
|
317
|
+
out.write("\033[H\033[J")
|
|
318
|
+
for line in view:
|
|
319
|
+
out.write(line + "\n")
|
|
320
|
+
out.flush()
|
|
321
|
+
time.sleep(delay)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def random_piece():
|
|
325
|
+
name, art = random.choice(list(ART.items()))
|
|
326
|
+
return name, art
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def pieces_count():
|
|
330
|
+
return len(ART)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def main_extras():
|
|
334
|
+
return None
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: artlab
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: ASCII art in your terminal. Zero dependencies.
|
|
5
|
+
Author: dsk-dev-ai
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dsk-dev-ai/artlab
|
|
8
|
+
Project-URL: Source, https://github.com/dsk-dev-ai/artlab
|
|
9
|
+
Project-URL: Issues, https://github.com/dsk-dev-ai/artlab/issues
|
|
10
|
+
Project-URL: Funding, https://github.com/sponsors/dsk-dev-ai
|
|
11
|
+
Keywords: ascii,art,terminal,cli,fun,text,rainbow,ascii-art
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Artistic Software
|
|
24
|
+
Classifier: Topic :: Terminals
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
|
|
33
|
+
# 🎨 artlab
|
|
34
|
+
|
|
35
|
+
**ASCII art in your terminal.**
|
|
36
|
+
|
|
37
|
+
Bundled retro art, big block-letter text, rainbow colors, and animation.
|
|
38
|
+
Zero dependencies. Works in Python 3.8+.
|
|
39
|
+
|
|
40
|
+
[](https://pypi.org/project/artlab/)
|
|
41
|
+
[](#)
|
|
42
|
+
[](#)
|
|
43
|
+
[](https://github.com/dsk-dev-ai/artlab/pkgs/container/artlab)
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Why artlab?
|
|
50
|
+
|
|
51
|
+
Because the terminal deserves a little personality.
|
|
52
|
+
|
|
53
|
+
- **Bundled ASCII art** — robots, cats, dragons, unicorns, yoda, octopuses, hearts and more.
|
|
54
|
+
- **Block-letter text** — print any word big, `figlet`-style, with zero dependencies.
|
|
55
|
+
- **Rainbow & colors** — 256-color palettes, or dims down for your pipelines.
|
|
56
|
+
- **Animation** — reveal art with a slick frame-by-frame intro.
|
|
57
|
+
- **Zero dependencies** — pure Python stdlib. No install drama.
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install artlab
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or run without installing:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pipx run artlab --help
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Via Docker:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
docker run --rm ghcr.io/dsk-dev-ai/artlab --help
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Show a random piece
|
|
81
|
+
artlab
|
|
82
|
+
|
|
83
|
+
# Show a specific piece
|
|
84
|
+
artlab robot
|
|
85
|
+
artlab cat
|
|
86
|
+
artlab dragon
|
|
87
|
+
|
|
88
|
+
# List everything
|
|
89
|
+
artlab -l
|
|
90
|
+
|
|
91
|
+
# Print big block text
|
|
92
|
+
artlab --text "SHIP IT"
|
|
93
|
+
artlab -t "artlab" -c cyan
|
|
94
|
+
|
|
95
|
+
# Force a palette
|
|
96
|
+
artlab robot -c green
|
|
97
|
+
artlab dragon -c magenta
|
|
98
|
+
|
|
99
|
+
# Animate the reveal
|
|
100
|
+
artlab unicorn -a
|
|
101
|
+
|
|
102
|
+
# No color (for scripts / piping)
|
|
103
|
+
artlab robot --no-color
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Options
|
|
107
|
+
|
|
108
|
+
| Flag | Description |
|
|
109
|
+
|------|-------------|
|
|
110
|
+
| `-l, --list` | List all available pieces |
|
|
111
|
+
| `-r, --random` | Show a random piece |
|
|
112
|
+
| `-t, --text TEXT` | Render text as big block letters |
|
|
113
|
+
| `-c, --color` | Palette: `rainbow`, `green`, `cyan`, `magenta`, `none` |
|
|
114
|
+
| `-w, --width N` | Pad art to a fixed width |
|
|
115
|
+
| `--no-center` | Don't center the art |
|
|
116
|
+
| `-a, --animate` | Reveal the art with an animation |
|
|
117
|
+
| `--no-color` | Disable color output |
|
|
118
|
+
| `--version` | Print version |
|
|
119
|
+
|
|
120
|
+
## Gallery
|
|
121
|
+
|
|
122
|
+
Run `artlab` to see a random piece, or `artlab -l` to browse the full library.
|
|
123
|
+
Everything is bundled — no API, no network, no nonsense.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
/\_/\
|
|
127
|
+
( o.o )
|
|
128
|
+
> ^ <
|
|
129
|
+
/ \
|
|
130
|
+
\_____/ meow
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Tests
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
python -m pytest
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Tested on Python 3.8–3.12 via GitHub Actions.
|
|
140
|
+
|
|
141
|
+
## Sponsor
|
|
142
|
+
|
|
143
|
+
Like `artlab`? Keep the terminal artsy for free by [becoming a sponsor](https://github.com/sponsors/dsk-dev-ai) on GitHub 💜.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
<div align="center">
|
|
148
|
+
|
|
149
|
+
**artlab** · because the terminal deserves art.
|
|
150
|
+
|
|
151
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
artlab/__init__.py
|
|
5
|
+
artlab/__main__.py
|
|
6
|
+
artlab/cli.py
|
|
7
|
+
artlab/core.py
|
|
8
|
+
artlab.egg-info/PKG-INFO
|
|
9
|
+
artlab.egg-info/SOURCES.txt
|
|
10
|
+
artlab.egg-info/dependency_links.txt
|
|
11
|
+
artlab.egg-info/entry_points.txt
|
|
12
|
+
artlab.egg-info/top_level.txt
|
|
13
|
+
tests/test_artlab.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
artlab
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "artlab"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "ASCII art in your terminal. Zero dependencies."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "dsk-dev-ai" }]
|
|
13
|
+
keywords = ["ascii", "art", "terminal", "cli", "fun", "text", "rainbow", "ascii-art"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: End Users/Desktop",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Topic :: Artistic Software",
|
|
27
|
+
"Topic :: Terminals",
|
|
28
|
+
"Topic :: Utilities",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
artlab = "artlab.cli:main"
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/dsk-dev-ai/artlab"
|
|
36
|
+
Source = "https://github.com/dsk-dev-ai/artlab"
|
|
37
|
+
Issues = "https://github.com/dsk-dev-ai/artlab/issues"
|
|
38
|
+
Funding = "https://github.com/sponsors/dsk-dev-ai"
|
|
39
|
+
|
|
40
|
+
[tool.setuptools]
|
|
41
|
+
packages = ["artlab"]
|
artlab-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
import artlab.core as core
|
|
7
|
+
from artlab.cli import main
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_art_library_has_core_pieces():
|
|
11
|
+
assert "robot" in core.ART
|
|
12
|
+
assert "cat" in core.ART
|
|
13
|
+
assert "heart" in core.ART
|
|
14
|
+
assert len(core.ART) >= 10
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_pieces_are_multiline_art():
|
|
18
|
+
for name, art in core.ART.items():
|
|
19
|
+
assert len(art.splitlines()) >= 2, name
|
|
20
|
+
assert len(art) > 10, name
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_render_text_returns_5_lines():
|
|
24
|
+
lines = core.render_text("HI")
|
|
25
|
+
assert len(lines) == 5
|
|
26
|
+
assert all(isinstance(line, str) for line in lines)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_render_text_produces_h_height():
|
|
30
|
+
lines = core.render_text("H")
|
|
31
|
+
assert lines[2] == "#######"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_render_text_handles_unknown_char_gracefully():
|
|
35
|
+
lines = core.render_text("!!!")
|
|
36
|
+
assert len(lines) == 5
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_render_text_uppercases():
|
|
40
|
+
lines = core.render_text("a")
|
|
41
|
+
assert lines[0].startswith(" ##")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_pick_piece_exact():
|
|
45
|
+
name, art = core.pick_piece("cat")
|
|
46
|
+
assert name == "cat"
|
|
47
|
+
assert "meow" in art
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_pick_piece_prefix():
|
|
51
|
+
name, art = core.pick_piece("cat")
|
|
52
|
+
assert name == "cat"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_pick_piece_unknown():
|
|
56
|
+
assert core.pick_piece("zzz_not_real") == (None, None)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_list_pieces_sorted():
|
|
60
|
+
assert core.list_pieces() == sorted(core.ART.keys())
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_show_returns_lines():
|
|
64
|
+
lines = core.show(name="cat", color="none", return_lines=True)
|
|
65
|
+
assert lines and any("meow" in line for line in lines)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_show_unknown_returns_none():
|
|
69
|
+
assert core.show(name="zzz", color="none", return_lines=True) is None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_render_text_width_positive():
|
|
73
|
+
lines = core.render_text("ARTLAB")
|
|
74
|
+
assert all(len(line) >= 3 for line in lines)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_no_color_flag_disables_escape_codes():
|
|
78
|
+
core.FORCE_COLOR = True
|
|
79
|
+
core.NO_COLOR = True
|
|
80
|
+
out = io.StringIO()
|
|
81
|
+
core.show(name="cat", color="rainbow", out=out)
|
|
82
|
+
assert "\033[" not in out.getvalue()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_cli_show_named_piece(monkeypatch, capsys):
|
|
86
|
+
rc = main(["cat", "--no-color"])
|
|
87
|
+
assert rc == 0
|
|
88
|
+
captured = capsys.readouterr().out
|
|
89
|
+
assert "meow" in captured
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_cli_list(monkeypatch, capsys):
|
|
93
|
+
rc = main(["-l"])
|
|
94
|
+
assert rc == 0
|
|
95
|
+
captured = capsys.readouterr().out
|
|
96
|
+
assert "robot" in captured
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_cli_unknown_piece_errors(capsys):
|
|
100
|
+
rc = main(["zzz_not_a_piece"])
|
|
101
|
+
assert rc == 1
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_cli_text_render(monkeypatch, capsys):
|
|
105
|
+
rc = main(["--text", "AB", "--no-color"])
|
|
106
|
+
assert rc == 0
|
|
107
|
+
captured = capsys.readouterr().out
|
|
108
|
+
assert "##" in captured
|