termrender 0.7.3__tar.gz → 1.0.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.
Files changed (51) hide show
  1. termrender-1.0.0/.git +1 -0
  2. {termrender-0.7.3 → termrender-1.0.0}/CHANGELOG.md +103 -28
  3. termrender-1.0.0/CLAUDE.md +25 -0
  4. {termrender-0.7.3 → termrender-1.0.0}/PKG-INFO +5 -5
  5. {termrender-0.7.3 → termrender-1.0.0}/README.md +4 -4
  6. termrender-1.0.0/src/termrender/CLAUDE.md +15 -0
  7. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/__main__.py +15 -5
  8. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/emit.py +13 -1
  9. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/layout.py +10 -4
  10. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/parser.py +13 -35
  11. termrender-1.0.0/src/termrender/renderers/mermaid.py +98 -0
  12. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/text.py +1 -1
  13. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/timeline.py +7 -9
  14. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/style.py +9 -2
  15. {termrender-0.7.3 → termrender-1.0.0}/tests/test_column_alignment.py +8 -8
  16. termrender-1.0.0/tests/test_linebreak.py +79 -0
  17. termrender-1.0.0/tests/test_mermaid_compat.py +115 -0
  18. {termrender-0.7.3 → termrender-1.0.0}/tests/test_myst_gaps.py +25 -38
  19. termrender-0.7.3/CLAUDE.md +0 -64
  20. termrender-0.7.3/src/termrender/CLAUDE.md +0 -73
  21. termrender-0.7.3/src/termrender/renderers/mermaid.py +0 -48
  22. {termrender-0.7.3 → termrender-1.0.0}/.github/workflows/publish.yml +0 -0
  23. {termrender-0.7.3 → termrender-1.0.0}/.gitignore +0 -0
  24. {termrender-0.7.3 → termrender-1.0.0}/LICENSE +0 -0
  25. {termrender-0.7.3 → termrender-1.0.0}/design.json +0 -0
  26. {termrender-0.7.3 → termrender-1.0.0}/pyproject.toml +0 -0
  27. {termrender-0.7.3 → termrender-1.0.0}/requirements.json +0 -0
  28. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/__init__.py +0 -0
  29. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/blocks.py +0 -0
  30. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/py.typed +0 -0
  31. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/CLAUDE.md +0 -0
  32. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/__init__.py +0 -0
  33. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/borders.py +0 -0
  34. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/charts.py +0 -0
  35. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/code.py +0 -0
  36. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/columns.py +0 -0
  37. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/diff.py +0 -0
  38. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/divider.py +0 -0
  39. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/panel.py +0 -0
  40. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/quote.py +0 -0
  41. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/stat.py +0 -0
  42. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/table.py +0 -0
  43. {termrender-0.7.3 → termrender-1.0.0}/src/termrender/renderers/tree.py +0 -0
  44. {termrender-0.7.3 → termrender-1.0.0}/tests/__init__.py +0 -0
  45. {termrender-0.7.3 → termrender-1.0.0}/tests/test_charts.py +0 -0
  46. {termrender-0.7.3 → termrender-1.0.0}/tests/test_diff.py +0 -0
  47. {termrender-0.7.3 → termrender-1.0.0}/tests/test_inline_badge.py +0 -0
  48. {termrender-0.7.3 → termrender-1.0.0}/tests/test_stat.py +0 -0
  49. {termrender-0.7.3 → termrender-1.0.0}/tests/test_tasklist.py +0 -0
  50. {termrender-0.7.3 → termrender-1.0.0}/tests/test_timeline.py +0 -0
  51. {termrender-0.7.3 → termrender-1.0.0}/tests/test_variable_colons.py +0 -0
