termrender 2.0.0__tar.gz → 2.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.
Files changed (52) hide show
  1. {termrender-2.0.0 → termrender-2.1.0}/CHANGELOG.md +32 -0
  2. {termrender-2.0.0 → termrender-2.1.0}/PKG-INFO +2 -2
  3. {termrender-2.0.0 → termrender-2.1.0}/pyproject.toml +6 -1
  4. termrender-2.1.0/scripts/build-mermaid-ascii.sh +36 -0
  5. termrender-2.1.0/src/termrender/CLAUDE.md +1 -0
  6. termrender-2.1.0/src/termrender/_bin/mermaid-ascii-darwin-arm64 +0 -0
  7. termrender-2.1.0/src/termrender/_mermaid_bin.py +41 -0
  8. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/layout.py +2 -1
  9. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/CLAUDE.md +2 -0
  10. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/borders.py +1 -1
  11. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/mermaid.py +2 -1
  12. termrender-2.0.0/src/termrender/CLAUDE.md +0 -15
  13. {termrender-2.0.0 → termrender-2.1.0}/.github/workflows/publish.yml +0 -0
  14. {termrender-2.0.0 → termrender-2.1.0}/.gitignore +0 -0
  15. {termrender-2.0.0 → termrender-2.1.0}/CLAUDE.md +0 -0
  16. {termrender-2.0.0 → termrender-2.1.0}/LICENSE +0 -0
  17. {termrender-2.0.0 → termrender-2.1.0}/README.md +0 -0
  18. {termrender-2.0.0 → termrender-2.1.0}/design.json +0 -0
  19. {termrender-2.0.0 → termrender-2.1.0}/requirements.json +0 -0
  20. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/__init__.py +0 -0
  21. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/__main__.py +0 -0
  22. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/blocks.py +0 -0
  23. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/emit.py +0 -0
  24. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/parser.py +0 -0
  25. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/py.typed +0 -0
  26. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/__init__.py +0 -0
  27. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/charts.py +0 -0
  28. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/code.py +0 -0
  29. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/columns.py +0 -0
  30. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/diff.py +0 -0
  31. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/divider.py +0 -0
  32. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/panel.py +0 -0
  33. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/quote.py +0 -0
  34. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/stat.py +0 -0
  35. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/table.py +0 -0
  36. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/text.py +0 -0
  37. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/timeline.py +0 -0
  38. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/renderers/tree.py +0 -0
  39. {termrender-2.0.0 → termrender-2.1.0}/src/termrender/style.py +0 -0
  40. {termrender-2.0.0 → termrender-2.1.0}/tests/__init__.py +0 -0
  41. {termrender-2.0.0 → termrender-2.1.0}/tests/test_charts.py +0 -0
  42. {termrender-2.0.0 → termrender-2.1.0}/tests/test_cli_contract.py +0 -0
  43. {termrender-2.0.0 → termrender-2.1.0}/tests/test_column_alignment.py +0 -0
  44. {termrender-2.0.0 → termrender-2.1.0}/tests/test_diff.py +0 -0
  45. {termrender-2.0.0 → termrender-2.1.0}/tests/test_inline_badge.py +0 -0
  46. {termrender-2.0.0 → termrender-2.1.0}/tests/test_linebreak.py +0 -0
  47. {termrender-2.0.0 → termrender-2.1.0}/tests/test_mermaid_compat.py +0 -0
  48. {termrender-2.0.0 → termrender-2.1.0}/tests/test_myst_gaps.py +0 -0
  49. {termrender-2.0.0 → termrender-2.1.0}/tests/test_stat.py +0 -0
  50. {termrender-2.0.0 → termrender-2.1.0}/tests/test_tasklist.py +0 -0
  51. {termrender-2.0.0 → termrender-2.1.0}/tests/test_timeline.py +0 -0
  52. {termrender-2.0.0 → termrender-2.1.0}/tests/test_variable_colons.py +0 -0
