Skip to content

Commit 5f5e099

Browse files
committed
it's ready for the public
1 parent 5528eb7 commit 5f5e099

File tree

8 files changed

+401
-706
lines changed

8 files changed

+401
-706
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
__pycache__
2+
*.egg-info/
3+
dist/
4+
build/

LICENSE

Lines changed: 162 additions & 672 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# tranci: puts you in a trance.
2+
3+
<img src="screenshot.png" alt="Screenshot of Tranci's __main__.py output.">
4+
5+
## What in the world is a "tranci"?
6+
7+
`tranci` is a no-dependencies, lightweight, easy-to-use, Pythonic ANSI library. It officially supports Python 3.9-3.13. You can move the cursor around, do some colors. Idk, just general ANSI.
8+
9+
## How do I use this?
10+
11+
Install the `tranci` package with `pip`. Run `python -m tranci` to confirm it works.
12+
13+
Example code:
14+
15+
```py
16+
import tranci
17+
18+
print(tranci.Red("Red text"))
19+
print(tranci.BGRed("Red background"))
20+
print(tranci.RGB(164, 106, 120, "RGB code"))
21+
print(tranci.HEX("#A44A44", "HEX code"))
22+
23+
weird_cyan_green_color_thing = tranci.HEX(0x3affad)
24+
25+
print(weird_cyan_green_color_thing("You can save them too"))
26+
```
27+
28+
You can figure out everything else just by looking at your IDE's autocomplete! (or just look at the cool `tranci/__main__.py` source code)
29+
30+
## Why would I use this over anything else
31+
32+
- Auto reset handling
33+
- Actual nesting functionality
34+
- IDE auto-complete won't cry seeing the code
35+
- True color
36+
- Zero dependencies
37+
38+
## Ok but ~~[that one clone of a JS library that shall not be named]~~ exists
39+
40+
- It's a clone of a JS library. What do you think?
41+
- `tranci` has almost everything ~~[that one clone of a JS library that shall not be named]~~ has except fallbacks (and maybe the edge cases though I'm not sure what edge cases they're referring to).
42+
- You don't need fallbacks/capabilities-detection. It just adds bloat.
43+
- Even if the JS clone is slightly lighter, `tranci` isn't just colors and styles and <font color="red"><u>**_~~oooo look at this bold italic striked underlined red text!!!~~_**</u></font>. It also supports a bit more general ANSI, in a more Pythonic extendable syntax. You can add your own ANSI things to `tranci` with the class system. Plus the world won't end if your project is 51.82KiB larger than it could be.
44+
45+
Download tranci now! or something uhh what do those mobile game ads say at the end again

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["setuptools", "wheel"]
3+
build-backend = "setuptools.build_meta"

screenshot.png

31.1 KB
Loading

setup.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from setuptools import setup, find_packages
2+
from pathlib import Path
3+
4+
setup(
5+
name="tranci",
6+
version="1.0.0",
7+
description="Tranci: a no-dependencies, lightweight, easy-to-use ANSI library",
8+
long_description=(Path(__file__).parent / "README.md").read_text(encoding="utf-8"),
9+
long_description_content_type="text/markdown",
10+
author="Butterroach",
11+
author_email="[email protected]",
12+
url="https://github.com/Butterroach/tranci",
13+
license="LGPLv3+",
14+
packages=find_packages(),
15+
classifiers=[
16+
"Development Status :: 5 - Production/Stable",
17+
"Programming Language :: Python :: 3",
18+
"License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
19+
"Intended Audience :: Developers",
20+
"Operating System :: OS Independent",
21+
],
22+
python_requires=">=3.9",
23+
project_urls={
24+
"Source": "https://github.com/Butterroach/tranci",
25+
"Bug Tracker": "https://github.com/Butterroach/tranci/issues",
26+
},
27+
)

tranci/__init__.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import os
2121
import sys
22+
from enum import Enum
2223
from typing import Union
2324

2425
if sys.platform == "win32":
@@ -46,9 +47,31 @@ def __call__(self, text: str):
4647
"""
4748
Returns the text provided in the same style. Useful for saving colors and reusing them over and over again.
4849
"""
50+
4951
return BaseText.__new__(BaseText, self.code, text)
5052

5153