termrender-1.0.0/.git ADDED
@@ -0,0 +1 @@
1
+ gitdir: /Users/silasrhyneer/Code/cli/termrender/.git/worktrees/tr-v100
@@ -1,18 +1,93 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v1.0.0 (2026-04-27)
5
+
6
+ ### Documentation
7
+
8
+ - **claude-md**: Tighten root and src CLAUDE.md
9
+ ([`7fe01ec`](https://github.com/crouton-labs/termrender/commit/7fe01ec2da0c7a0c1aa0f4eccf9b958496562be8))
10
+
11
+ ### Features
12
+
13
+ - **mermaid**: Switch to :::mermaid directive, drop backtick fence forms
14
+ ([`083f590`](https://github.com/crouton-labs/termrender/commit/083f5900b4b516f9df599dd08129afee34310e3d))
15
+
16
+ BREAKING CHANGE: ```mermaid fenced code blocks are no longer rendered as mermaid diagrams — they now
17
+ render as plain code blocks. Mermaid diagrams must use the new :::mermaid directive. The
18
+ MyST-style ```{name} backtick directive form is also removed; backtick fences now always produce a
19
+ code block, regardless of the language tag. Every directive uses ::: exclusively.
20
+
21
+ ### Breaking Changes
22
+
23
+ - **mermaid**: ```mermaid fenced code blocks are no longer rendered as mermaid diagrams — they now
24
+ render as plain code blocks. Mermaid diagrams must use the new :::mermaid directive. The
25
+ MyST-style ```{name} backtick directive form is also removed; backtick fences now always produce a
26
+ code block, regardless of the language tag. Every directive uses ::: exclusively.
27
+
28
+
29
+ ## v0.9.1 (2026-04-25)
30
+
31
+ ### Bug Fixes
32
+
33
+ - **timeline**: Wrap event text instead of truncating with ellipsis
34
+ ([`5af0e04`](https://github.com/crouton-labs/termrender/commit/5af0e04c3256075a2016e2cf8ae3d44e9e78c8fc))
35
+
36
+ Long event entries previously got clipped with `…` when they exceeded event_w. Now they wrap across
37
+ multiple lines, with continuation lines indented under the bullet and prefixed by the accent bar.
38
+ Layout height sums per-entry wrapped line counts so the block reserves the right space.
39
+
40
+
41
+ ## v0.9.0 (2026-04-21)
42
+
43
+ ### Bug Fixes
44
+
45
+ - **wrap**: Honor hard line breaks in wrap_text
46
+ ([`a383f4d`](https://github.com/crouton-labs/termrender/commit/a383f4de66b3d8370bda500dd4c3771a591563aa))
47
+
48
+ Markdown hard breaks were parsed as \n spans but wrap_text only split on spaces, leaking raw \n into
49
+ wrapped output. Inside panels and columns this broke border alignment because visual_ljust padded
50
+ the string once, not per visual line.
51
+
52
+ wrap_text now recursively wraps each \n-separated segment; the text-renderer offset heuristic skips
53
+ \n as well as space between lines. Layout height calcs pick up the extra lines automatically.
54
+
55
+ ### Features
56
+
57
+ - **spacing**: Add blank lines between hard breaks and top-level blocks
58
+ ([`7610189`](https://github.com/crouton-labs/termrender/commit/761018928504cf9626678fe46b5ee66d5e899d5d))
59
+
60
+ Hard line breaks now render a blank line between the two sides (parser emits \n\n so wrap_text
61
+ naturally produces the gap), and DOCUMENT-level siblings are separated by a blank padded line so
62
+ paragraphs, headings, and blocks no longer visually run together.
63
+
64
+
65
+ ## v0.8.0 (2026-04-18)
66
+
67
+ ### Features
68
+
69
+ - **mermaid**: Preprocess sequence diagrams for mermaid-ascii compatibility
70
+ ([`a642576`](https://github.com/crouton-labs/termrender/commit/a642576d41d5dbde372d7de2ab47745296a78e32))
71
+
72
+ mermaid-ascii only parses ->> / -->> arrows, participants, and self-loops; every other common
73
+ sequence-diagram construct made it fail and fall back to raw source. Rewrite Note lines into
74
+ self-loops, map -> / -x / --x / -) / --) / -- > onto the supported arrow pair, drop block keywords
75
+ (loop/alt/activate/ autonumber/end/…), and flatten <br/> to ' / '. Non-sequence diagrams pass
76
+ through unchanged.
77
+
78
+
4
79
  ## v0.7.3 (2026-04-15)
5
80
 
6
81
  ### Bug Fixes
7
82
 
8
83
  - **code**: Wrap long code lines to fit layout width
9
- ([`31c6e59`](https://github.com/CaptainCrouton89/termrender/commit/31c6e595a438c4ced8c61fff679b59d4ae55f938))
84
+ ([`31c6e59`](https://github.com/crouton-labs/termrender/commit/31c6e595a438c4ced8c61fff679b59d4ae55f938))
10
85
 
11
86
  Code blocks previously used raw line count for height and let render_box grow beyond the layout
12
87
  allocation. Now wraps source lines to the available content width in both layout and renderer.
13
88
 
14
89
  - **parser**: Add directive trace and file-absolute line numbers to error messages
15
- ([`0f99ea0`](https://github.com/CaptainCrouton89/termrender/commit/0f99ea0310116f8fa06e933cd26126246d7a3b43))
90
+ ([`0f99ea0`](https://github.com/crouton-labs/termrender/commit/0f99ea0310116f8fa06e933cd26126246d7a3b43))
16
91
 
17
92
  Stray-closer and unclosed-directive errors now print the full open/close trace and, when nested
18
93
  directives share a colon count, name the specific cause and suggest the fix. Recursive body
@@ -25,7 +100,7 @@ Stray-closer and unclosed-directive errors now print the full open/close trace a
25
100
  ### Bug Fixes
26
101
 
27
102
  - **cli**: Default --tmux pane to 1/3 window width
28
- ([`d9c1bcc`](https://github.com/CaptainCrouton89/termrender/commit/d9c1bccbe95a4e5cf1f975b82cbafde6d9d3807a))
103
+ ([`d9c1bcc`](https://github.com/crouton-labs/termrender/commit/d9c1bccbe95a4e5cf1f975b82cbafde6d9d3807a))
29
104
 
30
105
  Instead of preview-rendering at 80 cols to measure content width, default to (window_width - 2) // 3
31
106
  for a consistent 1/3 split.
@@ -36,7 +111,7 @@ Instead of preview-rendering at 80 cols to measure content width, default to (wi
36
111
  ### Bug Fixes
37
112
 
38
113
  - **cli**: Give --pane error paths actionable recovery guidance
39
- ([`f857c32`](https://github.com/CaptainCrouton89/termrender/commit/f857c32c89afe32a3a668f03a3d570b0f14dae97))
114
+ ([`f857c32`](https://github.com/crouton-labs/termrender/commit/f857c32c89afe32a3a668f03a3d570b0f14dae97))
40
115
 
41
116
  The two --pane error paths now tell the agent how to recover instead of restating the problem.
42
117
  "Check that the pane id is valid" is a dead end for an agent — it needs either a command to list
@@ -48,7 +123,7 @@ The two --pane error paths now tell the agent how to recover instead of restatin
48
123
  ### Features
49
124
 
50
125
  - **cli**: Add --pane for in-place tmux pane updates
51
- ([`4ab1d77`](https://github.com/CaptainCrouton89/termrender/commit/4ab1d77b996aa356926407dcc11c1b408e68e0ee))
126
+ ([`4ab1d77`](https://github.com/crouton-labs/termrender/commit/4ab1d77b996aa356926407dcc11c1b408e68e0ee))
52
127
 
53
128
  --tmux now prints the newly-created pane id to stdout (via split-window -P -F) so callers can
54
129
  capture it for subsequent updates. --pane <ID> targets an existing pane via tmux respawn-pane -k
@@ -68,7 +143,7 @@ Also in this commit: - Expand -h epilog to cover the 8 visualization directives
68
143
  ### Bug Fixes
69
144
 
70
145
  - **borders**: Grow render_box to fit overflowing content and titles
71
- ([`dc108c8`](https://github.com/CaptainCrouton89/termrender/commit/dc108c8242763828245569f719abce64b26ddf5b))
146
+ ([`dc108c8`](https://github.com/crouton-labs/termrender/commit/dc108c8242763828245569f719abce64b26ddf5b))
72
147
 
73
148
  mermaid-ascii's --maxWidth is non-strict, so a child mermaid block can return lines wider than the
74
149
  panel's allocated content area. Previously the side walls floated outward to accommodate the
@@ -85,7 +160,7 @@ render_box now measures the widest content line (and the title) and grows its ef
85
160
  ### Features
86
161
 
87
162
  - Add diff, charts, stat, timeline, tasklist, and inline badges
88
- ([`e14f615`](https://github.com/CaptainCrouton89/termrender/commit/e14f615ae8d0723405db61c79b0f858d7bf0f863))
163
+ ([`e14f615`](https://github.com/crouton-labs/termrender/commit/e14f615ae8d0723405db61c79b0f858d7bf0f863))
89
164
 
90
165
  New block-level directives: - :::diff — colored unified diff with +/- gutters - :::bar — multi-bar
91
166
  chart with sub-cell precision via eighth blocks - :::progress — single-line progress bar (auto
@@ -108,7 +183,7 @@ Cross-cutting changes: - InlineSpan gained fg/bg fields; render_spans and span-s
108
183
  63 new tests across six test files. All 94 tests pass.
109
184
 
110
185
  - **cli**: Add --watch mode for live re-rendering
111
- ([`4223ad8`](https://github.com/CaptainCrouton89/termrender/commit/4223ad86805b0b3ad45450bd7ca4441a668f0e23))
186
+ ([`4223ad8`](https://github.com/crouton-labs/termrender/commit/4223ad86805b0b3ad45450bd7ca4441a668f0e23))
112
187
 
113
188
  Re-renders the file whenever its mtime changes, with terminal-resize detection and inline error
114
189
  display so the watcher survives malformed input. Uses the alternate screen buffer so Ctrl+C
@@ -120,7 +195,7 @@ Composes with --tmux: --tmux --watch points the spawned pane at the real file pa
120
195
  ### Refactoring
121
196
 
122
197
  - **parser**: Require strictly more colons on outer fences
123
- ([`4a501d9`](https://github.com/CaptainCrouton89/termrender/commit/4a501d917db191f758874bb6c3d922c879a763be))
198
+ ([`4a501d9`](https://github.com/crouton-labs/termrender/commit/4a501d917db191f758874bb6c3d922c879a763be))
124
199
 
125
200
  Drops the depth-counter that allowed `:::outer ... :::inner ... ::: ... :::` nesting with same colon
126
201
  counts. Termrender now matches the standard followed by MyST, Pandoc fenced divs,
@@ -140,7 +215,7 @@ Fixtures in test_column_alignment.py rewritten to ascending colon counts (7/6/5/
140
215
  ### Features
141
216
 
142
217
  - **table**: Render horizontal separator lines between data rows
143
- ([`3e4c74a`](https://github.com/CaptainCrouton89/termrender/commit/3e4c74a10d63470f2eb2ec096bb47cf41f0b7f70))
218
+ ([`3e4c74a`](https://github.com/crouton-labs/termrender/commit/3e4c74a10d63470f2eb2ec096bb47cf41f0b7f70))
144
219
 
145
220
 
146
221
  ## v0.4.0 (2026-04-05)
@@ -148,7 +223,7 @@ Fixtures in test_column_alignment.py rewritten to ascending colon counts (7/6/5/
148
223
  ### Features
149
224
 
150
225
  - **parser**: Variable colon counts, backtick fence directives, and gloam-inspired theming
151
- ([`47fac7f`](https://github.com/CaptainCrouton89/termrender/commit/47fac7fcf13d33e5d9986d3f9ca42ddaf5e7207d))
226
+ ([`47fac7f`](https://github.com/crouton-labs/termrender/commit/47fac7fcf13d33e5d9986d3f9ca42ddaf5e7207d))
152
227
 
153
228
  Parser changes: - Support 3+ colon openers/closers with stack-based matching - Backtick fence
154
229
  directive syntax (```{name}) via mistune AST interception - Option line stripping (:key: value)
@@ -170,18 +245,18 @@ Theming (gloam-inspired defaults): - Headings: depth-based colored fg + dim tint
170
245
  ### Documentation
171
246
 
172
247
  - Update CLAUDE.md notes for mermaid, tmux, and layout
173
- ([`9e104d5`](https://github.com/CaptainCrouton89/termrender/commit/9e104d5ee7bad9a57902e79586c02b0e8d80c589))
248
+ ([`9e104d5`](https://github.com/crouton-labs/termrender/commit/9e104d5ee7bad9a57902e79586c02b0e8d80c589))
174
249
 
175
250
  ### Features
176
251
 
177
252
  - **cli**: Auto-size tmux pane to fit rendered content
178
- ([`91f0414`](https://github.com/CaptainCrouton89/termrender/commit/91f0414d0bf8bfbe4d7167159b928ed9c736db74))
253
+ ([`91f0414`](https://github.com/crouton-labs/termrender/commit/91f0414d0bf8bfbe4d7167159b928ed9c736db74))
179
254
 
180
255
  - **mermaid**: Pass width and vertical padding to mermaid-ascii
181
- ([`96145c2`](https://github.com/CaptainCrouton89/termrender/commit/96145c2789a52a4d94e9bc5f4adf7f3a88d8501f))
256
+ ([`96145c2`](https://github.com/crouton-labs/termrender/commit/96145c2789a52a4d94e9bc5f4adf7f3a88d8501f))
182
257
 
183
258
  - **table**: Auto-wrap cell content when columns overflow
184
- ([`0fae56f`](https://github.com/CaptainCrouton89/termrender/commit/0fae56f8f00260c3263671df9a63a5bea17820bb))
259
+ ([`0fae56f`](https://github.com/crouton-labs/termrender/commit/0fae56f8f00260c3263671df9a63a5bea17820bb))
185
260
 
186
261
  When a table exceeds available width, cells now wrap text within their proportionally-shrunk column
187
262
  widths instead of overflowing. Layout height calculation updated to account for multi-line cells.
@@ -192,7 +267,7 @@ When a table exceeds available width, cells now wrap text within their proportio
192
267
  ### Bug Fixes
193
268
 
194
269
  - **mermaid**: Undo double-encoded UTF-8 from mermaid-ascii output
195
- ([`9e0560c`](https://github.com/CaptainCrouton89/termrender/commit/9e0560ce46b6dc3f90d2d716a97780713e5e5e53))
270
+ ([`9e0560c`](https://github.com/crouton-labs/termrender/commit/9e0560ce46b6dc3f90d2d716a97780713e5e5e53))
196
271
 
197
272
  mermaid-ascii misinterprets UTF-8 bytes as Latin-1 and re-encodes, corrupting multi-byte characters
198
273
  (e.g. → renders as â<U+0086><U+0092>). Apply latin-1 round-trip to recover original UTF-8 in both
@@ -201,7 +276,7 @@ mermaid-ascii misinterprets UTF-8 bytes as Latin-1 and re-encodes, corrupting mu
201
276
  ### Documentation
202
277
 
203
278
  - Add tmux pane lifecycle and --check interaction notes to CLAUDE.md
204
- ([`9400092`](https://github.com/CaptainCrouton89/termrender/commit/9400092e507d470acb97ac5a17b66fcf0e9aa2f6))
279
+ ([`9400092`](https://github.com/crouton-labs/termrender/commit/9400092e507d470acb97ac5a17b66fcf0e9aa2f6))
205
280
 
206
281
 
207
282
  ## v0.2.0 (2026-04-05)
@@ -209,27 +284,27 @@ mermaid-ascii misinterprets UTF-8 bytes as Latin-1 and re-encodes, corrupting mu
209
284
  ### Bug Fixes
210
285
 
211
286
  - Handle zero-width and emoji presentation chars in visual width calculation
212
- ([`d0bb8dc`](https://github.com/CaptainCrouton89/termrender/commit/d0bb8dcfa5ca0d2c16d78a1d7f81825231b9cb59))
287
+ ([`d0bb8dc`](https://github.com/crouton-labs/termrender/commit/d0bb8dcfa5ca0d2c16d78a1d7f81825231b9cb59))
213
288
 
214
289
  _char_width now returns 0 for combining marks and format characters (ZWJ, variation selectors).
215
290
  visual_len handles VS16 emoji presentation sequences by promoting the preceding character to width
216
291
  2. Fixes panel border misalignment when content contains emoji or special Unicode.
217
292
 
218
293
  - **docs**: Update README output examples to match actual rendered output
219
- ([`de6d0cc`](https://github.com/CaptainCrouton89/termrender/commit/de6d0ccfd8a60aca20f2b2659a313f8d8c87d853))
294
+ ([`de6d0cc`](https://github.com/crouton-labs/termrender/commit/de6d0ccfd8a60aca20f2b2659a313f8d8c87d853))
220
295
 
221
296
  ### Chores
222
297
 
223
298
  - Add README, design specs, and project CLAUDE.md files
224
- ([`93ac358`](https://github.com/CaptainCrouton89/termrender/commit/93ac35857981c549797a9359573cacea1478b3ad))
299
+ ([`93ac358`](https://github.com/crouton-labs/termrender/commit/93ac35857981c549797a9359573cacea1478b3ad))
225
300
 
226
301
  - Derive version from git tags via hatch-vcs
227
- ([`33595a0`](https://github.com/CaptainCrouton89/termrender/commit/33595a0b64363e445b90c9df135a50a4652e2bae))
302
+ ([`33595a0`](https://github.com/crouton-labs/termrender/commit/33595a0b64363e445b90c9df135a50a4652e2bae))
228
303
 
229
304
  ### Continuous Integration
230
305
 
231
306
  - Auto-release and publish via conventional commits
232
- ([`80a456b`](https://github.com/CaptainCrouton89/termrender/commit/80a456b7301c57f2fd2b0cd30622b78f2d4b931e))
307
+ ([`80a456b`](https://github.com/crouton-labs/termrender/commit/80a456b7301c57f2fd2b0cd30622b78f2d4b931e))
233
308
 
234
309
  Replace manual GitHub release trigger with python-semantic-release. On push to main, conventional
235
310
  commits are analyzed to determine version bumps (feat→minor, fix→patch) and publish to PyPI
@@ -238,12 +313,12 @@ Replace manual GitHub release trigger with python-semantic-release. On push to m
238
313
  ### Documentation
239
314
 
240
315
  - Update README token count and expand CLAUDE.md implementation notes
241
- ([`1f70a53`](https://github.com/CaptainCrouton89/termrender/commit/1f70a5352cbced30219012dbede7040c6ac97457))
316
+ ([`1f70a53`](https://github.com/crouton-labs/termrender/commit/1f70a5352cbced30219012dbede7040c6ac97457))
242
317
 
243
318
  ### Features
244
319
 
245
320
  - Add CJK ambiguous-width support, strict directive parsing, and rendering fixes
246
- ([`c000883`](https://github.com/CaptainCrouton89/termrender/commit/c0008835d66b721b0a09c7a34dde11d08b3d3d94))
321
+ ([`c000883`](https://github.com/crouton-labs/termrender/commit/c0008835d66b721b0a09c7a34dde11d08b3d3d94))
247
322
 
248
323
  - Add emoji presentation and East Asian ambiguous-width character handling with --cjk flag and
249
324
  TERMRENDER_CJK env var - All renderers (borders, divider, quote, tree) now compute box-drawing
@@ -252,14 +327,14 @@ Replace manual GitHub release trigger with python-semantic-release. On push to m
252
327
  for inter-column gaps - Support 'author' as alias for 'by' attribute on quote blocks
253
328
 
254
329
  - Add GFM table rendering with box-drawing borders
255
- ([`c3b61cd`](https://github.com/CaptainCrouton89/termrender/commit/c3b61cdd659fdb782089cbca2fd3f74b18486605))
330
+ ([`c3b61cd`](https://github.com/crouton-labs/termrender/commit/c3b61cdd659fdb782089cbca2fd3f74b18486605))
256
331
 
257
332
  Enable mistune table plugin, parse table AST into TABLE blocks, and render with box-drawing
258
333
  characters. Supports left/center/right column alignment, bold headers, auto-sized columns, and
259
334
  proportional overflow distribution.
260
335
 
261
336
  - **cli**: Add --tmux pane output, --check validation, and structured error handling
262
- ([`36b52ee`](https://github.com/CaptainCrouton89/termrender/commit/36b52eed9701cd0acd363db2d0fa3d277244c8b0))
337
+ ([`36b52ee`](https://github.com/crouton-labs/termrender/commit/36b52eed9701cd0acd363db2d0fa3d277244c8b0))
263
338
 
264
339
  - --tmux renders in a new tmux side pane via split-window, piped through less -R - --check validates
265
340
  directive syntax without rendering (exit 0/2) - Structured _error() helper with fix/hint guidance
@@ -269,12 +344,12 @@ Enable mistune table plugin, parse table AST into TABLE blocks, and render with
269
344
  depth note
270
345
 
271
346
  - **cli**: Improve help output with examples, version flag, and tty detection
272
- ([`cb3e7e2`](https://github.com/CaptainCrouton89/termrender/commit/cb3e7e2752ae860e5c3cbd4c4f1627e925a9c431))
347
+ ([`cb3e7e2`](https://github.com/crouton-labs/termrender/commit/cb3e7e2752ae860e5c3cbd4c4f1627e925a9c431))
273
348
 
274
349
  ### Testing
275
350
 
276
351
  - Add column alignment and visual width tests
277
- ([`f8b6099`](https://github.com/CaptainCrouton89/termrender/commit/f8b60998625977b10dd4697f8e772d80125cb9ce))
352
+ ([`f8b6099`](https://github.com/crouton-labs/termrender/commit/f8b60998625977b10dd4697f8e772d80125cb9ce))
278
353
 
279
354
  Covers showpiece rendering, column line width consistency, status marker visual widths (text vs
280
355
  emoji presentation), and panel border alignment.
@@ -0,0 +1,25 @@
1
+ # termrender
2
+
3
+ ## Commands
4
+ ```bash
5
+ pip install -e .
6
+ pytest tests/
7
+ python -m termrender <file.md>
8
+ python -m build
9
+ ```
10
+
11
+ No linter or formatter is configured.
12
+
13
+ ## Constraints
14
+ - **Layout pass order is load-bearing**: `resolve_width()` top-down must complete before `resolve_height()` bottom-up — height calls `wrap_text(text, width)`, which requires width already set.
15
+ - **`borders.py` `render_box` width**: takes **total** width including borders, not content width. Passing content width silently overflows.
16
+ - **`wrap_text()` CJK bug**: uses `len()` internally, not `visual_len()` — silently overflows for CJK content.
17
+ - **`_ambiguous_width` is global mutable state** with no reset path — `set_ambiguous_width()` or `TERMRENDER_CJK` env var changes persist for the process lifetime.
18
+ - **Version**: derived from git tags via hatch-vcs — no version in `pyproject.toml`. Adding one will conflict.
19
+ - **Commits**: conventional commits. `feat` → minor, `fix`/`perf` → patch. Auto-released via python-semantic-release on main.
20
+
21
+ ## Supplementary CLAUDE.md files
22
+ - `src/termrender/CLAUDE.md` — parser, layout, mermaid, nesting, and `--check`/`--tmux` implementation gotchas
23
+ - `src/termrender/renderers/CLAUDE.md` — renderer contracts, `render_box` width semantics, EAW edge cases
24
+
25
+ Read these before modifying layout, parsing, or renderer code.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: termrender
3
- Version: 0.7.3
3
+ Version: 1.0.0
4
4
  Summary: Rich terminal rendering of directive-flavored markdown
5
5
  Project-URL: Homepage, https://github.com/CaptainCrouton89/termrender
6
6
  Project-URL: Repository, https://github.com/CaptainCrouton89/termrender
@@ -393,17 +393,17 @@ Horizontal rules with optional centered labels.
393
393
 
394
394
  ### Mermaid diagrams
395
395
 
396
- Renders mermaid flowcharts as ASCII art via [mermaid-ascii](https://github.com/mermaid-js/mermaid-ascii). Use standard mermaid fenced code blocks.
396
+ Renders mermaid flowcharts as ASCII art via [mermaid-ascii](https://github.com/mermaid-js/mermaid-ascii). Mermaid uses the `:::mermaid` directive — backtick fences (`` ```mermaid ``) are treated as plain code blocks.
397
397
 
398
398
  **Input:**
399
- ````markdown
400
- ```mermaid
399
+ ```markdown
400
+ :::mermaid
401
401
  graph LR
402
402
  A[Request] --> B{Auth?}
403
403
  B -->|Yes| C[Handler]
404
404
  B -->|No| D[401]
405
+ :::
405
406
  ```
406
- ````
407
407
 
408
408
  The diagram gets rendered as ASCII art inline in the terminal output. Requires `mermaid-ascii` to be installed (it's a dependency, so it should be).
409
409
 
@@ -365,17 +365,17 @@ Horizontal rules with optional centered labels.
365
365
 
366
366
  ### Mermaid diagrams
367
367
 
368
- Renders mermaid flowcharts as ASCII art via [mermaid-ascii](https://github.com/mermaid-js/mermaid-ascii). Use standard mermaid fenced code blocks.
368
+ Renders mermaid flowcharts as ASCII art via [mermaid-ascii](https://github.com/mermaid-js/mermaid-ascii). Mermaid uses the `:::mermaid` directive — backtick fences (`` ```mermaid ``) are treated as plain code blocks.
369
369
 
370
370
  **Input:**
371
- ````markdown
372
- ```mermaid
371
+ ```markdown
372
+ :::mermaid
373
373
  graph LR
374
374
  A[Request] --> B{Auth?}
375
375
  B -->|Yes| C[Handler]
376
376
  B -->|No| D[401]
377
+ :::
377
378
  ```
378
- ````
379
379
 
380
380
  The diagram gets rendered as ASCII art inline in the terminal output. Requires `mermaid-ascii` to be installed (it's a dependency, so it should be).
381
381
 
@@ -0,0 +1,15 @@
1
+ - `layout.py` imports `fix_mermaid_encoding` and `preprocess_mermaid_for_ascii` from `renderers/mermaid.py` — the only reverse dependency from layout into renderers. Reorganizing `renderers/` must preserve these imports. The two mermaid subprocess call sites differ: layout uses `check=True` (non-zero exit raises → caught → raw-source fallback); the renderer omits `check` (non-zero exit silently reads `stdout`, which may be empty or partial).
2
+
3
+ - `--check` calls `parse()` and exits — it never runs `layout.py`. Layout-time failures (mermaid subprocess missing, column percent overflow, `resolve_width`/`resolve_height` exceptions) pass `--check` cleanly but crash at render time.
4
+
5
+ - Column widths: explicit percent/absolute allocations are subtracted first; remaining space is split among auto-width columns with `max(remaining, 0)`. Two columns each claiming 80% of a 100px terminal leaves auto-width columns with `width = 1` — no error, no proportional scaling.
6
+
7
+ - **QUOTE** height gets `+1` only when `author` or `by` attr is set. Using any other key (`attribution`, `source`) silently omits the extra line — the renderer's attribution line is clipped.
8
+
9
+ - **LIST_ITEM** layout wraps at `max(width - 2, 1)`, hardcoding a 2-column indent. If the renderer changes the indent width, layout height and actual render height diverge silently.
10
+
11
+ - `text.py:32–33`: after each wrapped line, the offset skips one character if the next plain-text character is a space (the space `wrap_text` consumed). If wrapping preserves trailing spaces, this skip shifts subsequent span styling by one character.
12
+
13
+ - `--tmux` exits `EXIT_OK` after the tmux pane command succeeds — the `--check` branch comes later and is never reached. `--check` is silently dropped when combined with `--tmux`.
14
+
15
+ - `_EMOJI_WIDE_RANGES` in `style.py`: `_char_width()` exits early when `cp < lo`, assuming ranges are in ascending codepoint order. Adding a range out of order silently misclassifies all codepoints whose `lo` is higher than the inserted range's `lo`.
@@ -53,7 +53,8 @@ directives (close each with a matching colon count):
53
53
  :::tasklist Checkbox list — [x] checked, [ ] unchecked, [!] in-progress
54
54
  Plain lists with at least one marker auto-promote; use the directive
55
55
  to force unchecked styling on items without explicit markers.
56
- ```mermaid ... ``` Mermaid diagram (via mermaid-ascii)
56
+ :::mermaid Mermaid diagram (via mermaid-ascii)
57
+ body: standard mermaid source (graph TD, flowchart, sequenceDiagram, ...)
57
58
 
58
59
  Inline:
59
60
  :badge[text]{color=c} Inline pill badge
@@ -335,18 +336,27 @@ def main() -> None:
335
336
  if args.width:
336
337
  pane_width = args.width
337
338
  else:
338
- # Default: 1/3 of window width (minus separator)
339
+ # Default sizing aims for a readable side pane (~80 cols) and
340
+ # only falls back to 1/3-of-window when the window is too narrow
341
+ # to give that much without crushing the source pane.
339
342
  try:
340
343
  result = subprocess.run(
341
344
  ["tmux", "display-message", "-p", "#{window_width}"],
342
345
  capture_output=True, text=True, check=True,
343
346
  )
344
347
  window_width = int(result.stdout.strip())
345
- pane_width = (window_width - 2) // 3
348
+ # Prefer 80 cols if the source pane keeps at least 60.
349
+ # Otherwise split evenly. Floor of 40 keeps text legible.
350
+ if window_width >= 140:
351
+ pane_width = 80
352
+ elif window_width >= 100:
353
+ pane_width = window_width // 2
354
+ else:
355
+ pane_width = max(window_width - 2 - 50, 40)
346
356
  except Exception:
347
- pane_width = 60
357
+ pane_width = 80
348
358
 
349
- pane_width = max(pane_width, 20) # absolute minimum
359
+ pane_width = max(pane_width, 40) # absolute minimum for readability
350
360
 
351
361
  # Watch mode points the new pane at the user's real file so edits
352
362
  # propagate; non-watch mode snapshots source into a tempfile.
@@ -7,12 +7,24 @@ from termrender.renderers import (
7
7
  panel, columns, tree, code, text, divider, quote, mermaid, table,
8
8
  diff, charts, stat, timeline,
9
9
  )
10
+ from termrender.style import visual_ljust
10
11
 
11
12
 
12
13
  def emit_block(block: Block, color: bool) -> list[str]:
13
14
  """Render a single block and its children, returning output lines."""
14
15
  match block.type:
15
- case BlockType.DOCUMENT | BlockType.COL:
16
+ case BlockType.DOCUMENT:
17
+ # Insert a blank padded line between top-level siblings so
18
+ # paragraphs, headings, and blocks don't visually run together.
19
+ lines: list[str] = []
20
+ sep = visual_ljust("", block.width or 0)
21
+ for i, child in enumerate(block.children):
22
+ if i > 0:
23
+ lines.append(sep)
24
+ lines.extend(emit_block(child, color))
25
+ return lines
26
+
27
+ case BlockType.COL:
16
28
  lines: list[str] = []
17
29
  for child in block.children:
18
30
  lines.extend(emit_block(child, color))
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import subprocess
6
6
 
7
7
  from termrender.blocks import Block, BlockType
8
- from termrender.renderers.mermaid import fix_mermaid_encoding
8
+ from termrender.renderers.mermaid import fix_mermaid_encoding, preprocess_mermaid_for_ascii
9
9
  from termrender.style import wrap_text, visual_len
10
10
 
11
11
 
@@ -148,7 +148,7 @@ def resolve_height(block: Block) -> None:
148
148
  try:
149
149
  result = subprocess.run(
150
150
  ["mermaid-ascii", "-f", "-", "-w", str(block.width or 80), "-y", "1"],
151
- input=source,
151
+ input=preprocess_mermaid_for_ascii(source),
152
152
  capture_output=True,
153
153
  text=True,
154
154
  check=True,
@@ -192,9 +192,15 @@ def resolve_height(block: Block) -> None:
192
192
  elif bt == BlockType.TIMELINE:
193
193
  entries = block.attrs.get("entries", [])
194
194
  title_h = 1 if block.attrs.get("title") else 0
195
- # Each entry takes 1 line + 1 connector line between entries (none after last)
196
195
  if entries:
197
- block.height = title_h + len(entries) * 2 - 1
196
+ date_w = max(visual_len(e["date"]) for e in entries)
197
+ event_w = max((block.width or 60) - date_w - 4, 5)
198
+ total = 0
199
+ for entry in entries:
200
+ wrapped = wrap_text(entry["event"], event_w) or [""]
201
+ total += len(wrapped)
202
+ total += len(entries) - 1 # connector between entries
203
+ block.height = title_h + total
198
204
  else:
199
205
  block.height = max(title_h, 1)
200
206
 
@@ -69,15 +69,13 @@ _DIRECTIVE_TO_BLOCK: dict[str, BlockType] = {
69
69
  "gauge": BlockType.GAUGE,
70
70
  "stat": BlockType.STAT,
71
71
  "timeline": BlockType.TIMELINE,
72
+ "mermaid": BlockType.MERMAID,
72
73
  "tasklist": BlockType.LIST, # alias: forces tasklist styling on the inner list
73
74
  }
74
75
 
75
76
  _SELF_CLOSING_DIRECTIVES = frozenset({"divider", "progress", "gauge"})
76
77
 
77
- # MyST backtick fence directive: ```{name} optional-argument
78
- _BACKTICK_DIRECTIVE_RE = re.compile(r"^\{(\w[\w-]*)\}(.*)")
79
-
80
- # MyST option line: :key: value — intentionally requires a value after the key
78
+ # Option line: :key: value — intentionally requires a value after the key
81
79
  # (the \s+(.+) part). Flag-style options like :nosandbox: (no value) won't match
82
80
  # and will be treated as body content.
83
81
  _OPTION_LINE_RE = re.compile(r"^:(\w[\w-]*):\s+(.+)$")
@@ -134,7 +132,10 @@ def _convert_inline(nodes: list[dict]) -> list[InlineSpan]:
134
132
  elif ntype == "softbreak":
135
133
  spans.append(InlineSpan(text=" "))
136
134
  elif ntype == "linebreak":
137
- spans.append(InlineSpan(text="\n"))
135
+ # Two \n so wrap_text emits a blank line between the two sides
136
+ # of the hard break, giving more vertical breathing room than a
137
+ # soft wrap. See tests/test_linebreak.py.
138
+ spans.append(InlineSpan(text="\n\n"))
138
139
  else:
139
140
  # Fallback: try raw text
140
141
  if "raw" in node:
@@ -245,7 +246,7 @@ def _expand_inline_roles(spans: list[InlineSpan]) -> list[InlineSpan]:
245
246
 
246
247
 
247
248
  def _strip_options(body: str) -> tuple[dict[str, str], str]:
248
- """Strip MyST option lines from the start of a directive body.
249
+ """Strip option lines from the start of a directive body.
249
250
 
250
251
  Option lines have the form `:key: value` and appear at the start of the body.
251
252
  Blank lines between option lines are allowed. Scanning stops at the first
@@ -302,33 +303,10 @@ def _convert_ast(nodes: list[dict], _depth: int = 0) -> list[Block]:
302
303
  elif ntype == "block_code":
303
304
  raw = node.get("raw", "")
304
305
  info = node.get("attrs", {}).get("info", "")
305
- # MyST backtick fence directive: ```{name} optional-arg
306
- m_directive = _BACKTICK_DIRECTIVE_RE.match(info) if info else None
307
- if m_directive:
308
- dir_name = m_directive.group(1)
309
- arg_text = m_directive.group(2).strip()
310
- if dir_name == "mermaid":
311
- options, body = _strip_options(raw)
312
- attrs = dict(options)
313
- if arg_text:
314
- attrs["argument"] = arg_text
315
- attrs["source"] = body
316
- blocks.append(Block(type=BlockType.MERMAID, attrs=attrs))
317
- else:
318
- attrs: dict[str, Any] = {}
319
- if arg_text:
320
- attrs["argument"] = arg_text
321
- blocks.append(_directive_to_block(dir_name, attrs, raw, _depth=_depth))
322
- elif info == "mermaid":
323
- blocks.append(Block(
324
- type=BlockType.MERMAID,
325
- attrs={"source": raw},
326
- ))
327
- else:
328
- blocks.append(Block(
329
- type=BlockType.CODE,
330
- attrs={"lang": info, "source": raw},
331
- ))
306
+ blocks.append(Block(
307
+ type=BlockType.CODE,
308
+ attrs={"lang": info, "source": raw},
309
+ ))
332
310
 
333
311
  elif ntype == "list":
334
312
  ordered = node.get("attrs", {}).get("ordered", False)
@@ -610,8 +588,8 @@ def _directive_to_block(name: str, attrs: dict[str, Any], body: str, _depth: int
610
588
 
611
589
  block_type = _DIRECTIVE_TO_BLOCK.get(name, BlockType.PANEL)
612
590
 
613
- # Tree, Code, Diff: store raw body, don't parse as markdown
614
- if block_type in (BlockType.TREE, BlockType.CODE, BlockType.DIFF):
591
+ # Tree, Code, Diff, Mermaid: store raw body, don't parse as markdown
592
+ if block_type in (BlockType.TREE, BlockType.CODE, BlockType.DIFF, BlockType.MERMAID):
615
593
  attrs["source"] = body
616
594
  return Block(type=block_type, attrs=attrs)
617
595