@@ -1,6 +1,38 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v2.1.0 (2026-05-16)
5
+
6
+ ### Documentation
7
+
8
+ - **claude-md**: Document mermaid-ascii vendoring and correct stale --maxWidth
9
+ ([`643e25a`](https://github.com/crouton-labs/termrender/commit/643e25a791de1f32591b245dee064f290221cfc2))
10
+
11
+ Note _mermaid_bin resolution, the pinned master binary, the no-width-flag reality, and the back-edge
12
+ panic in renderers/CLAUDE.md.
13
+
14
+ - **claude-md**: Note QUOTE +1 height only applies to author/by attrs
15
+ ([`3184d43`](https://github.com/crouton-labs/termrender/commit/3184d43e7293af735b82d559bc3253d7542cbf85))
16
+
17
+ ### Features
18
+
19
+ - **mermaid**: Vendor engine from upstream master, fix broken -w invocation
20
+ ([`d801de5`](https://github.com/crouton-labs/termrender/commit/d801de5bb22ba5205fe52ef65e80eda96590be93))
21
+
22
+ mermaid-ascii has no -w/width flag and never has; layout.py and mermaid.py passed `-w <width>`, so
23
+ every diagram exited non-zero and degraded to raw source text on the 1.2.0 wheel. Drop -w (call is
24
+ now `-f - -y 1`).
25
+
26
+ Resolve the binary via new _mermaid_bin.mermaid_ascii_bin(): prefer vendored
27
+ _bin/mermaid-ascii-<os>-<arch> (built from pinned upstream master 6fffb8e via
28
+ scripts/build-mermaid-ascii.sh), else fall back to `mermaid-ascii` on PATH (PyPI wheel, capped at
29
+ 1.2.0). pyproject ships the binary via hatch artifacts and bumps the fallback dep to >=1.2.
30
+
31
+ Caveats: only darwin-arm64 is vendored (other platforms fall back to the older PyPI engine); a
32
+ labeled back-edge in graph LR panics the binary on both 1.2.0 and master and degrades to source.
33
+ mermaid-ascii has no width control, so wide diagrams overflow (renderer pads, never truncates).
34
+
35
+
4
36
  ## v2.0.0 (2026-05-16)
5
37
 
6
38
  ### Continuous Integration
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: termrender
3
- Version: 2.0.0
3
+ Version: 2.1.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
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Topic :: Terminals
22
22
  Classifier: Topic :: Text Processing :: Markup :: Markdown
23
23
  Requires-Python: >=3.10
24
- Requires-Dist: mermaid-ascii>=1.0
24
+ Requires-Dist: mermaid-ascii>=1.2
25
25
  Requires-Dist: mistune>=3.0
26
26
  Requires-Dist: pygments>=2.0
27
27
  Description-Content-Type: text/markdown
@@ -28,7 +28,7 @@ classifiers = [
28
28
  ]
29
29
  dependencies = [
30
30
  "mistune>=3.0",
31
- "mermaid-ascii>=1.0",
31
+ "mermaid-ascii>=1.2",
32
32
  "pygments>=2.0",
33
33
  ]
34
34
 
@@ -45,6 +45,11 @@ source = "vcs"
45
45
 
46
46
  [tool.hatch.build.targets.wheel]
47
47
  packages = ["src/termrender"]
48
+ # Ship the vendored mermaid-ascii binary (built from pinned upstream master;
49
+ # see scripts/build-mermaid-ascii.sh). It lives inside the package dir so the
50
+ # default file selection includes it; artifacts ensures it survives even if
51
+ # VCS-ignored, and hatchling preserves its 0o755 exec bit.
52
+ artifacts = ["src/termrender/_bin/*"]
48
53
 
49
54
  [tool.semantic_release]
50
55
  version_toml = []
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ # Build the vendored mermaid-ascii binary from pinned upstream master.
3
+ #
4
+ # Usage: scripts/build-mermaid-ascii.sh
5
+ #
6
+ # Produces src/termrender/_bin/mermaid-ascii-<os>-<arch> for the *host*
7
+ # platform. Run on each target platform (or with GOOS/GOARCH set) to refresh
8
+ # the vendored binaries. Requires a Go toolchain (>=1.21).
9
+ #
10
+ # Why vendored: the PyPI `mermaid-ascii` wheel only ships up to 1.2.0, which
11
+ # lacks the master-only flowchart fixes (multiline node labels, duplicate /
12
+ # bidirectional edge-label separation, subgraph titles, wide-rune label
13
+ # widths). termrender prefers this binary and falls back to the PyPI wheel's
14
+ # `mermaid-ascii` on PATH for platforms not vendored here.
15
+ set -euo pipefail
16
+
17
+ # Pinned upstream commit (github.com/AlexanderGrooff/mermaid-ascii master).
18
+ PIN="6fffb8e2714acab2c4cb41c78894fabbc62cee56"
19
+
20
+ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
21
+ workdir="$(mktemp -d)"
22
+ trap 'rm -rf "$workdir"' EXIT
23
+
24
+ git clone https://github.com/AlexanderGrooff/mermaid-ascii.git "$workdir/src"
25
+ git -C "$workdir/src" checkout --quiet "$PIN"
26
+
27
+ os="$(go env GOOS)"
28
+ arch="$(go env GOARCH)"
29
+ out="$repo_root/src/termrender/_bin/mermaid-ascii-${os}-${arch}"
30
+
31
+ mkdir -p "$(dirname "$out")"
32
+ ( cd "$workdir/src" && go build -trimpath -o "$out" . )
33
+ chmod 755 "$out"
34
+
35
+ echo "Built $out"
36
+ "$out" --help >/dev/null && echo "Smoke test OK (pin ${PIN:0:7})"
@@ -0,0 +1 @@
1
+ - **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.
@@ -0,0 +1,41 @@
1
+ """Resolve the ``mermaid-ascii`` executable.
2
+
3
+ Prefers the vendored build from pinned upstream master (commit ``6fffb8e``,
4
+ 2026-04-27) under ``_bin/mermaid-ascii-<os>-<arch>``; this carries the
5
+ master-only flowchart fixes the PyPI wheel (capped at 1.2.0) lacks. Falls back
6
+ to a ``mermaid-ascii`` on PATH (the PyPI ``mermaid-ascii`` wheel) for platforms
7
+ not vendored here.
8
+
9
+ Rebuild vendored binaries with ``scripts/build-mermaid-ascii.sh``.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import platform
16
+ from functools import lru_cache
17
+
18
+ _BIN_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "_bin")
19
+
20
+ _OS_MAP = {"darwin": "darwin", "linux": "linux", "windows": "windows"}
21
+ _ARCH_MAP = {
22
+ "arm64": "arm64",
23
+ "aarch64": "arm64",
24
+ "x86_64": "amd64",
25
+ "amd64": "amd64",
26
+ }
27
+
28
+
29
+ def _platform_tag() -> str:
30
+ osname = _OS_MAP.get(platform.system().lower(), platform.system().lower())
31
+ arch = _ARCH_MAP.get(platform.machine().lower(), platform.machine().lower())
32
+ return f"{osname}-{arch}"
33
+
34
+
35
+ @lru_cache(maxsize=1)
36
+ def mermaid_ascii_bin() -> str:
37
+ """Path to the vendored mermaid-ascii, or ``"mermaid-ascii"`` for PATH lookup."""
38
+ vendored = os.path.join(_BIN_DIR, f"mermaid-ascii-{_platform_tag()}")
39
+ if os.path.isfile(vendored) and os.access(vendored, os.X_OK):
40
+ return vendored
41
+ return "mermaid-ascii"
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import subprocess
6
6
 
7
+ from termrender._mermaid_bin import mermaid_ascii_bin
7
8
  from termrender.blocks import Block, BlockType
8
9
  from termrender.renderers.mermaid import fix_mermaid_encoding, preprocess_mermaid_for_ascii
9
10
  from termrender.style import wrap_text, visual_len
@@ -147,7 +148,7 @@ def resolve_height(block: Block) -> None:
147
148
  rendered = source # fallback
148
149
  try:
149
150
  result = subprocess.run(
150
- ["mermaid-ascii", "-f", "-", "-w", str(block.width or 80), "-y", "1"],
151
+ [mermaid_ascii_bin(), "-f", "-", "-y", "1"],
151
152
  input=preprocess_mermaid_for_ascii(source),
152
153
  capture_output=True,
153
154
  text=True,
@@ -52,3 +52,5 @@ Attribution line is rendered for `block.attrs["author"]` **or** `block.attrs["by
52
52
  **Trailing blank line**: `rendered.split("\n")` on output ending with `\n` (typical subprocess output) produces a trailing empty string, which becomes a `block.width`-wide blank line appended to every mermaid block.
53
53
 
54
54
  Layout pre-renders into `block.attrs["_rendered"]` (see `src/termrender/CLAUDE.md`); this renderer re-runs the subprocess only when that key is absent.
55
+
56
+ **Binary resolution & no width flag**: the executable comes from `_mermaid_bin.mermaid_ascii_bin()` — the vendored `_bin/mermaid-ascii-<os>-<arch>` (pinned upstream master `6fffb8e`, built via `scripts/build-mermaid-ascii.sh`) if present for the platform, else `mermaid-ascii` on PATH (the PyPI wheel, capped at 1.2.0). **`mermaid-ascii` has no width-control flag** (never has — `--maxWidth`/`-w` do not exist); the call passes only `-f - -y 1`, so `block.width` does not constrain the diagram and wide output overflows (compounding the no-truncation gotcha above). A labeled back-edge (`X -->|lbl| Y` in `graph LR`) panics the binary on **both** 1.2.0 and master; the non-zero exit is caught and the block degrades to raw source.
@@ -31,7 +31,7 @@ def render_box(
31
31
  corner_v = visual_len("┌") # same as ┐, └, ┘
32
32
 
33
33
  # Grow the box if any content line (or the title) won't fit at the
34
- # requested width. mermaid-ascii's --maxWidth is non-strict, so a child
34
+ # requested width. mermaid-ascii has no width-control flag, so a child
35
35
  # mermaid block can return lines wider than its allocated content area.
36
36
  # Truncating would corrupt the diagram; growing keeps the box's top,
37
37
  # bottom, and side borders aligned at the same column even if it
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import re
6
6
  import subprocess
7
7
 
8
+ from termrender._mermaid_bin import mermaid_ascii_bin
8
9
  from termrender.blocks import Block
9
10
  from termrender.style import visual_ljust
10
11
 
@@ -81,7 +82,7 @@ def render(block: Block, color: bool) -> list[str]:
81
82
  source = block.attrs.get("source", "")
82
83
  try:
83
84
  result = subprocess.run(
84
- ["mermaid-ascii", "-f", "-", "-w", str(block.width or 80), "-y", "1"],
85
+ [mermaid_ascii_bin(), "-f", "-", "-y", "1"],
85
86
  input=preprocess_mermaid_for_ascii(source),
86
87
  capture_output=True,
87
88
  text=True,
@@ -1,15 +0,0 @@
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`.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes