termrender 4.3.2__tar.gz → 4.5.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 (74) hide show
  1. {termrender-4.3.2 → termrender-4.5.0}/CHANGELOG.md +33 -0
  2. {termrender-4.3.2 → termrender-4.5.0}/PKG-INFO +2 -1
  3. {termrender-4.3.2 → termrender-4.5.0}/pyproject.toml +1 -0
  4. termrender-4.5.0/src/termrender/renderers/mermaid_flow.py +110 -0
  5. termrender-4.5.0/src/termrender/renderers/mermaid_flow_layout.py +952 -0
  6. termrender-4.5.0/src/termrender/renderers/mermaid_flow_parser.py +406 -0
  7. termrender-4.5.0/tests/test_mermaid_flow.py +168 -0
  8. termrender-4.5.0/tests/test_mermaid_flow_layout.py +386 -0
  9. termrender-4.5.0/tests/test_mermaid_flow_parser.py +356 -0
  10. {termrender-4.3.2 → termrender-4.5.0}/.github/workflows/publish.yml +0 -0
  11. {termrender-4.3.2 → termrender-4.5.0}/.gitignore +0 -0
  12. {termrender-4.3.2 → termrender-4.5.0}/CLAUDE.md +0 -0
  13. {termrender-4.3.2 → termrender-4.5.0}/LICENSE +0 -0
  14. {termrender-4.3.2 → termrender-4.5.0}/README.md +0 -0
  15. {termrender-4.3.2 → termrender-4.5.0}/design.json +0 -0
  16. {termrender-4.3.2 → termrender-4.5.0}/requirements.json +0 -0
  17. {termrender-4.3.2 → termrender-4.5.0}/scripts/build-mermaid-ascii.sh +0 -0
  18. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/CLAUDE.md +0 -0
  19. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/__init__.py +0 -0
  20. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/__main__.py +0 -0
  21. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/_bin/mermaid-ascii-darwin-arm64 +0 -0
  22. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/_mermaid_bin.py +0 -0
  23. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/blocks.py +0 -0
  24. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/emit.py +0 -0
  25. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/layout.py +0 -0
  26. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/parser.py +0 -0
  27. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/py.typed +0 -0
  28. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/CLAUDE.md +0 -0
  29. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/__init__.py +0 -0
  30. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/borders.py +0 -0
  31. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/charts.py +0 -0
  32. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/code.py +0 -0
  33. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/columns.py +0 -0
  34. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/diff.py +0 -0
  35. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/divider.py +0 -0
  36. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/mermaid.py +0 -0
  37. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/mermaid_flow_model.py +0 -0
  38. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/mermaid_gantt.py +0 -0
  39. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/mermaid_journey.py +0 -0
  40. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/mermaid_mindmap.py +0 -0
  41. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/mermaid_pie.py +0 -0
  42. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/mermaid_prelude.py +0 -0
  43. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/mermaid_sequence.py +0 -0
  44. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/mermaid_timeline.py +0 -0
  45. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/panel.py +0 -0
  46. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/quote.py +0 -0
  47. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/stat.py +0 -0
  48. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/table.py +0 -0
  49. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/text.py +0 -0
  50. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/timeline.py +0 -0
  51. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/renderers/tree.py +0 -0
  52. {termrender-4.3.2 → termrender-4.5.0}/src/termrender/style.py +0 -0
  53. {termrender-4.3.2 → termrender-4.5.0}/tests/__init__.py +0 -0
  54. {termrender-4.3.2 → termrender-4.5.0}/tests/test_charts.py +0 -0
  55. {termrender-4.3.2 → termrender-4.5.0}/tests/test_cli_contract.py +0 -0
  56. {termrender-4.3.2 → termrender-4.5.0}/tests/test_column_alignment.py +0 -0
  57. {termrender-4.3.2 → termrender-4.5.0}/tests/test_diff.py +0 -0
  58. {termrender-4.3.2 → termrender-4.5.0}/tests/test_inline_badge.py +0 -0
  59. {termrender-4.3.2 → termrender-4.5.0}/tests/test_linebreak.py +0 -0
  60. {termrender-4.3.2 → termrender-4.5.0}/tests/test_mermaid_compat.py +0 -0
  61. {termrender-4.3.2 → termrender-4.5.0}/tests/test_mermaid_dispatch.py +0 -0
  62. {termrender-4.3.2 → termrender-4.5.0}/tests/test_mermaid_flow_model.py +0 -0
  63. {termrender-4.3.2 → termrender-4.5.0}/tests/test_mermaid_gantt.py +0 -0
  64. {termrender-4.3.2 → termrender-4.5.0}/tests/test_mermaid_journey.py +0 -0
  65. {termrender-4.3.2 → termrender-4.5.0}/tests/test_mermaid_mindmap.py +0 -0
  66. {termrender-4.3.2 → termrender-4.5.0}/tests/test_mermaid_pie.py +0 -0
  67. {termrender-4.3.2 → termrender-4.5.0}/tests/test_mermaid_sequence.py +0 -0
  68. {termrender-4.3.2 → termrender-4.5.0}/tests/test_mermaid_timeline.py +0 -0
  69. {termrender-4.3.2 → termrender-4.5.0}/tests/test_myst_gaps.py +0 -0
  70. {termrender-4.3.2 → termrender-4.5.0}/tests/test_stat.py +0 -0
  71. {termrender-4.3.2 → termrender-4.5.0}/tests/test_tasklist.py +0 -0
  72. {termrender-4.3.2 → termrender-4.5.0}/tests/test_timeline.py +0 -0
  73. {termrender-4.3.2 → termrender-4.5.0}/tests/test_variable_colons.py +0 -0
  74. {termrender-4.3.2 → termrender-4.5.0}/uv.lock +0 -0
@@ -1,6 +1,39 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v4.5.0 (2026-07-06)
5
+
6
+ ### Features
7
+
8
+ - Add flowchart edge router and public render_flowchart
9
+ ([`a1b46cf`](https://github.com/crouton-labs/termrender/commit/a1b46cf11607ec6b31ea8fe1b4d90b11aafff10f))
10
+
11
+ - Replace the placeholder edge drawing in mermaid_flow_layout.py with the full orthogonal router:
12
+ rank-relative endpoint anchors (forward/same-rank/ back-edge), L/Z staircase paths for forward
13
+ edges, growing side-lane C-paths for back-edges so they never overlay forward edges, arrowheads
14
+ chosen from final-segment direction, edge labels centered on the longest straight run (shifting
15
+ off reserved cells), and self-loops modeled on the sequence renderer's self-message loop. - Add
16
+ mermaid_flow.py exposing the public, pure, never-raising render_flowchart(source, width) ->
17
+ list[str], wiring parse -> layout_flowgraph -> lines behind the three-case degradation contract
18
+ (no header, zero nodes, unexpected layout exception -> raw echo). - Add
19
+ tests/test_mermaid_flow.py: real end-to-end geometry/topology assertions, including the labeled
20
+ back-edge cycle that panics the Go binary, multi-parent attachment, headless edges, and echo
21
+ degradation.
22
+
23
+ Unwired: nothing imports mermaid_flow yet (later phase's cutover).
24
+
25
+
26
+ ## v4.4.0 (2026-07-06)
27
+
28
+ ### Features
29
+
30
+ - Add flowchart layout engine core (grandalf adapter + canvas rasterizer)
31
+ ([`23e26e9`](https://github.com/crouton-labs/termrender/commit/23e26e9c3228bfac302c270b4c0c5ffe880bfc20))
32
+
33
+ - Add flowchart mermaid parser
34
+ ([`c9c80c3`](https://github.com/crouton-labs/termrender/commit/c9c80c30ed638c3532ef9464ce1cb7f4af4fa3ce))
35
+
36
+
4
37
  ## v4.3.2 (2026-07-06)
5
38
 
6
39
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: termrender
3
- Version: 4.3.2
3
+ Version: 4.5.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,6 +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: grandalf>=0.8
24
25
  Requires-Dist: mermaid-ascii>=1.2
25
26
  Requires-Dist: mistune>=3.0
26
27
  Requires-Dist: pygments>=2.0
@@ -30,6 +30,7 @@ dependencies = [
30
30
  "mistune>=3.0",
31
31
  "mermaid-ascii>=1.2",
32
32
  "pygments>=2.0",
33
+ "grandalf>=0.8",
33
34
  ]
34
35
 
35
36
  [project.urls]
@@ -0,0 +1,110 @@
1
+ """Public entry point for the native mermaid flowchart/graph renderer.
2
+
3
+ Standalone module: exposes a single pure function, :func:`render_flowchart`.
4
+ Not yet wired into ``mermaid.py``'s dispatcher (a later, orchestrator-owned
5
+ phase) — nothing in this package imports it. This module is a thin
6
+ orchestrator over the parser and layout engine; it owns none of the grammar
7
+ or layout logic itself, only header sniffing and the degradation contract.
8
+
9
+ Layout model
10
+ ------------
11
+ ``render_flowchart`` is ``parse -> layout_flowgraph -> lines``, wrapped in a
12
+ degradation guard (see *Degradation contract* below). :func:`~termrender.
13
+ renderers.mermaid_flow_parser.parse` turns mermaid source into a
14
+ :class:`~termrender.renderers.mermaid_flow_model.FlowGraph`;
15
+ :func:`~termrender.renderers.mermaid_flow_layout.layout_flowgraph` lays it
16
+ out with ``grandalf`` (pure-Python Sugiyama) and rasterizes it — bordered
17
+ rectangle nodes, an orthogonal edge router (L/Z paths for forward edges,
18
+ growing side lanes for back-edges/cycles, arrowheads, edge labels,
19
+ self-loops) — onto a char grid. See ``mermaid_flow_parser.py`` and
20
+ ``mermaid_flow_layout.py``'s module docstrings for the full grammar and
21
+ routing model; this module covers only the public contract.
22
+
23
+ Grammar supported
24
+ ------------------
25
+ Header: ``graph``/``flowchart`` + direction (``TD``/``TB``/``LR``/``RL``/
26
+ ``BT``). Node shapes: ``[rect]``, ``(round)``, ``([stadium])``,
27
+ ``[(cylinder)]``, ``((circle))``, ``{diamond}``, ``{{hexagon}}``,
28
+ ``[[subroutine]]``, ``[/parallelogram/]`` — this phase renders every shape
29
+ as a plain rectangle (distinct shape borders are a later phase; only the
30
+ label text is load-bearing here). Edges: ``-->``, ``---``, ``-.->``, ``==>``
31
+ (and bidirectional forms), with ``|label|`` and inline ``A -- text --> B``
32
+ labels, and ``&`` fan-out. ``subgraph ... end`` blocks parse but are not yet
33
+ rendered as frames (nodes inside them still place and render normally).
34
+ ``class``/``classDef``/``style``/``click``/``linkStyle`` lines are ignored.
35
+
36
+ Known degradations (by design, not bugs)
37
+ -----------------------------------------
38
+ - Node shapes other than a plain rectangle border are not yet visually
39
+ distinct (Phase 4 of this renderer's build).
40
+ - Subgraph blocks parse but are not yet drawn as enclosing frames (Phase 4).
41
+ - See ``mermaid_flow_layout.py``'s module docstring for the edge-routing
42
+ degradations (dense-graph crossings, label-lane overlap, CJK wrapping,
43
+ minimum-box-size self-loop cosmetics).
44
+ - ``width`` is advisory only: like the other native renderers in this
45
+ package, this renderer sizes to content and may overflow a narrow
46
+ terminal rather than truncate the diagram.
47
+ """
48
+
49
+ from __future__ import annotations
50
+
51
+ from termrender.renderers.mermaid_flow_layout import layout_flowgraph
52
+ from termrender.renderers.mermaid_flow_parser import FlowchartError, parse
53
+
54
+ __all__ = ["render_flowchart"]
55
+
56
+
57
+ def render_flowchart(source: str, width: int) -> list[str]:
58
+ """Render a mermaid ``graph``/``flowchart`` source to unicode lines.
59
+
60
+ Pure and total: never raises. Degrades to a raw echo of ``source``
61
+ (each line ``\\n``-split and ``.rstrip()``-ed, containing no
62
+ box-drawing glyphs) exactly when:
63
+
64
+ 1. The source isn't a flowchart at all (no ``graph``/``flowchart``
65
+ header on its first non-blank, post-prelude line) — :func:`parse`
66
+ raises :class:`FlowchartError`.
67
+ 2. Parsing succeeds but yields zero nodes (an empty or comment-only
68
+ diagram body) — nothing to draw.
69
+ 3. Any unexpected exception escapes :func:`layout_flowgraph` — a
70
+ defensive catch-all; this should never happen for well-typed input,
71
+ but the guarantee is "never crash, degrade" regardless.
72
+
73
+ Otherwise, returns the rendered diagram: guaranteed to contain unicode
74
+ box-drawing glyphs (every node is a bordered box), never ANSI escapes.
75
+ The distinction matters downstream — code that detects render success
76
+ by the *presence* of box-drawing glyphs relies on the echo path never
77
+ emitting any.
78
+
79
+ Args:
80
+ source: The mermaid fence body (with or without the surrounding
81
+ fence markers — only the text between them).
82
+ width: Advisory terminal width. See the module docstring's
83
+ "Known degradations" for why this is advisory rather than a
84
+ hard wrap.
85
+
86
+ Returns:
87
+ Rendered lines on success, or a raw-echo of ``source`` on any of
88
+ the three degradation conditions above.
89
+ """
90
+ try:
91
+ graph = parse(source)
92
+ except FlowchartError:
93
+ return _raw_echo(source)
94
+
95
+ if not graph.nodes:
96
+ return _raw_echo(source)
97
+
98
+ try:
99
+ lines = layout_flowgraph(graph, width)
100
+ except Exception:
101
+ return _raw_echo(source)
102
+
103
+ if not lines:
104
+ return _raw_echo(source)
105
+
106
+ return lines
107
+
108
+
109
+ def _raw_echo(source: str) -> list[str]:
110
+ return [line.rstrip() for line in source.splitlines()]