logbar 0.2.4__tar.gz → 0.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: LogBar
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: A unified Logger and ProgressBar util with zero dependencies.
5
5
  Author-email: ModelCloud <qubitium@modelcloud.ai>
6
6
  License-Expression: Apache-2.0
@@ -36,6 +36,7 @@ Dynamic: license-file
36
36
  - Shared singleton logger with per-level colorized output.
37
37
  - `once` helpers prevent duplicate log spam automatically.
38
38
  - Stackable progress bars that stay anchored while your logs flow freely.
39
+ - Sub-cell Unicode bar rasterization for smoother, more accurate terminal fills.
39
40
  - Built-in styling for progress bar fills, colors, gradients, and head glyphs.
40
41
  - Animated progress titles with a subtle sweeping highlight.
41
42
  Set `LOGBAR_ANIMATION=0` to disable the highlight animation.
@@ -52,6 +53,46 @@ pip install logbar
52
53
 
53
54
  LogBar works out-of-the-box with CPython 3.8+ on Linux, macOS, and Windows terminals.
54
55
 
56
+ ## Renderer Design
57
+
58
+ LogBar keeps progress bars, spinners, tables, and normal log lines readable in the same terminal session. Compared with traditional loggers, it lets long-running CLI programs show live status without flooding the screen with repeated status lines or breaking the flow of regular logs.
59
+
60
+ Main rendering APIs:
61
+
62
+ - `log.pb(...)` for live progress bars
63
+ - `log.spinner(...)` for work with no fixed total
64
+ - `log.columns(...)` for aligned table output
65
+
66
+ Examples:
67
+
68
+ ```py
69
+ from logbar import LogBar
70
+
71
+ log = LogBar.shared()
72
+
73
+ for _ in log.pb(range(5)).title("下载 📦").subtitle("phase 1"):
74
+ pass
75
+ ```
76
+
77
+ ```py
78
+ jobs = ["scan", "parse", "index", "flush"]
79
+ pb = log.pb(jobs, output_interval=1).title("Indexing").manual()
80
+ for job in pb:
81
+ log.info("processing %s", job)
82
+ pb.subtitle(job).draw()
83
+ ```
84
+
85
+ ```py
86
+ cols = log.columns(
87
+ {"label": "task", "width": "fit"},
88
+ {"label": "status", "width": "fit"},
89
+ {"label": "detail", "width": "50%"},
90
+ )
91
+
92
+ cols.info.header()
93
+ cols.info("render", "active", "width and alignment stay terminal-aware")
94
+ ```
95
+
55
96
  # Quick Start
56
97
 
57
98
  ```py
@@ -199,6 +240,28 @@ pb.head('>', color='82') # custom head glyph + color index
199
240
 
200
241
  `ProgressBar.available_styles()` lists builtin styles, and you can register additional ones with `ProgressBar.register_style(...)` or switch defaults globally via `ProgressBar.set_default_style(...)`. Custom colors accept ANSI escape codes, 256-color indexes (e.g. `'82'`), or hex strings (`'#4c1d95'`).
201
242
 
243
+ For direct style registration and introspection, import the advanced style APIs from `logbar.progress`:
244
+
245
+ ```py
246
+ from logbar.progress import ProgressBar, ProgressStyle, progress_style_names
247
+
248
+ print(ProgressBar.available_styles())
249
+ print(progress_style_names())
250
+
251
+ ProgressBar.register_style(
252
+ ProgressStyle(
253
+ name="ice",
254
+ fill_char="■",
255
+ empty_char="·",
256
+ fill_colors=("#7dd3fc", "#38bdf8"),
257
+ gradient=True,
258
+ head_char=">",
259
+ )
260
+ )
261
+
262
+ ProgressBar.set_default_style("ice")
263
+ ```
264
+
202
265
  Styled output (plain-text view with ANSI removed):
203
266
 
204
267
  ```
@@ -248,6 +311,14 @@ cols.update({
248
311
  })
