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 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
+ [![PyPI](https://img.shields.io/pypi/v/artlab?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/artlab/)
41
+ [![Python](https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue)](#)
42
+ [![License](https://img.shields.io/badge/license-MIT-green)](#)
43
+ [![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fdsk--dev--ai%2Fartlab-blue?logo=docker)](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
+ [![PyPI](https://img.shields.io/pypi/v/artlab?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/artlab/)
11
+ [![Python](https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue)](#)
12
+ [![License](https://img.shields.io/badge/license-MIT-green)](#)
13
+ [![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fdsk--dev--ai%2Fartlab-blue?logo=docker)](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,3 @@
1
+ """artlab - ASCII art in your terminal."""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,4 @@
1
+ from artlab.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -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
+ [![PyPI](https://img.shields.io/pypi/v/artlab?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/artlab/)
41
+ [![Python](https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue)](#)
42
+ [![License](https://img.shields.io/badge/license-MIT-green)](#)
43
+ [![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fdsk--dev--ai%2Fartlab-blue?logo=docker)](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,2 @@
1
+ [console_scripts]
2
+ artlab = artlab.cli:main
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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