chromatic-python 0.9.0__tar.gz → 0.9.2__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.
Files changed (60) hide show
  1. chromatic_python-0.9.2/.github/workflows/build.yml +33 -0
  2. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/PKG-INFO +72 -71
  3. chromatic_python-0.9.2/README.md +153 -0
  4. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/__init__.pyi +3 -2
  5. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/__main__.py +9 -2
  6. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/_version.py +3 -3
  7. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/color/core.py +5 -10
  8. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/color/core.pyi +2 -2
  9. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/data/userfont.pyi +3 -11
  10. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic_python.egg-info/PKG-INFO +72 -71
  11. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic_python.egg-info/SOURCES.txt +5 -1
  12. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic_python.egg-info/scm_file_list.json +5 -1
  13. chromatic_python-0.9.2/chromatic_python.egg-info/scm_version.json +8 -0
  14. chromatic_python-0.9.2/docs/index.md +153 -0
  15. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/pyproject.toml +4 -0
  16. chromatic_python-0.9.2/tests/test_color_chain.py +244 -0
  17. chromatic_python-0.9.2/tests/test_colorconv.py +370 -0
  18. chromatic_python-0.9.2/tests/test_colorstr.py +241 -0
  19. chromatic_python-0.9.2/tests/test_image.py +143 -0
  20. chromatic_python-0.9.0/README.md +0 -152
  21. chromatic_python-0.9.0/chromatic_python.egg-info/scm_version.json +0 -8
  22. chromatic_python-0.9.0/docs/index.md +0 -152
  23. chromatic_python-0.9.0/tests/test_color_str.py +0 -338
  24. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/.gitattributes +0 -0
  25. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/.github/dependabot.yml +0 -0
  26. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/.github/workflows/docs.yml +0 -0
  27. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/.github/workflows/release.yml +0 -0
  28. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/.gitignore +0 -0
  29. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/LICENSE +0 -0
  30. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/banner.png +0 -0
  31. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/__init__.py +0 -0
  32. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/_typing.py +0 -0
  33. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/color/__init__.py +0 -0
  34. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/color/__init__.pyi +0 -0
  35. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/color/colorconv.py +0 -0
  36. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/color/colorconv.pyi +0 -0
  37. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/color/palette.py +0 -0
  38. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/color/palette.pyi +0 -0
  39. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/data/__init__.py +0 -0
  40. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/data/fonts/userfont.schema.json +0 -0
  41. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/data/fonts/vga437.ttf +0 -0
  42. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/data/userfont.py +0 -0
  43. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/image/__init__.py +0 -0
  44. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/image/__init__.pyi +0 -0
  45. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/image/_array.py +0 -0
  46. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/image/_curses.py +0 -0
  47. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic/image/_glyph.py +0 -0
  48. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic_python.egg-info/dependency_links.txt +0 -0
  49. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic_python.egg-info/entry_points.txt +0 -0
  50. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic_python.egg-info/requires.txt +0 -0
  51. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/chromatic_python.egg-info/top_level.txt +0 -0
  52. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/docs/api/chromatic.md +0 -0
  53. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/docs/api/color.md +0 -0
  54. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/docs/api/data.md +0 -0
  55. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/docs/api/image.md +0 -0
  56. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/docs/cli/index.md +0 -0
  57. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/mkdocs.yml +0 -0
  58. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/mkdocs_hooks.py +0 -0
  59. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/setup.cfg +0 -0
  60. {chromatic_python-0.9.0 → chromatic_python-0.9.2}/tests/__init__.py +0 -0