249
312
  ```
250
313
 
314
+ Useful column helpers:
315
+
316
+ - `cols.info.header()` or `cols.info.headers()` prints the current border + header block.
317
+ - `cols.info.simulate(...)` recomputes widths without emitting a row.
318
+ - `cols.update(...)` changes labels, spans, or widths at runtime.
319
+ - `cols.width()` returns the current rendered table width, including borders.
320
+ - `cols.widths`, `cols.padding`, and `cols.column_specs` expose the current layout.
321
+
251
322
  # Replacing `tqdm`
252
323
 
253
324
  The API mirrors common `tqdm` patterns while staying more Pythonic:
@@ -275,7 +346,7 @@ with tqdm.tqdm(total=len(items)) as pb:
275
346
  with log.pb(items).manual() as pb:
276
347
  for item in pb:
277
348
  handle(item)
278
- pb.render()
349
+ pb.draw()
279
350
  ```
280
351
 
281
352
  # Advanced Tips
@@ -283,3 +354,69 @@ with log.pb(items).manual() as pb:
283
354
  - Combine columns and progress bars by logging summaries at key checkpoints.
284
355
  - Use `log.warn.once(...)` to keep noisy health checks readable.
285
356
  - For multi-line messages, pre-format text and pass it as a single string; LogBar keeps borders intact.
357
+
358
+ # API Reference
359
+
360
+ ## `LogBar`
361
+
362
+ - `LogBar.shared(override_logger=False)` returns the process-wide shared logger.
363
+ - `override_logger=True` is useful in tests or embedded environments that replaced the active `logging` logger class.
364
+ - Level methods: `debug`, `info`, `warn`, `error`, `critical`.
365
+ - Deduplicated level methods: `debug.once`, `info.once`, `warn.once`, `error.once`, `critical.once`.
366
+ - `setLevel(level)` accepts strings like `"INFO"`, `"WARN"`, `"CRIT"`, numeric levels, numeric strings, and constants such as `LogBar.WARNING`.
367
+ - `pb(iterable_or_total, output_interval=None)` creates and attaches a progress bar.
368
+ - `spinner(title="", interval=0.5, tail_length=4)` creates and attaches an indeterminate rolling progress bar.
369
+ - `columns(..., cols=None, width=None, padding=2)` creates a column printer.
370
+
371
+ ## `ProgressBar`
372
+
373
+ `log.pb(...)` returns an attached `ProgressBar`. For direct imports, use:
374
+
375
+ ```py
376
+ from logbar.progress import ProgressBar, ProgressStyle
377
+ ```
378
+
379
+ Common chainable methods:
380
+
381
+ - `title(text)` and `subtitle(text)`
382
+ - `style(name_or_style)`
383
+ - `fill(fill_char, empty=None)`
384
+ - `colors(fill=None, empty=None, gradient=None, head=None)`
385
+ - `head(char=None, color=None)`
386
+ - `set(show_left_steps=None, left_steps_offset=None)`
387
+ - `output_interval(interval)`
388
+ - `mode(RenderMode)` if you prefer explicit mode switching over `auto()` / `manual()`
389
+
390
+ Render and lifecycle control:
391
+
392
+ - `draw(force=False)` renders the current snapshot immediately.
393
+ - `auto()` enables redraw-on-iteration mode.
394
+ - `manual()` disables automatic redraw so you can call `draw()` yourself.
395
+ - `attach(logger=None)` attaches the bar to a logger.
396
+ - `detach()` detaches the bar without destroying the object.
397
+ - `close()` forces a final render if needed and removes the bar from the stack.
398
+ - `step()` returns the current iteration index and `next()` advances once outside a `for` loop.
399
+
400
+ Style registry helpers:
401
+
402
+ - `ProgressBar.available_styles()`
403
+ - `ProgressBar.register_style(style)`
404
+ - `ProgressBar.set_default_style(style)`
405
+ - `ProgressBar.default_style()`
406
+
407
+ ## `RollingProgressBar`
408
+
409
+ `log.spinner(...)` returns a `RollingProgressBar`, which inherits from `ProgressBar` and adds:
410
+
411
+ - `pulse()` to advance the spinner immediately between automatic ticks.
412
+ - `interval` and `tail_length` constructor arguments for animation speed and tail size.
413
+
414
+ ## `ColumnsPrinter`
415
+
416
+ `log.columns(...)` returns a `ColumnsPrinter` with per-level proxies:
417
+
418
+ - `cols.info(...)`, `cols.warn(...)`, `cols.error(...)`, `cols.debug(...)`, `cols.critical(...)`
419
+ - `cols.info.header()` and `cols.info.headers()` for border + header emission
420
+ - `cols.info.simulate(...)` for dry-run width growth without output
421
+ - `cols.update(...)` for runtime schema changes
422
+ - `cols.width()` for the current rendered width
@@ -8,11 +8,14 @@ LogBar.egg-info/top_level.txt
8
8
  logbar/__init__.py