54+
class Hyperlink(str):
55+
"""
56+
Bet you didn't know this was a thing! Yeah, it's real. Hyperlinks. In a terminal. Crazy. Just be aware that this is not too standard.
57+
"""
58+
59+
def __new__(cls, url: str, text: Union[str, BaseText, None] = None):
60+
if text is None:
61+
return super().__new__(cls)
62+
return super().__new__(cls, f"\033]8;;{url}\033\\{text}\033]8;;\033")
63+
64+
def __init__(self, url: str, text: Union[str, BaseText, None] = None):
65+
self.url = url
66+
67+
def __call__(self, text: str):
68+
"""
69+
Why are you doing this with hyperlinks
70+
"""
71+
72+
return Hyperlink.__new__(Hyperlink, self.url, text)
73+
74+
5275
class Bold(BaseText):
5376
def __new__(cls, text: Union[str, BaseText, None] = None):
5477
code = "\033[1m"
@@ -349,3 +372,74 @@ def __init__(self, hexa: Union[str, int], text: Union[str, BaseText, None] = Non
349372
if isinstance(hexa, str):
350373
hexa = int(hexa.replace("#", ""), 16)
351374
super().__init__((hexa >> 16) & 255, (hexa >> 8) & 255, hexa & 255)
375+
376+
377+
class Direction(Enum):
378+
UP = "A"
379+
DOWN = "B"
380+
RIGHT = "C"
381+
LEFT = "D"
382+
383+
384+
def move_cursor_dir(dir: Direction, lines: int, do_print: bool = True):
385+
code = f"\033[{lines}{dir.value}"
386+
if do_print:
387+
print(code, end="")
388+
return code
389+
390+
391+
def move_cursor_pos(row: int, col: int, do_print: bool = True):
392+
"""
393+
move_cursor_pos(1, 1) will move the cursor to the top left corner.
394+
"""
395+
396+
code = f"\033[{row};{col}H"
397+
if do_print:
398+
print(code, end="")
399+
return code
400+
401+
402+
def save_cursor_pos(do_print: bool = True):
403+
"""
404+
Tells the terminal emulator to save the current position for later use during the current session.
405+
"""
406+
407+
code = "\033[s"
408+
if do_print:
409+
print(code, end="")
410+
return code
411+
412+
413+
def restore_cursor_pos(do_print: bool = True):
414+
"""
415+
Tells the terminal emulator to move the cursor back to the last saved position.
416+
"""
417+
418+
code = "\033[u"
419+
if do_print:
420+
print(code, end="")
421+
return code
422+
423+
424+
def set_cursor_visibility(visible: bool, do_print: bool = True):
425+
if visible:
426+
code = "\033[?25h"
427+
else:
428+
code = "\033[?25l"
429+
if do_print:
430+
print(code, end="")
431+
return code
432+
433+
434+
def clear_screen(do_print: bool = True):
435+
code = "\033[2K"
436+
if do_print:
437+
print(code, end="")
438+
return code
439+
440+
441+
def clear_line(do_print: bool = True):
442+
code = "\033[2J"
443+
if do_print:
444+
print(code, end="")
445+
return code

tranci/__main__.py

Lines changed: 67 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,67 +18,100 @@
1818
"""
1919

2020
import colorsys
21+
import os
22+
import shutil
2123
import time
2224
import tranci
25+
from pathlib import Path
2326

27+
if __name__ == "__main__":
28+
try:
29+
for hue in range(360):
30+
r, g, b = colorsys.hsv_to_rgb(hue / 360, 1, 1)
31+
r, g, b = r * 255, g * 255, b * 255
32+
print(tranci.RGB(r, g, b, f"tranci"), end="\r")
33+
time.sleep(0.01)
34+
except KeyboardInterrupt:
35+
pass
36+
37+
FOLLOWUP_1 = ": puts you in a trance."
38+
FOLLOWUP_2 = f" v{tranci.__version__} | https://github.com/Butterroach/tranci"
39+
40+
whitening_followup_1 = []
41+
whitening_followup_2 = []
42+
43+
for i in range(len(FOLLOWUP_1)):
44+
_r, _g, _b = colorsys.hsv_to_rgb(hue / 360, 1 - (i + 1) / len(FOLLOWUP_1), 1)
45+
_r, _g, _b = _r * 255, _g * 255, _b * 255
46+
whitening_followup_1.append(tranci.RGB(_r, _g, _b, FOLLOWUP_1[i]))
47+
48+
for i in range(len(FOLLOWUP_2)):
49+
_r, _g, _b = colorsys.hsv_to_rgb(hue / 360, 1 - (i + 1) / len(FOLLOWUP_2), 1)
50+
_r, _g, _b = _r * 255, _g * 255, _b * 255
51+
whitening_followup_2.append(tranci.RGB(_r, _g, _b, FOLLOWUP_2[i]))
52+
53+
try:
54+
for i in range(1, len(FOLLOWUP_1)):
55+
if i > 1:
56+
time.sleep(0.15)
57+
print(
58+
f"\r{tranci.RGB(r, g, b, 'tranci')}{''.join(whitening_followup_1[:i])}",
59+
end="\r",
60+
)
61+
except KeyboardInterrupt:
62+
print(
63+
f"\r{tranci.RGB(r, g, b, 'tranci')}{''.join(whitening_followup_1)}",
64+
end="\r",
65+
)
2466

25-
def main():
26-
print(tranci.Red("This is red text"))
27-
print(tranci.Green("This is green text"))
28-
print(tranci.Blue("This is blue text"))
29-
30-
print(tranci.BrightRed("This is bright red text"))
31-
print(tranci.BrightGreen("This is bright green text"))
32-
print(tranci.BrightBlue("This is bright blue text"))
33-
34-
print(tranci.BGRed("This text has a red background"))
35-
print(tranci.BGGreen("This text has a green background"))
36-
print(tranci.BGBlue("This text has a blue background"))
67+
try:
68+
time.sleep(1)
69+
except:
70+
print(
71+
f"\r{tranci.RGB(r, g, b, 'tranci')}{''.join(whitening_followup_1)}",
72+
end="\r",
73+
)
3774

38-
print(tranci.BGBrightRed("This text has a bright red background"))
39-
print(tranci.BGBrightGreen("This text has a bright green background"))
40-
print(tranci.BGBrightBlue("This text has a bright blue background"))
75+
print(" " * shutil.get_terminal_size().columns, end="\r")
76+
print(f"{tranci.RGB(r, g, b, 'tranci')}{''.join(whitening_followup_2)}\n")
4177

42-
print(tranci.Bold(tranci.Red("This is bold and red text")))
43-
print(tranci.Italicized(tranci.Green("This is italicized and green text")))
44-
print(tranci.Underlined(tranci.Blue("This is underlined and blue text")))
78+
time.sleep(0.1)
4579

4680
print(
4781
tranci.Red(
48-
f"This is red text, but {tranci.Blue('this is blue')}. And the rest is red"
82+
f"This is red text, \nbut {tranci.Blue('this is blue')}. And the rest is red."
4983
)
5084
)
5185

52-
print(tranci.RGB(255, 165, 0, "This is custom RGB color text"))
53-
print(tranci.HEX("#AFCBED", "This is custom HEX color text with str"))
54-
print(tranci.HEX(0xEFEABD, "This is custom HEX color text with int"))
86+
print(tranci.RGB(255, 165, 0, "This is custom RGB text."))
87+
print(tranci.HEX("#AFCBED", "This is custom HEX text with str."))
88+
print(tranci.HEX(0xEFEABD, "This is custom HEX text with int."))
5589
print(
5690
tranci.Bold(
5791
tranci.Italicized(
5892
tranci.Underlined(
5993
tranci.BrightMagenta(
60-
"This is bold, italicized, underlined, and bright magenta text"
94+
"This is bold, italicized, underlined, and bright magenta text."
6195
)
6296
)
6397
)
6498
)
6599
)
66100

67-
for hue in range(360):
68-
r, g, b = colorsys.hsv_to_rgb(hue / 360, 1, 1)
69-
r, g, b = r * 255, g * 255, b * 255
70-
print(tranci.RGB(r, g, b, f"Rainbow!!"), end="\r")
71-
time.sleep(0.005)
72-
73-
print()
74-
75101
warning = tranci.HEX("#fff374")
76102

77-
print(warning("This is a warning colored by saving a color and calling it"))
103+
print(warning("This is a warning colored by saving a color and calling it."))
78104
print(warning("Another warning!"))
79105
print(tranci.Inverted(warning("And that's the inverted one!")))
80106
print(tranci.BGHEX("#fff374", tranci.HEX(0, "Or if you wanna be real, I suppose.")))
81107

108+
print(
109+
tranci.Hyperlink(
110+
"https://rickroll.link",
111+
f"...{tranci.Italicized(warning('Hyperlinks'))}? What?",
112+
)
113+
)
114+
115+
google = tranci.Hyperlink("https://www.google.com/")
82116

83-
if __name__ == "__main__":
84-
main()
117+
print(google("This supports saving too, for whatever reason."))

0 commit comments

Comments
 (0)