richer-tui 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,871 @@
1
+ Metadata-Version: 2.4
2
+ Name: richer-tui
3
+ Version: 0.1.0
4
+ Summary: Zero-dependency Python TUI toolkit — true-color logging, progress bars, graphs, live dashboards, syntax highlighting, and more
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/uqhf/richer
7
+ Keywords: tui,terminal,cli,logging,ansi,color,progress
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+
11
+ # richer
12
+
13
+ Zero-dependency Python TUI toolkit. True-color ANSI rendering, logging, progress bars, spinners, graphs, live dashboards, syntax highlighting, markdown, markup language, prompts, file trees, themes -- all from scratch, no `rich`, no `curses`, no `pygments`.
14
+
15
+ ```
16
+ pip install richer-tui
17
+ python -m richer.demo # live preview of everything
18
+ ```
19
+
20
+ Requires Python 3.10+, a terminal with true-color support (any modern terminal on Windows/macOS/Linux).
21
+
22
+ ---
23
+
24
+ ## Table of Contents
25
+
26
+ 1. [Color & Gradient](#1-color--gradient)
27
+ 2. [Style & Layout](#2-style--layout)
28
+ 3. [LogKit -- structured logger](#3-logkit----structured-logger)
29
+ 4. [Spinners](#4-spinners)
30
+ 5. [Progress Bars](#5-progress-bars)
31
+ 6. [Graphs](#6-graphs)
32
+ 7. [Markup Language](#7-markup-language)
33
+ 8. [Syntax Highlighting](#8-syntax-highlighting)
34
+ 9. [Live & Dashboard](#9-live--dashboard)
35
+ 10. [Themes](#10-themes)
36
+ 11. [Screen Control](#11-screen-control)
37
+ 12. [Prompt](#12-prompt)
38
+ 13. [Logging Bridge](#13-logging-bridge)
39
+ 14. [Text Object](#14-text-object)
40
+ 15. [File Tree](#15-file-tree)
41
+ 16. [Markdown Renderer](#16-markdown-renderer)
42
+ 17. [Loading Screen](#17-loading-screen)
43
+
44
+ ---
45
+
46
+ ## 1. Color & Gradient
47
+
48
+ ```python
49
+ from richer import Color, Gradient
50
+ ```
51
+
52
+ ### `Color` -- ANSI escape helpers
53
+
54
+ ```python
55
+ # 16-color constants
56
+ Color.RED # "\033[31m"
57
+ Color.BRIGHT_WHITE # "\033[97m"
58
+ Color.BG_BLUE # "\033[44m"
59
+ Color.RESET # "\033[0m"
60
+
61
+ # True-color (24-bit)
62
+ Color.rgb(88, 101, 242) # foreground escape
63
+ Color.bg_rgb(30, 30, 40) # background escape
64
+ Color.from_hex("#5865F2") # hex string -> rgb escape
65
+
66
+ # 256-color
67
+ Color.color256(196)
68
+ Color.bg_color256(52)
69
+
70
+ # Gradient text
71
+ Color.gradient_text("hello", (255, 80, 0), (255, 220, 0))
72
+
73
+ # Multi-stop gradient -- stops are (r,g,b) tuples
74
+ Color.multi_gradient("loading...", (88,101,242), (0,200,255), (0,255,150))
75
+
76
+ # Full HSV rainbow rotation
77
+ Color.rainbow("spectral")
78
+
79
+ # Utility
80
+ Color.blend((255,0,0), (0,0,255), 0.5) # lerp -> (127, 0, 127)
81
+ Color.pulse((88,101,242), t) # sinusoidal brightness, t in [0,1]
82
+ Color.dim_rgb((200,200,200), factor=0.4) # darken tuple -> rgb escape
83
+ Color.strip("text with \033[31mcodes\033[0m") # strip all ANSI
84
+
85
+ # Convenience wrappers
86
+ Color.bold("text")
87
+ Color.italic("text")
88
+ Color.underline("text")
89
+ Color.strike("text")
90
+ Color.dim_text("text")
91
+ ```
92
+
93
+ ### `Gradient` -- named stop lists
94
+
95
+ Every preset is a `list[tuple[int,int,int]]` passed to `Color.multi_gradient`.
96
+
97
+ | Name | Visual |
98
+ |-----------|--------|
99
+ | `FIRE` | red -> orange -> yellow |
100
+ | `OCEAN` | deep navy -> cyan |
101
+ | `NEON` | pink -> violet -> cyan |
102
+ | `MATRIX` | dark green -> bright green |
103
+ | `GOLD` | amber -> bright gold -> amber |
104
+ | `BLOOD` | dark red -> bright red |
105
+ | `CYBER` | teal -> blue -> violet |
106
+ | `DISCORD` | discord blurple loop |
107
+ | `CANDY` | pink -> yellow -> light blue |
108
+ | `TOXIC` | lime -> yellow-green |
109
+ | `VOID` | deep purple -> violet |
110
+ | `LAVA` | red -> orange -> yellow |
111
+ | `SUNSET` | orange -> crimson -> indigo |
112
+ | `ARCTIC` | ice blue -> white-blue |
113
+ | `RUST` | dark brown -> orange |
114
+ | `ACID` | lime -> bright green |
115
+ | `GHOST` | cool grey loop |
116
+ | `INFRA` | deep blue -> purple -> red -> yellow |
117
+ | `ROYAL` | purple -> lavender loop |
118
+
119
+ ```python
120
+ # Build from hex strings
121
+ stops = Gradient.from_hex("#ff0080", "#8000ff", "#00c8ff")
122
+
123
+ # Expand stop list to n evenly-spaced (r,g,b) tuples
124
+ pts = Gradient.interpolate(Gradient.FIRE, 256)
125
+ ```
126
+
127
+ ---
128
+
129
+ ## 2. Style & Layout
130
+
131
+ ```python
132
+ from richer import Style, BorderStyle, render_panel, render_table, render_two_col, render_header, render_badge, strip_ansi, term_width
133
+ ```
134
+
135
+ ### `Style` -- ANSI text attributes
136
+
137
+ ```python
138
+ Style.BOLD, Style.DIM, Style.ITALIC, Style.UNDERLINE, Style.BLINK, Style.STRIKE, Style.RESET
139
+ ```
140
+
141
+ ### `BorderStyle` -- box-drawing presets
142
+
143
+ `SINGLE`, `DOUBLE`, `ROUNDED`, `BOLD`, `DASHED`, `ASCII`
144
+
145
+ ### Layout functions
146
+
147
+ ```python
148
+ # Bordered panel -- returns string
149
+ render_panel(
150
+ lines, # list[str]
151
+ title=None,
152
+ border=BorderStyle.ROUNDED,
153
+ border_color=Color.rgb(88,101,242),
154
+ title_color=Color.BRIGHT_WHITE,
155
+ padding=1,
156
+ width=None,
157
+ )
158
+
159
+ # Table -- returns string
160
+ render_table(
161
+ headers, # list[str]
162
+ rows, # list[list[str]]
163
+ border=BorderStyle.SINGLE,
164
+ header_color=...,
165
+ border_color=...,
166
+ col_colors=None, # list[str] per column
167
+ zebra=False,
168
+ align=None, # list["left"|"right"|"center"]
169
+ )
170
+
171
+ # Two-column layout -- returns string
172
+ render_two_col(left_lines, right_lines, gap=4)
173
+
174
+ # Centered header bar -- returns string
175
+ render_header(text, gradient=None, width=None)
176
+
177
+ # Small badge pill -- returns string
178
+ render_badge(text, color=None)
179
+
180
+ # Strip ANSI escapes
181
+ strip_ansi(s)
182
+
183
+ # Terminal width (falls back to 80)
184
+ term_width()
185
+ ```
186
+
187
+ ---
188
+
189
+ ## 3. LogKit -- structured logger
190
+
191
+ ```python
192
+ from richer import LogKit, Level
193
+
194
+ log = LogKit(
195
+ name="app",
196
+ min_level=Level.DEBUG,
197
+ show_time=True,
198
+ show_name=True,
199
+ name_gradient=Gradient.DISCORD,
200
+ time_color=Color.BRIGHT_BLACK,
201
+ file=sys.stdout,
202
+ thread_safe=False,
203
+ history=False,
204
+ history_maxlen=500,
205
+ )
206
+ ```
207
+
208
+ ### Log levels
209
+
210
+ ```python
211
+ log.debug("probe sent")
212
+ log.info("handshake complete")
213
+ log.success("payload injected")
214
+ log.warning("retry 3/5")
215
+ log.error("connection refused")
216
+ log.critical("watchdog timeout")
217
+
218
+ # Custom color override
219
+ log.info("custom", color=Color.rgb(200, 100, 255))
220
+ ```
221
+
222
+ `Level` constants: `DEBUG=0`, `INFO=1`, `SUCCESS=2`, `WARNING=3`, `ERROR=4`, `CRITICAL=5`
223
+
224
+ ### Display methods
225
+
226
+ ```python
227
+ log.banner("SYSTEM COMPROMISED", gradient=Gradient.BLOOD, border=BorderStyle.DOUBLE)
228
+
229
+ log.rule("section header", gradient=Gradient.NEON)
230
+ log.rule()
231
+ log.divider()
232
+
233
+ log.section("phase 2", gradient=Gradient.CYBER)
234
+
235
+ log.panel(["line 1", "line 2"], title="status", border=BorderStyle.ROUNDED)
236
+
237
+ log.table(
238
+ ["host", "status", "ip"],
239
+ [["web01", "up", "10.0.0.1"], ["db02", "down", "10.0.0.2"]],
240
+ zebra=True,
241
+ )
242
+
243
+ log.kv([("pid", "1234"), ("arch", "x64")], sep=" -> ")
244
+ log.keyval("target", "192.168.1.100")
245
+
246
+ log.columns(["item1","item2","item3","item4"], ncols=2, headers=["col A","col B"])
247
+
248
+ log.callout("Heap base leaked at 0x7fff00001000", style="note")
249
+ # styles: "note", "tip", "warn", "danger", "info"
250
+
251
+ log.badge("STABLE", color=Color.rgb(50,220,100))
252
+ ```
253
+
254
+ ### Data inspection
255
+
256
+ ```python
257
+ log.tree({"a": {"b": [1, 2, 3]}, "c": "val"})
258
+
259
+ log.json({"status": "ok", "code": 200, "data": None})
260
+
261
+ log.hex_dump(bytes(range(64)), width=16, highlight=[0x00, 0xff])
262
+
263
+ log.diff(old_str, new_str, label_a="before", label_b="after")
264
+
265
+ log.inspect(obj, show_private=False, show_methods=False)
266
+
267
+ log.traceback() # current exception
268
+ log.traceback(exc) # specific exception object
269
+ log.multiline(["line1", "line2"], level=Level.INFO)
270
+ ```
271
+
272
+ ### Progress & metrics
273
+
274
+ ```python
275
+ # Inline progress bar (overwrites current line)
276
+ for i in range(total + 1):
277
+ log.progress_bar(i, total, label="downloading", show_eta=True, start_time=t0)
278
+
279
+ # Inline counter
280
+ log.counter("packets", current, total, bar=True, bar_width=20)
281
+
282
+ # Status line (overwritten by next log call)
283
+ log.status("waiting for beacon...")
284
+ log.status_clear()
285
+
286
+ # Sparkline
287
+ log.sparkline([10, 25, 18, 40, 35, 55], label="rtt", gradient=Gradient.NEON)
288
+
289
+ # Rate and elapsed
290
+ log.rate("req", count=1024, elapsed=2.5, unit="/s")
291
+ log.elapsed(start_time, label="total time")
292
+ ```
293
+
294
+ ### Integration methods
295
+
296
+ ```python
297
+ log.syntax(code, lang="python", line_numbers=True, title="exploit.py")
298
+ log.markdown(md_text)
299
+ log.markup("[fire]armed[/fire] and [bold green]ready[/bold green]")
300
+ log.file_tree("./src", max_depth=3, show_size=True)
301
+ log.theme("dracula")
302
+ ```
303
+
304
+ ### History
305
+
306
+ ```python
307
+ log = LogKit("app", history=True)
308
+ log.info("boot")
309
+ records = log._hist.all() # [{"level": 1, "msg": "boot", "ts": "..."}]
310
+ log._hist.clear()
311
+ ```
312
+
313
+ ---
314
+
315
+ ## 4. Spinners
316
+
317
+ ```python
318
+ from richer import Spinner, SpinnerGroup, SPINNER_FRAMES
319
+ ```
320
+
321
+ ### `Spinner`
322
+
323
+ ```python
324
+ s = Spinner("connecting to C2", style="dots")
325
+ s.start()
326
+ # ... work ...
327
+ s.stop("connected")
328
+
329
+ # Context manager
330
+ with Spinner("encrypting payload"):
331
+ encrypt(data)
332
+ ```
333
+
334
+ ### `SpinnerGroup`
335
+
336
+ ```python
337
+ sg = SpinnerGroup([
338
+ "stage 1 -- recon",
339
+ "stage 2 -- exploit",
340
+ "stage 3 -- persist",
341
+ ])
342
+ sg.start()
343
+ time.sleep(1)
344
+ sg.done(0)
345
+ time.sleep(1)
346
+ sg.done(1)
347
+ sg.stop()
348
+ ```
349
+
350
+ ---
351
+
352
+ ## 5. Progress Bars
353
+
354
+ ```python
355
+ from richer import ProgressBar, MultiBar, ThreadedBar, RateBar, BAR_STYLES
356
+ ```
357
+
358
+ ### `ProgressBar`
359
+
360
+ ```python
361
+ b = ProgressBar(
362
+ total=100,
363
+ width=40,
364
+ style="block",
365
+ fill_gradient=Gradient.FIRE,
366
+ prefix="download ",
367
+ show_count=True,
368
+ show_pct=True,
369
+ )
370
+
371
+ for i in range(101):
372
+ b.update(i)
373
+ time.sleep(0.02)
374
+ print()
375
+
376
+ # Iterable wrapper
377
+ for item in b(my_list):
378
+ process(item)
379
+ ```
380
+
381
+ ### `MultiBar`
382
+
383
+ ```python
384
+ mb = MultiBar([
385
+ ("loader", 40, Gradient.DISCORD),
386
+ ("encoder", 60, Gradient.MATRIX),
387
+ ("packer", 30, Gradient.FIRE),
388
+ ], width=35)
389
+
390
+ vals = [0, 0, 0]
391
+ for i in range(60):
392
+ vals[0] = min(i + 1, 40)
393
+ vals[1] = i + 1
394
+ vals[2] = min(i + 1, 30)
395
+ mb.render_all(vals)
396
+ time.sleep(0.03)
397
+ print()
398
+ ```
399
+
400
+ ### `ThreadedBar`
401
+
402
+ ```python
403
+ tb = ThreadedBar(total=100, interval=0.05, prefix="upload ", fill_gradient=Gradient.OCEAN)
404
+ tb.start()
405
+ for chunk in chunks:
406
+ upload(chunk)
407
+ tb.inc()
408
+ tb.stop()
409
+ ```
410
+
411
+ ---
412
+
413
+ ## 6. Graphs
414
+
415
+ ```python
416
+ from richer import (
417
+ bar_chart, sparkline, heatmap_row, column_chart,
418
+ line_graph, scatter, gauge, timeline,
419
+ stacked_bar_chart, pie_chart,
420
+ )
421
+ ```
422
+
423
+ All graph functions return strings -- `print()` them or embed in panels.
424
+
425
+ ```python
426
+ # Horizontal bar chart
427
+ print(bar_chart(
428
+ [92, 78, 45, 61],
429
+ labels=["RCE", "LPE", "SQLi", "XSS"],
430
+ title="severity",
431
+ gradient=Gradient.BLOOD,
432
+ width=35,
433
+ ))
434
+
435
+ # Inline sparkline
436
+ s = sparkline([10, 25, 18, 40, 35, 55], gradient=Gradient.NEON)
437
+
438
+ # Heatmap row
439
+ print(heatmap_row([0.1, 0.5, 0.9, 0.3], labels=["M","T","W","T"]))
440
+
441
+ # Vertical column chart
442
+ print(column_chart([30, 80, 50, 90], labels=["Q1","Q2","Q3","Q4"], height=10))
443
+
444
+ # Multi-series line graph
445
+ # NOTE: colors must be (r,g,b) tuples, NOT ANSI escape strings
446
+ print(line_graph(
447
+ [[10,25,18,40], [5,15,30,20]],
448
+ labels=["in", "out"],
449
+ colors=[(88,101,242), (237,66,69)],
450
+ height=8, width=50,
451
+ ))
452
+
453
+ # Scatter plot
454
+ pts = [(x, y), ...]
455
+ print(scatter(pts, width=50, height=16, gradient=Gradient.NEON))
456
+
457
+ # Radial gauge
458
+ print(gauge(0.73, label="CPU", gradient=Gradient.LAVA))
459
+
460
+ # Timeline
461
+ events = [("boot", 0), ("exploit", 3), ("shell", 7), ("exfil", 12)]
462
+ print(timeline(events, width=60))
463
+
464
+ # Stacked bar chart
465
+ print(stacked_bar_chart(
466
+ series=[[30,50,20], [40,35,25]],
467
+ labels=["host1", "host2"],
468
+ series_labels=["user", "kernel", "idle"],
469
+ colors=[(88,101,242),(59,165,93),(250,166,26)],
470
+ title="CPU breakdown",
471
+ width=40,
472
+ ))
473
+
474
+ # Pie chart
475
+ print(pie_chart(
476
+ data=[40, 30, 20, 10],
477
+ labels=["RCE", "LPE", "DoS", "Info"],
478
+ title="vuln classes",
479
+ show_pct=True,
480
+ ))
481
+ ```
482
+
483
+ ---
484
+
485
+ ## 7. Markup Language
486
+
487
+ ```python
488
+ from richer import markup_render, markup_strip, mprint, mformat
489
+ ```
490
+
491
+ ### Tags
492
+
493
+ ```
494
+ [red]text[/red] [green] [blue] [yellow] [cyan] [magenta] [white] [dim]
495
+ [bold]text[/bold] [italic] [underline] [strike] [blink]
496
+ [rgb(88,101,242)]text[/rgb]
497
+ [#5865f2]text[/#5865f2]
498
+ [bg_rgb(30,30,40)]text[/bg_rgb]
499
+
500
+ # Combined
501
+ [bold red]critical alert[/bold red]
502
+
503
+ # Named gradients
504
+ [fire]armed[/fire] [neon] [matrix] [ocean] [blood] [cyber] [discord] [candy]
505
+ [rainbow]full spectrum[/rainbow]
506
+
507
+ # Hyperlink
508
+ [link=https://example.com]click here[/link]
509
+ ```
510
+
511
+ ```python
512
+ s = markup_render("[bold red]ALERT[/bold red]: [neon]payload staged[/neon]")
513
+ plain = markup_strip("[bold]text[/bold]") # -> "text"
514
+ mprint("[fire]system compromised[/fire]")
515
+ mprint(mformat("[bold]{host}[/bold] responded in {ms}ms", host="10.0.0.1", ms=42))
516
+ ```
517
+
518
+ ---
519
+
520
+ ## 8. Syntax Highlighting
521
+
522
+ ```python
523
+ from richer import highlight, highlight_auto, detect_lang
524
+ ```
525
+
526
+ Regex-based tokenizer, zero external deps.
527
+
528
+ **Supported languages:** `python`, `json`, `c`, `cpp`, `js`, `javascript`, `sh`, `bash`, `shell`, `yaml`, `sql`, `html`, `css`, `rust`, `go`
529
+
530
+ ```python
531
+ code = "def exploit(buf): return buf[:8] + p64(0xdeadbeef)"
532
+ print(highlight(code, lang="python", line_numbers=True))
533
+
534
+ print(highlight_auto(code, filename="exploit.py", line_numbers=True))
535
+
536
+ lang = detect_lang(code, filename="main.rs") # -> "rust"
537
+ ```
538
+
539
+ ---
540
+
541
+ ## 9. Live & Dashboard
542
+
543
+ ```python
544
+ from richer import Live, Dashboard, LogPane
545
+ ```
546
+
547
+ ### `Live`
548
+
549
+ ```python
550
+ data = {"count": 0}
551
+
552
+ def render():
553
+ return f"packets: {data['count']}"
554
+
555
+ with Live(render, refresh_rate=10, transient=False):
556
+ for i in range(100):
557
+ data["count"] = i
558
+ time.sleep(0.05)
559
+ ```
560
+
561
+ - `content` -- string or `callable() -> str`
562
+ - `refresh_rate` -- redraws per second
563
+ - `transient=True` -- clears the block on exit
564
+
565
+ ### `Dashboard`
566
+
567
+ ```python
568
+ dash = Dashboard()
569
+ dash.row("header", height=3)
570
+ dash.cols(["left", "right"], heights=[20, 20])
571
+ dash.row("footer", height=1)
572
+
573
+ dash["header"] = render_panel(["STATUS BOARD"], border=BorderStyle.DOUBLE)
574
+ dash["left"] = "cell content left"
575
+ dash["right"] = "cell content right"
576
+
577
+ with Live(dash.render, refresh_rate=4):
578
+ for t in range(50):
579
+ dash["footer"] = f"tick {t}"
580
+ time.sleep(0.1)
581
+ ```
582
+
583
+ ### `LogPane`
584
+
585
+ ```python
586
+ pane = LogPane(height=10)
587
+ pane.push("[OK] boot complete")
588
+ pane.push("[ERR] write failed")
589
+
590
+ with Live(pane.render, refresh_rate=5):
591
+ for event in event_stream():
592
+ pane.push(format_event(event))
593
+ time.sleep(0.05)
594
+ ```
595
+
596
+ ---
597
+
598
+ ## 10. Themes
599
+
600
+ ```python
601
+ from richer import theme_apply, theme_color, theme_names, theme_info
602
+ from richer import DISCORD, HACKER, DRACULA, NORD, MONOKAI, SOLARIZED, CYBERPUNK, BLOOD
603
+ ```
604
+
605
+ **Available themes:** `discord`, `hacker`, `dracula`, `nord`, `monokai`, `solarized`, `cyberpunk`, `blood`
606
+
607
+ ```python
608
+ theme_apply("dracula")
609
+
610
+ # Semantic color keys: "primary", "secondary", "success", "warning",
611
+ # "error", "info", "dim", "bright", "bg" (tuple), "gradient" (stop list)
612
+ c = theme_color("error") # ANSI escape string
613
+ bg = theme_color("bg") # (r,g,b) tuple
614
+ grad = theme_color("gradient") # gradient stop list
615
+
616
+ names = theme_names()
617
+ info = theme_info("monokai")
618
+ info = theme_info() # active theme
619
+ ```
620
+
621
+ ---
622
+
623
+ ## 11. Screen Control
624
+
625
+ ```python
626
+ from richer import Screen, ScreenWriter
627
+ ```
628
+
629
+ ```python
630
+ Screen.move(row, col)
631
+ Screen.clear()
632
+ Screen.hide_cursor()
633
+ Screen.show_cursor()
634
+ Screen.set_title("my app")
635
+ Screen.bell()
636
+
637
+ w, h = Screen.size()
638
+
639
+ Screen.draw_box(row, col, width, height, border=BorderStyle.SINGLE)
640
+ Screen.fill_rect(row, col, width, height, char=" ", color=None)
641
+ Screen.hyperlink(text, url)
642
+ Screen.notify(title, body)
643
+
644
+ # Context managers
645
+ with Screen.fullscreen:
646
+ ...
647
+
648
+ with Screen.cursor_hidden:
649
+ ...
650
+
651
+ with Screen.at(5, 10):
652
+ print("drawn at row 5 col 10")
653
+ ```
654
+
655
+ ```python
656
+ sw = ScreenWriter()
657
+ sw.at(3, 5, f"{Color.rgb(88,101,242)}hello{Color.RESET}")
658
+ sw.flush()
659
+ ```
660
+
661
+ ---
662
+
663
+ ## 12. Prompt
664
+
665
+ ```python
666
+ from richer import Prompt
667
+
668
+ name = Prompt.ask("target hostname", default="localhost", hint="e.g. 10.0.0.1")
669
+ confirmed = Prompt.confirm("continue?", default=True)
670
+ choice = Prompt.choose("select payload", choices=["revshell", "bind", "meter"])
671
+ selected = Prompt.choose("select modules", choices=[...], multi=True)
672
+ key = Prompt.secret("encryption key", confirm=True)
673
+ port = Prompt.integer("port", min_val=1, max_val=65535, default=4444)
674
+ timeout = Prompt.float_("timeout seconds", default=5.0)
675
+ path = Prompt.path("config file", must_exist=True)
676
+ ```
677
+
678
+ ---
679
+
680
+ ## 13. Logging Bridge
681
+
682
+ ```python
683
+ from richer import LogKitHandler, setup_logging, suppress_loggers
684
+ import logging
685
+
686
+ log = LogKit("app")
687
+
688
+ # Route all logging.* calls through log
689
+ setup_logging(log, level=logging.DEBUG, capture_warnings=True)
690
+
691
+ # Specific loggers only
692
+ setup_logging(log, loggers=["httpx", "asyncio"])
693
+
694
+ # Silence noisy loggers
695
+ suppress_loggers("urllib3", "PIL")
696
+ ```
697
+
698
+ ---
699
+
700
+ ## 14. Text Object
701
+
702
+ ```python
703
+ from richer import Text
704
+
705
+ t = Text()
706
+ t.append("status: ", "bold")
707
+ t.append("ONLINE", "rgb(50,220,100)")
708
+ t.append_markup("[fire]armed[/fire]")
709
+
710
+ print(t.render()) # ANSI string
711
+ print(t.plain) # stripped text
712
+ print(t.width) # visual width
713
+
714
+ t.truncate(40, overflow="...")
715
+ lines = t.wrap(60)
716
+ t.justify("center", 80)
717
+ t.highlight_substr("ONLINE", "bold yellow")
718
+
719
+ # Class methods
720
+ t = Text.assemble(("label: ", "dim"), ("value", "bold green"))
721
+ t = Text.from_markup("[bold red]ALERT[/bold red]")
722
+ t = Text.gradient("spectral", (255,0,128), (0,200,255))
723
+ t = Text.rule(title="section", width=60, char="-")
724
+ ```
725
+
726
+ ---
727
+
728
+ ## 15. File Tree
729
+
730
+ ```python
731
+ from richer import render_file_tree, print_file_tree
732
+
733
+ tree_str = render_file_tree(
734
+ path="./src",
735
+ max_depth=4,
736
+ show_hidden=False,
737
+ show_size=True,
738
+ dir_only=False,
739
+ sort_dirs_first=True,
740
+ summary=True,
741
+ )
742
+
743
+ print_file_tree("./src", max_depth=3, show_size=True)
744
+ ```
745
+
746
+ 60+ extension color mappings: `.py` (blue), `.rs` (orange), `.c`/`.cpp` (cyan), `.sh` (green), `.json`/`.yaml` (yellow), `.md` (white), `.env` (red), images (magenta), binaries (bright red), and more.
747
+
748
+ ---
749
+
750
+ ## 16. Markdown Renderer
751
+
752
+ ```python
753
+ from richer import markdown_render, md_print
754
+ ```
755
+
756
+ GFM renderer, zero deps.
757
+
758
+ **Supported:** headings h1-h6, bold/italic/strikethrough, inline code, links, blockquotes, unordered/ordered lists (3 nesting levels), GFM tables with alignment, fenced code blocks (syntax highlighted), horizontal rules.
759
+
760
+ ```python
761
+ md = """
762
+ # Exploitation Report
763
+
764
+ Target responded to **CVE-2024-XXXX** with a *9.8 CVSS* score.
765
+
766
+ ```python
767
+ payload = b"\\x00" * 8 + p64(win_addr)
768
+ sock.send(payload)
769
+ ```
770
+
771
+ | Component | Status | Severity |
772
+ |-----------|--------|----------|
773
+ | web | pwned | critical |
774
+ """
775
+
776
+ md_print(md)
777
+ s = markdown_render(md) # -> ANSI string
778
+ ```
779
+
780
+ ---
781
+
782
+ ## 17. Loading Screen
783
+
784
+ ```python
785
+ from richer import loading_screen, G_BOOT, G_READY, G_PULSE
786
+
787
+ loading_screen(
788
+ title="IMPLANT LOADER v2.0",
789
+ steps=[
790
+ "initializing crypto",
791
+ "probing target",
792
+ "establishing tunnel",
793
+ "loading modules",
794
+ ],
795
+ gradient=G_BOOT,
796
+ step_delay=0.8,
797
+ final_msg="ready",
798
+ )
799
+ ```
800
+
801
+ ---
802
+
803
+ ## Quick-start
804
+
805
+ ```python
806
+ from richer import LogKit, Color, Gradient, Spinner, ProgressBar, bar_chart, mprint, theme_apply
807
+ import time
808
+
809
+ theme_apply("dracula")
810
+
811
+ log = LogKit("demo", show_time=True)
812
+ log.banner("RICHER DEMO", gradient=Gradient.NEON)
813
+ log.info("starting up")
814
+ log.success("connected to 10.0.0.1:4444")
815
+ log.warning("retry 2/5 -- timeout")
816
+ log.error("module load failed: access denied")
817
+
818
+ log.kv([
819
+ ("pid", "31337"),
820
+ ("arch", "x64"),
821
+ ("priv", "SYSTEM"),
822
+ ("os", "Windows 11 22H2"),
823
+ ])
824
+
825
+ with Spinner("staging payload", style="dots"):
826
+ time.sleep(1.5)
827
+
828
+ b = ProgressBar(total=100, width=35, fill_gradient=Gradient.FIRE, prefix="upload ")
829
+ for i in range(101):
830
+ b.update(i)
831
+ time.sleep(0.01)
832
+ print()
833
+
834
+ print(bar_chart([92,78,45,61], labels=["RCE","LPE","SQLi","XSS"], gradient=Gradient.BLOOD))
835
+ mprint("[fire]armed[/fire] and [bold green]ready[/bold green]")
836
+ ```
837
+
838
+ ---
839
+
840
+ ## Project layout
841
+
842
+ ```
843
+ richer/
844
+ |-- __init__.py exports
845
+ |-- colors.py Color, Gradient
846
+ |-- styles.py Style, BorderStyle, layout helpers
847
+ |-- logger.py LogKit, Level
848
+ |-- spinners.py Spinner, SpinnerGroup
849
+ |-- bars.py ProgressBar, MultiBar, ThreadedBar, RateBar
850
+ |-- graphs.py bar_chart, line_graph, scatter, pie_chart, ...
851
+ |-- markup.py tag markup language
852
+ |-- syntax.py regex-based syntax highlighter
853
+ |-- live.py Live, Dashboard, LogPane
854
+ |-- themes.py 8 named themes
855
+ |-- screen.py Screen, ScreenWriter
856
+ |-- prompt.py Prompt.*
857
+ |-- loghandler.py stdlib logging bridge
858
+ |-- text.py Text object
859
+ |-- filetree.py render_file_tree
860
+ |-- markdown.py GFM markdown renderer
861
+ |-- loadscreen.py animated boot screen
862
+
863
+ examples/
864
+ |-- demo.py runnable feature showcase
865
+ ```
866
+
867
+ ---
868
+
869
+ ## License
870
+
871
+ MIT