9
9
  logbar/buffer.py
10
10
  logbar/columns.py
11
+ logbar/drawing.py
11
12
  logbar/logbar.py
12
13
  logbar/progress.py
13
14
  logbar/terminal.py
14
15
  logbar/util.py
15
16
  tests/test_columns.py
17
+ tests/test_drawing.py
16
18
  tests/test_example.py
17
19
  tests/test_log.py
18
- tests/test_progress.py
20
+ tests/test_progress.py
21
+ tests/test_snake_render.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: LogBar
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: A unified Logger and ProgressBar util with zero dependencies.
5
5
  Author-email: ModelCloud <qubitium@modelcloud.ai>
6
6
  License-Expression: Apache-2.0
@@ -36,6 +36,7 @@ Dynamic: license-file
36
36
  - Shared singleton logger with per-level colorized output.
37
37
  - `once` helpers prevent duplicate log spam automatically.
38
38
  - Stackable progress bars that stay anchored while your logs flow freely.
39
+ - Sub-cell Unicode bar rasterization for smoother, more accurate terminal fills.
39
40
  - Built-in styling for progress bar fills, colors, gradients, and head glyphs.
40
41
  - Animated progress titles with a subtle sweeping highlight.
41
42
  Set `LOGBAR_ANIMATION=0` to disable the highlight animation.
@@ -52,6 +53,46 @@ pip install logbar
52
53
 
53
54
  LogBar works out-of-the-box with CPython 3.8+ on Linux, macOS, and Windows terminals.
54
55
 
56
+ ## Renderer Design
57
+
58
+ LogBar keeps progress bars, spinners, tables, and normal log lines readable in the same terminal session. Compared with traditional loggers, it lets long-running CLI programs show live status without flooding the screen with repeated status lines or breaking the flow of regular logs.
59
+
60
+ Main rendering APIs:
61
+
62
+ - `log.pb(...)` for live progress bars
63
+ - `log.spinner(...)` for work with no fixed total
64
+ - `log.columns(...)` for aligned table output
65
+
66
+ Examples:
67
+
68
+ ```py
69
+ from logbar import LogBar
70
+
71
+ log = LogBar.shared()
72
+
73
+ for _ in log.pb(range(5)).title("下载 📦").subtitle("phase 1"):
74
+ pass
75
+ ```
76
+
77
+ ```py
78
+ jobs = ["scan", "parse", "index", "flush"]
79
+ pb = log.pb(jobs, output_interval=1).title("Indexing").manual()
80
+ for job in pb:
81
+ log.info("processing %s", job)
82
+ pb.subtitle(job).draw()
83
+ ```
84
+
85
+ ```py
86
+ cols = log.columns(
87
+ {"label": "task", "width": "fit"},
88
+ {"label": "status", "width": "fit"},
89
+ {"label": "detail", "width": "50%"},
90
+ )
91
+
92
+ cols.info.header()
93
+ cols.info("render", "active", "width and alignment stay terminal-aware")
94
+ ```
95
+
55
96
  # Quick Start
