shinyshell 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adnan Ahamed Himal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: shinyshell
3
+ Version: 0.1.0
4
+ Summary: Beautiful terminal output for Python. Zero dependencies.
5
+ Home-page: https://github.com/adnanahamed66772ndpc/shinyshell
6
+ Author: Adnan Ahamed Himal
7
+ Keywords: terminal,shell,pretty,beautiful,output,cli,print,colors
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Terminals
19
+ Classifier: Environment :: Console
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Dynamic: author
24
+ Dynamic: classifier
25
+ Dynamic: description
26
+ Dynamic: description-content-type
27
+ Dynamic: home-page
28
+ Dynamic: keywords
29
+ Dynamic: license-file
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # ✨ shinyshell
34
+
35
+ **Beautiful terminal output for Python. Zero dependencies. One import.**
36
+
37
+ [![PyPI version](https://img.shields.io/pypi/v/shinyshell.svg)](https://pypi.org/project/shinyshell/)
38
+ [![Python](https://img.shields.io/pypi/pyversions/shinyshell.svg)](https://pypi.org/project/shinyshell/)
39
+ [![License](https://img.shields.io/github/license/adnanahamed66772ndpc/shinyshell)](LICENSE)
40
+
41
+ ```python
42
+ from shinyshell import Shell
43
+
44
+ sh = Shell()
45
+
46
+ sh.success("Deployed to production!")
47
+ sh.table(users, title="Team Members")
48
+ sh.progress("Installing deps...")
49
+ sh.header("My CLI App")
50
+ sh.code("print('hello')")
51
+ sh.countdown(5, "Launching")
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 📸 Demo
57
+
58
+ ```python
59
+ from shinyshell import Shell
60
+ sh = Shell()
61
+
62
+ # Headers & banners
63
+ sh.header("DEPLOYMENT REPORT")
64
+ sh.banner("SHINY SHELL")
65
+
66
+ # Success / Warning / Error / Info
67
+ sh.success("Database migrated successfully")
68
+ sh.warning("Rate limit approaching — 93% used")
69
+ sh.error("Connection to API failed after 3 retries")
70
+ sh.info("Server running on http://localhost:8000")
71
+
72
+ # Beautiful tables
73
+ sh.table([
74
+ {"Name": "Alice Chen", "Role": "Backend Dev", "Status": "✅ Active"},
75
+ {"Name": "Bob Kumar", "Role": "Frontend", "Status": "✅ Active"},
76
+ {"Name": "Charlie D.", "Role": "Design Lead", "Status": "âŗ On Leave"},
77
+ ], title="Team Status")
78
+
79
+ # Progress with spinner
80
+ sh.spinner("Compiling assets...")
81
+ update = sh.progress("Uploading")
82
+ for i in range(100):
83
+ update(i + 1, 100)
84
+
85
+ # Countdown
86
+ sh.countdown(5, "Deploying to production")
87
+
88
+ # Boxed content
89
+ sh.box("API Key: sk-****abcd\nEndpoint: /v1/chat\nModel: gpt-4", title="Configuration")
90
+
91
+ # Directory tree
92
+ sh.tree("./src", max_depth=2)
93
+
94
+ # Code blocks
95
+ sh.code("""
96
+ def fibonacci(n):
97
+ if n <= 1:
98
+ return n
99
+ return fibonacci(n-1) + fibonacci(n-2)
100
+ """)
101
+
102
+ # Metrics dashboard
103
+ sh.metrics({
104
+ "Users": 12483,
105
+ "Active Now": 342,
106
+ "Uptime": "✅ 99.9%",
107
+ "Error Rate": "0.02%",
108
+ "Avg Response": "23ms",
109
+ })
110
+
111
+ # Colored diff
112
+ sh.diff("hello world", "hello beautiful world")
113
+
114
+ # Interactive
115
+ if sh.confirm("Deploy to production?"):
116
+ sh.success("Ship it! 🚀")
117
+
118
+ # Horizontal rule
119
+ sh.hr("Section 2")
120
+ ```
121
+
122
+ ---
123
+
124
+ ## đŸ“Ļ Install
125
+
126
+ ```bash
127
+ pip install shinyshell
128
+ ```
129
+
130
+ No dependencies. Uses only Python standard library. Works on **Linux**, **macOS**, and **Windows** (PowerShell, CMD, Windows Terminal).
131
+
132
+ ---
133
+
134
+ ## đŸŽ¯ Features
135
+
136
+ | Feature | Description |
137
+ |---------|-------------|
138
+ | `success()` | ✅ Green success messages |
139
+ | `warning()` | âš ī¸ Yellow warnings |
140
+ | `error()` | ❌ Red errors |
141
+ | `info()` | â„šī¸ Cyan info |
142
+ | `header()` | Styled section headers |
143
+ | `banner()` | ASCII art banners |
144
+ | `table()` | Beautiful data tables |
145
+ | `box()` | Content in styled boxes |
146
+ | `code()` | Syntax-highlighted code blocks |
147
+ | `diff()` | Colored git diff |
148
+ | `tree()` | Directory trees |
149
+ | `metrics()` | Key-value dashboards |
150
+ | `spinner()` | Animated loading spinner |
151
+ | `progress()` | Progress bars |
152
+ | `countdown()` | Animated countdown |
153
+ | `confirm()` | Interactive y/n prompts |
154
+ | `choice()` | Interactive option picker |
155
+ | `hr()` | Horizontal rules with labels |
156
+
157
+ ---
158
+
159
+ ## 🤔 Why?
160
+
161
+ Every Python script prints to the terminal. Most output looks boring. **shinyshell** makes it beautiful — with zero dependencies.
162
+
163
+ Before:
164
+ ```
165
+ Task completed
166
+ Users: 1042
167
+ ERROR: Connection failed
168
+ ```
169
+
170
+ After:
171
+ ```
172
+ ✨ Task completed
173
+ 📊 Users: 1,042
174
+ đŸ’Ĩ Connection failed
175
+ ```
176
+
177
+ ---
178
+
179
+ ## 🔧 Advanced
180
+
181
+ ```python
182
+ # Custom width
183
+ sh = Shell(width=80)
184
+
185
+ # Disable color
186
+ sh = Shell(color=False)
187
+
188
+ # Access icons
189
+ print(sh.icons["rocket"]) # 🚀
190
+
191
+ # Chaining
192
+ sh.success("Step 1 done").progress("Step 2...")
193
+ ```
194
+
195
+ ---
196
+
197
+ ## 📄 License
198
+
199
+ MIT — © 2026 Adnan Ahamed Himal
200
+
201
+ ---
202
+
203
+ ## ⭐ Support
204
+
205
+ Found this useful? **Star this repo** and share it with your team!
206
+
207
+ [âŦ† Back to top](#-shinyshell)
@@ -0,0 +1,175 @@
1
+ # ✨ shinyshell
2
+
3
+ **Beautiful terminal output for Python. Zero dependencies. One import.**
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/shinyshell.svg)](https://pypi.org/project/shinyshell/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/shinyshell.svg)](https://pypi.org/project/shinyshell/)
7
+ [![License](https://img.shields.io/github/license/adnanahamed66772ndpc/shinyshell)](LICENSE)
8
+
9
+ ```python
10
+ from shinyshell import Shell
11
+
12
+ sh = Shell()
13
+
14
+ sh.success("Deployed to production!")
15
+ sh.table(users, title="Team Members")
16
+ sh.progress("Installing deps...")
17
+ sh.header("My CLI App")
18
+ sh.code("print('hello')")
19
+ sh.countdown(5, "Launching")
20
+ ```
21
+
22
+ ---
23
+
24
+ ## 📸 Demo
25
+
26
+ ```python
27
+ from shinyshell import Shell
28
+ sh = Shell()
29
+
30
+ # Headers & banners
31
+ sh.header("DEPLOYMENT REPORT")
32
+ sh.banner("SHINY SHELL")
33
+
34
+ # Success / Warning / Error / Info
35
+ sh.success("Database migrated successfully")
36
+ sh.warning("Rate limit approaching — 93% used")
37
+ sh.error("Connection to API failed after 3 retries")
38
+ sh.info("Server running on http://localhost:8000")
39
+
40
+ # Beautiful tables
41
+ sh.table([
42
+ {"Name": "Alice Chen", "Role": "Backend Dev", "Status": "✅ Active"},
43
+ {"Name": "Bob Kumar", "Role": "Frontend", "Status": "✅ Active"},
44
+ {"Name": "Charlie D.", "Role": "Design Lead", "Status": "âŗ On Leave"},
45
+ ], title="Team Status")
46
+
47
+ # Progress with spinner
48
+ sh.spinner("Compiling assets...")
49
+ update = sh.progress("Uploading")
50
+ for i in range(100):
51
+ update(i + 1, 100)
52
+
53
+ # Countdown
54
+ sh.countdown(5, "Deploying to production")
55
+
56
+ # Boxed content
57
+ sh.box("API Key: sk-****abcd\nEndpoint: /v1/chat\nModel: gpt-4", title="Configuration")
58
+
59
+ # Directory tree
60
+ sh.tree("./src", max_depth=2)
61
+
62
+ # Code blocks
63
+ sh.code("""
64
+ def fibonacci(n):
65
+ if n <= 1:
66
+ return n
67
+ return fibonacci(n-1) + fibonacci(n-2)
68
+ """)
69
+
70
+ # Metrics dashboard
71
+ sh.metrics({
72
+ "Users": 12483,
73
+ "Active Now": 342,
74
+ "Uptime": "✅ 99.9%",
75
+ "Error Rate": "0.02%",
76
+ "Avg Response": "23ms",
77
+ })
78
+
79
+ # Colored diff
80
+ sh.diff("hello world", "hello beautiful world")
81
+
82
+ # Interactive
83
+ if sh.confirm("Deploy to production?"):
84
+ sh.success("Ship it! 🚀")
85
+
86
+ # Horizontal rule
87
+ sh.hr("Section 2")
88
+ ```
89
+
90
+ ---
91
+
92
+ ## đŸ“Ļ Install
93
+
94
+ ```bash
95
+ pip install shinyshell
96
+ ```
97
+
98
+ No dependencies. Uses only Python standard library. Works on **Linux**, **macOS**, and **Windows** (PowerShell, CMD, Windows Terminal).
99
+
100
+ ---
101
+
102
+ ## đŸŽ¯ Features
103
+
104
+ | Feature | Description |
105
+ |---------|-------------|
106
+ | `success()` | ✅ Green success messages |
107
+ | `warning()` | âš ī¸ Yellow warnings |
108
+ | `error()` | ❌ Red errors |
109
+ | `info()` | â„šī¸ Cyan info |
110
+ | `header()` | Styled section headers |
111
+ | `banner()` | ASCII art banners |
112
+ | `table()` | Beautiful data tables |
113
+ | `box()` | Content in styled boxes |
114
+ | `code()` | Syntax-highlighted code blocks |
115
+ | `diff()` | Colored git diff |
116
+ | `tree()` | Directory trees |
117
+ | `metrics()` | Key-value dashboards |
118
+ | `spinner()` | Animated loading spinner |
119
+ | `progress()` | Progress bars |
120
+ | `countdown()` | Animated countdown |
121
+ | `confirm()` | Interactive y/n prompts |
122
+ | `choice()` | Interactive option picker |
123
+ | `hr()` | Horizontal rules with labels |
124
+
125
+ ---
126
+
127
+ ## 🤔 Why?
128
+
129
+ Every Python script prints to the terminal. Most output looks boring. **shinyshell** makes it beautiful — with zero dependencies.
130
+
131
+ Before:
132
+ ```
133
+ Task completed
134
+ Users: 1042
135
+ ERROR: Connection failed
136
+ ```
137
+
138
+ After:
139
+ ```
140
+ ✨ Task completed
141
+ 📊 Users: 1,042
142
+ đŸ’Ĩ Connection failed
143
+ ```
144
+
145
+ ---
146
+
147
+ ## 🔧 Advanced
148
+
149
+ ```python
150
+ # Custom width
151
+ sh = Shell(width=80)
152
+
153
+ # Disable color
154
+ sh = Shell(color=False)
155
+
156
+ # Access icons
157
+ print(sh.icons["rocket"]) # 🚀
158
+
159
+ # Chaining
160
+ sh.success("Step 1 done").progress("Step 2...")
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 📄 License
166
+
167
+ MIT — © 2026 Adnan Ahamed Himal
168
+
169
+ ---
170
+
171
+ ## ⭐ Support
172
+
173
+ Found this useful? **Star this repo** and share it with your team!
174
+
175
+ [âŦ† Back to top](#-shinyshell)
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,32 @@
1
+ """Setup for shinyshell."""
2
+ from setuptools import setup, find_packages
3
+
4
+ with open("README.md", "r") as f:
5
+ long_description = f.read()
6
+
7
+ setup(
8
+ name="shinyshell",
9
+ version="0.1.0",
10
+ author="Adnan Ahamed Himal",
11
+ description="Beautiful terminal output for Python. Zero dependencies.",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/adnanahamed66772ndpc/shinyshell",
15
+ packages=find_packages(),
16
+ classifiers=[
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
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 :: Software Development :: Libraries :: Python Modules",
27
+ "Topic :: Terminals",
28
+ "Environment :: Console",
29
+ ],
30
+ python_requires=">=3.8",
31
+ keywords="terminal, shell, pretty, beautiful, output, cli, print, colors",
32
+ )
@@ -0,0 +1,559 @@
1
+ """
2
+ shinyshell — Beautiful terminal output for Python. Zero dependencies.
3
+ =====================================
4
+ One import. All the pretty you need.
5
+
6
+ from shinyshell import Shell
7
+ sh = Shell()
8
+ sh.success("Deployed!")
9
+ sh.table(users)
10
+ sh.progress("Loading...")
11
+
12
+ Pure Python stdlib. Works on Linux, macOS, Windows.
13
+ """
14
+
15
+ __version__ = "0.1.0"
16
+ __all__ = ["Shell"]
17
+
18
+ import os
19
+ import sys
20
+ import shutil
21
+ import textwrap
22
+ import math
23
+ from typing import Any, List, Dict, Optional, Union, Callable
24
+
25
+
26
+ # ── ANSI / Color utilities (stdlib only) ───────────────────────
27
+
28
+ def _supports_color() -> bool:
29
+ """Check if the terminal supports color output."""
30
+ if not hasattr(sys.stdout, "isatty"):
31
+ return False
32
+ if not sys.stdout.isatty():
33
+ return False
34
+ if sys.platform == "win32":
35
+ try:
36
+ import ctypes
37
+ kernel32 = ctypes.windll.kernel32
38
+ mode = ctypes.c_uint32()
39
+ handle = kernel32.GetStdHandle(-11) # STD_OUTPUT_HANDLE
40
+ kernel32.GetConsoleMode(handle, ctypes.byref(mode))
41
+ kernel32.SetConsoleMode(handle, mode.value | 0x0004) # ENABLE_VIRTUAL_TERMINAL
42
+ return True
43
+ except Exception:
44
+ return False
45
+ term = os.environ.get("TERM", "")
46
+ if term == "dumb":
47
+ return False
48
+ if "NO_COLOR" in os.environ:
49
+ return False
50
+ return True
51
+
52
+
53
+ class _ANSICodes:
54
+ """ANSI escape codes for colors and styles."""
55
+ RESET = "\033[0m"
56
+ BOLD = "\033[1m"
57
+ DIM = "\033[2m"
58
+ ITALIC = "\033[3m"
59
+ UNDERLINE = "\033[4m"
60
+ BLINK = "\033[5m"
61
+ REVERSE = "\033[7m"
62
+ STRIKE = "\033[9m"
63
+
64
+ COLORS = {
65
+ "black": 30, "red": 31, "green": 32, "yellow": 33,
66
+ "blue": 34, "magenta": 35, "cyan": 36, "white": 37,
67
+ "bright_black": 90, "bright_red": 91, "bright_green": 92,
68
+ "bright_yellow": 93, "bright_blue": 94, "bright_magenta": 95,
69
+ "bright_cyan": 96, "bright_white": 97,
70
+ }
71
+
72
+ BG_COLORS = {
73
+ "black": 40, "red": 41, "green": 42, "yellow": 43,
74
+ "blue": 44, "magenta": 45, "cyan": 46, "white": 47,
75
+ }
76
+
77
+ @classmethod
78
+ def color(cls, name: str, background: bool = False) -> str:
79
+ if background:
80
+ return f"\033[{cls.BG_COLORS.get(name, 40)}m"
81
+ return f"\033[{cls.COLORS.get(name, 37)}m"
82
+
83
+ @classmethod
84
+ def rgb(cls, r: int, g: int, b: int, background: bool = False) -> str:
85
+ base = 48 if background else 38
86
+ return f"\033[{base};2;{r};{g};{b}m"
87
+
88
+
89
+ class _Style:
90
+ """Applies styles to text only if color is supported."""
91
+ def __init__(self, enabled: bool = True):
92
+ self.enabled = enabled
93
+
94
+ def __call__(self, text: str, *styles: str, color: Optional[str] = None,
95
+ bg: Optional[str] = None) -> str:
96
+ if not self.enabled:
97
+ return str(text)
98
+ codes = _ANSICodes()
99
+ parts = []
100
+ for s in styles:
101
+ code = getattr(codes, s.upper(), None)
102
+ if code:
103
+ parts.append(code)
104
+ if color:
105
+ parts.append(codes.color(color))
106
+ if bg:
107
+ parts.append(codes.color(bg, background=True))
108
+ if not parts:
109
+ return str(text)
110
+ return "".join(parts) + str(text) + codes.RESET
111
+
112
+
113
+ # ── Icons (Unicode, works everywhere) ──────────────────────────
114
+ _ICONS = {
115
+ "success": "✨",
116
+ "error": "đŸ’Ĩ",
117
+ "warning": "âš ī¸",
118
+ "info": "â„šī¸",
119
+ "question": "❓",
120
+ "star": "⭐",
121
+ "heart": "💜",
122
+ "fire": "đŸ”Ĩ",
123
+ "rocket": "🚀",
124
+ "check": "✅",
125
+ "cross": "❌",
126
+ "arrow": "→",
127
+ "bullet": "â€ĸ",
128
+ "diamond": "◆",
129
+ "pointer": "▸",
130
+ "dot": "¡",
131
+ "lightning": "⚡",
132
+ "clock": "🕐",
133
+ "lock": "🔒",
134
+ "unlock": "🔓",
135
+ "key": "🔑",
136
+ "gear": "âš™ī¸",
137
+ "package": "đŸ“Ļ",
138
+ "link": "🔗",
139
+ "globe": "🌍",
140
+ "mail": "📧",
141
+ "phone": "📞",
142
+ "pin": "📌",
143
+ "bookmark": "🔖",
144
+ "tag": "đŸˇī¸",
145
+ "flag": "🚩",
146
+ "target": "đŸŽ¯",
147
+ "trophy": "🏆",
148
+ "medal": "đŸĨ‡",
149
+ "gift": "🎁",
150
+ "party": "🎉",
151
+ "sparkles": "✨",
152
+ "magic": "đŸĒ„",
153
+ "robot": "🤖",
154
+ "bug": "🐛",
155
+ "eyes": "👀",
156
+ "brain": "🧠",
157
+ "tools": "đŸ› ī¸",
158
+ "chart": "📊",
159
+ "database": "đŸ—„ī¸",
160
+ "file": "📄",
161
+ "folder": "📁",
162
+ "download": "đŸ“Ĩ",
163
+ "upload": "📤",
164
+ "save": "💾",
165
+ "print": "đŸ–¨ī¸",
166
+ "search": "🔍",
167
+ "shield": "đŸ›Ąī¸",
168
+ "money": "💰",
169
+ "credit": "đŸ’ŗ",
170
+ "shopping": "🛒",
171
+ "home": "🏠",
172
+ "world": "🌐",
173
+ "mobile": "📱",
174
+ "desktop": "đŸ–Ĩī¸",
175
+ "server": "đŸ–Ĩ",
176
+ "cloud": "â˜ī¸",
177
+ "terminal": "đŸ’ģ",
178
+ }
179
+
180
+
181
+ # ── Borders / Frames ───────────────────────────────────────────
182
+ _BORDERS = {
183
+ "single": "─│┌┐└┘├┤â”Ŧ┴â”ŧ",
184
+ "double": "â•â•‘â•”â•—â•šâ•â• â•Ŗâ•Ļ╩â•Ŧ",
185
+ "round": "─│╭╎╰╯├┤â”Ŧ┴â”ŧ",
186
+ "bold": "â”â”ƒâ”â”“â”—â”›â”Ŗâ”Ģâ”ŗâ”ģ╋",
187
+ "dashed": "┄┆┌┐└┘├┤â”Ŧ┴â”ŧ",
188
+ "none": " ",
189
+ }
190
+
191
+
192
+ # ── Shell Class ─────────────────────────────────────────────────
193
+
194
+ class Shell:
195
+ """Beautiful terminal output for Python scripts and CLIs."""
196
+
197
+ def __init__(self, color: bool = True, width: Optional[int] = None):
198
+ self._color_enabled = color and _supports_color()
199
+ self._style = _Style(self._color_enabled)
200
+ self._width = width or min(shutil.get_terminal_size().columns, 120)
201
+
202
+ # ── Message Helpers ─────────────────────────────────────────
203
+
204
+ def success(self, message: str) -> None:
205
+ """Print a success message with green checkmark."""
206
+ self._log("success", message, "green")
207
+
208
+ def error(self, message: str) -> None:
209
+ """Print an error message with red cross."""
210
+ self._log("error", message, "red")
211
+
212
+ def warning(self, message: str) -> None:
213
+ """Print a warning with yellow triangle."""
214
+ self._log("warning", message, "yellow")
215
+
216
+ def info(self, message: str) -> None:
217
+ """Print an info message with blue circle."""
218
+ self._log("info", message, "cyan")
219
+
220
+ def _log(self, level: str, message: str, color: str) -> None:
221
+ icon = _ICONS.get(level, "â€ĸ")
222
+ prefix = self._style(f" {icon} ", bg=color, color="white")
223
+ text = self._style(f" {message}", color=color)
224
+ print(f"{prefix}{text}")
225
+
226
+ # ── Sections & Headers ──────────────────────────────────────
227
+
228
+ def header(self, title: str, level: int = 1) -> None:
229
+ """Print a styled header."""
230
+ if level == 1:
231
+ line = "═" * (self._width - 4)
232
+ text = self._style(f" {title} ", "bold")
233
+ print(f"\n{self._style(f'╔{line}╗', color='cyan')}")
234
+ print(f"{self._style('║', color='cyan')}{text.center(self._width - 2)}{self._style('║', color='cyan')}")
235
+ print(f"{self._style(f'╚{line}╝', color='cyan')}")
236
+ else:
237
+ text = self._style(f"── {title} ", "bold", color="cyan")
238
+ rest = "─" * max(0, self._width - len(title) - 10)
239
+ print(f"\n{text}{self._style(rest, color='cyan', dim=True)}")
240
+
241
+ def banner(self, text: str, color: str = "cyan") -> None:
242
+ """Display a large ASCII banner."""
243
+ from shinyshell.banner import render
244
+ print(self._style(render(text), color=color))
245
+
246
+ # ── Progress ────────────────────────────────────────────────
247
+
248
+ def spinner(self, message: str, duration: float = 3.0) -> None:
249
+ """Show a brief spinning animation with message."""
250
+ frames = ["⠋", "⠙", "â š", "â ¸", "â ŧ", "â ´", "â Ļ", "â §", "⠇", "⠏"]
251
+ import time
252
+ start = time.time()
253
+ i = 0
254
+ print()
255
+ try:
256
+ while time.time() - start < duration:
257
+ frame = self._style(frames[i % len(frames)], color="cyan")
258
+ sys.stdout.write(f"\r {frame} {message}")
259
+ sys.stdout.flush()
260
+ time.sleep(0.08)
261
+ i += 1
262
+ sys.stdout.write(f"\r {self._style(_ICONS['check'], color='green')} {message} {self._style('Done!', color='green')}\n")
263
+ except KeyboardInterrupt:
264
+ sys.stdout.write(f"\r {self._style(_ICONS['cross'], color='red')} {message} Cancelled\n")
265
+
266
+ def progress(self, message: str = "Working") -> Callable[[int, int], None]:
267
+ """Return a progress updater function. Use as context:
268
+
269
+ update = sh.progress("Downloading")
270
+ for i in range(100):
271
+ update(i+1, 100)
272
+ """
273
+ def update(current: int, total: int):
274
+ percent = int((current / max(total, 1)) * 100)
275
+ bar_width = 30
276
+ filled = int(bar_width * current / max(total, 1))
277
+ bar = "█" * filled + "░" * (bar_width - filled)
278
+ pct = f"{percent:3d}%"
279
+ sys.stdout.write(f"\r {message} {self._style(bar, color='cyan')} {pct}")
280
+ sys.stdout.flush()
281
+ if current >= total:
282
+ print()
283
+ return update
284
+
285
+ def countdown(self, seconds: int, message: str = "Starting in") -> None:
286
+ """Show an animated countdown."""
287
+ import time
288
+ for i in range(seconds, 0, -1):
289
+ sys.stdout.write(f"\r {self._style(_ICONS['clock'], color='yellow')} {message} {self._style(str(i), 'bold', color='yellow')}...")
290
+ sys.stdout.flush()
291
+ time.sleep(1)
292
+ print(f"\r {self._style(_ICONS['rocket'], color='green')} {self._style('Go!', 'bold', color='green')} ")
293
+
294
+ # ── Tables ──────────────────────────────────────────────────
295
+
296
+ def table(self, data: List[Dict[str, Any]], title: Optional[str] = None,
297
+ style: str = "single") -> None:
298
+ """Display data as a beautiful table.
299
+
300
+ data = [
301
+ {"Name": "Alice", "Role": "Developer", "Stars": 42},
302
+ {"Name": "Bob", "Role": "Designer", "Stars": 17},
303
+ ]
304
+ sh.table(data, title="Team Members")
305
+ """
306
+ if not data:
307
+ return
308
+
309
+ keys = list(data[0].keys())
310
+ col_widths = {k: max(len(str(k)), max(len(str(row.get(k, ""))) for row in data)) + 2
311
+ for k in keys}
312
+
313
+ if title:
314
+ print()
315
+ print(self._style(f" {title}", "bold"))
316
+
317
+ border = _BORDERS.get(style, _BORDERS["single"])
318
+ h, v, tl, tr, bl, br, l, r, t, b, x = list(border)
319
+
320
+ total_width = sum(col_widths.values()) + len(keys) - 1
321
+
322
+ # Top border
323
+ top = tl + h * total_width + tr
324
+ print(self._style(f" {top}", color="bright_black"))
325
+
326
+ # Header
327
+ cells = [self._style(f" {str(k).ljust(col_widths[k])}", "bold")
328
+ for k in keys]
329
+ print(f" {v}{v.join(cells)}{v}")
330
+
331
+ # Separator
332
+ sep = l + h * total_width + r
333
+ print(self._style(f" {sep}", color="bright_black"))
334
+
335
+ # Rows
336
+ for row in data:
337
+ cells = [f" {str(row.get(k, '')).ljust(col_widths[k])}" for k in keys]
338
+ print(f" {self._style(v, color='bright_black')}{v.join(cells)}{self._style(v, color='bright_black')}")
339
+
340
+ # Bottom border
341
+ bottom = bl + h * total_width + br
342
+ print(self._style(f" {bottom}", color="bright_black"))
343
+ print()
344
+
345
+ # ── Code / Syntax ───────────────────────────────────────────
346
+
347
+ def code(self, source: str, language: str = "python") -> None:
348
+ """Display syntax-highlighted code block."""
349
+ keywords = {"def", "class", "import", "from", "return", "if", "else",
350
+ "elif", "for", "while", "try", "except", "finally", "with",
351
+ "as", "in", "not", "and", "or", "True", "False", "None",
352
+ "async", "await", "yield", "raise", "pass", "break", "continue"}
353
+ strings_color = "green"
354
+ kw_color = "magenta"
355
+ comment_color = "bright_black"
356
+ num_color = "yellow"
357
+ func_color = "cyan"
358
+
359
+ lines = source.strip().split("\n")
360
+ max_num = len(str(len(lines)))
361
+ print()
362
+
363
+ for i, line in enumerate(lines):
364
+ num = str(i + 1).rjust(max_num)
365
+ prefix = self._style(f" {num} ", color="bright_black")
366
+
367
+ # Basic syntax highlighting
368
+ highlighted = []
369
+ words = line.split(" ")
370
+ for w in words:
371
+ if w in keywords:
372
+ highlighted.append(self._style(w, color=kw_color))
373
+ elif w.startswith("#"):
374
+ highlighted.append(self._style(w, color=comment_color))
375
+ elif w.startswith(('"', "'")):
376
+ highlighted.append(self._style(w, color=strings_color))
377
+ elif w.isdigit():
378
+ highlighted.append(self._style(w, color=num_color))
379
+ elif w.endswith("(") and not w.startswith(('"', "'")):
380
+ highlighted.append(self._style(w, color=func_color))
381
+ else:
382
+ highlighted.append(w)
383
+
384
+ print(f"{prefix}{' '.join(highlighted)}")
385
+ print()
386
+
387
+ # ── Diff ────────────────────────────────────────────────────
388
+
389
+ def diff(self, old: str, new: str, old_label: str = "OLD",
390
+ new_label: str = "NEW") -> None:
391
+ """Show a colored diff between two strings."""
392
+ import difflib
393
+ old_lines = old.splitlines(keepends=True)
394
+ new_lines = new.splitlines(keepends=True)
395
+ differ = difflib.unified_diff(old_lines, new_lines,
396
+ fromfile=old_label, tofile=new_label)
397
+ print()
398
+ for line in differ:
399
+ line = line.rstrip("\n")
400
+ if line.startswith("+++"):
401
+ print(self._style(f" {line}", color="green"))
402
+ elif line.startswith("---"):
403
+ print(self._style(f" {line}", color="red"))
404
+ elif line.startswith("@@"):
405
+ print(self._style(f" {line}", color="cyan"))
406
+ elif line.startswith("+"):
407
+ print(self._style(f" {line}", color="green"))
408
+ elif line.startswith("-"):
409
+ print(self._style(f" {line}", color="red"))
410
+ else:
411
+ print(f" {self._style(line, color='bright_black')}")
412
+ print()
413
+
414
+ # ── Tree ────────────────────────────────────────────────────
415
+
416
+ def tree(self, path: str = ".", max_depth: int = 3,
417
+ exclude: Optional[List[str]] = None) -> None:
418
+ """Display a directory tree."""
419
+ if exclude is None:
420
+ exclude = [".git", "__pycache__", ".DS_Store", "node_modules",
421
+ ".venv", "venv", ".idea", ".vscode", "*.pyc"]
422
+
423
+ print()
424
+ print(self._style(f" {path}", "bold", color="cyan"))
425
+
426
+ def _match_exclude(name: str) -> bool:
427
+ for pat in exclude:
428
+ if pat.startswith("*"):
429
+ if name.endswith(pat[1:]):
430
+ return True
431
+ elif name == pat:
432
+ return True
433
+ return False
434
+
435
+ def _walk(current: str, prefix: str = "", depth: int = 0):
436
+ if depth >= max_depth:
437
+ return
438
+ try:
439
+ entries = sorted(os.listdir(current))
440
+ except PermissionError:
441
+ print(f"{prefix}{self._style('└── [denied]', color='red')}")
442
+ return
443
+
444
+ entries = [e for e in entries if not _match_exclude(e)]
445
+ for i, entry in enumerate(entries):
446
+ full = os.path.join(current, entry)
447
+ is_last = i == len(entries) - 1
448
+ connector = "└── " if is_last else "├── "
449
+ if os.path.isdir(full):
450
+ icon = _ICONS["folder"]
451
+ print(f"{prefix}{connector}{self._style(icon + ' ' + entry, color='cyan')}")
452
+ next_prefix = prefix + (" " if is_last else "│ ")
453
+ _walk(full, next_prefix, depth + 1)
454
+ else:
455
+ icon = _ICONS["file"]
456
+ size = ""
457
+ try:
458
+ s = os.path.getsize(full)
459
+ if s > 1024 * 1024:
460
+ size = f" ({s / 1024 / 1024:.1f}MB)"
461
+ elif s > 1024:
462
+ size = f" ({s / 1024:.1f}KB)"
463
+ except Exception:
464
+ pass
465
+ print(f"{prefix}{connector}{self._style(icon, color='bright_black')} {entry}{self._style(size, color='bright_black')}")
466
+
467
+ _walk(path)
468
+ print()
469
+
470
+ # ── Interactive ─────────────────────────────────────────────
471
+
472
+ def confirm(self, question: str, default: bool = True) -> bool:
473
+ """Ask a y/n question and return True/False."""
474
+ hint = "[Y/n]" if default else "[y/N]"
475
+ prompt = f" {_ICONS['question']} {question} {self._style(hint, color='bright_black')}: "
476
+ try:
477
+ answer = input(prompt).strip().lower()
478
+ except (KeyboardInterrupt, EOFError):
479
+ print()
480
+ return False
481
+ if not answer:
482
+ return default
483
+ return answer in ("y", "yes", "yeah", "yep", "sure")
484
+
485
+ def choice(self, question: str, options: List[str]) -> Optional[str]:
486
+ """Ask user to pick from a list of options."""
487
+ print(f"\n {self._style(_ICONS['question'], color='yellow')} {self._style(question, 'bold')}")
488
+ for i, opt in enumerate(options, 1):
489
+ print(f" {self._style(str(i), color='cyan')}. {opt}")
490
+ try:
491
+ num = input(f" {self._style('Enter number:', color='bright_black')} ")
492
+ idx = int(num) - 1
493
+ if 0 <= idx < len(options):
494
+ return options[idx]
495
+ except (ValueError, KeyboardInterrupt, EOFError):
496
+ pass
497
+ return None
498
+
499
+ # ── Boxed Content ──────────────────────────────────────────
500
+
501
+ def box(self, content: str, title: Optional[str] = None,
502
+ style: str = "round", color: str = "cyan") -> None:
503
+ """Display content inside a styled box."""
504
+ lines = content.strip().split("\n")
505
+ max_len = max(len(l) for l in lines)
506
+ width = min(max_len + 4, self._width - 4)
507
+
508
+ border = _BORDERS.get(style, _BORDERS["round"])
509
+ h, v, tl, tr, bl, br, l, r, t, b, x = list(border)
510
+
511
+ print()
512
+ if title:
513
+ top = tl + h * 2 + f" {title} " + h * (width - len(title) - 4) + tr
514
+ else:
515
+ top = tl + h * width + tr
516
+ print(self._style(f" {top}", color=color))
517
+
518
+ for line in lines:
519
+ padded = line.ljust(width)
520
+ print(f" {self._style(v, color=color)} {padded} {self._style(v, color=color)}")
521
+
522
+ bottom = bl + h * width + br
523
+ print(self._style(f" {bottom}", color=color))
524
+ print()
525
+
526
+ # ── Metrics / Stats ─────────────────────────────────────────
527
+
528
+ def metrics(self, items: Dict[str, Union[str, int, float]]) -> None:
529
+ """Display key-value metrics in a compact format."""
530
+ print()
531
+ max_key = max(len(k) for k in items.keys())
532
+ for key, value in items.items():
533
+ k = self._style(f" {key.ljust(max_key)}", color="bright_black")
534
+ if isinstance(value, (int, float)) and value > 0:
535
+ v = self._style(f" {value:,}", "bold", color="green")
536
+ elif isinstance(value, str) and value.startswith("✅"):
537
+ v = self._style(f" {value}", "bold", color="green")
538
+ elif isinstance(value, str) and value.startswith("❌"):
539
+ v = self._style(f" {value}", color="red")
540
+ else:
541
+ v = f" {value}"
542
+ print(f"{k} {self._style('→', color='cyan')}{v}")
543
+ print()
544
+
545
+ # ── Horizontal Rule ─────────────────────────────────────────
546
+
547
+ def hr(self, label: Optional[str] = None) -> None:
548
+ """Print a horizontal rule, optionally with a label."""
549
+ if label:
550
+ left = "─" * 4
551
+ right = "─" * max(0, self._width - len(label) - 12)
552
+ print(f"\n {self._style(left, color='bright_black')} {self._style(label, 'bold', color='cyan')} {self._style(right, color='bright_black')}")
553
+ else:
554
+ print(f" {self._style('─' * (self._width - 4), color='bright_black')}")
555
+
556
+ @property
557
+ def icons(self):
558
+ """Access icon constants."""
559
+ return _ICONS
@@ -0,0 +1,71 @@
1
+ """
2
+ shinyshell ASCII Banner renderer.
3
+ ==================================
4
+ Converts text to large ASCII art banners.
5
+ Uses FIGlet-style rendering with built-in font.
6
+ """
7
+
8
+ __all__ = ["render", "fonts"]
9
+
10
+ _FONTS = {
11
+ "default": {
12
+ "A": [" █████ ", " ██ ██ ", "█████████", "██ ██", "██ ██"],
13
+ "B": ["███████ ", "██ ██ ", "███████ ", "██ ██ ", "███████ "],
14
+ "C": [" ██████ ", "██ ██ ", "██ ", "██ ██ ", " ██████ "],
15
+ "D": ["███████ ", "██ ██ ", "██ ██ ", "██ ██ ", "███████ "],
16
+ "E": ["████████", "██ ", "██████ ", "██ ", "████████"],
17
+ "F": ["████████", "██ ", "██████ ", "██ ", "██ "],
18
+ "G": [" ██████ ", "██ ", "██ ███ ", "██ ██ ", " ██████ "],
19
+ "H": ["██ ██", "██ ██", "████████", "██ ██", "██ ██"],
20
+ "I": ["████", " ██ ", " ██ ", " ██ ", "████"],
21
+ "J": [" █████", " ██ ", " ██ ", "██ ██ ", " █████ "],
22
+ "K": ["██ ███", "██ ██ ", "█████ ", "██ ██ ", "██ ███"],
23
+ "L": ["██ ", "██ ", "██ ", "██ ", "█████████"],
24
+ "M": ["███ ███", "████ ████", "██ ████ ██", "██ ██ ██", "██ ██"],
25
+ "N": ["███ ██", "████ ██", "██ ██ ██", "██ ██ ██", "██ ████"],
26
+ "O": [" █████ ", " ██ ██ ", "██ ██", " ██ ██ ", " █████ "],
27
+ "P": ["███████ ", "██ ██ ", "███████ ", "██ ", "██ "],
28
+ "Q": [" █████ ", " ██ ██ ", "██ █ ██", " ██ ██ ", " ████ █"],
29
+ "R": ["███████ ", "██ ██ ", "███████ ", "██ ██ ", "██ ███"],
30
+ "S": [" ███████ ", "██ ", " █████ ", " ██", "███████ "],
31
+ "T": ["██████████", " ██ ", " ██ ", " ██ ", " ██ "],
32
+ "U": ["██ ██", "██ ██", "██ ██", "██ ██", " ███████ "],
33
+ "V": ["██ ██", " ██ ██ ", " ██ ██ ", " ███ ", " █ "],
34
+ "W": ["██ ██", "██ ██ ██", "██ ████ ██", " ███ ███ ", " ██ ██ "],
35
+ "X": ["██ ██", " ██ ██ ", " ███ ", " ██ ██ ", "██ ██"],
36
+ "Y": ["██ ██", " ██ ██ ", " █████ ", " ███ ", " ███ "],
37
+ "Z": ["████████", " ███ ", " ███ ", "███ ", "████████"],
38
+ "0": [" █████ ", " ██ ███ ", "██ ██ ██ ", "███ ██ ", " █████ "],
39
+ "1": [" ████", " ██", " ██", " ██", "██████"],
40
+ "2": ["███████ ", " ██", "███████ ", "██ ", "████████"],
41
+ "3": ["██████ ", " ██", " █████ ", " ██", "██████ "],
42
+ "4": ["██ ██", "██ ██", "████████", " ██", " ██"],
43
+ "5": ["███████", "██ ", "███████", " ██", "███████"],
44
+ "6": [" ██████ ", "██ ", "███████ ", "██ ██", " ██████ "],
45
+ "7": ["████████", " ██ ", " ██ ", " ██ ", " ██ "],
46
+ "8": [" █████ ", "██ ██ ", " █████ ", "██ ██ ", " █████ "],
47
+ "9": [" █████ ", "██ ██ ", " ██████ ", " ██ ", " █████ "],
48
+ " ": [" ", " ", " ", " ", " "],
49
+ ".": [" ", " ", " ", " ", " ██ "],
50
+ ",": [" ", " ", " ", " ██ ", "██ "],
51
+ "!": [" ██ ", " ██ ", " ██ ", " ", " ██ "],
52
+ "?": ["██████ ", " ██", " ████ ", " ", " ██ "],
53
+ "-": [" ", " ", "███████", " ", " "],
54
+ "_": [" ", " ", " ", " ", "████████"],
55
+ ":": [" ", " ██ ", " ", " ██ ", " "],
56
+ ";": [" ", " ██ ", " ", " ██ ", "██ "],
57
+ }
58
+ }
59
+
60
+
61
+ def render(text: str, font: str = "default") -> str:
62
+ """Render text as ASCII art banner."""
63
+ chars = _FONTS.get(font, _FONTS["default"])
64
+ lines = [""] * 5
65
+ for ch in text.upper():
66
+ glyph = chars.get(ch, chars.get(" ", [" "] * 5))
67
+ for i, row in enumerate(glyph):
68
+ lines[i] += row + " "
69
+ return "\n".join(lines)
70
+
71
+ fonts = list(_FONTS.keys())
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: shinyshell
3
+ Version: 0.1.0
4
+ Summary: Beautiful terminal output for Python. Zero dependencies.
5
+ Home-page: https://github.com/adnanahamed66772ndpc/shinyshell
6
+ Author: Adnan Ahamed Himal
7
+ Keywords: terminal,shell,pretty,beautiful,output,cli,print,colors
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Terminals
19
+ Classifier: Environment :: Console
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Dynamic: author
24
+ Dynamic: classifier
25
+ Dynamic: description
26
+ Dynamic: description-content-type
27
+ Dynamic: home-page
28
+ Dynamic: keywords
29
+ Dynamic: license-file
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # ✨ shinyshell
34
+
35
+ **Beautiful terminal output for Python. Zero dependencies. One import.**
36
+
37
+ [![PyPI version](https://img.shields.io/pypi/v/shinyshell.svg)](https://pypi.org/project/shinyshell/)
38
+ [![Python](https://img.shields.io/pypi/pyversions/shinyshell.svg)](https://pypi.org/project/shinyshell/)
39
+ [![License](https://img.shields.io/github/license/adnanahamed66772ndpc/shinyshell)](LICENSE)
40
+
41
+ ```python
42
+ from shinyshell import Shell
43
+
44
+ sh = Shell()
45
+
46
+ sh.success("Deployed to production!")
47
+ sh.table(users, title="Team Members")
48
+ sh.progress("Installing deps...")
49
+ sh.header("My CLI App")
50
+ sh.code("print('hello')")
51
+ sh.countdown(5, "Launching")
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 📸 Demo
57
+
58
+ ```python
59
+ from shinyshell import Shell
60
+ sh = Shell()
61
+
62
+ # Headers & banners
63
+ sh.header("DEPLOYMENT REPORT")
64
+ sh.banner("SHINY SHELL")
65
+
66
+ # Success / Warning / Error / Info
67
+ sh.success("Database migrated successfully")
68
+ sh.warning("Rate limit approaching — 93% used")
69
+ sh.error("Connection to API failed after 3 retries")
70
+ sh.info("Server running on http://localhost:8000")
71
+
72
+ # Beautiful tables
73
+ sh.table([
74
+ {"Name": "Alice Chen", "Role": "Backend Dev", "Status": "✅ Active"},
75
+ {"Name": "Bob Kumar", "Role": "Frontend", "Status": "✅ Active"},
76
+ {"Name": "Charlie D.", "Role": "Design Lead", "Status": "âŗ On Leave"},
77
+ ], title="Team Status")
78
+
79
+ # Progress with spinner
80
+ sh.spinner("Compiling assets...")
81
+ update = sh.progress("Uploading")
82
+ for i in range(100):
83
+ update(i + 1, 100)
84
+
85
+ # Countdown
86
+ sh.countdown(5, "Deploying to production")
87
+
88
+ # Boxed content
89
+ sh.box("API Key: sk-****abcd\nEndpoint: /v1/chat\nModel: gpt-4", title="Configuration")
90
+
91
+ # Directory tree
92
+ sh.tree("./src", max_depth=2)
93
+
94
+ # Code blocks
95
+ sh.code("""
96
+ def fibonacci(n):
97
+ if n <= 1:
98
+ return n
99
+ return fibonacci(n-1) + fibonacci(n-2)
100
+ """)
101
+
102
+ # Metrics dashboard
103
+ sh.metrics({
104
+ "Users": 12483,
105
+ "Active Now": 342,
106
+ "Uptime": "✅ 99.9%",
107
+ "Error Rate": "0.02%",
108
+ "Avg Response": "23ms",
109
+ })
110
+
111
+ # Colored diff
112
+ sh.diff("hello world", "hello beautiful world")
113
+
114
+ # Interactive
115
+ if sh.confirm("Deploy to production?"):
116
+ sh.success("Ship it! 🚀")
117
+
118
+ # Horizontal rule
119
+ sh.hr("Section 2")
120
+ ```
121
+
122
+ ---
123
+
124
+ ## đŸ“Ļ Install
125
+
126
+ ```bash
127
+ pip install shinyshell
128
+ ```
129
+
130
+ No dependencies. Uses only Python standard library. Works on **Linux**, **macOS**, and **Windows** (PowerShell, CMD, Windows Terminal).
131
+
132
+ ---
133
+
134
+ ## đŸŽ¯ Features
135
+
136
+ | Feature | Description |
137
+ |---------|-------------|
138
+ | `success()` | ✅ Green success messages |
139
+ | `warning()` | âš ī¸ Yellow warnings |
140
+ | `error()` | ❌ Red errors |
141
+ | `info()` | â„šī¸ Cyan info |
142
+ | `header()` | Styled section headers |
143
+ | `banner()` | ASCII art banners |
144
+ | `table()` | Beautiful data tables |
145
+ | `box()` | Content in styled boxes |
146
+ | `code()` | Syntax-highlighted code blocks |
147
+ | `diff()` | Colored git diff |
148
+ | `tree()` | Directory trees |
149
+ | `metrics()` | Key-value dashboards |
150
+ | `spinner()` | Animated loading spinner |
151
+ | `progress()` | Progress bars |
152
+ | `countdown()` | Animated countdown |
153
+ | `confirm()` | Interactive y/n prompts |
154
+ | `choice()` | Interactive option picker |
155
+ | `hr()` | Horizontal rules with labels |
156
+
157
+ ---
158
+
159
+ ## 🤔 Why?
160
+
161
+ Every Python script prints to the terminal. Most output looks boring. **shinyshell** makes it beautiful — with zero dependencies.
162
+
163
+ Before:
164
+ ```
165
+ Task completed
166
+ Users: 1042
167
+ ERROR: Connection failed
168
+ ```
169
+
170
+ After:
171
+ ```
172
+ ✨ Task completed
173
+ 📊 Users: 1,042
174
+ đŸ’Ĩ Connection failed
175
+ ```
176
+
177
+ ---
178
+
179
+ ## 🔧 Advanced
180
+
181
+ ```python
182
+ # Custom width
183
+ sh = Shell(width=80)
184
+
185
+ # Disable color
186
+ sh = Shell(color=False)
187
+
188
+ # Access icons
189
+ print(sh.icons["rocket"]) # 🚀
190
+
191
+ # Chaining
192
+ sh.success("Step 1 done").progress("Step 2...")
193
+ ```
194
+
195
+ ---
196
+
197
+ ## 📄 License
198
+
199
+ MIT — © 2026 Adnan Ahamed Himal
200
+
201
+ ---
202
+
203
+ ## ⭐ Support
204
+
205
+ Found this useful? **Star this repo** and share it with your team!
206
+
207
+ [âŦ† Back to top](#-shinyshell)
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ shinyshell/__init__.py
5
+ shinyshell/banner.py
6
+ shinyshell.egg-info/PKG-INFO
7
+ shinyshell.egg-info/SOURCES.txt
8
+ shinyshell.egg-info/dependency_links.txt
9
+ shinyshell.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ shinyshell