@@ -0,0 +1,33 @@
1
+ name: build
2
+
3
+ on: [workflow_dispatch, push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ python-version: ['3.12', '3.13', '3.14']
11
+ steps:
12
+ - uses: actions/checkout@v7
13
+ - uses: astral-sh/setup-uv@v7
14
+ with:
15
+ python-version: ${{ matrix.python-version }}
16
+ - name: Install OpenCV runtime libraries
17
+ run: sudo apt-get update && sudo apt-get install -y libgl1
18
+ - run: uv run --group tests pytest
19
+
20
+ bench:
21
+ runs-on: ubuntu-latest
22
+ permissions:
23
+ contents: read
24
+ id-token: write
25
+ steps:
26
+ - uses: actions/checkout@v7
27
+ - uses: astral-sh/setup-uv@v7
28
+ - name: Install OpenCV runtime libraries
29
+ run: sudo apt-get update && sudo apt-get install -y libgl1
30
+ - uses: CodSpeedHQ/action@v5
31
+ with:
32
+ mode: simulation
33
+ run: uv run --group tests pytest --codspeed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chromatic-python
3
- Version: 0.9.0
3
+ Version: 0.9.2
4
4
  Summary: ANSI art image processing and colored terminal text
5
5
  Author: crypt0lith
6
6
  License-Expression: MIT
@@ -31,6 +31,7 @@ Dynamic: license-file
31
31
  ![image](https://img.shields.io/pypi/pyversions/chromatic-python)
32
32
  [![image](https://static.pepy.tech/badge/chromatic-python)](https://pepy.tech/projects/chromatic-python)
33
33
  [![image](https://mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
34
+ [![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://app.codspeed.io/crypt0lith/chromatic?utm_source=badge)
34
35
 
35
36
  Chromatic is a library for ANSI art image processing and colored terminal text.
36
37
 
@@ -45,127 +46,127 @@ It offers a collection of algorithms and types for a variety of use cases:
45
46
 
46
47
  ### Usage
47
48
 
48
- #### `color_chain`
49
+ #### `ColorStr`
50
+
51
+ ```python
52
+ from chromatic import ColorStr
53
+
54
+ cs = ColorStr("hello world", fg=0xFF0000, ansi_type="8b")
55
+
56
+ assert cs.base_str == "hello world"
57
+ assert len(cs) == len("hello world")
58
+
59
+ head, tail = cs.split()
60
+
61
+ assert head.fg == tail.fg == cs.fg
62
+ assert all(c.fg == cs.fg for c in cs)
63
+ ```
64
+
65
+ `ColorStr` is a `str` subclass that carries SGR state as metadata.
66
+ Its `str` methods delegate to the base string and return `ColorStr` instances that retain the SGR state.
67
+
68
+ ```python
69
+ assert str(cs) == "\x1b[38;5;196mhello world\x1b[0m"
70
+ assert cs.ansi == b"\x1b[38;5;196m"
71
+ ```
72
+
73
+ The SGR sequence materializes when the `ColorStr` is converted back to a Python `str`.
49
74
 
50
- A `color_chain` is a printable sequence of colored text that round-trips with a NumPy array:
75
+ #### `color_chain`
51
76
 
52
77
  ```python
53
- from chromatic import color_chain, ColorStr
78
+ from chromatic import color_chain
54
79
 
55
80
  cc = color_chain(
56
81
  [ColorStr("hello", fg=0xFF0000), ColorStr(" world", fg=0x00FF00)], ansi_type="8b"
57
82
  )
58
83
 
59
- # parsed from a raw SGR string — no ansi_type needed
84
+ sgr, s = cc[0]
85
+
86
+ assert sgr.fg == (0xFF, 0, 0)
87
+ assert s == "hello"
88
+ ```
89
+
90
+ A `color_chain` is a sequence of colored-text fragments that round-trips with a structured NumPy array.
91
+ Each fragment is an `(sgr, text)` pair of the SGR state and the run of text it applies to.
92
+
93
+ ```python
60
94
  cc2 = color_chain("\x1b[38;5;196mhello\x1b[38;5;46m world")
61
95
 
62
96
  assert cc == cc2
97
+ ```
63
98
 
64
- # red "hello", green " world"
65
- print(cc)
99
+ The constructor also parses a raw SGR string.
100
+ It infers the color depth from the sequence, so `cc2` needs no `ansi_type`.
66
101
 
67
- # a structured ndarray of dtype [('char', '<U1'), ('sgr', '<u8'), ('rgb', 'u1', (2, 4))]
102
+ ```python
68
103
  arr = cc.term_array()
69
104
 
105
+ assert arr.dtype == [("char", "<U1"), ("sgr", "<u8"), ("rgb", "u1", (2, 4))]
70
106
  assert str(color_chain.fromarray(arr)) == str(cc)
71
107
  ```
72
108
 
73
- #### `img2ansi`
109
+ `term_array` exposes the structured `ndarray` behind the chain, and `fromarray` reverses it.
110
+ `fromarray` accepts any `ndarray` of that dtype, so an array built by other means converts to a printable `color_chain`.
74
111
 
75
- `img2ansi` reads an image and returns a `color_chain`:
112
+ #### `img2ansi`
76
113
 
77
114
  ```python
78
115
  from chromatic import img2ansi
79
- from chromatic.data import userfonts
80
-
81
- font = userfonts["vga437"]
82
-
83
- # the image, as ANSI art
84
- cc = img2ansi("input.png", font, factor=200)
85
- print(cc)
86
116
 
87
- # or the raw ndarray
88
- arr = img2ansi("input.png", font, factor=200, outarray=True)
117
+ cc = img2ansi("input.png")
89
118
  ```
90
119
 
91
- #### `ansi2img`
92
-
93
- `ansi2img` renders a `color_chain` (or array) back to an image:
120
+ `img2ansi` reads an image and returns a `color_chain`.
94
121
 
95
122
  ```python
96
- from chromatic import img2ansi, ansi2img
97
- from chromatic.data import userfonts
98
-
99
- font = userfonts["vga437"]
100
-
101
- cc = img2ansi("input.png", font, factor=200)
123
+ arr = img2ansi("input.png", outarray=True)
102
124
 
103
- # a PIL.Image.Image
104
- img = ansi2img(cc, font, font_size=16)
105
- img.show()
125
+ assert color_chain.fromarray(arr) == cc
106
126
  ```
107
127
 
108
- #### `ansify`
128
+ `outarray=True` returns the underlying `ndarray` instead.
109
129
 
110
- `ansify` runs both steps at once, returning the rendered image with its array on `Image.info`:
130
+ #### `ansi2img`
111
131
 
112
132
  ```python
113
- from chromatic import ansify
114
- from chromatic.data import userfonts
133
+ from chromatic import ansi2img
115
134
 
116
- font = userfonts["vga437"]
117
-
118
- img = ansify("input.png", font, font_size=16, factor=200)
135
+ img = ansi2img(cc)
119
136
  img.show()
120
-
121
- # the ndarray it rendered from
122
- arr = img.info["ansi_array"]
123
137
  ```
124
138
 
125
- #### Animated images
139
+ `ansi2img` renders a `color_chain` back to an image.
140
+ It also accepts a dtype-matching array.
126
141
 
127
- An animated image becomes a list of `color_chain` frames, one per frame — so a GIF plays straight to the terminal:
142
+ #### `ansify`
128
143
 
129
144
  ```python
130
- import sys
131
- import time
132
-
133
- from chromatic import img2ansi
134
- from chromatic.data import userfonts
135
-
136
- font = userfonts["vga437"]
137
-
138
- # list[color_chain], one per frame
139
- frames = img2ansi("input.gif", font, factor=200)
145
+ from chromatic import ansify
140
146
 
141
- for cc in frames:
142
- # redraw each frame in place
143
- sys.stdout.write(f"\x1b[H{cc}")
144
- time.sleep(0.1)
147
+ img = ansify("input.png")
148
+ arr = img.info["ansi_array"]
145
149
  ```
146
150
 
147
- `ansify` renders the same GIF back to an animated image, and `chromatic image ansify input.gif --stdout` plays it in the terminal for you.
148
-
149
- #### `ColorStr`
151
+ `ansify` runs both directions in one call, returning the rendered image with the array it rendered from on `Image.info`.
150
152
 
151
- `ColorStr` is a `str` subclass that carries its SGR color as metadata; the escape codes only surface when you render it:
153
+ #### Animated images
152
154
 
153
155
  ```python
154
- from chromatic import ColorStr
155
-
156
- cs = ColorStr("hello world", fg=0xFF0000, ansi_type="8b")
156
+ frames = img2ansi("input.gif")
157
157
 
158
- assert isinstance(cs, str)
158
+ assert isinstance(frames, list)
159
+ assert all(isinstance(f, color_chain) for f in frames)
160
+ ```
159
161
 
160
- # the codes aren't part of the string itself
161
- assert cs.base_str == "hello world"
162
- assert len(cs) == len("hello world")
162
+ An animated image renders to a `list[color_chain]`, one per frame.
163
163
 
164
- # they surface when you print / str() it
165
- assert str(cs) == "\x1b[38;5;196mhello world\x1b[0m"
166
- assert cs.ansi == b"\x1b[38;5;196m"
164
+ ```shell
165
+ chromatic image ansify input.gif --stdout
167
166
  ```
168
167
 
168
+ The [CLI](https://crypt0lith.github.io/chromatic/cli/image/#chromatic-image-ansify) renders and plays one straight to the terminal.
169
+
169
170
  ### Installation
170
171
 
171
172
  Install the package using `pip`:
@@ -0,0 +1,153 @@
1
+ ![image](https://raw.githubusercontent.com/crypt0lith/chromatic/master/banner.png)
2
+
3
+ [![image](https://img.shields.io/pypi/v/chromatic-python)](https://pypi.org/project/chromatic-python/)
4
+ ![image](https://img.shields.io/pypi/pyversions/chromatic-python)
5
+ [![image](https://static.pepy.tech/badge/chromatic-python)](https://pepy.tech/projects/chromatic-python)
6
+ [![image](https://mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
7
+ [![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://app.codspeed.io/crypt0lith/chromatic?utm_source=badge)
8
+
9
+ Chromatic is a library for ANSI art image processing and colored terminal text.
10
+
11
+ It offers a collection of algorithms and types for a variety of use cases:
12
+
13
+ - Image-to-ASCII / Image-to-ANSI conversions.
14
+ - ANSI art rendering, with support for user-defined fonts.
15
+ - A `ColorStr` type for low-level control over ANSI SGR strings.
16
+ - [colorama](https://github.com/tartley/colorama/)-style wrappers (`Fore`, `Back`, `Style`).
17
+ - Conversion between 16-color, 256-color, and true-color (RGB) ANSI colorspace via the `colorbytes` type.
18
+ - Et Cetera 😲
19
+
20
+ ### Usage
21
+
22
+ #### `ColorStr`
23
+
24
+ ```python
25
+ from chromatic import ColorStr
26
+
27
+ cs = ColorStr("hello world", fg=0xFF0000, ansi_type="8b")
28
+
29
+ assert cs.base_str == "hello world"
30
+ assert len(cs) == len("hello world")
31
+
32
+ head, tail = cs.split()
33
+
34
+ assert head.fg == tail.fg == cs.fg
35
+ assert all(c.fg == cs.fg for c in cs)
36
+ ```
37
+
38
+ `ColorStr` is a `str` subclass that carries SGR state as metadata.
39
+ Its `str` methods delegate to the base string and return `ColorStr` instances that retain the SGR state.
40
+
41
+ ```python
42
+ assert str(cs) == "\x1b[38;5;196mhello world\x1b[0m"
43
+ assert cs.ansi == b"\x1b[38;5;196m"
44
+ ```
45
+
46
+ The SGR sequence materializes when the `ColorStr` is converted back to a Python `str`.
47
+
48
+ #### `color_chain`
49
+
50
+ ```python
51
+ from chromatic import color_chain
52
+
53
+ cc = color_chain(
54
+ [ColorStr("hello", fg=0xFF0000), ColorStr(" world", fg=0x00FF00)], ansi_type="8b"
55
+ )
56
+
57
+ sgr, s = cc[0]
58
+
59
+ assert sgr.fg == (0xFF, 0, 0)
60
+ assert s == "hello"
61
+ ```
62
+
63
+ A `color_chain` is a sequence of colored-text fragments that round-trips with a structured NumPy array.
64
+ Each fragment is an `(sgr, text)` pair of the SGR state and the run of text it applies to.
65
+
66
+ ```python
67
+ cc2 = color_chain("\x1b[38;5;196mhello\x1b[38;5;46m world")
68
+
69
+ assert cc == cc2
70
+ ```
71
+
72
+ The constructor also parses a raw SGR string.
73
+ It infers the color depth from the sequence, so `cc2` needs no `ansi_type`.
74
+
75
+ ```python
76
+ arr = cc.term_array()
77
+
78
+ assert arr.dtype == [("char", "<U1"), ("sgr", "<u8"), ("rgb", "u1", (2, 4))]
79
+ assert str(color_chain.fromarray(arr)) == str(cc)
80
+ ```
81
+
82
+ `term_array` exposes the structured `ndarray` behind the chain, and `fromarray` reverses it.
83
+ `fromarray` accepts any `ndarray` of that dtype, so an array built by other means converts to a printable `color_chain`.
84
+
85
+ #### `img2ansi`
86
+
87
+ ```python
88
+ from chromatic import img2ansi
89
+
90
+ cc = img2ansi("input.png")
91
+ ```
92
+
93
+ `img2ansi` reads an image and returns a `color_chain`.
94
+
95
+ ```python
96
+ arr = img2ansi("input.png", outarray=True)
97
+
98
+ assert color_chain.fromarray(arr) == cc
99
+ ```
100
+
101
+ `outarray=True` returns the underlying `ndarray` instead.
102
+
103
+ #### `ansi2img`
104
+
105
+ ```python
106
+ from chromatic import ansi2img
107
+
108
+ img = ansi2img(cc)
109
+ img.show()
110
+ ```
111
+
112
+ `ansi2img` renders a `color_chain` back to an image.
113
+ It also accepts a dtype-matching array.
114
+
115
+ #### `ansify`
116
+
117
+ ```python
118
+ from chromatic import ansify
119
+
120
+ img = ansify("input.png")
121
+ arr = img.info["ansi_array"]
122
+ ```
123
+
124
+ `ansify` runs both directions in one call, returning the rendered image with the array it rendered from on `Image.info`.
125
+
126
+ #### Animated images
127
+
128
+ ```python
129
+ frames = img2ansi("input.gif")
130
+
131
+ assert isinstance(frames, list)
132
+ assert all(isinstance(f, color_chain) for f in frames)
133
+ ```
134
+
135
+ An animated image renders to a `list[color_chain]`, one per frame.
136
+
137
+ ```shell
138
+ chromatic image ansify input.gif --stdout
139
+ ```
140
+
141
+ The [CLI](https://crypt0lith.github.io/chromatic/cli/image/#chromatic-image-ansify) renders and plays one straight to the terminal.
142
+
143
+ ### Installation
144
+
145
+ Install the package using `pip`:
146
+
147
+ ```shell
148
+ pip install chromatic-python
149
+ ```
150
+
151
+ ### Credits
152
+
153
+ Banner artwork: [main rules by Crasher (2002)](https://16colo.rs/pack/galza-14/CRS-MAIN.ANS)
@@ -3,6 +3,7 @@ __all__ = [
3
3
  "Color",
4
4
  "ColorNamespace",
5
5
  "ColorStr",
6
+ "DEFAULT_FONT",
6
7
  "Fore",
7
8
  "SgrFlag",
8
9
  "SgrParameter",
@@ -38,10 +39,10 @@ __all__ = [
38
39
  "reshape_ansi",
39
40
  "rgb2hexstr",
40
41
  "rgb2int",
42
+ "set_default_ansi",
41
43
  "sort_glyphs",
42
44
  "ttf_extract_codepoints",
43
45
  "userfonts",
44
- "set_default_ansi",
45
46
  ]
46
47
  from . import color, data, image
47
48
  from ._version import version as __version__
@@ -66,7 +67,7 @@ from .color import (
66
67
  rgb2int,
67
68
  set_default_ansi,
68
69
  )
69
- from .data import UserFont, register_userfont, userfonts
70
+ from .data import DEFAULT_FONT, UserFont, register_userfont, userfonts
70
71
  from .image import (
71
72
  ansi2img,
72
73
  ansi_quantize,
@@ -581,7 +581,8 @@ def Parser(prog=None):
581
581
 
582
582
 
583
583
  def parse_args(argv=None):
584
- return Parser(os.path.basename((argv or sys.argv)[0])).parse_args(argv)
584
+ argv = argv or sys.argv
585
+ return Parser(os.path.basename(argv[0])).parse_args(argv[1:])
585
586
 
586
587
 
587
588
  def _call_from_ns[R](f: abc.Callable[..., R], /, ns, **kwargs) -> R:
@@ -655,10 +656,11 @@ def handle_image(ns):
655
656
  params = {}
656
657
  if is_anim := arr.ndim == 3:
657
658
  params.update(save_all=True)
659
+ isatty = False
658
660
  if hasattr(ns, "dumpfile"):
659
661
  from .color.core import color_chain
660
662
 
661
- if is_anim and ns.dumpfile.isatty():
663
+ if (isatty := ns.dumpfile.isatty()) and is_anim:
662
664
 
663
665
  def anim_loop(n: int | None, duration: int | float):
664
666
  from time import sleep
@@ -691,6 +693,11 @@ def handle_image(ns):
691
693
  ns.dumpfile.write(f"{cc}\x1b[0m\n".encode())
692
694
  if img is None:
693
695
  return
696
+ if not hasattr(ns, "outfile_callback"):
697
+ show = getattr(ns, "show", None)
698
+ if show is True or not (show is False or isatty):
699
+ return img.show()
700
+ return
694
701
  try:
695
702
  outpath = ns.outfile_callback(ns, img, **params)
696
703
  except Exception as e:
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.9.0'
22
- __version_tuple__ = version_tuple = (0, 9, 0)
21
+ __version__ = version = '0.9.2'
22
+ __version_tuple__ = version_tuple = (0, 9, 2)
23
23
 
24
- __commit_id__ = commit_id = 'g17e936a21'
24
+ __commit_id__ = commit_id = 'gbe4e6a5cd'
@@ -3,6 +3,7 @@ __all__ = [
3
3
  "Color",
4
4
  "ColorChainDType",
5
5
  "ColorStr",
6
+ "DEFAULT_ANSI",
6
7
  "SGR_RESET",
7
8
  "SgrFlag",
8
9
  "SgrParameter",
@@ -16,6 +17,7 @@ __all__ = [
16
17
  "is_vt_enabled",
17
18
  "randcolor",
18
19
  "rgb2ansi_escape",
20
+ "set_default_ansi",
19
21
  ]
20
22
 
21
23
  import collections.abc as abc
@@ -1349,17 +1351,10 @@ class ColorStr(str, _IntFloatMixin):
1349
1351
  return self._weak_var_update(sgr=sgr)
1350
1352
 
1351
1353
  def strip_style(self):
1352
- only_colors = []
1353
- diff = False
1354
- for x in self._sgr:
1355
- if x.is_color():
1356
- only_colors.append(x)
1357
- elif not diff:
1358
- diff = True
1359
- if not diff:
1354
+ if all(x.is_color() for x in self._sgr):
1360
1355
  return self
1361
- sgr = self._sgr.copy()
1362
- sgr[:] = only_colors
1356
+ sgr = SgrSequence()
1357
+ sgr.set_colors(self._sgr.rgb_dict, self.ansi_type)
1363
1358
  return self._weak_var_update(sgr=sgr)
1364
1359
 
1365
1360
  def add_reset(self):
@@ -3,6 +3,7 @@ __all__ = [
3
3
  "Color",
4
4
  "ColorChainDType",
5
5
  "ColorStr",
6
+ "DEFAULT_ANSI",
6
7
  "SGR_RESET",
7
8
  "SgrFlag",
8
9
  "SgrParameter",
@@ -16,6 +17,7 @@ __all__ = [
16
17
  "is_vt_enabled",
17
18
  "randcolor",
18
19
  "rgb2ansi_escape",
20
+ "set_default_ansi",
19
21
  ]
20
22
 
21
23
  import collections.abc as abc
@@ -169,8 +171,6 @@ class SgrFlag(enum.IntFlag):
169
171
  @property
170
172
  def parameters(self) -> list[SgrParameter]: ...
171
173
 
172
- _P2F: tp.Final[dict[int, int]]
173
- _F2P: tp.Final[dict[int, int]]
174
174
  _ANSI16C_I2KV: tp.Final[dict[int, tuple[ColorDictKeys, Int3Tuple]]]
175
175
  _ANSI16C_KV2I: tp.Final[dict[tuple[ColorDictKeys, Int3Tuple], int]]
176
176
  _ANSI256_B2KEY: tp.Final[dict[L[b"38", b"48"], ColorDictKeys]]
@@ -29,17 +29,9 @@ class UserFont:
29
29
  def __fspath__(self) -> str: ...
30
30
  def to_truetype(self) -> FreeTypeFont: ...
31
31
 
32
- class _TypedDictStruct(tp.NamedTuple):
33
- required: frozenset[str]
34
- optional: frozenset[str]
35
- all_keys: frozenset[str]
36
- annotations: MappingProxyType[str, type[tp.Any]]
37
-
38
- @classmethod
39
- def from_typeddict(cls, typ: type[tp.TypedDict], /) -> tp.Self: ...
40
- def match(self, obj: dict, /) -> bool: ...
41
-
42
- def _userfont_dict_struct() -> _TypedDictStruct: ...
32
+ def _userfont_dict_struct() -> (
33
+ tuple[frozenset[str], frozenset[str], frozenset[str], MappingProxyType[str, tp.Any]]
34
+ ): ...
43
35
 
44
36
  userfonts: tp.Final[MappingProxyType[str, UserFont]]
45
37