56
97
 
57
98
  ```py
@@ -199,6 +240,28 @@ pb.head('>', color='82') # custom head glyph + color index
199
240
 
200
241
  `ProgressBar.available_styles()` lists builtin styles, and you can register additional ones with `ProgressBar.register_style(...)` or switch defaults globally via `ProgressBar.set_default_style(...)`. Custom colors accept ANSI escape codes, 256-color indexes (e.g. `'82'`), or hex strings (`'#4c1d95'`).
201
242
 
243
+ For direct style registration and introspection, import the advanced style APIs from `logbar.progress`:
244
+
245
+ ```py
246
+ from logbar.progress import ProgressBar, ProgressStyle, progress_style_names
247
+
248
+ print(ProgressBar.available_styles())
249
+ print(progress_style_names())
250
+
251
+ ProgressBar.register_style(
252
+ ProgressStyle(
253
+ name="ice",
254
+ fill_char="■",
255
+ empty_char="·",
256
+ fill_colors=("#7dd3fc", "#38bdf8"),
257
+ gradient=True,
258
+ head_char=">",
259
+ )
260
+ )
261
+
262
+ ProgressBar.set_default_style("ice")
263
+ ```
264
+
202
265
  Styled output (plain-text view with ANSI removed):
203
266
 
204
267
  ```
@@ -248,6 +311,14 @@ cols.update({
248
311
  })
249
312
  ```
250
313
 
314
+ Useful column helpers:
315
+
316
+ - `cols.info.header()` or `cols.info.headers()` prints the current border + header block.
317
+ - `cols.info.simulate(...)` recomputes widths without emitting a row.
318
+ - `cols.update(...)` changes labels, spans, or widths at runtime.
319
+ - `cols.width()` returns the current rendered table width, including borders.
320
+ - `cols.widths`, `cols.padding`, and `cols.column_specs` expose the current layout.
321
+
251
322
  # Replacing `tqdm`
252
323
 
253
324
  The API mirrors common `tqdm` patterns while staying more Pythonic:
@@ -275,7 +346,7 @@ with tqdm.tqdm(total=len(items)) as pb:
275
346
  with log.pb(items).manual() as pb:
276
347
  for item in pb:
277
348
  handle(item)
278
- pb.render()
349
+ pb.draw()
279
350
  ```
280
351
 
281
352
  # Advanced Tips
@@ -283,3 +354,69 @@ with log.pb(items).manual() as pb:
283
354
  - Combine columns and progress bars by logging summaries at key checkpoints.
284
355
  - Use `log.warn.once(...)` to keep noisy health checks readable.
285
356
  - For multi-line messages, pre-format text and pass it as a single string; LogBar keeps borders intact.
