metrale_circuit/display/
glyphs.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-28: The two glyph sets of `met circuit display`: Unicode box drawing, and a pure
4//! ASCII fallback for dumb terminals.
5//!
6//! Owner: metrale-circuit.
7//! Invariants:
8//! - Every Unicode glyph here is one terminal column wide, so a line's width is its char count
9//!   (`display_tests.rs` checks this against `unicode-width`).
10//! - The ASCII set is 7-bit only. `+`, `#` and `=` are its structural characters; the Unicode
11//!   set never emits them.
12
13/// 2026-09-28: Which glyph set to draw with.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Glyphs {
16    /// 2026-09-28: Box drawing and symbols.
17    Unicode,
18    /// 2026-09-28: 7-bit ASCII only.
19    Ascii,
20}
21
22/// 2026-09-28: One box style: corners top-left, top-right, bottom-left, bottom-right, then the
23/// horizontal and vertical strokes.
24#[derive(Debug, Clone, Copy)]
25pub(crate) struct Frame {
26    pub tl: char,
27    pub tr: char,
28    pub bl: char,
29    pub br: char,
30    pub h: char,
31    pub v: char,
32}
33
34/// 2026-09-28: Every glyph the renderer draws.
35#[derive(Debug, Clone, Copy)]
36pub(crate) struct Set {
37    /// 2026-09-28: Light ops and the header card.
38    pub light: Frame,
39    /// 2026-09-28: Heavy (opaque) ops.
40    pub heavy: Frame,
41    /// 2026-09-28: A fused group's frame.
42    pub fused: Frame,
43    /// 2026-09-28: A materialised edge's stroke.
44    pub wire: char,
45    /// 2026-09-28: A fused (on-chip) edge's stroke.
46    pub dotted: char,
47    /// 2026-09-28: Arrow head.
48    pub arrow: char,
49    /// 2026-09-28: The branch off the main path, the corner turning down into the side path,
50    /// and the corner turning back in.
51    pub fan: [char; 3],
52    /// 2026-09-28: Layer strip: GatedDeltaNet, attention, dense FFN, MoE FFN.
53    pub layer: [char; 4],
54    /// 2026-09-28: Numerics badges: bit-identical, reference, differs.
55    pub badge: [&'static str; 3],
56    /// 2026-09-28: Separator between facts.
57    pub dot: &'static str,
58    /// 2026-09-28: Truncation marker.
59    pub ellipsis: &'static str,
60    /// 2026-09-28: Multiplication sign in shapes and counts.
61    pub times: &'static str,
62    /// 2026-09-28: Kernel sequence arrow.
63    pub then: &'static str,
64    /// 2026-09-28: The layer-boundary title glyph, and the dash of the cut line inside a frame
65    /// that spans two layers.
66    pub boundary: char,
67    pub cut: char,
68    /// 2026-09-28: Marks an input read from a node other than the one above.
69    pub reads: &'static str,
70}
71
72impl Glyphs {
73    pub(crate) fn set(self) -> Set {
74        match self {
75            Glyphs::Unicode => Set {
76                light: Frame {
77                    tl: '╭',
78                    tr: '╮',
79                    bl: '╰',
80                    br: '╯',
81                    h: '─',
82                    v: '│',
83                },
84                heavy: Frame {
85                    tl: '╔',
86                    tr: '╗',
87                    bl: '╚',
88                    br: '╝',
89                    h: '═',
90                    v: '║',
91                },
92                fused: Frame {
93                    tl: '┏',
94                    tr: '┓',
95                    bl: '┗',
96                    br: '┛',
97                    h: '━',
98                    v: '┃',
99                },
100                wire: '│',
101                dotted: '┊',
102                arrow: '▼',
103                fan: ['├', '┐', '┘'],
104                layer: ['▰', '◆', '▪', '✱'],
105                badge: ["✓ bit-identical", "≈ reference", "⚠ differs"],
106                dot: " · ",
107                ellipsis: "…",
108                times: "×",
109                then: " → ",
110                reads: "◂ ",
111                boundary: '╪',
112                cut: '╌',
113            },
114            Glyphs::Ascii => Set {
115                light: Frame {
116                    tl: '+',
117                    tr: '+',
118                    bl: '+',
119                    br: '+',
120                    h: '-',
121                    v: '|',
122                },
123                heavy: Frame {
124                    tl: '#',
125                    tr: '#',
126                    bl: '#',
127                    br: '#',
128                    h: '=',
129                    v: 'H',
130                },
131                fused: Frame {
132                    tl: '#',
133                    tr: '#',
134                    bl: '#',
135                    br: '#',
136                    h: '#',
137                    v: '#',
138                },
139                wire: '|',
140                dotted: ':',
141                arrow: 'v',
142                fan: ['+', '+', '+'],
143                layer: ['G', 'A', 'd', 'm'],
144                badge: ["[ok] bit-identical", "[~] reference", "[!] differs"],
145                dot: " . ",
146                ellipsis: "...",
147                times: "x",
148                then: " -> ",
149                reads: "< ",
150                boundary: '|',
151                cut: '~',
152            },
153        }
154    }
155}