rich-gradient-cli 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,107 @@
1
+ Metadata-Version: 2.3
2
+ Name: rich-gradient-cli
3
+ Version: 0.1.0
4
+ Summary: CLI for printing text in gradient color using the rich-gradient library.
5
+ Author: Maxwell Ludden
6
+ Author-email: Maxwell Ludden <maxludden@me.com>
7
+ Requires-Dist: typer>=0.21.1
8
+ Requires-Dist: rich>=14.3.2
9
+ Requires-Dist: rich-color-ext>=0.1.9
10
+ Requires-Dist: rich-gradient>=0.3.9
11
+ Requires-Dist: ruff>=0.15.0
12
+ Requires-Dist: mkdocs>=1.6.0 ; extra == 'docs'
13
+ Requires-Dist: mkdocs-material>=9.5.0 ; extra == 'docs'
14
+ Requires-Dist: mkdocstrings>=0.25.0 ; extra == 'docs'
15
+ Requires-Dist: mkdocstrings-python>=1.10.0 ; extra == 'docs'
16
+ Requires-Python: >=3.14
17
+ Provides-Extra: docs
18
+ Description-Content-Type: text/markdown
19
+
20
+ # rich-gradient-cli
21
+
22
+ `rich-gradient-cli` is a CLI for [`rich-gradient`](https://github.com/maxludden/rich-gradient). The tool allows you to create colorful gradients in your terminal output, making it easier to visualize data or simply add some flair to your command-line applications.
23
+
24
+ ## Installation
25
+
26
+ You can install rich-gradient-cli from PyPI
27
+
28
+ ### uv (recommended)
29
+
30
+ ```shell
31
+ uv add rich-gradient-cli
32
+ ```
33
+
34
+ ### pip
35
+
36
+ ```shell
37
+ pip install rich-gradient-cli
38
+ ```
39
+
40
+ ## Usage
41
+
42
+
43
+ ### Print
44
+
45
+ To use the `rich-gradient-cli`, you can run the following command in your terminal:
46
+
47
+ ```bash
48
+ gradient print --colors 'red,#ff9900,#ffff00' "This is gradient text that starts red, transitions through orange, and ends in yellow\!"
49
+ ```
50
+
51
+ ![Gradient Text Example](print-example.svg)
52
+
53
+
54
+ ### Rule
55
+
56
+ You can also create gradient rules using the `rule` command:
57
+
58
+ ```bash
59
+ gradient rule --colors 'blue,#00ff00,cyan' "Blue to Green to Cyan Rule"
60
+ ```
61
+
62
+ ![Gradient Rule Example](rule-example.svg)
63
+
64
+ ### Panel
65
+
66
+ To create a gradient panel, you can use the `panel` command:
67
+
68
+ ```bash
69
+ gradient panel --colors "red,#ff9999" -t "Error" --title-style "bold #ffffff" --title-align left 'This is an error message with a red to pink gradient background.'
70
+ ```
71
+
72
+ ![Gradient Panel Example](panel-example.svg)
73
+
74
+ ### Markdown
75
+
76
+ You can also render Markdown with gradients:
77
+
78
+ ```bash
79
+ echo "# Hello\n\n- This is **bold**.\n- This is *italic*." | gradient markdown -
80
+ ```
81
+
82
+ ![Gradient Markdown Example](markdown-example.svg)
83
+
84
+ ## Docs
85
+
86
+ Build and preview the documentation locally with MkDocs:
87
+
88
+ ```bash
89
+ uv pip install -e ".[docs]"
90
+ mkdocs serve
91
+ ```
92
+
93
+
94
+
95
+ <div align="center">
96
+ <a href="https://github.com/maxludden/maxludden" style="text-decoration:none; color:inherit;">
97
+ <p>Designed by Max Ludden</p>
98
+ </a>
99
+ <br />
100
+ <a href="https://github.com/maxludden/maxludden" style="text-decoration:none; color:inherit;">
101
+ <img
102
+ src="https://github.com/maxludden/maxludden/blob/f3913ecf11f90c97435e2b55d2e43dadd51c1fe4/Images/MaxLogo-animated.svg"
103
+ alt="Max Ludden's Logo"
104
+ width="20%"
105
+ />
106
+ </a>
107
+ </div>
@@ -0,0 +1,88 @@
1
+ # rich-gradient-cli
2
+
3
+ `rich-gradient-cli` is a CLI for [`rich-gradient`](https://github.com/maxludden/rich-gradient). The tool allows you to create colorful gradients in your terminal output, making it easier to visualize data or simply add some flair to your command-line applications.
4
+
5
+ ## Installation
6
+
7
+ You can install rich-gradient-cli from PyPI
8
+
9
+ ### uv (recommended)
10
+
11
+ ```shell
12
+ uv add rich-gradient-cli
13
+ ```
14
+
15
+ ### pip
16
+
17
+ ```shell
18
+ pip install rich-gradient-cli
19
+ ```
20
+
21
+ ## Usage
22
+
23
+
24
+ ### Print
25
+
26
+ To use the `rich-gradient-cli`, you can run the following command in your terminal:
27
+
28
+ ```bash
29
+ gradient print --colors 'red,#ff9900,#ffff00' "This is gradient text that starts red, transitions through orange, and ends in yellow\!"
30
+ ```
31
+
32
+ ![Gradient Text Example](print-example.svg)
33
+
34
+
35
+ ### Rule
36
+
37
+ You can also create gradient rules using the `rule` command:
38
+
39
+ ```bash
40
+ gradient rule --colors 'blue,#00ff00,cyan' "Blue to Green to Cyan Rule"
41
+ ```
42
+
43
+ ![Gradient Rule Example](rule-example.svg)
44
+
45
+ ### Panel
46
+
47
+ To create a gradient panel, you can use the `panel` command:
48
+
49
+ ```bash
50
+ gradient panel --colors "red,#ff9999" -t "Error" --title-style "bold #ffffff" --title-align left 'This is an error message with a red to pink gradient background.'
51
+ ```
52
+
53
+ ![Gradient Panel Example](panel-example.svg)
54
+
55
+ ### Markdown
56
+
57
+ You can also render Markdown with gradients:
58
+
59
+ ```bash
60
+ echo "# Hello\n\n- This is **bold**.\n- This is *italic*." | gradient markdown -
61
+ ```
62
+
63
+ ![Gradient Markdown Example](markdown-example.svg)
64
+
65
+ ## Docs
66
+
67
+ Build and preview the documentation locally with MkDocs:
68
+
69
+ ```bash
70
+ uv pip install -e ".[docs]"
71
+ mkdocs serve
72
+ ```
73
+
74
+
75
+
76
+ <div align="center">
77
+ <a href="https://github.com/maxludden/maxludden" style="text-decoration:none; color:inherit;">
78
+ <p>Designed by Max Ludden</p>
79
+ </a>
80
+ <br />
81
+ <a href="https://github.com/maxludden/maxludden" style="text-decoration:none; color:inherit;">
82
+ <img
83
+ src="https://github.com/maxludden/maxludden/blob/f3913ecf11f90c97435e2b55d2e43dadd51c1fe4/Images/MaxLogo-animated.svg"
84
+ alt="Max Ludden's Logo"
85
+ width="20%"
86
+ />
87
+ </a>
88
+ </div>
@@ -0,0 +1,31 @@
1
+ [project]
2
+ name = "rich-gradient-cli"
3
+ version = "0.1.0"
4
+ description = "CLI for printing text in gradient color using the rich-gradient library."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Maxwell Ludden", email = "maxludden@me.com" }
8
+ ]
9
+ requires-python = ">=3.14"
10
+ dependencies = [
11
+ "typer>=0.21.1",
12
+ "rich>=14.3.2",
13
+ "rich-color-ext>=0.1.9",
14
+ "rich-gradient>=0.3.9",
15
+ "ruff>=0.15.0",
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ docs = [
20
+ "mkdocs>=1.6.0",
21
+ "mkdocs-material>=9.5.0",
22
+ "mkdocstrings>=0.25.0",
23
+ "mkdocstrings-python>=1.10.0",
24
+ ]
25
+
26
+ [project.scripts]
27
+ gradient = "rich_gradient_cli:app"
28
+
29
+ [build-system]
30
+ requires = ["uv_build>=0.10.0,<0.11.0"]
31
+ build-backend = "uv_build"
@@ -0,0 +1,96 @@
1
+ """rich-gradient CLI entry point."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from typing import Any, Sequence
7
+
8
+ import click
9
+ import typer
10
+
11
+ from .common import VERSION
12
+ from .help import RichTyperCommand, RichTyperGroup
13
+ from .markdown_command import markdown_command
14
+ from .panel_command import panel_command
15
+ from .rule_command import rule_command
16
+ from .text_command import print_command
17
+
18
+
19
+ class DefaultTyperGroup(RichTyperGroup):
20
+ """Route unknown commands/options to the default command."""
21
+
22
+ def __init__(
23
+ self,
24
+ name: str | None = None,
25
+ commands: dict[str, click.Command] | Sequence[click.Command] | None = None,
26
+ invoke_without_command: bool = False,
27
+ no_args_is_help: bool | None = None,
28
+ subcommand_metavar: str | None = None,
29
+ chain: bool = False,
30
+ result_callback: Any | None = None,
31
+ *,
32
+ default_cmd_name: str = "print",
33
+ **kwargs: Any,
34
+ ) -> None:
35
+ super().__init__(
36
+ name=name,
37
+ commands=commands,
38
+ invoke_without_command=invoke_without_command,
39
+ no_args_is_help=no_args_is_help,
40
+ subcommand_metavar=subcommand_metavar,
41
+ chain=chain,
42
+ result_callback=result_callback,
43
+ **kwargs,
44
+ )
45
+ self.default_cmd_name = default_cmd_name
46
+
47
+ def resolve_command(
48
+ self, ctx: click.Context, args: list[str]
49
+ ) -> tuple[str | None, click.Command | None, list[str]]:
50
+ if args:
51
+ cmd = self.get_command(ctx, args[0])
52
+ if cmd is None or args[0].startswith("-"):
53
+ args.insert(0, self.default_cmd_name)
54
+ return super().resolve_command(ctx, args)
55
+
56
+
57
+ app = typer.Typer(
58
+ cls=DefaultTyperGroup,
59
+ invoke_without_command=True,
60
+ add_completion=False,
61
+ help="Create gradient-rich text, panels, and markdown.",
62
+ rich_markup_mode="rich",
63
+ context_settings={"help_option_names": ["-h", "--help"], "color": True},
64
+ )
65
+
66
+
67
+ @app.callback()
68
+ def main(
69
+ ctx: typer.Context,
70
+ version: bool = typer.Option(
71
+ False,
72
+ "--version",
73
+ help="Show the version and exit.",
74
+ is_eager=True,
75
+ ),
76
+ ) -> None:
77
+ """CLI entry callback for version handling and default routing."""
78
+ if version:
79
+ typer.echo(f"gradient version {VERSION}")
80
+ raise typer.Exit()
81
+ if ctx.invoked_subcommand is None:
82
+ if ctx.args or not sys.stdin.isatty():
83
+ print_command(text=list(ctx.args) if ctx.args else None)
84
+ raise typer.Exit()
85
+ typer.echo(ctx.get_help())
86
+
87
+
88
+ app.command("print", cls=RichTyperCommand)(print_command)
89
+ app.command("panel", cls=RichTyperCommand)(panel_command)
90
+ app.command("rule", cls=RichTyperCommand)(rule_command)
91
+ app.command("markdown", cls=RichTyperCommand)(markdown_command)
92
+
93
+
94
+ cli = app
95
+
96
+ __all__ = ["app", "cli"]
@@ -0,0 +1,6 @@
1
+ """Executable entry point for python -m rich_gradient_cli."""
2
+
3
+ from . import app
4
+
5
+ if __name__ == "__main__":
6
+ app()
@@ -0,0 +1,87 @@
1
+ """Shared helpers and configuration for the rich-gradient CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import List, Optional
7
+
8
+ from rich.console import Console, RenderableType
9
+ from rich.padding import Padding
10
+ from rich.style import Style
11
+ from rich_gradient.theme import GRADIENT_TERMINAL_THEME
12
+
13
+ console = Console()
14
+ VERSION = "0.3.10"
15
+
16
+ HEADER_TEXT = (
17
+ "[#ff5500]r[/][#ff6f00]i[/][#ff8300]c[/]"
18
+ "[#ff9500]h[/][#ffa600]-[/][#ffb600]g[/][#ffc500]r[/]"
19
+ "[#ffd400]a[/][#ffe500]d[/][#fff400]i[/][#f9ff00]e[/]"
20
+ "[#e2ff00]n[/][#c8ff00]t[/][#a9ff00] [/][bold #fff]CLI[/]"
21
+ )
22
+
23
+ FOOTER_TEXT = (
24
+ "[#ff5500]\U0001f308[/][#ff7400] [/][#ff8c00]h[/]"
25
+ "[#ffa100]t[/][#ffb500]t[/][#ffc700]p[/][#ffdc00]s[/]"
26
+ "[#ffef00]:[/][#fbff00]/[/][#dfff00]/[/][#bdff00]m[/]"
27
+ "[#9aff00]a[/][#73ff00]x[/][#2aff00]l[/][#00ff5c]u[/]"
28
+ "[#00ff83]d[/][#00ffa6]d[/][#00ffd1]e[/][#00fff3]n[/]"
29
+ "[#00f4ff].[/][#00e1ff]g[/][#00ccff]i[/][#00baff]t[/]"
30
+ "[#00a6ff]h[/][#2192ff]u[/][#3b81ff]b[/][#4c6cff].[/]"
31
+ "[#6061ff]i[/][#725bff]o[/][#8253ff]/[/][#9648ff]r[/]"
32
+ "[#a83bff]i[/][#c22eff]c[/][#e122ff]h[/][#fb0cff]-[/]"
33
+ "[#ff00e6]g[/][#ff00c6]r[/][#ff00a4]a[/][#ff0089]d[/]"
34
+ "[#ff0066]i[/][#ff004b]e[/][#ff0036]n[/][#ff0000]t[/]"
35
+ )
36
+
37
+
38
+ USAGE_PREFIX = "[bold #00ff00]Usage:[/]"
39
+ USAGE_PROG_STYLE = "#af00ff"
40
+ USAGE_CMD_STYLE = "#0099ff"
41
+ USAGE_BRACKET_STYLE = "#ffffff"
42
+
43
+
44
+ def parse_colors(colors: Optional[str]) -> Optional[List[str]]:
45
+ """Parse comma-separated color tokens into a list."""
46
+ if colors is None:
47
+ return None
48
+ return [c.strip() for c in colors.split(",") if c.strip()]
49
+
50
+
51
+ def parse_style(style: Optional[str]) -> Style:
52
+ """Parse a Rich style string or return a null style."""
53
+ if style is None:
54
+ return Style.null()
55
+ return Style.parse(style)
56
+
57
+
58
+ def export_svg(
59
+ renderable: RenderableType, svg_path: str, *, end: str = "\n", no_wrap: bool = False
60
+ ) -> None:
61
+ """Render a Rich renderable to an SVG file."""
62
+ svg_console = Console(record=True, force_terminal=True, color_system="truecolor")
63
+ padded = Padding(renderable, (1, 4))
64
+ svg_console.print(padded, end=end, no_wrap=no_wrap)
65
+
66
+ Path(svg_path).write_text(
67
+ svg_console.export_svg(
68
+ title="rich-gradient",
69
+ theme=GRADIENT_TERMINAL_THEME,
70
+ ),
71
+ encoding="utf-8",
72
+ )
73
+
74
+
75
+ __all__ = [
76
+ "VERSION",
77
+ "console",
78
+ "parse_colors",
79
+ "parse_style",
80
+ "HEADER_TEXT",
81
+ "FOOTER_TEXT",
82
+ "USAGE_PREFIX",
83
+ "USAGE_PROG_STYLE",
84
+ "USAGE_CMD_STYLE",
85
+ "USAGE_BRACKET_STYLE",
86
+ "export_svg",
87
+ ]
@@ -0,0 +1,231 @@
1
+ """Custom Rich help rendering for Typer commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import io
6
+ import re
7
+ from dataclasses import dataclass
8
+ from typing import Iterable, List, Optional, Sequence, Tuple
9
+
10
+ import click
11
+ from rich import box
12
+ from rich.console import Console
13
+ from rich.panel import Panel
14
+ from rich.table import Table
15
+ from rich.text import Text
16
+ from typer.core import TyperCommand, TyperGroup
17
+
18
+ from .common import (
19
+ FOOTER_TEXT,
20
+ HEADER_TEXT,
21
+ USAGE_BRACKET_STYLE,
22
+ USAGE_CMD_STYLE,
23
+ USAGE_PREFIX,
24
+ USAGE_PROG_STYLE,
25
+ )
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class HelpStyles:
30
+ border: str = "#2a4fff"
31
+ option_name: str = "cyan"
32
+ metavar: str = "bold #99ff00"
33
+ help_text: str = "white"
34
+ dim: str = "dim"
35
+
36
+
37
+ _STYLES = HelpStyles()
38
+
39
+
40
+ def _usage_markup(command_path: str, pieces: Sequence[str]) -> str:
41
+ parts = command_path.split()
42
+ prog = parts[0] if parts else ""
43
+ subcommands = parts[1:]
44
+
45
+ def color_token(token: str) -> str:
46
+ if token.startswith("[") and token.endswith("]"):
47
+ inner = token[1:-1]
48
+ return (
49
+ f"[{USAGE_BRACKET_STYLE}][[/][{USAGE_CMD_STYLE}]"
50
+ f"{inner}[/][{USAGE_BRACKET_STYLE}]][/]"
51
+ )
52
+ if token.startswith("[") and token.endswith("]..."):
53
+ inner = token[1:-4]
54
+ return (
55
+ f"[{USAGE_BRACKET_STYLE}][[/][{USAGE_CMD_STYLE}]"
56
+ f"{inner}[/][{USAGE_BRACKET_STYLE}]][/]"
57
+ f"[{USAGE_CMD_STYLE}]...[/]"
58
+ )
59
+ if token.isupper() or re.match(r"^[A-Z_]+(\.\.\.)?$", token):
60
+ return f"[{USAGE_CMD_STYLE}]{token}[/]"
61
+ return token
62
+
63
+ colored_prog = f"[{USAGE_PROG_STYLE}]{prog}[/]" if prog else ""
64
+ colored_subcommands = " ".join(
65
+ f"[{USAGE_CMD_STYLE}]{cmd}[/]" for cmd in subcommands
66
+ )
67
+ args = " ".join(color_token(piece) for piece in pieces)
68
+ if args:
69
+ args = f" {args}"
70
+ usage = " ".join(p for p in [colored_prog, colored_subcommands] if p)
71
+ return f"{USAGE_PREFIX} {usage}{args}".strip()
72
+
73
+
74
+ def _split_option_name(name: str) -> Tuple[str, str]:
75
+ parts = name.split()
76
+ if not parts:
77
+ return name, ""
78
+ last = parts[-1]
79
+ if not last.startswith("-") and last.isupper():
80
+ return " ".join(parts[:-1]), last
81
+ return name, ""
82
+
83
+
84
+ def _normalize_metavar(metavar: str) -> str:
85
+ value = metavar.strip()
86
+ if value.startswith("[") and value.endswith("]..."):
87
+ value = value[1:-4]
88
+ elif value.startswith("[") and value.endswith("]"):
89
+ value = value[1:-1]
90
+ if value.endswith("..."):
91
+ value = value[:-3]
92
+ return value
93
+
94
+
95
+ def _build_arguments_table(
96
+ args: Iterable[click.Argument], ctx: click.Context
97
+ ) -> Optional[Table]:
98
+ rows: List[Tuple[str, str]] = []
99
+ for arg in args:
100
+ metavar = _normalize_metavar(arg.make_metavar(ctx))
101
+ rows.append((metavar, metavar))
102
+
103
+ if not rows:
104
+ return None
105
+
106
+ table = Table.grid(padding=(0, 2))
107
+ table.add_column(justify="left", style="bold #ffff00", no_wrap=True)
108
+ table.add_column(justify="left", style=_STYLES.metavar)
109
+ for name, meta in rows:
110
+ table.add_row(name, meta)
111
+ return table
112
+
113
+
114
+ def _build_options_table(
115
+ options: Iterable[click.Option], ctx: click.Context
116
+ ) -> Optional[Table]:
117
+ rows: List[Tuple[str, str, Text]] = []
118
+ for opt in options:
119
+ record = opt.get_help_record(ctx)
120
+ if record is None:
121
+ continue
122
+ name, help_text = record
123
+ opt_names, metavar = _split_option_name(name)
124
+ help_rich = Text.from_markup(help_text, style=_STYLES.help_text)
125
+ rows.append((opt_names, metavar, help_rich))
126
+
127
+ if not rows:
128
+ return None
129
+
130
+ table = Table.grid(padding=(0, 2))
131
+ table.add_column(justify="left", style="bold #ffff00", no_wrap=True)
132
+ table.add_column(justify="left", style=_STYLES.metavar, no_wrap=True)
133
+ table.add_column(justify="left", style=_STYLES.help_text)
134
+ for opt_names, metavar, help_rich in rows:
135
+ table.add_row(opt_names, metavar, help_rich)
136
+ return table
137
+
138
+
139
+ def _build_commands_table(group: click.Group) -> Optional[Table]:
140
+ if not group.commands:
141
+ return None
142
+
143
+ table = Table.grid(padding=(0, 2))
144
+ table.add_column(justify="left", style="bold #ffff00", no_wrap=True)
145
+ table.add_column(justify="left", style=_STYLES.help_text)
146
+ for name, cmd in group.commands.items():
147
+ help_text = cmd.get_short_help_str() if cmd.help else ""
148
+ table.add_row(name, help_text)
149
+ return table
150
+
151
+
152
+ def render_help(command: click.Command, ctx: click.Context) -> str:
153
+ """Render help text for a Click command using Rich for formatting."""
154
+ console = Console(
155
+ record=True,
156
+ force_terminal=True,
157
+ color_system="truecolor",
158
+ file=io.StringIO(),
159
+ )
160
+
161
+ console.print(Text.from_markup(HEADER_TEXT))
162
+ console.print()
163
+
164
+ usage_line = _usage_markup(ctx.command_path, command.collect_usage_pieces(ctx))
165
+ console.print(Text.from_markup(usage_line))
166
+
167
+ if command.help:
168
+ console.print()
169
+ console.print(Text.from_markup(command.help))
170
+
171
+ args = [p for p in command.params if isinstance(p, click.Argument)]
172
+ options = [p for p in command.params if isinstance(p, click.Option)]
173
+
174
+ args_table = _build_arguments_table(args, ctx)
175
+ if args_table is not None:
176
+ console.print()
177
+ console.print(
178
+ Panel(
179
+ args_table,
180
+ title="[b #ffffff]Arguments[/]",
181
+ title_align="left",
182
+ border_style=_STYLES.border,
183
+ box=box.SQUARE,
184
+ )
185
+ )
186
+
187
+ options_table = _build_options_table(options, ctx)
188
+ if options_table is not None:
189
+ console.print()
190
+ console.print(
191
+ Panel(
192
+ options_table,
193
+ title="[b #ffffff]Options[/]",
194
+ title_align="left",
195
+ border_style=_STYLES.border,
196
+ box=box.SQUARE,
197
+ )
198
+ )
199
+
200
+ if isinstance(command, click.Group):
201
+ commands_table = _build_commands_table(command)
202
+ if commands_table is not None:
203
+ console.print()
204
+ console.print(
205
+ Panel(
206
+ commands_table,
207
+ title="[b #ffffff]Commands[/]",
208
+ title_align="left",
209
+ border_style=_STYLES.border,
210
+ box=box.SQUARE,
211
+ )
212
+ )
213
+
214
+ console.print()
215
+ console.print(Text.from_markup(FOOTER_TEXT))
216
+
217
+ return console.export_text(styles=True)
218
+
219
+
220
+ class RichTyperCommand(TyperCommand):
221
+ """Custom TyperCommand that uses Rich for help rendering."""
222
+
223
+ def get_help(self, ctx: click.Context) -> str: # type: ignore[override]
224
+ return render_help(self, ctx).rstrip("\n")
225
+
226
+
227
+ class RichTyperGroup(TyperGroup):
228
+ """Custom TyperGroup that uses Rich for help rendering."""
229
+
230
+ def get_help(self, ctx: click.Context) -> str: # type: ignore[override]
231
+ return render_help(self, ctx).rstrip("\n")
@@ -0,0 +1,168 @@
1
+ """Markdown command wiring for the CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Literal, Optional, cast
6
+
7
+ import typer
8
+ from rich.align import AlignMethod, VerticalAlignMethod
9
+
10
+ from rich_gradient.animated_markdown import AnimatedMarkdown
11
+ from rich_gradient.markdown import Markdown
12
+
13
+ from .common import console, export_svg, parse_colors, parse_style
14
+
15
+
16
+ def markdown_command(
17
+ markdown: str = typer.Argument(..., metavar="MARKDOWN"),
18
+ colors: Optional[str] = typer.Option(
19
+ None,
20
+ "-c",
21
+ "--colors",
22
+ metavar="COLORS",
23
+ help=(
24
+ "Comma-separated list of colors for the gradient. [dim](e.g., "
25
+ "`[/][red]red[/][dim], [/][#ff9900]#ff9900[/][dim], [/][yellow]yellow[/][dim]`). "
26
+ "If no colors are provided, the color stops are automatically generated."
27
+ ),
28
+ ),
29
+ bgcolors: Optional[str] = typer.Option(
30
+ None,
31
+ "--bgcolors",
32
+ metavar="BGCOLORS",
33
+ help=(
34
+ "Comma-separated list of background colors for the gradient. [dim](e.g., "
35
+ "`[/][red]red[/][dim], [/][#ff9900]#ff9900[/][dim], [/][#ff0]#ff0[/][dim]`). "
36
+ "Defaults to [/][bold #fff]transparent[/][dim].[/dim]"
37
+ ),
38
+ ),
39
+ rainbow: bool = typer.Option(
40
+ False,
41
+ "-r",
42
+ "--rainbow",
43
+ help="[#ff0000]U[/][#ff3b00]s[/][#ff5100]e[/][#ff7400] [/]\
44
+ [#ff9000]r[/][#ffa900]a[/][#ffc000]i[/][#ffd700]n[/]\
45
+ [#ffee00]b[/][#f7ff00]o[/][#d3ff00]w[/][#a7ff00] [/] \
46
+ [#7dff00]c[/][#2eff00]o[/][#00ff64]l[/][#00ff8e]o[/][#00ffc0]r[/]\
47
+ [#00ffec]s[/][#00f4ff] [/][#00ddff]f[/][#00c5ff]o[/][#00afff]r[/]\
48
+ [#1596ff] [/][#3b81ff]t[/][#4e67ff]h[/][#675fff]e[/][#7b57ff] [/]\
49
+ [#924bff]g[/][#a73bff]r[/][#c72cff]a[/][#eb1cff]d[/][#ff00f2]i[/]\
50
+ [#ff00ce]e[/][#ff00a4]n[/][#ff0084]t[/][#ff0054].[/]",
51
+ ),
52
+ hues: int = typer.Option(
53
+ 7,
54
+ "--hues",
55
+ metavar="HUES",
56
+ help="The number of hues to use for a random gradient.",
57
+ show_default=True,
58
+ ),
59
+ style: Optional[str] = typer.Option(
60
+ None,
61
+ "--style",
62
+ metavar="STYLE",
63
+ help=(
64
+ "The style to apply to the markdown text. [dim italic]*Only non-color styles "
65
+ "will be applied as the gradient's colors override color styles.[/]"
66
+ ),
67
+ ),
68
+ justify: Literal["left", "center", "right"] = typer.Option(
69
+ "left",
70
+ "-j",
71
+ "--justify",
72
+ metavar="JUSTIFY",
73
+ help="Justification of the markdown text. [lime](left, center, right)[/]",
74
+ show_default=True,
75
+ case_sensitive=False,
76
+ ),
77
+ vertical_justify: Literal["top", "middle", "bottom"] = typer.Option(
78
+ "top",
79
+ "--vertical-justify",
80
+ metavar="VERTICAL_JUSTIFY",
81
+ help="Vertical justification of the markdown text. [lime](top, middle, bottom)[/]",
82
+ show_default=True,
83
+ case_sensitive=False,
84
+ ),
85
+ no_wrap: bool = typer.Option(
86
+ False,
87
+ "--no-wrap",
88
+ help="Disable wrapping of markdown text.",
89
+ show_default=True,
90
+ ),
91
+ end: str = typer.Option(
92
+ "\n",
93
+ "--end",
94
+ metavar="END",
95
+ help=(
96
+ "String appended after the text is printed. [dim]\\[default: '\\n'][/dim]"
97
+ ),
98
+ ),
99
+ animate: bool = typer.Option(
100
+ False,
101
+ "--animate",
102
+ help="Animate the gradient markdown text.",
103
+ ),
104
+ duration: float = typer.Option(
105
+ 5.0,
106
+ "-d",
107
+ "--duration",
108
+ metavar="DURATION",
109
+ help="Duration of the animation in seconds (only used if --animate).",
110
+ ),
111
+ svg: Optional[str] = typer.Option(
112
+ None,
113
+ "--svg",
114
+ metavar="SVG",
115
+ help="Save output as an SVG file.",
116
+ ),
117
+ ) -> None:
118
+ """Render markdown text with gradient colors in a rich console."""
119
+ if markdown == "-":
120
+ markdown = typer.get_text_stream("stdin").read().rstrip("\n")
121
+ if not markdown:
122
+ raise typer.UsageError("Missing markdown argument.")
123
+
124
+ _colors = parse_colors(colors)
125
+ _bgcolors = parse_colors(bgcolors)
126
+ markdown_kwargs: dict[str, Any] = {}
127
+ if style:
128
+ markdown_kwargs["style"] = parse_style(style)
129
+
130
+ justify_value = cast(AlignMethod, justify)
131
+ vertical_value = cast(VerticalAlignMethod, vertical_justify)
132
+
133
+ if animate and svg:
134
+ raise typer.UsageError("--svg is not supported with --animate.")
135
+ if animate and console.is_terminal is True:
136
+ console.clear()
137
+ animated = AnimatedMarkdown(
138
+ markdown,
139
+ colors=_colors,
140
+ rainbow=rainbow,
141
+ hues=hues,
142
+ justify=justify_value,
143
+ vertical_justify=vertical_value,
144
+ bg_colors=_bgcolors,
145
+ markdown_kwargs=markdown_kwargs or None,
146
+ animate=True,
147
+ duration=duration,
148
+ )
149
+ animated.run()
150
+ return
151
+
152
+ md = Markdown(
153
+ markdown,
154
+ colors=_colors,
155
+ rainbow=rainbow,
156
+ hues=hues,
157
+ justify=justify_value,
158
+ vertical_justify=vertical_value,
159
+ bg_colors=_bgcolors,
160
+ markdown_kwargs=markdown_kwargs or None,
161
+ )
162
+ if svg:
163
+ export_svg(md, svg, end=end, no_wrap=no_wrap)
164
+ return
165
+ console.print(md, end=end, no_wrap=no_wrap)
166
+
167
+
168
+ __all__ = ["markdown_command"]
@@ -0,0 +1,290 @@
1
+ """Panel command wiring for the CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from typing import Any, Literal, Optional, Tuple, cast
7
+
8
+ import typer
9
+ from rich.align import Align, AlignMethod, VerticalAlignMethod
10
+
11
+ from rich_gradient.animated_panel import AnimatedPanel
12
+ from rich_gradient.panel import Panel
13
+
14
+ from .common import console, export_svg, parse_colors, parse_style
15
+
16
+
17
+ def panel_command(
18
+ renderable: str = typer.Argument(..., metavar="TEXT"),
19
+ colors: Optional[str] = typer.Option(
20
+ None,
21
+ "-c",
22
+ "--colors",
23
+ metavar="COLORS",
24
+ help=(
25
+ "Comma-separated list of colors for the gradient. [dim](e.g., "
26
+ "`[/][red]red[/][dim], [/][#ff9900]#ff9900[/][dim], [/][yellow]yellow[/][dim]`). "
27
+ "If no colors are provided, the color stops are automatically generated."
28
+ ),
29
+ ),
30
+ bgcolors: Optional[str] = typer.Option(
31
+ None,
32
+ "--bgcolors",
33
+ metavar="BGCOLORS",
34
+ help=(
35
+ "Comma-separated list of background colors for the gradient. [dim](e.g., "
36
+ "`[/][red]red[/][dim],[/][#ff9900]#ff9900[/][dim],[/][#ff0]#ff0[/][dim]`). "
37
+ "Defaults to [/][bold #fff]transparent[/][dim].[/dim]"
38
+ ),
39
+ ),
40
+ rainbow: bool = typer.Option(
41
+ False,
42
+ "-r",
43
+ "--rainbow",
44
+ help="[#ff0000]U[/][#ff3b00]s[/][#ff5100]e[/][#ff7400] [/]\
45
+ [#ff9000]r[/][#ffa900]a[/][#ffc000]i[/][#ffd700]n[/]\
46
+ [#ffee00]b[/][#f7ff00]o[/][#d3ff00]w[/][#a7ff00] [/] \
47
+ [#7dff00]c[/][#2eff00]o[/][#00ff64]l[/][#00ff8e]o[/][#00ffc0]r[/]\
48
+ [#00ffec]s[/][#00f4ff] [/][#00ddff]f[/][#00c5ff]o[/][#00afff]r[/]\
49
+ [#1596ff] [/][#3b81ff]t[/][#4e67ff]h[/][#675fff]e[/][#7b57ff] [/]\
50
+ [#924bff]g[/][#a73bff]r[/][#c72cff]a[/][#eb1cff]d[/][#ff00f2]i[/]\
51
+ [#ff00ce]e[/][#ff00a4]n[/][#ff0084]t[/][#ff0054].[/]",
52
+ ),
53
+ hues: int = typer.Option(
54
+ 5,
55
+ "--hues",
56
+ metavar="HUES",
57
+ help="The number of hues to use for a random gradient.",
58
+ show_default=True,
59
+ ),
60
+ title: Optional[str] = typer.Option(
61
+ None,
62
+ "-t",
63
+ "-title",
64
+ "--title",
65
+ metavar="TITLE",
66
+ help="Title of the panel.",
67
+ ),
68
+ title_style: str = typer.Option(
69
+ "bold",
70
+ "--title-style",
71
+ metavar="TITLE_STYLE",
72
+ help="Style of the panel title text (requires -t/--title).",
73
+ ),
74
+ title_align: Literal["left", "center", "right"] = typer.Option(
75
+ "center",
76
+ "--title-align",
77
+ metavar="TITLE_ALIGN",
78
+ help="Alignment of the panel title. [lime](left, center, right)[/]",
79
+ show_default=True,
80
+ case_sensitive=False,
81
+ ),
82
+ subtitle: Optional[str] = typer.Option(
83
+ None,
84
+ "-s",
85
+ "--subtitle",
86
+ metavar="SUBTITLE",
87
+ help="Subtitle of the panel.",
88
+ ),
89
+ subtitle_style: Optional[str] = typer.Option(
90
+ None,
91
+ "--subtitle-style",
92
+ metavar="SUBTITLE_STYLE",
93
+ help="Style of the panel subtitle text (requires --subtitle).",
94
+ ),
95
+ subtitle_align: Literal["left", "center", "right"] = typer.Option(
96
+ "right",
97
+ "--subtitle-align",
98
+ metavar="SUBTITLE_ALIGN",
99
+ help="Alignment of the panel subtitle. [lime](left, center, right)[/]",
100
+ show_default=True,
101
+ case_sensitive=False,
102
+ ),
103
+ style: Optional[str] = typer.Option(
104
+ None,
105
+ "--style",
106
+ metavar="STYLE",
107
+ help="The style to apply to the panel.",
108
+ ),
109
+ border_style: Optional[str] = typer.Option(
110
+ None,
111
+ "--border-style",
112
+ metavar="BORDER_STYLE",
113
+ help=(
114
+ "The style to apply to the panel border. [dim italic]*Only non-color styles "
115
+ "will be applied as the gradient's colors override color styles.[/]"
116
+ ),
117
+ ),
118
+ padding: Optional[str] = typer.Option(
119
+ "0,1",
120
+ "-p",
121
+ "--padding",
122
+ metavar="PADDING",
123
+ help="Padding inside the panel (1, 2, or 4 comma-separated integers).",
124
+ ),
125
+ vertical_justify: Literal["top", "middle", "bottom"] = typer.Option(
126
+ "top",
127
+ "-V",
128
+ "--vertical-justify",
129
+ metavar="VERTICAL_JUSTIFY",
130
+ help="Vertical justification of the panel inner text. [lime](top, middle, bottom)[/]",
131
+ show_default=True,
132
+ case_sensitive=False,
133
+ ),
134
+ text_justify: Literal["left", "center", "right"] = typer.Option(
135
+ "left",
136
+ "-J",
137
+ "--text-justify",
138
+ metavar="TEXT_JUSTIFY",
139
+ help="Justification of the text inside the panel. [lime](left, center, right)[/]",
140
+ show_default=True,
141
+ case_sensitive=False,
142
+ ),
143
+ justify: Literal["left", "center", "right"] = typer.Option(
144
+ "left",
145
+ "-j",
146
+ "--justify",
147
+ metavar="JUSTIFY",
148
+ help="Justification of the panel itself. [lime](left, center, right)[/]",
149
+ show_default=True,
150
+ case_sensitive=False,
151
+ ),
152
+ expand: bool = typer.Option(
153
+ True,
154
+ "--expand/--no-expand",
155
+ help="Whether to expand the panel to fill the width.",
156
+ ),
157
+ width: Optional[int] = typer.Option(
158
+ None,
159
+ "--width",
160
+ metavar="WIDTH",
161
+ help="Width of the panel (requires --no-expand if set).",
162
+ ),
163
+ height: Optional[int] = typer.Option(
164
+ None,
165
+ "--height",
166
+ metavar="HEIGHT",
167
+ help="Height of the panel; content determines by default.",
168
+ ),
169
+ end: str = typer.Option(
170
+ "\n",
171
+ "--end",
172
+ metavar="END",
173
+ help=(
174
+ "String appended after the text is printed. [dim]\\[default: '\\n'][/dim]"
175
+ ),
176
+ ),
177
+ box: Literal["SQUARE", "ROUNDED", "HEAVY", "DOUBLE", "ASCII"] = typer.Option(
178
+ "ROUNDED",
179
+ "--box",
180
+ metavar="BOX",
181
+ help=(
182
+ "Box style for the panel border. [dim](e.g., SQUARE, ROUNDED, HEAVY, "
183
+ "DOUBLE, ASCII).[/dim]"
184
+ ),
185
+ case_sensitive=False,
186
+ ),
187
+ animate: bool = typer.Option(
188
+ False,
189
+ "-a",
190
+ "--animate",
191
+ help="Animate the panel gradient.",
192
+ ),
193
+ duration: float = typer.Option(
194
+ 5.0,
195
+ "-d",
196
+ "--duration",
197
+ metavar="DURATION",
198
+ help="Duration of the panel animation in seconds (only used if --animate).",
199
+ ),
200
+ svg: Optional[str] = typer.Option(
201
+ None,
202
+ "--svg",
203
+ metavar="SVG",
204
+ help="Save output as an SVG file.",
205
+ ),
206
+ ) -> None:
207
+ """Display a renderable inside a gradient panel."""
208
+ if renderable == "-":
209
+ renderable = typer.get_text_stream("stdin").read().rstrip("\n")
210
+ if not renderable:
211
+ raise typer.UsageError("Missing text argument.")
212
+
213
+ fg_list = parse_colors(colors)
214
+ bg_list = parse_colors(bgcolors)
215
+ style_obj = parse_style(style)
216
+ _text_justify = cast(AlignMethod, text_justify)
217
+ padding_tuple: Optional[Tuple[int, ...]] = None
218
+ if padding:
219
+ padding_tuple = tuple(int(x) for x in padding.split(",") if x.strip())
220
+
221
+ from rich import box as rich_box
222
+
223
+ box_map: dict[str, Any] = {
224
+ "SQUARE": rich_box.SQUARE,
225
+ "ROUNDED": rich_box.ROUNDED,
226
+ "HEAVY": rich_box.HEAVY,
227
+ "DOUBLE": rich_box.DOUBLE,
228
+ "ASCII": rich_box.ASCII,
229
+ }
230
+ box_style = box_map.get(box.upper(), rich_box.ROUNDED)
231
+
232
+ if animate and svg:
233
+ raise typer.UsageError("--svg is not supported with --animate.")
234
+ if animate and console.is_terminal is True:
235
+ animated_panel: AnimatedPanel = AnimatedPanel(
236
+ Align(renderable, align=_text_justify),
237
+ colors=cast(Any, fg_list),
238
+ rainbow=rainbow,
239
+ hues=hues,
240
+ bg_colors=cast(Any, bg_list),
241
+ title=title,
242
+ title_style=parse_style(title_style),
243
+ title_align=cast(AlignMethod, title_align),
244
+ subtitle=subtitle,
245
+ subtitle_style=parse_style(subtitle_style),
246
+ subtitle_align=cast(AlignMethod, subtitle_align),
247
+ style=style_obj,
248
+ border_style=parse_style(border_style),
249
+ padding=cast(Any, padding_tuple),
250
+ vertical_justify=cast(Any, vertical_justify),
251
+ justify=cast(AlignMethod, justify),
252
+ expand=expand,
253
+ width=width,
254
+ height=height,
255
+ box=box_style,
256
+ animate=True,
257
+ duration=duration,
258
+ )
259
+ animated_panel.run()
260
+ sys.exit(0)
261
+
262
+ panel = Panel(
263
+ Align(renderable, align=_text_justify),
264
+ colors=cast(Any, fg_list),
265
+ rainbow=rainbow,
266
+ hues=hues,
267
+ bg_colors=cast(Any, bg_list),
268
+ title=title,
269
+ title_style=parse_style(title_style),
270
+ title_align=cast(AlignMethod, title_align),
271
+ subtitle=subtitle,
272
+ subtitle_style=parse_style(subtitle_style),
273
+ subtitle_align=cast(AlignMethod, subtitle_align),
274
+ style=style_obj,
275
+ border_style=parse_style(border_style),
276
+ padding=cast(Any, padding_tuple),
277
+ vertical_justify=cast(Any, vertical_justify),
278
+ justify=cast(AlignMethod, justify),
279
+ expand=expand,
280
+ width=width,
281
+ height=height,
282
+ box=box_style,
283
+ )
284
+ if svg:
285
+ export_svg(panel, svg, end=end)
286
+ return
287
+ console.print(panel, end=end)
288
+
289
+
290
+ __all__ = ["panel_command"]
File without changes
@@ -0,0 +1,128 @@
1
+ """Rule command wiring for the CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Literal, Optional, cast
6
+
7
+ import typer
8
+ from rich.align import AlignMethod
9
+
10
+ from rich_gradient.rule import Rule
11
+
12
+ from .common import console, export_svg, parse_colors, parse_style
13
+
14
+
15
+ def rule_command(
16
+ title: Optional[str] = typer.Option(
17
+ None,
18
+ "-t",
19
+ "--title",
20
+ metavar="TITLE",
21
+ help="Title of the rule.",
22
+ ),
23
+ title_style: Optional[str] = typer.Option(
24
+ "bold",
25
+ "-s",
26
+ "--title-style",
27
+ metavar="TITLE_STYLE",
28
+ help=(
29
+ "The style of the rule's title text. [dim italic]*Only non-color styles "
30
+ "will be applied as the gradient's colors override color styles.[/]"
31
+ ),
32
+ show_default=True,
33
+ ),
34
+ colors: Optional[str] = typer.Option(
35
+ "",
36
+ "-c",
37
+ "--colors",
38
+ metavar="COLORS",
39
+ help=(
40
+ "Comma-separated list of colors for the gradient. [dim](e.g., "
41
+ "`[/][red]red[/][dim], [/][#ff9900]#ff9900[/][dim], [/][yellow]yellow[/][dim]`)."
42
+ ),
43
+ show_default=True,
44
+ ),
45
+ bgcolors: Optional[str] = typer.Option(
46
+ "",
47
+ "--bgcolors",
48
+ metavar="BGCOLORS",
49
+ help=(
50
+ "Comma-separated list of background colors for the gradient. [dim](e.g., "
51
+ "`[/][red]red[/][dim], [/][#ff9900]#ff9900[/][dim], [/][#ff0]#ff0[/][dim]`). "
52
+ "Defaults to [/][bold #fff]transparent[/][dim].[/dim]"
53
+ ),
54
+ ),
55
+ rainbow: bool = typer.Option(
56
+ False,
57
+ "-r",
58
+ "--rainbow",
59
+ help="[#ff0000]U[/][#ff3b00]s[/][#ff5100]e[/][#ff7400] [/]\
60
+ [#ff9000]r[/][#ffa900]a[/][#ffc000]i[/][#ffd700]n[/]\
61
+ [#ffee00]b[/][#f7ff00]o[/][#d3ff00]w[/][#a7ff00] [/] \
62
+ [#7dff00]c[/][#2eff00]o[/][#00ff64]l[/][#00ff8e]o[/][#00ffc0]r[/]\
63
+ [#00ffec]s[/][#00f4ff] [/][#00ddff]f[/][#00c5ff]o[/][#00afff]r[/]\
64
+ [#1596ff] [/][#3b81ff]t[/][#4e67ff]h[/][#675fff]e[/][#7b57ff] [/]\
65
+ [#924bff]g[/][#a73bff]r[/][#c72cff]a[/][#eb1cff]d[/][#ff00f2]i[/]\
66
+ [#ff00ce]e[/][#ff00a4]n[/][#ff0084]t[/][#ff0054].[/]",
67
+ ),
68
+ hues: int = typer.Option(
69
+ 10,
70
+ "--hues",
71
+ metavar="HUES",
72
+ help="The number of hues to use for a random gradient.",
73
+ show_default=True,
74
+ ),
75
+ end: str = typer.Option(
76
+ "\n",
77
+ "--end",
78
+ metavar="END",
79
+ help=(
80
+ "String appended after the text is printed. [dim]\\[default: '\\n'][/dim]"
81
+ ),
82
+ ),
83
+ thickness: int = typer.Option(
84
+ 2,
85
+ "-T",
86
+ "--thickness",
87
+ metavar="THICKNESS",
88
+ help="Thickness of the rule line (choices 0-3).",
89
+ ),
90
+ align: Literal["left", "center", "right"] = typer.Option(
91
+ "center",
92
+ "-a",
93
+ "--align",
94
+ metavar="ALIGN",
95
+ help="Alignment of the rule in the console. [lime](left, center, right)[/]",
96
+ show_default=True,
97
+ case_sensitive=False,
98
+ ),
99
+ svg: Optional[str] = typer.Option(
100
+ None,
101
+ "--svg",
102
+ metavar="SVG",
103
+ help="Save output as an SVG file.",
104
+ ),
105
+ ) -> None:
106
+ """Display a gradient rule in the console."""
107
+ _colors = parse_colors(colors)
108
+ _bgcolors = parse_colors(bgcolors)
109
+ _title_style = parse_style(title_style)
110
+
111
+ rule = Rule(
112
+ title=title or "",
113
+ title_style=_title_style,
114
+ colors=_colors,
115
+ rainbow=rainbow,
116
+ hues=hues,
117
+ bg_colors=_bgcolors,
118
+ thickness=thickness,
119
+ end=end,
120
+ align=cast(AlignMethod, align),
121
+ )
122
+ if svg:
123
+ export_svg(rule, svg, end=end)
124
+ return
125
+ console.print(rule)
126
+
127
+
128
+ __all__ = ["rule_command"]
@@ -0,0 +1,139 @@
1
+ """Text/print command wiring for the CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from typing import List, Literal, Optional, cast
7
+
8
+ import typer
9
+ from rich.console import JustifyMethod, OverflowMethod
10
+
11
+ from rich_gradient.text import Text
12
+
13
+ from .common import console, export_svg, parse_colors, parse_style
14
+
15
+
16
+ def print_command(
17
+ text: Optional[List[str]] = typer.Argument(None),
18
+ colors: Optional[str] = typer.Option(
19
+ None,
20
+ "-c",
21
+ "--colors",
22
+ metavar="COLORS",
23
+ help=(
24
+ "Comma-separated list of colors for the gradient. [dim](e.g., "
25
+ "`[/][red]red[/][dim], [/][#ff9900]#ff9900[/][dim], [/][yellow]yellow[/][dim]`). "
26
+ "If no colors are provided, the color stops are automatically generated."
27
+ ),
28
+ ),
29
+ rainbow: bool = typer.Option(
30
+ False,
31
+ "-r",
32
+ "--rainbow",
33
+ help="[#ff0000]U[/][#ff3b00]s[/][#ff5100]e[/][#ff7400] [/]\
34
+ [#ff9000]r[/][#ffa900]a[/][#ffc000]i[/][#ffd700]n[/]\
35
+ [#ffee00]b[/][#f7ff00]o[/][#d3ff00]w[/][#a7ff00] [/] \
36
+ [#7dff00]c[/][#2eff00]o[/][#00ff64]l[/][#00ff8e]o[/][#00ffc0]r[/]\
37
+ [#00ffec]s[/][#00f4ff] [/][#00ddff]f[/][#00c5ff]o[/][#00afff]r[/]\
38
+ [#1596ff] [/][#3b81ff]t[/][#4e67ff]h[/][#675fff]e[/][#7b57ff] [/]\
39
+ [#924bff]g[/][#a73bff]r[/][#c72cff]a[/][#eb1cff]d[/][#ff00f2]i[/]\
40
+ [#ff00ce]e[/][#ff00a4]n[/][#ff0084]t[/][#ff0054].[/]",
41
+ ),
42
+ hues: int = typer.Option(
43
+ 7,
44
+ "-h",
45
+ "--hues",
46
+ metavar="HUES",
47
+ help="The number of hues to use for a random gradient.",
48
+ show_default=True,
49
+ ),
50
+ style: Optional[str] = typer.Option(
51
+ None,
52
+ "--style",
53
+ metavar="STYLE",
54
+ help=(
55
+ "The style to apply to the text. [dim italic]*Only non-color styles "
56
+ "will be applied as the gradient's colors override color styles.[/]"
57
+ ),
58
+ ),
59
+ justify: Literal["left", "center", "right"] = typer.Option(
60
+ "left",
61
+ "-j",
62
+ "--justify",
63
+ metavar="JUSTIFY",
64
+ help="Justification of the text. [lime](left, center, right)[/]",
65
+ show_default=True,
66
+ case_sensitive=False,
67
+ ),
68
+ overflow: Literal["crop", "fold", "ellipsis"] = typer.Option(
69
+ "fold",
70
+ "--overflow",
71
+ metavar="OVERFLOW",
72
+ help="How to handle overflow of text. [lime](crop, fold, ellipsis)[/]",
73
+ show_default=True,
74
+ case_sensitive=False,
75
+ ),
76
+ no_wrap: bool = typer.Option(
77
+ False,
78
+ "--no-wrap",
79
+ help="Disable wrapping of text.",
80
+ show_default=True,
81
+ ),
82
+ end: str = typer.Option(
83
+ "\n",
84
+ "--end",
85
+ metavar="END",
86
+ help=(
87
+ "String appended after the text is printed. [dim]\\[default: '\\n'][/dim]"
88
+ ),
89
+ ),
90
+ bgcolors: Optional[str] = typer.Option(
91
+ None,
92
+ "--bgcolors",
93
+ metavar="BGCOLORS",
94
+ help="Optional comma separated string of background colors. [dim]If not provided, background is transparent.[/]",
95
+ ),
96
+ svg: Optional[str] = typer.Option(
97
+ None,
98
+ "--svg",
99
+ metavar="SVG",
100
+ help="Save output as an SVG file.",
101
+ ),
102
+ ) -> None:
103
+ """Print text in gradient color to the console."""
104
+ if text:
105
+ if len(text) == 1 and text[0] == "-":
106
+ content = typer.get_text_stream("stdin").read().rstrip("\n")
107
+ if not content:
108
+ raise typer.BadParameter("Missing text argument.")
109
+ else:
110
+ content = " ".join(text)
111
+ else:
112
+ if sys.stdin.isatty():
113
+ raise typer.BadParameter("Missing text argument.")
114
+ content = typer.get_text_stream("stdin").read().rstrip("\n")
115
+ if not content:
116
+ raise typer.BadParameter("Missing text argument.")
117
+
118
+ fg_list = parse_colors(colors)
119
+ bg_list = parse_colors(bgcolors)
120
+ style_obj = parse_style(style)
121
+ gradient = Text(
122
+ content,
123
+ colors=fg_list,
124
+ rainbow=rainbow,
125
+ hues=hues,
126
+ style=style_obj,
127
+ justify=cast(JustifyMethod, justify),
128
+ overflow=cast(OverflowMethod, overflow),
129
+ end=end,
130
+ no_wrap=no_wrap,
131
+ bg_colors=bg_list,
132
+ )
133
+ if svg:
134
+ export_svg(gradient, svg, end="")
135
+ return
136
+ console.print(gradient)
137
+
138
+
139
+ __all__ = ["print_command"]