357
+
358
+ # API Reference
359
+
360
+ ## `LogBar`
361
+
362
+ - `LogBar.shared(override_logger=False)` returns the process-wide shared logger.
363
+ - `override_logger=True` is useful in tests or embedded environments that replaced the active `logging` logger class.
364
+ - Level methods: `debug`, `info`, `warn`, `error`, `critical`.
365
+ - Deduplicated level methods: `debug.once`, `info.once`, `warn.once`, `error.once`, `critical.once`.
366
+ - `setLevel(level)` accepts strings like `"INFO"`, `"WARN"`, `"CRIT"`, numeric levels, numeric strings, and constants such as `LogBar.WARNING`.
367
+ - `pb(iterable_or_total, output_interval=None)` creates and attaches a progress bar.
368
+ - `spinner(title="", interval=0.5, tail_length=4)` creates and attaches an indeterminate rolling progress bar.
369
+ - `columns(..., cols=None, width=None, padding=2)` creates a column printer.
370
+
371
+ ## `ProgressBar`
372
+
373
+ `log.pb(...)` returns an attached `ProgressBar`. For direct imports, use:
374
+
375
+ ```py
376
+ from logbar.progress import ProgressBar, ProgressStyle
377
+ ```
378
+
379
+ Common chainable methods:
380
+
381
+ - `title(text)` and `subtitle(text)`
382
+ - `style(name_or_style)`
383
+ - `fill(fill_char, empty=None)`
384
+ - `colors(fill=None, empty=None, gradient=None, head=None)`
385
+ - `head(char=None, color=None)`
386
+ - `set(show_left_steps=None, left_steps_offset=None)`
387
+ - `output_interval(interval)`
388
+ - `mode(RenderMode)` if you prefer explicit mode switching over `auto()` / `manual()`
389
+
390
+ Render and lifecycle control:
391
+
392
+ - `draw(force=False)` renders the current snapshot immediately.
393
+ - `auto()` enables redraw-on-iteration mode.
394
+ - `manual()` disables automatic redraw so you can call `draw()` yourself.
395
+ - `attach(logger=None)` attaches the bar to a logger.
396
+ - `detach()` detaches the bar without destroying the object.
397
+ - `close()` forces a final render if needed and removes the bar from the stack.
398
+ - `step()` returns the current iteration index and `next()` advances once outside a `for` loop.
399
+
400
+ Style registry helpers:
401
+
402
+ - `ProgressBar.available_styles()`
403
+ - `ProgressBar.register_style(style)`
404
+ - `ProgressBar.set_default_style(style)`
405
+ - `ProgressBar.default_style()`
406
+
407
+ ## `RollingProgressBar`
408
+
409
+ `log.spinner(...)` returns a `RollingProgressBar`, which inherits from `ProgressBar` and adds:
410
+
411
+ - `pulse()` to advance the spinner immediately between automatic ticks.
412
+ - `interval` and `tail_length` constructor arguments for animation speed and tail size.
413
+
414
+ ## `ColumnsPrinter`
415
+
416
+ `log.columns(...)` returns a `ColumnsPrinter` with per-level proxies:
417
+
418
+ - `cols.info(...)`, `cols.warn(...)`, `cols.error(...)`, `cols.debug(...)`, `cols.critical(...)`
419
+ - `cols.info.header()` and `cols.info.headers()` for border + header emission
420
+ - `cols.info.simulate(...)` for dry-run width growth without output
421
+ - `cols.update(...)` for runtime schema changes
422
+ - `cols.width()` for the current rendered width
@@ -21,6 +21,7 @@
21
21
  - Shared singleton logger with per-level colorized output.
22
22
  - `once` helpers prevent duplicate log spam automatically.
23
23
  - Stackable progress bars that stay anchored while your logs flow freely.
24
+ - Sub-cell Unicode bar rasterization for smoother, more accurate terminal fills.
24
25
  - Built-in styling for progress bar fills, colors, gradients, and head glyphs.
25
26
  - Animated progress titles with a subtle sweeping highlight.
26
27
  Set `LOGBAR_ANIMATION=0` to disable the highlight animation.
@@ -37,6 +38,46 @@ pip install logbar
37
38
 
38
39
  LogBar works out-of-the-box with CPython 3.8+ on Linux, macOS, and Windows terminals.
39
40
 
41
+ ## Renderer Design
42
+
43
+ LogBar keeps progress bars, spinners, tables, and normal log lines readable in the same terminal session. Compared with traditional loggers, it lets long-running CLI programs show live status without flooding the screen with repeated status lines or breaking the flow of regular logs.
44
+
45
+ Main rendering APIs:
46
+
47
+ - `log.pb(...)` for live progress bars
48
+ - `log.spinner(...)` for work with no fixed total
49
+ - `log.columns(...)` for aligned table output
50
+
51
+ Examples:
52
+
53
+ ```py
54
+ from logbar import LogBar
55
+
56
+ log = LogBar.shared()
57
+
58
+ for _ in log.pb(range(5)).title("下载 📦").subtitle("phase 1"):
59
+ pass
60
+ ```
61
+
62
+ ```py
63
+ jobs = ["scan", "parse", "index", "flush"]
64
+ pb = log.pb(jobs, output_interval=1).title("Indexing").manual()
65
+ for job in pb:
66
+ log.info("processing %s", job)
67
+ pb.subtitle(job).draw()
68
+ ```
69
+
70
+ ```py
71
+ cols = log.columns(
72
+ {"label": "task", "width": "fit"},
73
+ {"label": "status", "width": "fit"},
74
+ {"label": "detail", "width": "50%"},
75
+ )
76
+
77
+ cols.info.header()
78
+ cols.info("render", "active", "width and alignment stay terminal-aware")
79
+ ```
80
+
40
81
  # Quick Start
41
82
 
42
83
  ```py
@@ -184,6 +225,28 @@ pb.head('>', color='82') # custom head glyph + color index
184
225
 
185
226
  `ProgressBar.available_styles()` lists builtin styles, and you can register additional ones with `ProgressBar.register_style(...)` or switch defaults globally via `ProgressBar.set_default_style(...)`. Custom colors accept ANSI escape codes, 256-color indexes (e.g. `'82'`), or hex strings (`'#4c1d95'`).
186
227
 
228
+ For direct style registration and introspection, import the advanced style APIs from `logbar.progress`:
229
+
230
+ ```py
231
+ from logbar.progress import ProgressBar, ProgressStyle, progress_style_names
232
+
233
+ print(ProgressBar.available_styles())
234
+ print(progress_style_names())
235
+
236
+ ProgressBar.register_style(
237
+ ProgressStyle(
238
+ name="ice",
239
+ fill_char="■",
240
+ empty_char="·",
241
+ fill_colors=("#7dd3fc", "#38bdf8"),
242
+ gradient=True,
243
+ head_char=">",
244
+ )
245
+ )
246
+
247
+ ProgressBar.set_default_style("ice")
248
+ ```
249
+
187
250
  Styled output (plain-text view with ANSI removed):
188
251
 
189
252
  ```
@@ -233,6 +296,14 @@ cols.update({
233
296
  })
234
297
  ```
235
298
 
299
+ Useful column helpers:
300
+
301
+ - `cols.info.header()` or `cols.info.headers()` prints the current border + header block.
302
+ - `cols.info.simulate(...)` recomputes widths without emitting a row.
303
+ - `cols.update(...)` changes labels, spans, or widths at runtime.
304
+ - `cols.width()` returns the current rendered table width, including borders.
305
+ - `cols.widths`, `cols.padding`, and `cols.column_specs` expose the current layout.
306
+
236
307
  # Replacing `tqdm`
237
308
 
238
309
  The API mirrors common `tqdm` patterns while staying more Pythonic:
@@ -260,7 +331,7 @@ with tqdm.tqdm(total=len(items)) as pb:
260
331
  with log.pb(items).manual() as pb:
261
332
  for item in pb:
262
333
  handle(item)
263
- pb.render()
334
+ pb.draw()
264
335
  ```
265
336
 
266
337
  # Advanced Tips
@@ -268,3 +339,69 @@ with log.pb(items).manual() as pb:
268
339
  - Combine columns and progress bars by logging summaries at key checkpoints.
269
340
  - Use `log.warn.once(...)` to keep noisy health checks readable.
270
341
  - For multi-line messages, pre-format text and pass it as a single string; LogBar keeps borders intact.
342
+
343
+ # API Reference
344
+
345
+ ## `LogBar`
346
+
347
+ - `LogBar.shared(override_logger=False)` returns the process-wide shared logger.
348
+ - `override_logger=True` is useful in tests or embedded environments that replaced the active `logging` logger class.
349
+ - Level methods: `debug`, `info`, `warn`, `error`, `critical`.
350
+ - Deduplicated level methods: `debug.once`, `info.once`, `warn.once`, `error.once`, `critical.once`.
351
+ - `setLevel(level)` accepts strings like `"INFO"`, `"WARN"`, `"CRIT"`, numeric levels, numeric strings, and constants such as `LogBar.WARNING`.
352
+ - `pb(iterable_or_total, output_interval=None)` creates and attaches a progress bar.
353
+ - `spinner(title="", interval=0.5, tail_length=4)` creates and attaches an indeterminate rolling progress bar.
354
+ - `columns(..., cols=None, width=None, padding=2)` creates a column printer.
355
+
356
+ ## `ProgressBar`
357
+
358
+ `log.pb(...)` returns an attached `ProgressBar`. For direct imports, use:
359
+
360
+ ```py
361
+ from logbar.progress import ProgressBar, ProgressStyle
362
+ ```
363
+
364
+ Common chainable methods:
365
+
366
+ - `title(text)` and `subtitle(text)`
367
+ - `style(name_or_style)`
368
+ - `fill(fill_char, empty=None)`
369
+ - `colors(fill=None, empty=None, gradient=None, head=None)`
370
+ - `head(char=None, color=None)`
371
+ - `set(show_left_steps=None, left_steps_offset=None)`
372
+ - `output_interval(interval)`
373
+ - `mode(RenderMode)` if you prefer explicit mode switching over `auto()` / `manual()`
374
+
375
+ Render and lifecycle control:
376
+
377
+ - `draw(force=False)` renders the current snapshot immediately.
378
+ - `auto()` enables redraw-on-iteration mode.
379
+ - `manual()` disables automatic redraw so you can call `draw()` yourself.
380
+ - `attach(logger=None)` attaches the bar to a logger.
381
+ - `detach()` detaches the bar without destroying the object.
382
+ - `close()` forces a final render if needed and removes the bar from the stack.
383
+ - `step()` returns the current iteration index and `next()` advances once outside a `for` loop.
384
+
385
+ Style registry helpers:
386
+
387
+ - `ProgressBar.available_styles()`
388
+ - `ProgressBar.register_style(style)`
389
+ - `ProgressBar.set_default_style(style)`
390
+ - `ProgressBar.default_style()`
391
+
392
+ ## `RollingProgressBar`
393
+
394
+ `log.spinner(...)` returns a `RollingProgressBar`, which inherits from `ProgressBar` and adds:
395
+
396
+ - `pulse()` to advance the spinner immediately between automatic ticks.
397
+ - `interval` and `tail_length` constructor arguments for animation speed and tail size.
398
+
399
+ ## `ColumnsPrinter`
400
+
401
+ `log.columns(...)` returns a `ColumnsPrinter` with per-level proxies:
402
+
403
+ - `cols.info(...)`, `cols.warn(...)`, `cols.error(...)`, `cols.debug(...)`, `cols.critical(...)`
404
+ - `cols.info.header()` and `cols.info.headers()` for border + header emission
405
+ - `cols.info.simulate(...)` for dry-run width growth without output
406
+ - `cols.update(...)` for runtime schema changes
407
+ - `cols.width()` for the current rendered width
@@ -5,28 +5,14 @@
5
5
 
6
6
  """Column layout helpers for LogBar output."""
7
7
 
8
- import re
9
8
  from dataclasses import dataclass
10
9
  from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union
11
10
 
11
+ from .drawing import strip_ansi as _strip_ansi
12
+ from .drawing import visible_length as _visible_length
12
13
  from .terminal import terminal_size
13
14
 
14
15
 
15
- ANSI_ESCAPE_RE = re.compile(r"\x1B\[[0-9;?]*[ -/]*[@-~]")
16
-
17
-
18
- def _strip_ansi(text: str) -> str:
19
- return ANSI_ESCAPE_RE.sub("", text)
20
-
21
-
22
- def _visible_length(text: str) -> int:
23
- if not text:
24
- return 0
25
- cleaned = _strip_ansi(text)
26
- cleaned = cleaned.replace("\r", "").replace("\n", "")
27
- return len(cleaned)
28
-
29
-
30
16
  def _pad_visible(text: str, target: int) -> str:
31
17
  current = _visible_length(text)
32
18
  if current >= target: