@avodado/render 0.0.1

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.
package/dist/index.js ADDED
@@ -0,0 +1,2995 @@
1
+ import { Marked } from 'marked';
2
+
3
+ // src/css.ts
4
+ var houseCss = `*{box-sizing:border-box;margin:0;padding:0;}
5
+ html{scroll-behavior:smooth;}
6
+ /* Design tokens live on :root so a theme (applied as :root overrides) reaches
7
+ the whole page \u2014 body chrome included, not just .docskin content. */
8
+ :root{
9
+ --navy:#0e54a1; --navy-tint:#b5d4f2; --blue:#1a6dbe; --light-blue:#e5eff8;
10
+ --charcoal:#1a1a2e; --slate:#374151; --gray:#6b7280; --light-gray:#f3f4f6;
11
+ --rule:#d1d5db; --highlight:#f7952c; --highlight-soft:#fde7cd;
12
+ --positive:#1f9747; --positive-soft:#dcf1e2; --negative:#991b1b; --negative-soft:#fee2e2;
13
+ --purple:#6b21a8; --purple-soft:#ede9fe; --teal:#0f766e; --teal-soft:#ccfbf1; --white:#fff;
14
+ --radius:0px;
15
+ --font-display:Georgia,"Times New Roman",serif;
16
+ --font-body:Arial,"Helvetica Neue",sans-serif;
17
+ --font-mono:"Consolas","Monaco","Courier New",monospace;
18
+ }
19
+ body{background:var(--white);color:var(--charcoal);font-family:var(--font-body);font-size:14px;line-height:1.55;}
20
+ .docskin{
21
+ background:var(--white); color:var(--charcoal); font-family:var(--font-body); font-size:14px; line-height:1.55;
22
+ max-width:1180px; margin:0 auto; padding:0 56px 128px;
23
+ }
24
+ .docskin .cover-bar{height:8px;background:var(--navy);margin:0 -56px 36px;}
25
+ .docskin .cover-pad{padding:0 0 40px;margin-bottom:56px;border-bottom:1px solid var(--rule);}
26
+ .docskin .cover-meta{display:flex;justify-content:space-between;flex-wrap:wrap;gap:16px;font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:var(--gray);font-weight:600;margin-bottom:32px;}
27
+ .docskin .cover-meta .accent{color:var(--highlight);}
28
+ .docskin .cover-title{font-family:var(--font-display);font-weight:700;font-size:clamp(32px,4.4vw,50px);line-height:1.1;letter-spacing:-.02em;color:var(--navy);margin:0 0 20px;}
29
+ .docskin .cover-sub{font-size:17px;line-height:1.5;color:var(--slate);max-width:820px;margin:0 0 36px;}
30
+ .docskin .section{padding:0;margin-bottom:64px;}
31
+ .docskin .section > *:last-child{margin-bottom:0;}
32
+ .docskin .section-num{font-size:11px;text-transform:uppercase;letter-spacing:.14em;color:var(--highlight);font-weight:700;margin-bottom:8px;}
33
+ .docskin .section-head{margin-bottom:28px;padding-bottom:16px;border-bottom:2px solid var(--navy);}
34
+ .docskin .section-head .section-title{border-bottom:0;padding-bottom:0;margin-bottom:14px;}
35
+ .docskin .section-title{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3vw,32px);line-height:1.15;letter-spacing:-.015em;color:var(--navy);margin:0 0 14px;padding-bottom:12px;border-bottom:2px solid var(--navy);}
36
+ .docskin .section-lede{font-size:14.5px;color:var(--slate);line-height:1.55;max-width:820px;margin:0;}
37
+ .docskin .section-block{margin-bottom:64px;}
38
+ .docskin .section-block:last-child{margin-bottom:0;}
39
+ .docskin .diagram{margin:28px 0 36px;border:1px solid var(--rule);background:var(--white);padding:22px 26px 18px;border-radius:var(--radius);}
40
+ .docskin .diagram-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:10px;padding-bottom:12px;margin-bottom:16px;border-bottom:1px dashed var(--rule);}
41
+ .docskin .diagram-tag{font-family:var(--font-mono);font-size:10px;font-weight:700;padding:3px 9px;background:var(--navy);color:var(--white);letter-spacing:.08em;text-transform:uppercase;}
42
+ .docskin .diagram-tag.post{background:var(--navy);} .docskin .diagram-tag.get{background:var(--positive);} .docskin .diagram-tag.c4{background:var(--blue);}
43
+ .docskin .diagram-title{font-family:var(--font-display);font-weight:700;font-size:16px;color:var(--charcoal);flex:1;}
44
+ .docskin .diagram-fignum{font-size:10px;color:var(--gray);text-transform:uppercase;letter-spacing:.1em;font-weight:700;}
45
+ .docskin .diagram-desc{font-size:13px;color:var(--slate);margin:0 0 12px;}
46
+ .docskin .diagram svg{display:block;margin:0 auto;max-width:100%;height:auto;}
47
+ /* sequence */
48
+ .docskin .lane-head{fill:var(--navy);} .docskin .lane-head.ext{fill:var(--slate);}
49
+ .docskin .lane-head-text{fill:var(--white);font-family:var(--font-body);font-size:12px;font-weight:700;text-anchor:middle;}
50
+ .docskin .lane-head-sub{fill:var(--navy-tint);font-family:var(--font-mono);font-size:9px;text-anchor:middle;letter-spacing:.06em;}
51
+ .docskin .lane-head-sub.ext{fill:#cbd5e1;}
52
+ .docskin .lifeline{stroke:var(--gray);stroke-width:1;stroke-dasharray:3 3;}
53
+ .docskin .activation{fill:var(--light-blue);stroke:var(--navy);stroke-width:1;} .docskin .activation.pg{fill:var(--positive-soft);stroke:var(--positive);}
54
+ .docskin .msg-line{stroke:var(--charcoal);stroke-width:1.2;fill:none;}
55
+ .docskin .msg-line.dashed{stroke-dasharray:5 3;} .docskin .msg-line.err{stroke:var(--negative);stroke-width:1.4;}
56
+ .docskin .msg-text{fill:var(--charcoal);font-family:var(--font-mono);font-size:10.5px;}
57
+ .docskin .msg-text.em{fill:var(--navy);font-weight:700;} .docskin .msg-text.err{fill:var(--negative);font-weight:700;} .docskin .msg-text.note{fill:var(--gray);font-style:italic;}
58
+ .docskin .step-badge{fill:var(--navy);} .docskin .step-badge.err{fill:var(--negative);}
59
+ .docskin .step-badge-text{fill:var(--white);font-family:var(--font-mono);font-size:10px;font-weight:700;text-anchor:middle;}
60
+ .docskin .seq-steps{margin-top:16px;padding:14px 18px;background:var(--light-gray);border:1px solid var(--rule);}
61
+ .docskin .seq-steps-title{font-size:10px;text-transform:uppercase;letter-spacing:.12em;color:var(--navy);font-weight:700;margin-bottom:8px;}
62
+ .docskin .seq-steps ol{list-style:none;counter-reset:step;padding:0;margin:0;}
63
+ .docskin .seq-steps li{counter-increment:step;padding:7px 0 8px 40px;position:relative;border-bottom:1px solid var(--rule);}
64
+ .docskin .seq-steps li:last-child{border-bottom:none;}
65
+ .docskin .seq-steps li::before{content:counter(step);position:absolute;left:0;top:7px;width:26px;height:20px;background:var(--navy);color:var(--white);font-family:var(--font-mono);font-size:11px;font-weight:700;text-align:center;line-height:20px;border-radius:2px;}
66
+ .docskin .seq-steps li.err::before{background:var(--negative);}
67
+ .docskin .step-actor{font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--navy);margin-right:8px;text-transform:uppercase;letter-spacing:.06em;}
68
+ .docskin .step-actor.err{color:var(--negative);}
69
+ .docskin .step-summary{font-size:13px;color:var(--charcoal);}
70
+ /* c4 */
71
+ .docskin .c4-name{font-family:var(--font-display);font-size:14px;font-weight:700;}
72
+ .docskin .c4-tech{font-family:var(--font-mono);font-size:9.5px;}
73
+ .docskin .c4-desc{font-family:var(--font-body);font-size:10px;}
74
+ .docskin .c4-chip{font-family:var(--font-body);font-size:8px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;}
75
+ .docskin .edge-label{font-family:var(--font-body);font-size:9.5px;fill:var(--slate);text-anchor:middle;}
76
+ .docskin .edge-label.err{fill:var(--negative);font-weight:700;}
77
+ .docskin .c4-boundary{fill:none;stroke:var(--navy);stroke-width:1.4;stroke-dasharray:8 5;}
78
+ .docskin .c4-boundary-label{font-family:var(--font-body);font-size:10px;font-weight:700;fill:var(--navy);letter-spacing:.08em;text-transform:uppercase;}
79
+ .docskin .legend{display:flex;flex-wrap:wrap;gap:8px 16px;margin-top:14px;padding-top:12px;border-top:1px dashed var(--rule);}
80
+ .docskin .legend .item{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--slate);}
81
+ .docskin .legend .sw{width:13px;height:13px;border-radius:3px;}
82
+ /* code block */
83
+ .docskin .code-block{margin:12px 0 16px;border:1px solid var(--rule);}
84
+ .docskin .code-header{display:flex;justify-content:space-between;padding:6px 14px;background:var(--light-gray);font-family:var(--font-mono);font-size:11px;font-weight:700;color:var(--slate);border-bottom:1px solid var(--rule);letter-spacing:.04em;}
85
+ .docskin .code-block pre{padding:14px 16px;font-family:var(--font-mono);font-size:12px;line-height:1.55;color:var(--charcoal);overflow-x:auto;background:var(--white);white-space:pre;margin:0;}
86
+ .docskin .code-block .kw{color:var(--navy);font-weight:700;} .docskin .code-block .com{color:var(--gray);font-style:italic;}
87
+ .docskin .code-block .str{color:var(--positive);} .docskin .code-block .num{color:var(--purple);} .docskin .code-block .fn{color:var(--blue);} .docskin .code-block .ty{color:var(--teal);}
88
+ /* er */
89
+ .docskin .er-head-text{fill:#fff;font-family:var(--font-display);font-size:13px;font-weight:700;text-anchor:middle;}
90
+ .docskin .er-col{font-family:var(--font-mono);font-size:10.5px;fill:var(--charcoal);} .docskin .er-col.dim{fill:var(--gray);}
91
+ .docskin .er-key{font-family:var(--font-mono);font-size:9px;font-weight:700;fill:var(--navy);} .docskin .er-key.fk{fill:var(--highlight);}
92
+ .docskin .er-rowline{stroke:var(--light-gray);stroke-width:1;}
93
+ /* block / state / flow shared text */
94
+ .docskin .blk-name{font-family:var(--font-display);font-size:13px;font-weight:700;}
95
+ .docskin .blk-tech{font-family:var(--font-mono);font-size:9.5px;}
96
+ .docskin .blk-chip{font-family:var(--font-body);font-size:8px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;}
97
+ .docskin .grp-label{font-family:var(--font-body);font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;}
98
+ .docskin .sm-name{font-family:var(--font-display);font-size:13px;font-weight:700;text-anchor:middle;}
99
+ .docskin .fc-label{font-family:var(--font-display);font-size:12px;font-weight:700;text-anchor:middle;}
100
+ .docskin .endpoint-card{border:1px solid var(--rule);margin:16px 0;padding:18px 22px;background:var(--white);}
101
+ .docskin .endpoint-header{display:flex;align-items:center;gap:12px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px dashed var(--rule);}
102
+ .docskin .endpoint-method{font-family:var(--font-mono);font-size:11px;font-weight:700;padding:4px 10px;color:var(--white);letter-spacing:.08em;text-transform:uppercase;}
103
+ .docskin .endpoint-method.get{background:var(--positive);} .docskin .endpoint-method.post{background:var(--navy);} .docskin .endpoint-method.patch{background:var(--highlight);} .docskin .endpoint-method.delete{background:var(--negative);} .docskin .endpoint-method.put{background:var(--blue);}
104
+ .docskin .endpoint-path{font-family:var(--font-mono);font-size:15px;font-weight:700;color:var(--charcoal);flex:1;}
105
+ .docskin .endpoint-status{font-family:var(--font-mono);font-size:11.5px;color:var(--positive);font-weight:700;}
106
+ .docskin .endpoint-desc{font-size:13px;color:var(--slate);margin:0 0 8px;}
107
+ .docskin .endpoint-card h4{font-size:11px;text-transform:uppercase;letter-spacing:.12em;font-weight:700;color:var(--highlight);margin:14px 0 6px;}
108
+ .docskin .endpoint-card ul{margin:0 0 0 20px;} .docskin .endpoint-card li{font-size:13px;margin-bottom:3px;}
109
+ .docskin code{font-family:var(--font-mono);font-size:.86em;background:var(--light-gray);padding:2px 6px;border-radius:2px;border:1px solid var(--rule);}
110
+ .docskin .transition-table{width:100%;border-collapse:collapse;margin:16px 0 8px;font-size:12px;}
111
+ .docskin .transition-table thead{background:var(--navy);color:#fff;}
112
+ .docskin .transition-table th{text-align:left;padding:8px 10px;font-size:9.5px;text-transform:uppercase;letter-spacing:.1em;}
113
+ .docskin .transition-table td{padding:8px 10px;border-bottom:1px solid var(--rule);vertical-align:top;}
114
+ .docskin .pill{display:inline-block;font-family:var(--font-mono);font-size:10px;font-weight:700;padding:2px 7px;border-radius:2px;text-transform:uppercase;}
115
+ .docskin .pill-init{background:var(--light-gray);color:var(--slate);border:1px solid var(--rule);}
116
+ .docskin .pill-active{background:var(--positive-soft);color:var(--positive);border:1px solid var(--positive);}
117
+ .docskin .pill-wait{background:var(--highlight-soft);color:var(--highlight);border:1px solid var(--highlight);}
118
+ .docskin .pill-end{background:var(--charcoal);color:#fff;}
119
+ /* presentation: comparison table */
120
+ .docskin .pres-table{width:100%;border-collapse:collapse;margin:14px 0;font-size:13px;}
121
+ .docskin .pres-table thead{background:var(--navy);color:#fff;}
122
+ .docskin .pres-table th{padding:9px 12px;text-align:left;font-family:var(--font-body);font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;}
123
+ .docskin .pres-table th.r,.docskin .pres-table td.r{text-align:right;} .docskin .pres-table th.c,.docskin .pres-table td.c{text-align:center;}
124
+ .docskin .pres-table th.hi{background:var(--highlight);}
125
+ .docskin .pres-table td{padding:9px 12px;border-bottom:1px solid var(--rule);}
126
+ .docskin .pres-table tbody tr:nth-child(even){background:var(--light-gray);}
127
+ .docskin .pres-table td.lead{font-weight:700;color:var(--navy);font-family:var(--font-display);}
128
+ .docskin .pres-table td.hi{background:var(--highlight-soft);}
129
+ .docskin .cell-pos{color:var(--positive);font-weight:700;} .docskin .cell-neg{color:var(--negative);font-weight:700;} .docskin .cell-warn{color:var(--highlight);font-weight:700;} .docskin .cell-muted{color:var(--gray);}
130
+ .docskin .badge{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;font-size:12px;font-weight:700;}
131
+ .docskin .badge.yes{background:var(--positive-soft);color:var(--positive);} .docskin .badge.no{background:var(--negative-soft);color:var(--negative);}
132
+ .docskin .tbl-note{font-size:11px;color:var(--gray);font-style:italic;margin-top:6px;}
133
+ /* presentation: stat cards */
134
+ .docskin .stat-row{display:flex;flex-wrap:wrap;gap:14px;margin:16px 0;}
135
+ .docskin .stat-card{flex:1 1 150px;border:1px solid var(--rule);border-top:3px solid var(--navy);padding:16px 18px;background:var(--white);}
136
+ .docskin .stat-value{font-family:var(--font-display);font-size:30px;font-weight:700;color:var(--navy);line-height:1;}
137
+ .docskin .stat-label{font-size:10.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--gray);font-weight:700;margin-top:8px;}
138
+ .docskin .stat-delta{font-family:var(--font-mono);font-size:12px;font-weight:700;margin-top:6px;}
139
+ .docskin .stat-delta.up{color:var(--positive);} .docskin .stat-delta.down{color:var(--negative);} .docskin .stat-delta.flat{color:var(--gray);}
140
+ /* presentation: timeline */
141
+ .docskin .tl{position:relative;margin:18px 0;padding-left:8px;}
142
+ .docskin .tl::before{content:"";position:absolute;left:9px;top:6px;bottom:6px;width:2px;background:var(--rule);}
143
+ .docskin .tl-item{position:relative;padding:0 0 18px 30px;}
144
+ .docskin .tl-item:last-child{padding-bottom:0;}
145
+ .docskin .tl-dot{position:absolute;left:2px;top:2px;width:16px;height:16px;border-radius:50%;background:var(--white);border:3px solid var(--rule);box-sizing:border-box;}
146
+ .docskin .tl-dot.done{background:var(--positive);border-color:var(--positive);} .docskin .tl-dot.current{background:var(--highlight);border-color:var(--highlight);} .docskin .tl-dot.next{border-color:var(--navy);}
147
+ .docskin .tl-date{font-family:var(--font-mono);font-size:10.5px;font-weight:700;color:var(--highlight);text-transform:uppercase;letter-spacing:.06em;}
148
+ .docskin .tl-label{font-family:var(--font-display);font-size:15px;font-weight:700;color:var(--navy);margin:1px 0 2px;}
149
+ .docskin .tl-desc{font-size:12.5px;color:var(--slate);}
150
+ /* presentation: quadrant */
151
+ .docskin .quad-axis{stroke:var(--charcoal);stroke-width:1.5;}
152
+ .docskin .quad-end{font-family:var(--font-body);font-size:10px;font-weight:700;fill:var(--gray);text-transform:uppercase;letter-spacing:.06em;}
153
+ .docskin .quad-title{font-family:var(--font-body);font-size:11px;font-weight:700;fill:var(--navy);text-transform:uppercase;letter-spacing:.08em;}
154
+ .docskin .quad-pt-label{font-family:var(--font-body);font-size:11px;font-weight:700;fill:var(--charcoal);}
155
+ .docskin .toc{margin:18px 0 6px;padding:14px 20px;background:var(--light-gray);border:1px solid var(--rule);border-left:4px solid var(--highlight);}
156
+ .docskin .toc-title{font-size:10px;text-transform:uppercase;letter-spacing:.12em;color:var(--navy);font-weight:700;margin-bottom:8px;}
157
+ .docskin .toc ol{margin:0;padding-left:20px;} .docskin .toc li{font-size:13px;margin-bottom:4px;color:var(--slate);}
158
+ .docskin .toc li span{color:var(--gray);font-family:var(--font-mono);font-size:11px;}
159
+ /* swimlane */
160
+ .docskin .sl-lane-label{font-family:var(--font-display);font-size:12px;font-weight:700;fill:#fff;}
161
+ .docskin .sl-step{font-family:var(--font-body);font-size:11px;font-weight:700;text-anchor:middle;}
162
+ /* callouts */
163
+ .docskin .callout{border:1px solid var(--rule);border-left:4px solid var(--navy);padding:12px 16px;margin:10px 0;}
164
+ .docskin .callout.note{border-left-color:var(--navy);background:var(--light-blue);} .docskin .callout.tip{border-left-color:var(--positive);background:var(--positive-soft);} .docskin .callout.warn{border-left-color:var(--highlight);background:var(--highlight-soft);} .docskin .callout.danger{border-left-color:var(--negative);background:var(--negative-soft);}
165
+ .docskin .callout-title{font-size:10.5px;text-transform:uppercase;letter-spacing:.1em;font-weight:700;margin-bottom:4px;}
166
+ .docskin .callout.note .callout-title{color:var(--navy);} .docskin .callout.tip .callout-title{color:var(--positive);} .docskin .callout.warn .callout-title{color:#b45309;} .docskin .callout.danger .callout-title{color:var(--negative);}
167
+ .docskin .callout-body{font-size:13px;color:var(--slate);}
168
+ /* prose */
169
+ .docskin .prose h2{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3vw,32px);line-height:1.15;letter-spacing:-.015em;color:var(--navy);margin:40px 0 14px;padding-bottom:12px;border-bottom:2px solid var(--navy);}
170
+ .docskin .prose h2:first-child{margin-top:0;}
171
+ .docskin .prose h3{font-family:var(--font-display);font-weight:700;font-size:19px;letter-spacing:-.005em;color:var(--navy);margin:36px 0 12px;}
172
+ .docskin .prose h4{font-size:11px;text-transform:uppercase;letter-spacing:.12em;font-weight:700;color:var(--highlight);margin:22px 0 8px;}
173
+ .docskin .prose p{font-size:14px;color:var(--charcoal);margin:0 0 14px;line-height:1.6;max-width:880px;}
174
+ .docskin .prose ul,.docskin .prose ol{margin:0 0 14px 22px;}
175
+ .docskin .prose li{font-size:13.5px;color:var(--charcoal);margin-bottom:4px;max-width:880px;line-height:1.55;}
176
+ .docskin .prose blockquote{border-left:3px solid var(--highlight);padding:4px 14px;margin:14px 0;color:var(--slate);font-style:italic;font-family:var(--font-display);}
177
+ .docskin .prose code{font-family:var(--font-mono);font-size:.86em;background:var(--light-gray);padding:2px 6px;border-radius:2px;color:var(--charcoal);border:1px solid var(--rule);}
178
+ .docskin .prose strong{font-weight:700;color:var(--charcoal);}
179
+ .docskin .prose em{font-style:italic;}
180
+ /* glossary */
181
+ .docskin .glossary{margin:10px 0;}
182
+ .docskin .glossary .row{display:grid;grid-template-columns:170px 1fr;gap:14px;padding:9px 0;border-bottom:1px solid var(--rule);}
183
+ .docskin .glossary dt{font-family:var(--font-mono);font-size:13px;font-weight:700;color:var(--navy);}
184
+ .docskin .glossary dd{margin:0;font-size:13px;color:var(--slate);}
185
+ /* pros / cons */
186
+ .docskin .pc{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin:12px 0;}
187
+ .docskin .pc-col{border:1px solid var(--rule);padding:14px 16px;}
188
+ .docskin .pc-col.pro{border-top:3px solid var(--positive);} .docskin .pc-col.con{border-top:3px solid var(--negative);}
189
+ .docskin .pc-head{font-size:11px;text-transform:uppercase;letter-spacing:.1em;font-weight:700;margin-bottom:8px;}
190
+ .docskin .pc-col.pro .pc-head{color:var(--positive);} .docskin .pc-col.con .pc-head{color:var(--negative);}
191
+ .docskin .pc-item{font-size:13px;color:var(--slate);padding:4px 0 4px 22px;position:relative;}
192
+ .docskin .pc-item::before{position:absolute;left:0;top:4px;font-weight:700;}
193
+ .docskin .pc-col.pro .pc-item::before{content:"\\2713";color:var(--positive);} .docskin .pc-col.con .pc-item::before{content:"\\2717";color:var(--negative);}
194
+ /* current / target */
195
+ .docskin .ct{display:flex;align-items:stretch;margin:12px 0;}
196
+ .docskin .ct-panel{flex:1;border:1px solid var(--rule);padding:14px 18px;}
197
+ .docskin .ct-panel.cur{background:var(--light-gray);} .docskin .ct-panel.tgt{border-top:3px solid var(--navy);}
198
+ .docskin .ct-arrow{display:flex;align-items:center;padding:0 14px;color:var(--highlight);font-size:22px;font-weight:700;}
199
+ .docskin .ct-label{font-size:11px;text-transform:uppercase;letter-spacing:.1em;font-weight:700;color:var(--gray);margin-bottom:8px;}
200
+ .docskin .ct-panel.tgt .ct-label{color:var(--navy);}
201
+ .docskin .ct-item{font-size:13px;color:var(--slate);padding:3px 0;}
202
+ /* kanban */
203
+ .docskin .kanban{display:flex;gap:14px;margin:12px 0;overflow-x:auto;}
204
+ .docskin .kan-col{flex:1 1 0;min-width:150px;background:var(--light-gray);border:1px solid var(--rule);}
205
+ .docskin .kan-head{background:var(--navy);color:#fff;font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;padding:8px 12px;}
206
+ .docskin .kan-card{background:var(--white);border:1px solid var(--rule);border-left:3px solid var(--highlight);margin:8px;padding:9px 11px;}
207
+ .docskin .kan-card-title{font-size:13px;font-weight:700;color:var(--charcoal);}
208
+ .docskin .kan-card-tag{display:inline-block;font-family:var(--font-mono);font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--gray);margin-top:4px;}
209
+ /* pass 2 chart labels */
210
+ .docskin .dfd-name{font-family:var(--font-display);font-size:12px;font-weight:700;text-anchor:middle;}
211
+ .docskin .dfd-num{font-family:var(--font-mono);font-size:9px;font-weight:700;}
212
+ .docskin .gantt-label{font-family:var(--font-body);font-size:11px;fill:var(--charcoal);}
213
+ .docskin .gantt-head{font-family:var(--font-mono);font-size:9.5px;fill:var(--gray);text-anchor:middle;}
214
+ .docskin .funnel-label{font-family:var(--font-display);font-size:13px;font-weight:700;fill:#fff;text-anchor:middle;}
215
+ .docskin .funnel-val{font-family:var(--font-mono);font-size:11px;fill:#fff;text-anchor:middle;opacity:.9;}
216
+ .docskin .pyr-label{font-family:var(--font-display);font-size:13px;font-weight:700;fill:#fff;text-anchor:middle;}
217
+ .docskin .pyr-desc{font-family:var(--font-body);font-size:10px;fill:#fff;text-anchor:middle;opacity:.85;}
218
+ /* indented tree */
219
+ .docskin .tree-list{margin:10px 0;font-size:13px;}
220
+ .docskin .tree-row{display:flex;align-items:baseline;padding:3px 0;}
221
+ .docskin .tree-row .tw{color:var(--gray);margin-right:8px;font-family:var(--font-mono);font-size:11px;}
222
+ .docskin .tree-row.branch .tw{color:var(--navy);}
223
+ .docskin .tree-row .tlabel{color:var(--charcoal);font-family:var(--font-mono);}
224
+ .docskin .tree-row.branch .tlabel{font-weight:700;color:var(--navy);}
225
+ .docskin .tree-row .tnote{color:var(--gray);font-size:11px;margin-left:10px;font-family:var(--font-body);font-style:italic;}
226
+ /* agenda */
227
+ .docskin .agenda{margin:10px 0;}
228
+ .docskin .agenda-row{display:grid;grid-template-columns:88px 1fr;gap:14px;padding:10px 0;border-bottom:1px solid var(--rule);}
229
+ .docskin .agenda-time{font-family:var(--font-mono);font-size:12px;font-weight:700;color:var(--navy);}
230
+ .docskin .agenda-dur{font-family:var(--font-mono);font-size:10px;color:var(--gray);margin-top:2px;}
231
+ .docskin .agenda-title{font-family:var(--font-display);font-size:14px;font-weight:700;color:var(--charcoal);}
232
+ .docskin .agenda-owner{font-size:10.5px;color:var(--highlight);font-weight:700;text-transform:uppercase;letter-spacing:.05em;margin-left:8px;}
233
+ .docskin .agenda-desc{font-size:12.5px;color:var(--slate);margin-top:2px;}
234
+ /* tracker */
235
+ .docskin .trk{width:100%;border-collapse:collapse;margin:12px 0;font-size:13px;}
236
+ .docskin .trk thead{background:var(--navy);color:#fff;} .docskin .trk th{padding:8px 10px;text-align:left;font-size:9.5px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;}
237
+ .docskin .trk td{padding:8px 10px;border-bottom:1px solid var(--rule);vertical-align:middle;}
238
+ .docskin .trk tr.done .trk-task{text-decoration:line-through;color:var(--gray);}
239
+ .docskin .st{display:inline-block;font-family:var(--font-mono);font-size:9.5px;font-weight:700;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.04em;}
240
+ .docskin .st.todo{background:var(--light-gray);color:var(--slate);border:1px solid var(--rule);} .docskin .st.doing{background:var(--highlight-soft);color:#b45309;} .docskin .st.done{background:var(--positive-soft);color:var(--positive);} .docskin .st.blocked{background:var(--negative-soft);color:var(--negative);}
241
+ .docskin .pri{font-family:var(--font-mono);font-size:10px;font-weight:700;} .docskin .pri.high{color:var(--negative);} .docskin .pri.med{color:var(--highlight);} .docskin .pri.low{color:var(--gray);}
242
+ /* cluster */
243
+ .docskin .cl-head{font-family:var(--font-display);font-size:13px;font-weight:700;fill:#fff;}
244
+ .docskin .cl-kind{font-family:var(--font-mono);font-size:9px;fill:#cfe0f3;text-anchor:end;text-transform:uppercase;letter-spacing:.06em;}
245
+ /* user story */
246
+ .docskin .story{border:1px solid var(--rule);border-left:4px solid var(--navy);padding:18px 22px;margin:12px 0;background:var(--white);}
247
+ .docskin .story-stmt{font-family:var(--font-display);font-size:18px;line-height:1.55;color:var(--charcoal);}
248
+ .docskin .story-stmt b{color:var(--navy);}
249
+ .docskin .story-meta{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap;}
250
+ .docskin .story-chip{font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;padding:3px 9px;border-radius:3px;background:var(--light-gray);color:var(--slate);border:1px solid var(--rule);}
251
+ .docskin .ac-title{font-size:10.5px;text-transform:uppercase;letter-spacing:.12em;color:var(--highlight);font-weight:700;margin:16px 0 8px;}
252
+ .docskin .ac-item{border:1px solid var(--rule);padding:10px 14px;margin-bottom:8px;background:var(--white);}
253
+ .docskin .gwt{display:grid;grid-template-columns:60px 1fr;gap:4px 12px;font-size:13px;}
254
+ .docskin .gwt .k{font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;padding-top:1px;}
255
+ .docskin .gwt .k.g{color:var(--positive);} .docskin .gwt .k.w{color:var(--navy);} .docskin .gwt .k.t{color:var(--highlight);}
256
+ .docskin .gwt .v{color:var(--charcoal);}
257
+ .docskin .links-row{display:flex;gap:8px;flex-wrap:wrap;}
258
+ .docskin .link-chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;color:var(--navy);border:1px solid var(--navy);background:var(--white);padding:5px 11px;border-radius:20px;cursor:pointer;}
259
+ .docskin .link-chip .lt{color:var(--gray);font-family:var(--font-mono);font-size:9px;text-transform:uppercase;letter-spacing:.06em;}
260
+ .docskin .footer{margin-top:8px;padding:18px 32px 28px;border-top:2px solid var(--navy);font-size:11px;color:var(--gray);text-transform:uppercase;letter-spacing:.1em;font-weight:700;display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;}
261
+ .docskin .footer .accent{color:var(--highlight);}
262
+ .docskin .layer-label{font-family:var(--font-display);font-size:13px;font-weight:700;fill:#fff;}
263
+ .docskin .uml-name{font-family:var(--font-display);font-size:13px;font-weight:700;text-anchor:middle;fill:#0a3a6e;}
264
+ .docskin .uml-stereo{font-family:var(--font-body);font-size:9px;font-style:italic;text-anchor:middle;fill:#6b7280;}
265
+ .docskin .uml-row{font-family:var(--font-mono);font-size:10.5px;fill:var(--charcoal);}
266
+ .docskin .uml-sep{stroke:#0e54a1;stroke-width:1;}
267
+ .docskin .ft-note{font-family:var(--font-mono);font-size:9px;}
268
+ .docskin .tree-link{stroke:#9ca3af;stroke-width:1.3;fill:none;}
269
+ /* wireframe / UI mockups */
270
+ .docskin .wf-h{font-family:var(--font-display);font-size:18px;font-weight:700;}
271
+ .docskin .wf-sub{font-family:var(--font-body);font-size:12px;font-weight:600;}
272
+ .docskin .wf-btn{font-family:var(--font-body);font-size:11px;font-weight:700;fill:#fff;}
273
+ .docskin .wf-ph-text{font-family:var(--font-body);font-size:10px;fill:var(--gray);}
274
+ .docskin .wf-status{font-family:var(--font-mono);font-size:9px;fill:var(--charcoal);font-weight:700;}
275
+ .docskin .wf-url{font-family:var(--font-mono);font-size:8.5px;fill:var(--gray);}
276
+ .docskin .wf-tab{font-family:var(--font-body);font-size:8px;}
277
+ .docskin .wf-caption{font-family:var(--font-mono);font-size:10px;fill:var(--gray);letter-spacing:.04em;}
278
+ /* parse error */
279
+ .docskin .err{font-family:var(--font-mono);font-size:12px;color:var(--negative);background:#fdf2f2;border:1px solid #f3c9c9;padding:8px 12px;margin:12px 0;white-space:pre-wrap;}`;
280
+
281
+ // src/escape.ts
282
+ var REPL = {
283
+ "&": "&",
284
+ "<": "&lt;",
285
+ ">": "&gt;",
286
+ '"': "&quot;"
287
+ };
288
+ function escapeHtml(value) {
289
+ if (value === null || value === void 0) return "";
290
+ return String(value).replace(/[&<>"]/g, (c) => REPL[c] ?? c);
291
+ }
292
+
293
+ // src/sanitize.ts
294
+ var SAFE_COLOR_RE = /^(#[0-9a-fA-F]{3,8}|rgba?\([\d.,\s%]+\)|hsla?\([\d.,\s%]+\)|[a-zA-Z]{1,24})$/;
295
+ function safeColor(value, fallback) {
296
+ if (value === void 0) return fallback;
297
+ const trimmed = value.trim();
298
+ return SAFE_COLOR_RE.test(trimmed) ? trimmed : fallback;
299
+ }
300
+ var DANGEROUS_URL_RE = /^\s*(javascript|data|vbscript):/i;
301
+ function safeUrl(url) {
302
+ return DANGEROUS_URL_RE.test(url) ? "#" : url;
303
+ }
304
+ var marked = new Marked({ gfm: true, breaks: false });
305
+ marked.use({
306
+ tokenizer: {
307
+ // Decline raw HTML at both block and inline level; the text falls through
308
+ // to the paragraph/text tokenizer, which escapes it.
309
+ html() {
310
+ return void 0;
311
+ },
312
+ tag() {
313
+ return void 0;
314
+ }
315
+ },
316
+ renderer: {
317
+ link(token) {
318
+ const href = safeUrl(token.href);
319
+ const title = token.title !== null && token.title !== void 0 ? ` title="${token.title}"` : "";
320
+ const text = this.parser.parseInline(token.tokens);
321
+ return `<a href="${href}"${title}>${text}</a>`;
322
+ },
323
+ image(token) {
324
+ const href = safeUrl(token.href);
325
+ const title = token.title !== null && token.title !== void 0 ? ` title="${token.title}"` : "";
326
+ return `<img src="${href}" alt="${token.text}"${title}>`;
327
+ }
328
+ }
329
+ });
330
+ function renderProse(text) {
331
+ const html = marked.parse(text, { async: false });
332
+ return `<div class="prose">${html}</div>`;
333
+ }
334
+
335
+ // src/blocks/meta.ts
336
+ function renderCover(meta) {
337
+ const title = meta?.title ?? "Untitled";
338
+ const tag = meta?.tag ?? "";
339
+ const subtitle = meta?.subtitle;
340
+ const sub = subtitle !== void 0 ? `<p class="cover-sub">${escapeHtml(subtitle)}</p>` : "";
341
+ return `<div class="cover-bar"></div><div class="cover-pad"><div class="cover-meta"><span>DOCUMENT</span><span class="accent">${escapeHtml(tag)}</span></div><h1 class="cover-title">${escapeHtml(title)}</h1>` + sub + `</div>`;
342
+ }
343
+ function renderMetaBlock() {
344
+ return "";
345
+ }
346
+
347
+ // src/blocks/frame.ts
348
+ function diagramFrame(opts, inner) {
349
+ const tagClass = opts.tagClass !== void 0 ? ` ${opts.tagClass}` : "";
350
+ const tagStyle = opts.tagBg !== void 0 && opts.tagClass === void 0 ? ` style="background:${opts.tagBg}"` : "";
351
+ const titleHtml = opts.titleHtml !== void 0 ? `<span class="diagram-title">${opts.titleHtml}</span>` : opts.title !== void 0 && opts.title.length > 0 ? `<span class="diagram-title">${escapeHtml(opts.title)}</span>` : "";
352
+ const fignumHtml = opts.fignum !== void 0 && opts.fignum.length > 0 ? `<span class="diagram-fignum">${escapeHtml(opts.fignum)}</span>` : "";
353
+ const descHtml = opts.desc !== void 0 && opts.desc.length > 0 ? `<p class="diagram-desc">${escapeHtml(opts.desc)}</p>` : "";
354
+ const footerHtml = opts.footerHtml ?? "";
355
+ return `<div class="diagram"><div class="diagram-head"><span class="diagram-tag${tagClass}"${tagStyle}>${escapeHtml(opts.tag)}</span>` + titleHtml + fignumHtml + `</div>` + descHtml + inner + footerHtml + `</div>`;
356
+ }
357
+ var SECTION_LABEL = {
358
+ meta: "",
359
+ callout: "Note",
360
+ table: "Comparison",
361
+ sequence: "Sequence",
362
+ erd: "Entity model",
363
+ userstory: "User story",
364
+ timeline: "Roadmap",
365
+ kanban: "Board",
366
+ tracker: "Tracker",
367
+ prose: "Overview",
368
+ glossary: "Glossary",
369
+ proscons: "Trade-offs",
370
+ cvt: "Before / after",
371
+ stats: "Metrics",
372
+ code: "Code",
373
+ agenda: "Agenda",
374
+ tree: "Hierarchy",
375
+ pyramid: "Pyramid",
376
+ funnel: "Funnel",
377
+ flow: "Flowchart",
378
+ state: "State machine",
379
+ dfd: "Data flow",
380
+ journey: "Journey",
381
+ gantt: "Schedule",
382
+ graph: "Graph",
383
+ quadrant: "Matrix",
384
+ swimlane: "Process",
385
+ c4: "C4 model",
386
+ uml: "Class model",
387
+ mece: "Issue tree",
388
+ frontend: "Component tree",
389
+ cluster: "Cluster",
390
+ block: "Architecture",
391
+ infra: "Deployment",
392
+ event: "Events",
393
+ ddd: "Context map",
394
+ network: "Security zones",
395
+ felogic: "Frontend logic",
396
+ belogic: "Backend logic",
397
+ dag: "DAG",
398
+ wireframe: "Mockup"
399
+ };
400
+
401
+ // src/blocks/callout.ts
402
+ var DEFAULT_TITLE = {
403
+ note: "Note",
404
+ tip: "Tip",
405
+ warn: "Warning",
406
+ danger: "Danger"
407
+ };
408
+ function renderCallout(data) {
409
+ const tone = data.tone ?? "note";
410
+ const title = data.title ?? DEFAULT_TITLE[tone];
411
+ const body = data.body ?? "";
412
+ return `<div class="callout ${tone}"><div class="callout-title">${escapeHtml(title)}</div><div class="callout-body">${escapeHtml(body)}</div></div>`;
413
+ }
414
+
415
+ // src/blocks/erd.ts
416
+ function renderErd(data) {
417
+ const ents = data.entities ?? [];
418
+ const rels = data.relations ?? [];
419
+ const colW = 200;
420
+ const rowH = 22;
421
+ const headH = 30;
422
+ const gapX = 80;
423
+ const top = 14;
424
+ const pad = 16;
425
+ let x = pad;
426
+ let maxH = 0;
427
+ const boxes = ents.map((e) => {
428
+ const cols = e.columns ?? [];
429
+ const h = headH + cols.length * rowH + 10;
430
+ const box = { name: e.name, columns: cols, x, y: top, w: colW, h };
431
+ x += colW + gapX;
432
+ if (h > maxH) maxH = h;
433
+ return box;
434
+ });
435
+ const W = pad * 2 + ents.length * colW + Math.max(0, ents.length - 1) * gapX;
436
+ const H = top + maxH + 24;
437
+ const byName = /* @__PURE__ */ new Map();
438
+ for (const b of boxes) byName.set(b.name, b);
439
+ let s = `<svg viewBox="0 0 ${W} ${H}" role="img"><title>Entity-relationship diagram</title>`;
440
+ for (const r of rels) {
441
+ const a = byName.get(r.from);
442
+ const b = byName.get(r.to);
443
+ if (!a || !b) continue;
444
+ const x1 = a.x + a.w;
445
+ const y1 = a.y + 24;
446
+ const x2 = b.x;
447
+ const y2 = b.y + 24;
448
+ const mx = (x1 + x2) / 2;
449
+ s += `<path d="M${x1},${y1} H${mx} V${y2} H${x2}" fill="none" stroke="#8a96a3"/><path d="M${x2 - 11},${y2 - 7} L${x2},${y2} L${x2 - 11},${y2 + 7}" fill="none" stroke="#8a96a3"/>`;
450
+ if (r.card !== void 0) {
451
+ const w = 28;
452
+ s += `<rect x="${mx - w / 2}" y="${(y1 + y2) / 2 - 9}" width="${w}" height="18" rx="9" fill="#fff" stroke="#d1d5db"/><text x="${mx}" y="${(y1 + y2) / 2 + 3}" class="edge-label">${escapeHtml(r.card)}</text>`;
453
+ }
454
+ }
455
+ for (const b of boxes) {
456
+ s += `<rect x="${b.x}" y="${b.y}" width="${b.w}" height="${b.h}" rx="4" fill="#fff" stroke="#0e54a1"/><rect x="${b.x}" y="${b.y}" width="${b.w}" height="${headH}" rx="4" fill="#0e54a1"/><rect x="${b.x}" y="${b.y + headH - 4}" width="${b.w}" height="4" fill="#0e54a1"/><text x="${b.x + b.w / 2}" y="${b.y + 20}" class="er-head-text">${escapeHtml(b.name)}</text>`;
457
+ b.columns.forEach((f, j) => {
458
+ const fy = b.y + headH + 16 + j * rowH;
459
+ const nameX = f.pk === true || f.fk === true ? b.x + 38 : b.x + 12;
460
+ if (j > 0) {
461
+ s += `<line x1="${b.x}" y1="${fy - 14}" x2="${b.x + b.w}" y2="${fy - 14}" class="er-rowline"/>`;
462
+ }
463
+ if (f.pk === true) {
464
+ s += `<text x="${b.x + 12}" y="${fy}" class="er-key">PK</text>`;
465
+ } else if (f.fk === true) {
466
+ s += `<text x="${b.x + 12}" y="${fy}" class="er-key fk">FK</text>`;
467
+ }
468
+ s += `<text x="${nameX}" y="${fy}" class="er-col">${escapeHtml(f.name)}</text><text x="${b.x + b.w - 12}" y="${fy}" class="er-col dim" text-anchor="end">${escapeHtml(f.type ?? "")}</text>`;
469
+ });
470
+ }
471
+ s += `</svg>`;
472
+ const opts = {
473
+ tag: "ER",
474
+ tagBg: "#6b21a8",
475
+ ...data.title !== void 0 ? { title: data.title } : {},
476
+ ...data.description !== void 0 ? { desc: data.description } : {}
477
+ };
478
+ return diagramFrame(opts, s);
479
+ }
480
+
481
+ // src/blocks/kanban.ts
482
+ function renderKanban(data) {
483
+ const cols = data.columns ?? [];
484
+ let h = `<div class="kanban">`;
485
+ for (const col of cols) {
486
+ const cards = (col.cards ?? []).map((c) => {
487
+ const tag = c.tag !== void 0 ? `<div class="kan-card-tag">${escapeHtml(c.tag)}</div>` : "";
488
+ return `<div class="kan-card"><div class="kan-card-title">${escapeHtml(c.title)}</div>` + tag + `</div>`;
489
+ }).join("");
490
+ h += `<div class="kan-col"><div class="kan-head">${escapeHtml(col.label)}</div>` + cards + `</div>`;
491
+ }
492
+ return h + `</div>`;
493
+ }
494
+
495
+ // src/blocks/sequence.ts
496
+ var KIND = {
497
+ sync: { cls: "msg-line", marker: "sqArrow", txt: "msg-text em" },
498
+ response: { cls: "msg-line dashed", marker: "sqOpen", txt: "msg-text" },
499
+ async: { cls: "msg-line dashed", marker: "sqOpen", txt: "msg-text" },
500
+ error: { cls: "msg-line err", marker: "sqErr", txt: "msg-text err" },
501
+ note: { cls: "", marker: null, txt: "msg-text note" }
502
+ };
503
+ var DB_PATTERN = /postgres|sql|\bdb\b|database|store/i;
504
+ function renderStepList(rows, actorById) {
505
+ const items = rows.filter((r) => r.summary !== void 0 && r.summary.length > 0);
506
+ if (items.length === 0) return "";
507
+ const lis = items.map((r) => {
508
+ const errCls = r.kind === "error" ? ' class="err"' : "";
509
+ const actorErrCls = r.kind === "error" ? " err" : "";
510
+ const fromName = actorById.get(r.from)?.name ?? r.from;
511
+ const toName = r.kind === "note" ? "" : ` &rarr; ${escapeHtml(actorById.get(r.to)?.name ?? r.to)}`;
512
+ const actorLabel = `${escapeHtml(fromName)}${toName}`;
513
+ const code = r.code !== void 0 && r.code.length > 0 ? `<pre class="sql">${escapeHtml(r.code)}</pre>` : "";
514
+ const note = r.note !== void 0 && r.note.length > 0 ? `<span class="step-note">${escapeHtml(r.note)}</span>` : "";
515
+ return `<li${errCls}><span class="step-actor${actorErrCls}">${actorLabel}</span><span class="step-summary">${escapeHtml(r.summary ?? "")}</span>` + code + note + `</li>`;
516
+ }).join("");
517
+ return `<div class="seq-steps"><div class="seq-steps-title">Step-by-step</div><ol>${lis}</ol></div>`;
518
+ }
519
+ function renderFoot(foot) {
520
+ if (foot.length === 0) return "";
521
+ const parts = foot.map((f) => `<span><strong>${escapeHtml(f.label)}:</strong> ${escapeHtml(f.value)}</span>`).join("");
522
+ return `<div class="diagram-foot">${parts}</div>`;
523
+ }
524
+ function renderSequence(data) {
525
+ const actors = data.actors ?? [];
526
+ const messages = data.messages ?? [];
527
+ const N = Math.max(actors.length, 1);
528
+ const leftPad = 24;
529
+ const laneW = 168;
530
+ const gap = 58;
531
+ const headY = 16;
532
+ const headH = 42;
533
+ const cx = (i) => leftPad + laneW / 2 + i * (laneW + gap);
534
+ const width = leftPad * 2 + N * laneW + (N - 1) * gap;
535
+ const idx = (id) => actors.findIndex((a) => a.id === id);
536
+ const msgStartY = 92;
537
+ const step = 42;
538
+ const rows = messages.map((m, k) => {
539
+ const base = {
540
+ n: k + 1,
541
+ y: msgStartY + k * step,
542
+ fromI: idx(m.from),
543
+ toI: idx(m.to),
544
+ kind: m.kind ?? "sync",
545
+ label: m.label ?? "",
546
+ from: m.from,
547
+ to: m.to
548
+ };
549
+ if (m.summary !== void 0) base.summary = m.summary;
550
+ if (m.code !== void 0) base.code = m.code;
551
+ if (m.note !== void 0) base.note = m.note;
552
+ return base;
553
+ });
554
+ const bottom = msgStartY + messages.length * step + 12;
555
+ const height = bottom + 6;
556
+ const activations = actors.map((a, i) => {
557
+ if (i === 0) return null;
558
+ const ys = rows.filter((r) => r.fromI === i || r.toI === i).map((r) => r.y);
559
+ if (ys.length === 0) return null;
560
+ const db = DB_PATTERN.test(`${a.name} ${a.sub ?? ""}`);
561
+ return { i, y1: Math.min(...ys) - 8, y2: Math.max(...ys) + 8, db };
562
+ });
563
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Sequence diagram</title><defs><marker id="sqArrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#1a1a2e"/></marker><marker id="sqOpen" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10" fill="none" stroke="#1a1a2e" stroke-width="1.2"/></marker><marker id="sqErr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#991b1b"/></marker></defs>`;
564
+ for (let i = 0; i < actors.length; i++) {
565
+ const x = cx(i);
566
+ s += `<line x1="${x}" y1="${headY + headH}" x2="${x}" y2="${bottom}" class="lifeline"/>`;
567
+ }
568
+ for (const ac of activations) {
569
+ if (!ac) continue;
570
+ s += `<rect x="${cx(ac.i) - 4}" y="${ac.y1}" width="8" height="${ac.y2 - ac.y1}" class="activation${ac.db ? " pg" : ""}"/>`;
571
+ }
572
+ actors.forEach((a, i) => {
573
+ const extCls = a.external === true ? " ext" : "";
574
+ const sub = a.sub !== void 0 ? `<text x="${cx(i)}" y="${headY + 36}" class="lane-head-sub${extCls}">${escapeHtml(a.sub)}</text>` : "";
575
+ s += `<g><rect x="${cx(i) - laneW / 2}" y="${headY}" width="${laneW}" height="${headH}" class="lane-head${extCls}"/><text x="${cx(i)}" y="${headY + 22}" class="lane-head-text">${escapeHtml(a.name)}</text>` + sub + `</g>`;
576
+ });
577
+ for (const r of rows) {
578
+ const k = KIND[r.kind];
579
+ if (r.kind === "note" || r.toI < 0) {
580
+ if (r.fromI < 0) continue;
581
+ const x = cx(r.fromI);
582
+ s += `<g><circle cx="${x + 18}" cy="${r.y - 10}" r="10" class="step-badge"/><text x="${x + 18}" y="${r.y - 6.5}" class="step-badge-text">${r.n}</text><text x="${x + 34}" y="${r.y - 6}" class="msg-text note">${escapeHtml(r.label)}</text></g>`;
583
+ continue;
584
+ }
585
+ if (r.fromI === r.toI) {
586
+ const x = cx(r.fromI);
587
+ const errCls = r.kind === "error" ? " err" : "";
588
+ s += `<g><circle cx="${x + 18}" cy="${r.y - 10}" r="10" class="step-badge${errCls}"/><text x="${x + 18}" y="${r.y - 6.5}" class="step-badge-text">${r.n}</text><text x="${x + 34}" y="${r.y - 6}" class="msg-text note">${escapeHtml(r.label)}</text></g>`;
589
+ continue;
590
+ }
591
+ const x1 = cx(r.fromI);
592
+ const x2 = cx(r.toI);
593
+ const ltr = x2 > x1;
594
+ const end = x2 + (ltr ? -3 : 3);
595
+ const errBadge = r.kind === "error" ? " err" : "";
596
+ const badgeX = ltr ? x1 + 18 : x1 - 18;
597
+ const labelX = ltr ? badgeX + 16 : badgeX - 16;
598
+ const labelAnchor = ltr ? "start" : "end";
599
+ const markerAttr = k.marker !== null ? ` marker-end="url(#${k.marker})"` : "";
600
+ s += `<line x1="${x1}" y1="${r.y}" x2="${end}" y2="${r.y}" class="${k.cls}"${markerAttr}/><circle cx="${badgeX}" cy="${r.y - 10}" r="10" class="step-badge${errBadge}"/><text x="${badgeX}" y="${r.y - 6.5}" class="step-badge-text">${r.n}</text><text x="${labelX}" y="${r.y - 6}" class="${k.txt}" text-anchor="${labelAnchor}">${escapeHtml(r.label)}</text>`;
601
+ }
602
+ s += `</svg>`;
603
+ const actorById = /* @__PURE__ */ new Map();
604
+ for (const a of actors) actorById.set(a.id, { name: a.name });
605
+ const stepList = renderStepList(rows, actorById);
606
+ const footHtml = data.foot !== void 0 ? renderFoot(data.foot) : "";
607
+ const method = data.endpoint?.method.toLowerCase();
608
+ const tag = data.endpoint?.method ?? "FLOW";
609
+ const titleHtml = (() => {
610
+ if (data.endpoint?.path !== void 0) {
611
+ const t = data.title !== void 0 ? ` &mdash; ${escapeHtml(data.title)}` : "";
612
+ return `<code>${escapeHtml(data.endpoint.path)}</code>${t}`;
613
+ }
614
+ return data.title !== void 0 ? escapeHtml(data.title) : "";
615
+ })();
616
+ const frameOpts = {
617
+ tag,
618
+ ...method !== void 0 ? { tagClass: method } : { tagBg: "#374151" },
619
+ ...titleHtml.length > 0 ? { titleHtml } : {},
620
+ ...data.description !== void 0 ? { desc: data.description } : {},
621
+ ...footHtml.length > 0 ? { footerHtml: footHtml } : {}
622
+ };
623
+ return diagramFrame(frameOpts, s + stepList);
624
+ }
625
+
626
+ // src/blocks/table.ts
627
+ function colClass(c) {
628
+ if (typeof c === "string") return "";
629
+ const parts = [];
630
+ if (c.align === "r") parts.push("r");
631
+ if (c.align === "c") parts.push("c");
632
+ if (c.highlight === true) parts.push("hi");
633
+ return parts.join(" ");
634
+ }
635
+ function colLabel(c) {
636
+ return typeof c === "string" ? c : c.label;
637
+ }
638
+ function cellClass(c) {
639
+ if (typeof c === "string" || typeof c === "number") return "";
640
+ const parts = [];
641
+ if (c.tone) parts.push(`cell-${c.tone}`);
642
+ if (c.lead === true) parts.push("lead");
643
+ if (c.highlight === true) parts.push("hi");
644
+ return parts.join(" ");
645
+ }
646
+ function cellValue(c) {
647
+ if (typeof c === "string" || typeof c === "number") return escapeHtml(c);
648
+ return escapeHtml(c.v);
649
+ }
650
+ function renderTable(data) {
651
+ const cols = data.columns ?? [];
652
+ const rows = data.rows ?? [];
653
+ const head = cols.map((c) => {
654
+ const cls = colClass(c);
655
+ return `<th${cls ? ` class="${cls}"` : ""}>${escapeHtml(colLabel(c))}</th>`;
656
+ }).join("");
657
+ const body = rows.map((r) => {
658
+ const cells = (r ?? []).map((c, i) => {
659
+ const col = cols[i];
660
+ const cls = [col !== void 0 ? colClass(col) : "", cellClass(c)].filter(Boolean).join(" ");
661
+ return `<td${cls ? ` class="${cls}"` : ""}>${cellValue(c)}</td>`;
662
+ }).join("");
663
+ return `<tr>${cells}</tr>`;
664
+ }).join("");
665
+ const note = data.note !== void 0 ? `<p class="tbl-note">${escapeHtml(data.note)}</p>` : "";
666
+ return `<table class="pres-table"><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table>${note}`;
667
+ }
668
+
669
+ // src/blocks/timeline.ts
670
+ function renderTimeline(data) {
671
+ const items = data.items ?? [];
672
+ let h = `<div class="tl">`;
673
+ for (const it of items) {
674
+ const st = it.status ?? "future";
675
+ const date = it.date !== void 0 ? `<div class="tl-date">${escapeHtml(it.date)}</div>` : "";
676
+ const desc = it.desc !== void 0 ? `<div class="tl-desc">${escapeHtml(it.desc)}</div>` : "";
677
+ h += `<div class="tl-item"><span class="tl-dot ${st}"></span>` + date + `<div class="tl-label">${escapeHtml(it.label)}</div>` + desc + `</div>`;
678
+ }
679
+ return h + `</div>`;
680
+ }
681
+
682
+ // src/blocks/tracker.ts
683
+ function renderTracker(data) {
684
+ const items = data.items ?? [];
685
+ const hasOwner = items.some((i) => i.owner !== void 0);
686
+ const hasDue = items.some((i) => i.due !== void 0);
687
+ const headCells = ["<th>Task</th>", "<th>Status</th>", "<th>Priority</th>"];
688
+ if (hasOwner) headCells.push("<th>Owner</th>");
689
+ if (hasDue) headCells.push("<th>Due</th>");
690
+ let h = `<table class="trk"><thead><tr>${headCells.join("")}</tr></thead><tbody>`;
691
+ for (const it of items) {
692
+ const st = it.status ?? "todo";
693
+ const pr = it.priority;
694
+ const prCell = pr !== void 0 ? `<span class="pri ${pr}">${escapeHtml(pr)}</span>` : "";
695
+ const rowCls = st === "done" ? ' class="done"' : "";
696
+ const cells = [
697
+ `<td class="trk-task">${escapeHtml(it.task)}</td>`,
698
+ `<td><span class="st ${st}">${escapeHtml(st)}</span></td>`,
699
+ `<td>${prCell}</td>`
700
+ ];
701
+ if (hasOwner) cells.push(`<td>${escapeHtml(it.owner ?? "")}</td>`);
702
+ if (hasDue) cells.push(`<td>${escapeHtml(it.due ?? "")}</td>`);
703
+ h += `<tr${rowCls}>${cells.join("")}</tr>`;
704
+ }
705
+ return h + `</tbody></table>`;
706
+ }
707
+
708
+ // src/blocks/userstory.ts
709
+ function renderUserStory(data) {
710
+ const role = data.role ?? "user";
711
+ const want = data.want ?? "\u2026";
712
+ const soThat = data.soThat ?? "\u2026";
713
+ const chips = [];
714
+ if (data.priority !== void 0)
715
+ chips.push(`<span class="story-chip">Priority \xB7 ${escapeHtml(data.priority)}</span>`);
716
+ if (data.points !== void 0 && data.points !== null)
717
+ chips.push(`<span class="story-chip">${escapeHtml(data.points)} pts</span>`);
718
+ let h = `<div class="story"><div class="story-stmt">As a <b>${escapeHtml(role)}</b>, I want to <b>${escapeHtml(want)}</b>, so that <b>${escapeHtml(soThat)}</b>.</div>`;
719
+ if (chips.length > 0) {
720
+ h += `<div class="story-meta">${chips.join("")}</div>`;
721
+ }
722
+ h += `</div>`;
723
+ const crit = data.criteria ?? [];
724
+ if (crit.length > 0) {
725
+ h += `<div class="ac-title">Acceptance criteria</div>`;
726
+ for (const c of crit) {
727
+ h += `<div class="ac-item"><div class="gwt"><span class="k g">Given</span><span class="v">${escapeHtml(c.given ?? "")}</span><span class="k w">When</span><span class="v">${escapeHtml(c.when ?? "")}</span><span class="k t">Then</span><span class="v">${escapeHtml(c.then ?? "")}</span></div></div>`;
728
+ }
729
+ }
730
+ const links = data.links ?? [];
731
+ if (links.length > 0) {
732
+ h += `<div class="ac-title">Related</div><div class="links-row">`;
733
+ for (const l of links) {
734
+ h += `<span class="link-chip"><span class="lt">${escapeHtml(l.mode ?? "")}</span>${escapeHtml(l.label ?? "")}</span>`;
735
+ }
736
+ h += `</div>`;
737
+ }
738
+ return h;
739
+ }
740
+
741
+ // src/blocks/prose.ts
742
+ function renderProseBlock(data) {
743
+ const blocks = data.blocks ?? [];
744
+ const html = blocks.map((b) => {
745
+ const t = (b.type ?? "p").toLowerCase();
746
+ const text = b.text ?? "";
747
+ if (t === "h") return `<h3>${escapeHtml(text)}</h3>`;
748
+ if (t === "quote") return `<blockquote>${escapeHtml(text)}</blockquote>`;
749
+ if (t === "ul") {
750
+ const items = (b.items ?? []).map((x) => `<li>${escapeHtml(x)}</li>`).join("");
751
+ return `<ul>${items}</ul>`;
752
+ }
753
+ if (t === "ol") {
754
+ const items = (b.items ?? []).map((x) => `<li>${escapeHtml(x)}</li>`).join("");
755
+ return `<ol>${items}</ol>`;
756
+ }
757
+ return `<p>${escapeHtml(text)}</p>`;
758
+ }).join("");
759
+ return `<div class="prose">${html}</div>`;
760
+ }
761
+
762
+ // src/blocks/glossary.ts
763
+ function renderGlossary(data) {
764
+ const terms = data.terms ?? [];
765
+ const rows = terms.map(
766
+ (t) => `<div class="row"><dt>${escapeHtml(t.term)}</dt><dd>${escapeHtml(t.def)}</dd></div>`
767
+ ).join("");
768
+ return `<div class="glossary">${rows}</div>`;
769
+ }
770
+
771
+ // src/blocks/proscons.ts
772
+ function renderProsCons(data) {
773
+ const prosLabel = data.prosLabel ?? "Pros";
774
+ const consLabel = data.consLabel ?? "Cons";
775
+ const pros = (data.pros ?? []).map((p) => `<div class="pc-item">${escapeHtml(p)}</div>`).join("");
776
+ const cons = (data.cons ?? []).map((c) => `<div class="pc-item">${escapeHtml(c)}</div>`).join("");
777
+ return `<div class="pc"><div class="pc-col pro"><div class="pc-head">${escapeHtml(prosLabel)}</div>` + pros + `</div><div class="pc-col con"><div class="pc-head">${escapeHtml(consLabel)}</div>` + cons + `</div></div>`;
778
+ }
779
+
780
+ // src/blocks/cvt.ts
781
+ function renderCvt(data) {
782
+ const cur = data.current ?? {};
783
+ const tgt = data.target ?? {};
784
+ const curItems = (cur.items ?? []).map((x) => `<div class="ct-item">${escapeHtml(x)}</div>`).join("");
785
+ const tgtItems = (tgt.items ?? []).map((x) => `<div class="ct-item">${escapeHtml(x)}</div>`).join("");
786
+ const curLabel = cur.label ?? "Current";
787
+ const tgtLabel = tgt.label ?? "Target";
788
+ const note = data.note !== void 0 ? `<div class="tbl-note">${escapeHtml(data.note)}</div>` : "";
789
+ return `<div><div class="ct"><div class="ct-panel cur"><div class="ct-label">${escapeHtml(curLabel)}</div>` + curItems + `</div><div class="ct-arrow">&rarr;</div><div class="ct-panel tgt"><div class="ct-label">${escapeHtml(tgtLabel)}</div>` + tgtItems + `</div></div>` + note + `</div>`;
790
+ }
791
+
792
+ // src/blocks/stats.ts
793
+ var TREND_GLYPH = { up: "\u25B2", down: "\u25BC", flat: "\u2014" };
794
+ function renderStats(data) {
795
+ const stats = data.stats ?? [];
796
+ const cards = stats.map((s) => {
797
+ const accent = s.accent !== void 0 ? ` style="border-top-color:${safeColor(s.accent, "#0e54a1")}"` : "";
798
+ const delta = s.delta !== void 0 ? `<div class="stat-delta ${s.trend ?? "flat"}">${TREND_GLYPH[s.trend ?? "flat"]} ${escapeHtml(s.delta)}</div>` : "";
799
+ return `<div class="stat-card"${accent}><div class="stat-value">${escapeHtml(s.value)}</div><div class="stat-label">${escapeHtml(s.label)}</div>` + delta + `</div>`;
800
+ }).join("");
801
+ return `<div class="stat-row">${cards}</div>`;
802
+ }
803
+
804
+ // src/highlight.ts
805
+ var KW = new Set(
806
+ "const let var function return if else for while do switch case break continue class extends new await async import from export default try catch finally throw typeof instanceof void delete yield static public private protected readonly abstract implements interface type enum namespace def elif lambda pass with raise except none true false and or not is in self nil func struct map range defer chan select fallthrough echo fi done local require module package this super create table alter add drop select insert update delete into values where join group order by limit primary key foreign references not null default unique index on check constraint cascade returning begin commit rollback".split(/\s+/)
807
+ );
808
+ var TY = new Set(
809
+ "string number boolean int integer float double bool char byte long short void object any unknown never bigint promise array list set optional uuid text varchar timestamptz timestamp date numeric decimal jsonb json serial bigserial smallint".split(/\s+/)
810
+ );
811
+ var TOKEN_RE = /(\/\*[\s\S]*?\*\/|\/\/[^\n]*|#[^\n]*)|("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`)|(\b\d[\w.]*)|([A-Za-z_$][\w$]*)/g;
812
+ function highlightCode(code) {
813
+ if (code.length === 0) return "";
814
+ const src = String(code);
815
+ let out = "";
816
+ let last = 0;
817
+ TOKEN_RE.lastIndex = 0;
818
+ let m;
819
+ while ((m = TOKEN_RE.exec(src)) !== null) {
820
+ if (m.index > last) out += escapeHtml(src.slice(last, m.index));
821
+ const t = m[0];
822
+ if (m[1] !== void 0) {
823
+ out += `<span class="com">${escapeHtml(t)}</span>`;
824
+ } else if (m[2] !== void 0) {
825
+ out += `<span class="str">${escapeHtml(t)}</span>`;
826
+ } else if (m[3] !== void 0) {
827
+ out += `<span class="num">${escapeHtml(t)}</span>`;
828
+ } else {
829
+ const lt = t.toLowerCase();
830
+ if (KW.has(lt)) out += `<span class="kw">${escapeHtml(t)}</span>`;
831
+ else if (TY.has(lt)) out += `<span class="ty">${escapeHtml(t)}</span>`;
832
+ else if (src[TOKEN_RE.lastIndex] === "(")
833
+ out += `<span class="fn">${escapeHtml(t)}</span>`;
834
+ else out += escapeHtml(t);
835
+ }
836
+ last = TOKEN_RE.lastIndex;
837
+ }
838
+ if (last < src.length) out += escapeHtml(src.slice(last));
839
+ return out;
840
+ }
841
+
842
+ // src/blocks/code.ts
843
+ function renderCode(data) {
844
+ const blocks = data.blocks ?? [];
845
+ return blocks.map(
846
+ (b) => `<div class="code-block"><div class="code-header"><span>${escapeHtml(b.title ?? "")}</span><span>${escapeHtml(b.lang ?? "")}</span></div><pre>${highlightCode(b.code)}</pre></div>`
847
+ ).join("");
848
+ }
849
+
850
+ // src/blocks/agenda.ts
851
+ function renderAgenda(data) {
852
+ const items = data.items ?? [];
853
+ const rows = items.map((it) => {
854
+ const dur = it.duration !== void 0 ? `<div class="agenda-dur">${escapeHtml(it.duration)}</div>` : "";
855
+ const owner = it.owner !== void 0 ? `<span class="agenda-owner">${escapeHtml(it.owner)}</span>` : "";
856
+ const desc = it.desc !== void 0 ? `<div class="agenda-desc">${escapeHtml(it.desc)}</div>` : "";
857
+ const time = it.time !== void 0 ? `<div class="agenda-time">${escapeHtml(it.time)}</div>` : "";
858
+ return `<div class="agenda-row"><div>${time}${dur}</div><div><div class="agenda-title">${escapeHtml(it.title)}${owner}</div>` + desc + `</div></div>`;
859
+ }).join("");
860
+ return `<div class="agenda">${rows}</div>`;
861
+ }
862
+
863
+ // src/blocks/tree.ts
864
+ function renderTree(data) {
865
+ const nodes = data.nodes ?? [];
866
+ const byId = /* @__PURE__ */ new Map();
867
+ const children = /* @__PURE__ */ new Map();
868
+ for (const n of nodes) {
869
+ byId.set(n.id, n);
870
+ children.set(n.id, []);
871
+ }
872
+ const roots = [];
873
+ for (const n of nodes) {
874
+ if (n.parent !== void 0 && byId.has(n.parent)) {
875
+ children.get(n.parent)?.push(n.id);
876
+ } else {
877
+ roots.push(n.id);
878
+ }
879
+ }
880
+ const out = [];
881
+ const seen = /* @__PURE__ */ new Set();
882
+ const walk = (id, depth) => {
883
+ if (seen.has(id)) return;
884
+ seen.add(id);
885
+ const node = byId.get(id);
886
+ if (node === void 0) return;
887
+ const kids = children.get(id) ?? [];
888
+ out.push({ node, depth, branch: kids.length > 0 });
889
+ for (const c of kids) walk(c, depth + 1);
890
+ };
891
+ for (const r of roots) walk(r, 0);
892
+ const rows = out.map((row) => {
893
+ const branchCls = row.branch ? " branch" : "";
894
+ const glyph = row.branch ? "\u25B8" : "\u2014";
895
+ const note = row.node.note !== void 0 ? `<span class="tnote">${escapeHtml(row.node.note)}</span>` : "";
896
+ return `<div class="tree-row${branchCls}" style="padding-left:${row.depth * 22}px"><span class="tw">${glyph}</span><span class="tlabel">${escapeHtml(row.node.label)}</span>` + note + `</div>`;
897
+ }).join("");
898
+ return `<div class="tree-list">${rows}</div>`;
899
+ }
900
+
901
+ // src/blocks/pyramid.ts
902
+ var CHART_COLORS = ["#0e54a1", "#1a6dbe", "#0f766e", "#1f9747", "#6b21a8", "#f7952c"];
903
+ function renderPyramid(data) {
904
+ const levels = data.levels ?? [];
905
+ const n = Math.max(levels.length, 1);
906
+ const W = 520;
907
+ const top = 16;
908
+ const rowH = Math.min(72, Math.floor(280 / n));
909
+ const gap = 4;
910
+ const apex = W / 2;
911
+ const pad = 16;
912
+ const maxW = 430;
913
+ const H = top + n * (rowH + gap) + pad;
914
+ const polygons = levels.map((L, i) => {
915
+ const y = top + i * (rowH + gap);
916
+ const wTop = maxW * (i / n);
917
+ const wBot = maxW * ((i + 1) / n);
918
+ const fill = CHART_COLORS[i % CHART_COLORS.length] ?? "#0e54a1";
919
+ const desc = L.desc !== void 0 ? `<text x="${apex}" y="${y + rowH / 2 + 14}" class="pyr-desc">${escapeHtml(L.desc)}</text>` : "";
920
+ return `<g><polygon points="${apex - wTop / 2},${y} ${apex + wTop / 2},${y} ${apex + wBot / 2},${y + rowH} ${apex - wBot / 2},${y + rowH}" fill="${fill}"/><text x="${apex}" y="${y + rowH / 2 - 1}" class="pyr-label">${escapeHtml(L.label)}</text>` + desc + `</g>`;
921
+ }).join("");
922
+ return `<svg viewBox="0 0 ${W} ${H}" role="img"><title>Pyramid</title>${polygons}</svg>`;
923
+ }
924
+
925
+ // src/blocks/funnel.ts
926
+ var CHART_COLORS2 = ["#0e54a1", "#1a6dbe", "#0f766e", "#1f9747", "#6b21a8", "#f7952c"];
927
+ function toNum(v) {
928
+ if (typeof v === "number") return v;
929
+ const n = parseFloat(v.replace(/[, _]/g, ""));
930
+ return Number.isFinite(n) ? n : 0;
931
+ }
932
+ function fmt(v) {
933
+ return typeof v === "number" ? v.toLocaleString() : v;
934
+ }
935
+ function renderFunnel(data) {
936
+ const stages = (data.stages ?? []).filter(Boolean);
937
+ const W = 560;
938
+ const top = 14;
939
+ const rowH = 62;
940
+ const maxW = 420;
941
+ const pad = 14;
942
+ const cx = W / 2;
943
+ const H = top + stages.length * rowH + pad;
944
+ const vals = stages.map((s) => toNum(s.value));
945
+ const first = vals[0] ?? 0;
946
+ const max = Math.max(1, ...vals);
947
+ const wOf = (v) => Math.max(78, maxW * (v / max));
948
+ const groups = stages.map((s, i) => {
949
+ const v = vals[i] ?? 0;
950
+ const vNext = i < stages.length - 1 ? vals[i + 1] ?? 0 : v * 0.82;
951
+ const wTop = wOf(v);
952
+ const wBot = wOf(vNext);
953
+ const y = top + i * rowH;
954
+ const fill = CHART_COLORS2[i % CHART_COLORS2.length] ?? "#0e54a1";
955
+ const pct = first > 0 ? Math.round(v / first * 100) : 0;
956
+ const sub = i > 0 && first > 0 ? ` \xB7 ${pct}%` : "";
957
+ return `<g><polygon points="${cx - wTop / 2},${y} ${cx + wTop / 2},${y} ${cx + wBot / 2},${y + rowH} ${cx - wBot / 2},${y + rowH}" fill="${fill}" stroke="#fff" stroke-width="2"/><text x="${cx}" y="${y + rowH / 2 - 3}" class="funnel-label">${escapeHtml(s.label)}</text><text x="${cx}" y="${y + rowH / 2 + 15}" class="funnel-val">${escapeHtml(fmt(s.value))}${sub}</text></g>`;
958
+ }).join("");
959
+ return `<svg viewBox="0 0 ${W} ${H}" role="img"><title>Funnel</title>${groups}</svg>`;
960
+ }
961
+
962
+ // src/svg/ortho.ts
963
+ function ortho(A, B) {
964
+ const a = { x: A.x + A.w / 2, y: A.y + A.h / 2 };
965
+ const b = { x: B.x + B.w / 2, y: B.y + B.h / 2 };
966
+ const dx = b.x - a.x;
967
+ const dy = b.y - a.y;
968
+ if (Math.abs(dx) >= Math.abs(dy)) {
969
+ const sx2 = dx >= 0 ? A.x + A.w : A.x;
970
+ const ex2 = dx >= 0 ? B.x : B.x + B.w;
971
+ const sy2 = a.y;
972
+ const ey2 = b.y;
973
+ const mx = (sx2 + ex2) / 2;
974
+ return { d: `M ${sx2} ${sy2} H ${mx} V ${ey2} H ${ex2}`, lx: mx, ly: (sy2 + ey2) / 2 };
975
+ }
976
+ const sy = dy >= 0 ? A.y + A.h : A.y;
977
+ const ey = dy >= 0 ? B.y : B.y + B.h;
978
+ const sx = a.x;
979
+ const ex = b.x;
980
+ const my = (sy + ey) / 2;
981
+ return { d: `M ${sx} ${sy} V ${my} H ${ex} V ${ey}`, lx: (sx + ex) / 2, ly: my };
982
+ }
983
+
984
+ // src/svg/wrapText.ts
985
+ function wrapText(text, max, maxLines) {
986
+ if (text === void 0 || text === null || text === "") return [];
987
+ const words = String(text).split(/\s+/);
988
+ const lines = [];
989
+ let cur = "";
990
+ for (const w of words) {
991
+ if (!cur) {
992
+ cur = w;
993
+ continue;
994
+ }
995
+ if ((cur + " " + w).length <= max) {
996
+ cur += " " + w;
997
+ } else {
998
+ lines.push(cur);
999
+ cur = w;
1000
+ if (lines.length === maxLines) break;
1001
+ }
1002
+ }
1003
+ if (cur && lines.length < maxLines) lines.push(cur);
1004
+ return lines.slice(0, maxLines);
1005
+ }
1006
+
1007
+ // src/svg/edgePill.ts
1008
+ function edgePill(p, label, err = false) {
1009
+ if (label === void 0 || label === "") return "";
1010
+ const w = Math.max(26, label.length * 5.4);
1011
+ const errClass = err ? " err" : "";
1012
+ return `<g><rect x="${p.lx - w / 2}" y="${p.ly - 9}" width="${w}" height="18" rx="9" fill="#fff" stroke="#d1d5db"/><text x="${p.lx}" y="${p.ly + 3}" class="edge-label${errClass}">${escapeHtml(label)}</text></g>`;
1013
+ }
1014
+
1015
+ // src/blocks/flow.ts
1016
+ function flowStyle(kind) {
1017
+ switch (kind ?? "process") {
1018
+ case "start":
1019
+ return { shape: "stadium", fill: "#dcf1e2", stroke: "#1f9747", text: "#0f3d22" };
1020
+ case "end":
1021
+ return { shape: "stadium", fill: "#1a1a2e", stroke: "#1a1a2e", text: "#fff" };
1022
+ case "decision":
1023
+ return { shape: "diamond", fill: "#fde7cd", stroke: "#f7952c", text: "#7a3d00" };
1024
+ default:
1025
+ return { shape: "rect", fill: "#e5eff8", stroke: "#0e54a1", text: "#0a3a6e" };
1026
+ }
1027
+ }
1028
+ var ERR_LABEL_RE = /^(no|fail|error|reject)/i;
1029
+ function renderFlowSvg(data) {
1030
+ const nodes = data.nodes ?? [];
1031
+ const edges = data.edges ?? [];
1032
+ const cellW = 176;
1033
+ const cellH = 70;
1034
+ const gapX = 60;
1035
+ const gapY = 56;
1036
+ const padX = 26;
1037
+ const padTop = 26;
1038
+ const padBot = 22;
1039
+ const cols = Math.max(1, ...nodes.map((n) => n.col + ((n.w ?? 1) - 1)));
1040
+ const rows = Math.max(1, ...nodes.map((n) => n.row));
1041
+ const xOf = (c) => padX + (c - 1) * (cellW + gapX);
1042
+ const yOf = (r) => padTop + (r - 1) * (cellH + gapY);
1043
+ const rectFor2 = (n) => ({
1044
+ x: xOf(n.col),
1045
+ y: yOf(n.row),
1046
+ w: (n.w ?? 1) * cellW + ((n.w ?? 1) - 1) * gapX,
1047
+ h: cellH
1048
+ });
1049
+ const byId = new Map(nodes.map((n) => [n.id, n]));
1050
+ const width = padX * 2 + cols * cellW + (cols - 1) * gapX;
1051
+ const height = padTop + rows * cellH + (rows - 1) * gapY + padBot;
1052
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Flowchart</title>`;
1053
+ for (const e of edges) {
1054
+ const A = byId.get(e.from);
1055
+ const B = byId.get(e.to);
1056
+ if (!A || !B) continue;
1057
+ const p = ortho(rectFor2(A), rectFor2(B));
1058
+ const isErr = e.kind === "error" || ERR_LABEL_RE.test(e.label ?? "");
1059
+ const stroke = isErr ? "#991b1b" : "#1a1a2e";
1060
+ const marker = isErr ? "gErr" : "gArrow";
1061
+ const sw = isErr ? 1.6 : 1.4;
1062
+ s += `<g><path d="${p.d}" fill="none" stroke="${stroke}" stroke-width="${sw}" marker-end="url(#${marker})"/>` + edgePill(p, e.label, isErr) + `</g>`;
1063
+ }
1064
+ for (const n of nodes) {
1065
+ const r = rectFor2(n);
1066
+ const st = flowStyle(n.kind);
1067
+ const cx = r.x + r.w / 2;
1068
+ const cy = r.y + r.h / 2;
1069
+ let shape;
1070
+ if (st.shape === "diamond") {
1071
+ shape = `<polygon points="${cx},${r.y} ${r.x + r.w},${cy} ${cx},${r.y + r.h} ${r.x},${cy}" fill="${st.fill}" stroke="${st.stroke}" stroke-width="1.5"/>`;
1072
+ } else if (st.shape === "stadium") {
1073
+ shape = `<rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="${r.h / 2}" fill="${st.fill}" stroke="${st.stroke}" stroke-width="1.5"/>`;
1074
+ } else {
1075
+ shape = `<rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="7" fill="${st.fill}" stroke="${st.stroke}" stroke-width="1.4"/>`;
1076
+ }
1077
+ const lines = wrapText(n.label, 22, 2);
1078
+ const texts = lines.map(
1079
+ (ln, j) => `<text x="${cx}" y="${cy + 4 - (lines.length - 1) * 7 + j * 14}" class="fc-label" fill="${st.text}">${escapeHtml(ln)}</text>`
1080
+ ).join("");
1081
+ s += `<g filter="url(#gshadow)">${shape}${texts}</g>`;
1082
+ }
1083
+ s += `</svg>`;
1084
+ return s;
1085
+ }
1086
+ function renderFlowFrame(data, frame) {
1087
+ return diagramFrame(
1088
+ {
1089
+ tag: frame.tag,
1090
+ ...frame.tagBg !== void 0 ? { tagBg: frame.tagBg } : {},
1091
+ ...data.title !== void 0 ? { title: data.title } : {},
1092
+ ...data.description !== void 0 ? { desc: data.description } : {}
1093
+ },
1094
+ renderFlowSvg(data)
1095
+ );
1096
+ }
1097
+ function renderFlow(data) {
1098
+ return renderFlowFrame(data, { tag: "FLOW", tagBg: "#374151" });
1099
+ }
1100
+ function renderDag(data) {
1101
+ return renderFlowFrame(data, { tag: "DAG", tagBg: "#0e54a1" });
1102
+ }
1103
+
1104
+ // src/blocks/state.ts
1105
+ function rectFor(s, cellW, cellH, gapX, gapY, padX, padTop) {
1106
+ const cx = padX + (s.col - 1) * (cellW + gapX) + cellW / 2;
1107
+ const cy = padTop + (s.row - 1) * (cellH + gapY) + cellH / 2;
1108
+ if (s.kind === "start" || s.kind === "terminal") {
1109
+ return { x: cx - 13, y: cy - 13, w: 26, h: 26, cx, cy };
1110
+ }
1111
+ const pw = Math.max(96, (s.name ?? "").length * 8 + 26);
1112
+ const ph = 46;
1113
+ return { x: cx - pw / 2, y: cy - ph / 2, w: pw, h: ph, cx, cy };
1114
+ }
1115
+ function pillCls(kind) {
1116
+ if (kind === "terminal") return "pill pill-end";
1117
+ if (kind === "wait") return "pill pill-wait";
1118
+ if (kind === "start") return "pill pill-init";
1119
+ return "pill pill-active";
1120
+ }
1121
+ function renderState(data) {
1122
+ const states = data.states ?? [];
1123
+ const trans = data.transitions ?? [];
1124
+ const cellW = 168;
1125
+ const cellH = 64;
1126
+ const gapX = 74;
1127
+ const gapY = 60;
1128
+ const padX = 30;
1129
+ const padTop = 30;
1130
+ const padBot = 20;
1131
+ const cols = Math.max(1, ...states.map((s2) => s2.col));
1132
+ const rows = Math.max(1, ...states.map((s2) => s2.row));
1133
+ const width = padX * 2 + cols * cellW + (cols - 1) * gapX;
1134
+ const height = padTop * 2 + rows * cellH + (rows - 1) * gapY + padBot;
1135
+ const byId = new Map(states.map((s2) => [s2.id, s2]));
1136
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>State machine</title>`;
1137
+ for (const t of trans) {
1138
+ const A = byId.get(t.from);
1139
+ const B = byId.get(t.to);
1140
+ if (!A || !B) continue;
1141
+ const label = t.event + (t.guard !== void 0 ? ` ${t.guard}` : "");
1142
+ if (t.from === t.to) {
1143
+ const r = rectFor(A, cellW, cellH, gapX, gapY, padX, padTop);
1144
+ s += `<g><path d="M ${r.cx - 12} ${r.y} C ${r.cx - 30} ${r.y - 32}, ${r.cx + 30} ${r.y - 32}, ${r.cx + 12} ${r.y}" fill="none" stroke="#1a1a2e" stroke-width="1.3" marker-end="url(#gArrow)"/>` + edgePill({ lx: r.cx, ly: r.y - 28 }, label) + `</g>`;
1145
+ continue;
1146
+ }
1147
+ const p = ortho(
1148
+ rectFor(A, cellW, cellH, gapX, gapY, padX, padTop),
1149
+ rectFor(B, cellW, cellH, gapX, gapY, padX, padTop)
1150
+ );
1151
+ s += `<g><path d="${p.d}" fill="none" stroke="#1a1a2e" stroke-width="1.3" marker-end="url(#gArrow)"/>` + edgePill(p, label) + `</g>`;
1152
+ }
1153
+ for (const st of states) {
1154
+ const r = rectFor(st, cellW, cellH, gapX, gapY, padX, padTop);
1155
+ if (st.kind === "start") {
1156
+ s += `<circle cx="${r.cx}" cy="${r.cy}" r="10" fill="#1a1a2e"/>`;
1157
+ } else if (st.kind === "terminal") {
1158
+ s += `<g><circle cx="${r.cx}" cy="${r.cy}" r="12" fill="#fff" stroke="#1a1a2e" stroke-width="1.5"/><circle cx="${r.cx}" cy="${r.cy}" r="6" fill="#1a1a2e"/></g>`;
1159
+ } else {
1160
+ const fill = st.kind === "wait" ? "#fde7cd" : "#dcf1e2";
1161
+ const stroke = st.kind === "wait" ? "#f7952c" : "#1f9747";
1162
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="23" fill="${fill}" stroke="${stroke}" stroke-width="1.5"/><text x="${r.cx}" y="${r.cy + 4.5}" class="sm-name" fill="#1a1a2e">${escapeHtml(st.name ?? "")}</text></g>`;
1163
+ }
1164
+ }
1165
+ s += `</svg>`;
1166
+ const name = (id) => byId.get(id)?.name ?? id;
1167
+ const rows2 = trans.map(
1168
+ (t) => `<tr><td><span class="${pillCls(byId.get(t.from)?.kind)}">${escapeHtml(name(t.from))}</span></td><td style="font-family:var(--font-mono);font-size:11px">${escapeHtml(t.event)}</td><td style="color:#6b7280;font-size:11px">${escapeHtml(t.guard ?? "\u2014")}</td><td><span class="${pillCls(byId.get(t.to)?.kind)}">${escapeHtml(name(t.to))}</span></td></tr>`
1169
+ ).join("");
1170
+ const table = trans.length > 0 ? `<table class="transition-table"><thead><tr><th>From</th><th>Event</th><th>Guard</th><th>To</th></tr></thead><tbody>${rows2}</tbody></table>` : "";
1171
+ return diagramFrame(
1172
+ {
1173
+ tag: "STATE",
1174
+ tagBg: "#6b21a8",
1175
+ ...data.title !== void 0 ? { title: data.title } : {},
1176
+ ...data.description !== void 0 ? { desc: data.description } : {}
1177
+ },
1178
+ s + table
1179
+ );
1180
+ }
1181
+
1182
+ // src/blocks/dfd.ts
1183
+ function dfdStyle(kind) {
1184
+ switch ((kind ?? "process").toLowerCase()) {
1185
+ case "external":
1186
+ return { fill: "#f3f4f6", stroke: "#6b7280", text: "#374151" };
1187
+ case "store":
1188
+ case "datastore":
1189
+ return { fill: "#fde7cd", stroke: "#f7952c", text: "#7a3d00" };
1190
+ default:
1191
+ return { fill: "#e5eff8", stroke: "#0e54a1", text: "#0a3a6e" };
1192
+ }
1193
+ }
1194
+ function renderDfd(data) {
1195
+ const nodes = data.nodes ?? [];
1196
+ const edges = data.edges ?? [];
1197
+ const cellW = 168;
1198
+ const cellH = 76;
1199
+ const gapX = 60;
1200
+ const gapY = 58;
1201
+ const padX = 26;
1202
+ const padTop = 26;
1203
+ const padBot = 20;
1204
+ const cols = Math.max(1, ...nodes.map((n) => n.col));
1205
+ const rows = Math.max(1, ...nodes.map((n) => n.row));
1206
+ const xOf = (c) => padX + (c - 1) * (cellW + gapX);
1207
+ const yOf = (r) => padTop + (r - 1) * (cellH + gapY);
1208
+ const rectFor2 = (n) => ({ x: xOf(n.col), y: yOf(n.row), w: cellW, h: cellH });
1209
+ const byId = new Map(nodes.map((n) => [n.id, n]));
1210
+ const width = padX * 2 + cols * cellW + (cols - 1) * gapX;
1211
+ const height = padTop + rows * cellH + (rows - 1) * gapY + padBot;
1212
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Data-flow diagram</title>`;
1213
+ for (const e of edges) {
1214
+ const A = byId.get(e.from);
1215
+ const B = byId.get(e.to);
1216
+ if (!A || !B) continue;
1217
+ const p = ortho(rectFor2(A), rectFor2(B));
1218
+ s += `<g><path d="${p.d}" fill="none" stroke="#1a1a2e" stroke-width="1.4" marker-end="url(#gArrow)"/>` + edgePill(p, e.label) + `</g>`;
1219
+ }
1220
+ for (const n of nodes) {
1221
+ const r = rectFor2(n);
1222
+ const st = dfdStyle(n.kind);
1223
+ const k = (n.kind ?? "process").toLowerCase();
1224
+ let shape;
1225
+ if (k === "process") {
1226
+ shape = `<rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="16" fill="${st.fill}" stroke="${st.stroke}" stroke-width="1.4"/>`;
1227
+ } else if (k === "store" || k === "datastore") {
1228
+ shape = `<rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" fill="${st.fill}" stroke="none"/><line x1="${r.x}" y1="${r.y}" x2="${r.x + r.w}" y2="${r.y}" stroke="${st.stroke}" stroke-width="1.6"/><line x1="${r.x}" y1="${r.y + r.h}" x2="${r.x + r.w}" y2="${r.y + r.h}" stroke="${st.stroke}" stroke-width="1.6"/><line x1="${r.x}" y1="${r.y}" x2="${r.x}" y2="${r.y + r.h}" stroke="${st.stroke}" stroke-width="1.6"/>`;
1229
+ } else {
1230
+ shape = `<rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" fill="${st.fill}" stroke="${st.stroke}" stroke-width="1.4"/>`;
1231
+ }
1232
+ const num = n.num !== void 0 && k === "process" ? `<text x="${r.x + 12}" y="${r.y + 18}" class="dfd-num" fill="${st.text}">${escapeHtml(n.num)}</text>` : "";
1233
+ s += `<g filter="url(#gshadow)">${shape}${num}<text x="${r.x + r.w / 2}" y="${r.y + r.h / 2 + 4}" class="dfd-name" fill="${st.text}">${escapeHtml(n.name)}</text></g>`;
1234
+ }
1235
+ s += `</svg>`;
1236
+ return diagramFrame(
1237
+ {
1238
+ tag: "DFD",
1239
+ tagBg: "#0e54a1",
1240
+ ...data.title !== void 0 ? { title: data.title } : {},
1241
+ ...data.description !== void 0 ? { desc: data.description } : {}
1242
+ },
1243
+ s
1244
+ );
1245
+ }
1246
+
1247
+ // src/blocks/journey.ts
1248
+ function clamp01(v) {
1249
+ if (v < 0) return 0;
1250
+ if (v > 1) return 1;
1251
+ return v;
1252
+ }
1253
+ function renderJourney(data) {
1254
+ const stages = data.stages ?? [];
1255
+ const rows = data.rows ?? [];
1256
+ const emotion = data.emotion ?? [];
1257
+ const n = Math.max(stages.length, 1);
1258
+ const W = Math.max(380, n * 150);
1259
+ const H = 92;
1260
+ const pad = 20;
1261
+ const colW = (W - pad * 2) / n;
1262
+ const ex = (i) => pad + colW * (i + 0.5);
1263
+ const ey = (v) => H - 14 - (H - 30) * clamp01(v);
1264
+ const head = `<tr><th></th>` + stages.map((s) => `<th class="c">${escapeHtml(s.label)}</th>`).join("") + `</tr>`;
1265
+ const body = rows.map(
1266
+ (r) => `<tr><td class="lead">${escapeHtml(r.label)}</td>` + (r.cells ?? []).map((c) => `<td class="c">${escapeHtml(c)}</td>`).join("") + `</tr>`
1267
+ ).join("");
1268
+ let svg = "";
1269
+ if (emotion.length > 0) {
1270
+ const points = emotion.map((v, i) => `${ex(i)},${ey(v)}`).join(" ");
1271
+ const dots = emotion.map((v, i) => {
1272
+ const fill = v >= 0.6 ? "#1f9747" : v <= 0.35 ? "#991b1b" : "#f7952c";
1273
+ return `<circle cx="${ex(i)}" cy="${ey(v)}" r="5" fill="${fill}" stroke="#fff" stroke-width="1.5"/>`;
1274
+ }).join("");
1275
+ svg = `<div style="margin-top:10px"><div style="font-size:10px;color:#6b7280;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px">Emotion</div><svg viewBox="0 0 ${W} ${H}" style="width:100%" role="img"><title>Emotion curve</title><polyline points="${points}" fill="none" stroke="#0e54a1" stroke-width="2"/>` + dots + `</svg></div>`;
1276
+ }
1277
+ return `<div><table class="pres-table"><thead>${head}</thead><tbody>${body}</tbody></table>` + svg + `</div>`;
1278
+ }
1279
+
1280
+ // src/blocks/gantt.ts
1281
+ function ganttColor(kind) {
1282
+ switch ((kind ?? "").toLowerCase()) {
1283
+ case "done":
1284
+ return "#1f9747";
1285
+ case "active":
1286
+ case "current":
1287
+ return "#f7952c";
1288
+ case "milestone":
1289
+ return "#6b21a8";
1290
+ default:
1291
+ return "#0e54a1";
1292
+ }
1293
+ }
1294
+ function renderGantt(data) {
1295
+ const periods = data.periods ?? [];
1296
+ const tasks = data.tasks ?? [];
1297
+ const P = Math.max(periods.length, 1);
1298
+ const labelW = 156;
1299
+ const padX = 20;
1300
+ const padTop = 34;
1301
+ const rowH = 30;
1302
+ const barH = 18;
1303
+ const colW = 64;
1304
+ const padBot = 14;
1305
+ const width = labelW + padX * 2 + P * colW;
1306
+ const height = padTop + tasks.length * rowH + padBot;
1307
+ const xCol = (i) => labelW + padX + i * colW;
1308
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Schedule</title>`;
1309
+ for (let i = 0; i < periods.length; i++) {
1310
+ s += `<g><line x1="${xCol(i)}" y1="${padTop - 6}" x2="${xCol(i)}" y2="${height - padBot}" stroke="#eef0f3" stroke-width="1"/><text x="${xCol(i) + colW / 2}" y="${padTop - 12}" class="gantt-head">${escapeHtml(periods[i] ?? "")}</text></g>`;
1311
+ }
1312
+ s += `<line x1="${xCol(P)}" y1="${padTop - 6}" x2="${xCol(P)}" y2="${height - padBot}" stroke="#eef0f3" stroke-width="1"/>`;
1313
+ for (let i = 0; i < tasks.length; i++) {
1314
+ const t = tasks[i];
1315
+ if (t === void 0) continue;
1316
+ const y = padTop + i * rowH;
1317
+ const bx = xCol(t.start ?? 0);
1318
+ const span = Math.max(1, t.span ?? 1);
1319
+ const bw = span * colW - 8;
1320
+ s += `<g><text x="${padX}" y="${y + rowH / 2 + 4}" class="gantt-label">${escapeHtml(t.label)}</text><rect x="${bx + 4}" y="${y + (rowH - barH) / 2}" width="${bw}" height="${barH}" rx="4" fill="${ganttColor(t.kind)}" filter="url(#gshadow)"/></g>`;
1321
+ }
1322
+ s += `</svg>`;
1323
+ return s;
1324
+ }
1325
+
1326
+ // src/blocks/graph.ts
1327
+ var CHART_COLORS3 = ["#0e54a1", "#1a6dbe", "#0f766e", "#1f9747", "#6b21a8", "#f7952c"];
1328
+ function renderGraph(data) {
1329
+ const nodes = data.nodes ?? [];
1330
+ const edges = data.edges ?? [];
1331
+ const cellW = 150;
1332
+ const cellH = 84;
1333
+ const gapX = 44;
1334
+ const gapY = 40;
1335
+ const padX = 26;
1336
+ const padTop = 24;
1337
+ const padBot = 20;
1338
+ const cols = Math.max(1, ...nodes.map((n) => n.col));
1339
+ const rows = Math.max(1, ...nodes.map((n) => n.row));
1340
+ const cxOf = (c) => padX + (c - 1) * (cellW + gapX) + cellW / 2;
1341
+ const cyOf = (r) => padTop + (r - 1) * (cellH + gapY) + cellH / 2;
1342
+ const rectFor2 = (n) => {
1343
+ const cx = cxOf(n.col);
1344
+ const cy = cyOf(n.row);
1345
+ const w = Math.max(98, n.label.length * 8 + 26);
1346
+ return { x: cx - w / 2, y: cy - 20, w, h: 40, cx, cy };
1347
+ };
1348
+ const byId = new Map(nodes.map((n) => [n.id, n]));
1349
+ const width = padX * 2 + cols * cellW + (cols - 1) * gapX;
1350
+ const height = padTop + rows * cellH + (rows - 1) * gapY + padBot;
1351
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Graph</title>`;
1352
+ for (const e of edges) {
1353
+ const A = byId.get(e.from);
1354
+ const B = byId.get(e.to);
1355
+ if (!A || !B) continue;
1356
+ const p = ortho(rectFor2(A), rectFor2(B));
1357
+ const directed = (e.dir ?? "directed") !== "undirected";
1358
+ const markerAttr = directed ? ` marker-end="url(#gArrow)"` : "";
1359
+ s += `<g><path d="${p.d}" fill="none" stroke="#6b7280" stroke-width="1.4"${markerAttr}/>` + edgePill(p, e.label) + `</g>`;
1360
+ }
1361
+ for (const n of nodes) {
1362
+ const r = rectFor2(n);
1363
+ const c = CHART_COLORS3[(n.group ?? 0) % CHART_COLORS3.length] ?? "#0e54a1";
1364
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="20" fill="#fff" stroke="${c}" stroke-width="1.6"/><text x="${r.cx}" y="${r.cy + 4}" class="blk-name" fill="${c}" text-anchor="middle" style="font-size:12px">${escapeHtml(n.label)}</text></g>`;
1365
+ }
1366
+ s += `</svg>`;
1367
+ return diagramFrame(
1368
+ {
1369
+ tag: "GRAPH",
1370
+ tagBg: "#374151",
1371
+ ...data.title !== void 0 ? { title: data.title } : {},
1372
+ ...data.description !== void 0 ? { desc: data.description } : {}
1373
+ },
1374
+ s
1375
+ );
1376
+ }
1377
+
1378
+ // src/blocks/quadrant.ts
1379
+ function clamp012(v) {
1380
+ if (v === void 0 || Number.isNaN(v)) return 0.5;
1381
+ if (v < 0) return 0;
1382
+ if (v > 1) return 1;
1383
+ return v;
1384
+ }
1385
+ function renderQuadrant(data) {
1386
+ const W = 580;
1387
+ const H = 440;
1388
+ const pad = 56;
1389
+ const x0 = pad;
1390
+ const x1 = W - pad;
1391
+ const y0 = pad - 16;
1392
+ const y1 = H - pad;
1393
+ const mx = (x0 + x1) / 2;
1394
+ const my = (y0 + y1) / 2;
1395
+ const px = (v) => x0 + (x1 - x0) * clamp012(v);
1396
+ const py = (v) => y1 - (y1 - y0) * clamp012(v);
1397
+ const items = data.items ?? [];
1398
+ const xA = data.xAxis ?? {};
1399
+ const yA = data.yAxis ?? {};
1400
+ let s = `<svg viewBox="0 0 ${W} ${H}" role="img"><title>Quadrant</title>`;
1401
+ s += `<rect x="${x0}" y="${y0}" width="${x1 - x0}" height="${y1 - y0}" fill="#fafbfc" stroke="#e5e7eb"/>`;
1402
+ s += `<rect x="${mx}" y="${y0}" width="${x1 - mx}" height="${my - y0}" fill="#0e54a1" fill-opacity="0.04"/>`;
1403
+ s += `<line x1="${x0}" y1="${my}" x2="${x1}" y2="${my}" class="quad-axis" marker-end="url(#gArrow)"/>`;
1404
+ s += `<line x1="${mx}" y1="${y1}" x2="${mx}" y2="${y0}" class="quad-axis" marker-end="url(#gArrow)"/>`;
1405
+ if (xA.label !== void 0)
1406
+ s += `<text x="${x1}" y="${y1 + 30}" class="quad-title" text-anchor="end">${escapeHtml(xA.label)} \u2192</text>`;
1407
+ if (yA.label !== void 0)
1408
+ s += `<text x="${mx - 8}" y="${y0 - 4}" class="quad-title" text-anchor="end">\u2191 ${escapeHtml(yA.label)}</text>`;
1409
+ if (xA.low !== void 0)
1410
+ s += `<text x="${x0}" y="${y1 + 16}" class="quad-end" text-anchor="start">${escapeHtml(xA.low)}</text>`;
1411
+ if (xA.high !== void 0)
1412
+ s += `<text x="${x1}" y="${y1 + 16}" class="quad-end" text-anchor="end">${escapeHtml(xA.high)}</text>`;
1413
+ if (yA.high !== void 0)
1414
+ s += `<text x="${x0 - 10}" y="${y0 + 6}" class="quad-end" text-anchor="end">${escapeHtml(yA.high)}</text>`;
1415
+ if (yA.low !== void 0)
1416
+ s += `<text x="${x0 - 10}" y="${y1}" class="quad-end" text-anchor="end">${escapeHtml(yA.low)}</text>`;
1417
+ for (const it of items) {
1418
+ const cx = px(it.x);
1419
+ const cy = py(it.y);
1420
+ const left = cx > mx;
1421
+ const tx = cx + (left ? -12 : 12);
1422
+ const anchor = left ? "end" : "start";
1423
+ s += `<g filter="url(#gshadow)"><circle cx="${cx}" cy="${cy}" r="7" fill="#f7952c" stroke="#fff" stroke-width="1.5"/><text x="${tx}" y="${cy + 4}" class="quad-pt-label" text-anchor="${anchor}">${escapeHtml(it.label)}</text></g>`;
1424
+ }
1425
+ s += `</svg>`;
1426
+ return diagramFrame(
1427
+ {
1428
+ tag: "2\xD72",
1429
+ tagBg: "#0f766e",
1430
+ ...data.title !== void 0 ? { title: data.title } : {},
1431
+ ...data.description !== void 0 ? { desc: data.description } : {}
1432
+ },
1433
+ s
1434
+ );
1435
+ }
1436
+
1437
+ // src/blocks/swimlane.ts
1438
+ function laneColor(kind) {
1439
+ switch ((kind ?? "action").toLowerCase()) {
1440
+ case "decision":
1441
+ return { fill: "#fde7cd", stroke: "#f7952c", text: "#7a3d00" };
1442
+ case "start":
1443
+ case "end":
1444
+ return { fill: "#dcf1e2", stroke: "#1f9747", text: "#0f3d22" };
1445
+ case "wait":
1446
+ return { fill: "#f3f4f6", stroke: "#6b7280", text: "#374151" };
1447
+ default:
1448
+ return { fill: "#e5eff8", stroke: "#0e54a1", text: "#0a3a6e" };
1449
+ }
1450
+ }
1451
+ function renderSwimlane(data) {
1452
+ const lanes = data.lanes ?? [];
1453
+ const steps = data.steps ?? [];
1454
+ const links = data.links ?? [];
1455
+ const labelW = 132;
1456
+ const padX = 18;
1457
+ const padTop = 24;
1458
+ const padBot = 20;
1459
+ const laneH = 92;
1460
+ const colW = 168;
1461
+ const gapCol = 34;
1462
+ const boxW = 150;
1463
+ const boxH = 52;
1464
+ const cols = Math.max(1, ...steps.map((s2) => s2.col));
1465
+ const xCol = (c) => labelW + padX + (c - 1) * (colW + gapCol);
1466
+ const yLane = (l) => padTop + l * laneH;
1467
+ const rectFor2 = (s2) => ({
1468
+ x: xCol(s2.col) + (colW - boxW) / 2,
1469
+ y: yLane(s2.lane) + (laneH - boxH) / 2,
1470
+ w: boxW,
1471
+ h: boxH
1472
+ });
1473
+ const byId = new Map(steps.map((s2) => [s2.id, s2]));
1474
+ const width = labelW + padX * 2 + cols * colW + (cols - 1) * gapCol;
1475
+ const height = padTop + lanes.length * laneH + padBot;
1476
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Swimlane</title>`;
1477
+ for (let i = 0; i < lanes.length; i++) {
1478
+ const L = lanes[i];
1479
+ if (L === void 0) continue;
1480
+ s += `<g><rect x="${padX}" y="${yLane(i)}" width="${width - padX * 2}" height="${laneH}" fill="${i % 2 ? "#fafafa" : "#fff"}" stroke="#e5e7eb"/><rect x="${padX}" y="${yLane(i)}" width="${labelW}" height="${laneH}" fill="#0e54a1"/><text x="${padX + 14}" y="${yLane(i) + laneH / 2 + 4}" class="sl-lane-label">${escapeHtml(L.label)}</text></g>`;
1481
+ }
1482
+ for (const lk of links) {
1483
+ const A = byId.get(lk.from);
1484
+ const B = byId.get(lk.to);
1485
+ if (!A || !B) continue;
1486
+ const p = ortho(rectFor2(A), rectFor2(B));
1487
+ s += `<g><path d="${p.d}" fill="none" stroke="#1a1a2e" stroke-width="1.4" marker-end="url(#gArrow)"/>` + edgePill(p, lk.label) + `</g>`;
1488
+ }
1489
+ for (const st of steps) {
1490
+ const r = rectFor2(st);
1491
+ const c = laneColor(st.kind);
1492
+ const lines = wrapText(st.label, 20, 2);
1493
+ const texts = lines.map(
1494
+ (ln, j) => `<text x="${r.x + r.w / 2}" y="${r.y + r.h / 2 + 4 - (lines.length - 1) * 7 + j * 14}" class="sl-step" fill="${c.text}">${escapeHtml(ln)}</text>`
1495
+ ).join("");
1496
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="7" fill="${c.fill}" stroke="${c.stroke}" stroke-width="1.3"/>` + texts + `</g>`;
1497
+ }
1498
+ s += `</svg>`;
1499
+ return diagramFrame(
1500
+ {
1501
+ tag: "LANES",
1502
+ tagBg: "#0e54a1",
1503
+ ...data.title !== void 0 ? { title: data.title } : {},
1504
+ ...data.description !== void 0 ? { desc: data.description } : {}
1505
+ },
1506
+ s
1507
+ );
1508
+ }
1509
+
1510
+ // src/svg/blockStyle.ts
1511
+ function blockStyle(kind) {
1512
+ switch ((kind ?? "").toLowerCase()) {
1513
+ case "client":
1514
+ return { accent: "#0e54a1", fill: "#e5eff8", text: "#0a3a6e" };
1515
+ case "service":
1516
+ case "microservice":
1517
+ case "compute":
1518
+ case "container":
1519
+ return { accent: "#1f9747", fill: "#dcf1e2", text: "#0f3d22" };
1520
+ case "data":
1521
+ return { accent: "#6b21a8", fill: "#ede9fe", text: "#4a1772" };
1522
+ case "store":
1523
+ case "db":
1524
+ case "database":
1525
+ return { accent: "#f7952c", fill: "#fde7cd", text: "#7a3d00" };
1526
+ case "bucket":
1527
+ case "blob":
1528
+ case "object":
1529
+ return { accent: "#b45309", fill: "#fef3c7", text: "#7a3d00" };
1530
+ case "queue":
1531
+ return { accent: "#0f766e", fill: "#ccfbf1", text: "#0f4f49" };
1532
+ case "cache":
1533
+ return { accent: "#0891b2", fill: "#cffafe", text: "#0e4f5c" };
1534
+ case "gateway":
1535
+ case "lb":
1536
+ return { accent: "#0e54a1", fill: "#cfe0f3", text: "#0a3a6e" };
1537
+ case "function":
1538
+ case "lambda":
1539
+ return { accent: "#7c3aed", fill: "#ede9fe", text: "#4a1772" };
1540
+ case "cdn":
1541
+ return { accent: "#1a6dbe", fill: "#e5eff8", text: "#0a3a6e" };
1542
+ case "external":
1543
+ return { accent: "#6b7280", fill: "#f3f4f6", text: "#374151" };
1544
+ case "producer":
1545
+ return { accent: "#1f9747", fill: "#dcf1e2", text: "#0f3d22" };
1546
+ case "topic":
1547
+ return { accent: "#0f766e", fill: "#ccfbf1", text: "#0f4f49" };
1548
+ case "consumer":
1549
+ return { accent: "#1a6dbe", fill: "#e5eff8", text: "#0a3a6e" };
1550
+ case "context":
1551
+ return { accent: "#6b21a8", fill: "#ede9fe", text: "#4a1772" };
1552
+ case "firewall":
1553
+ return { accent: "#991b1b", fill: "#fee2e2", text: "#991b1b" };
1554
+ default:
1555
+ return { accent: "#374151", fill: "#fff", text: "#1a1a2e" };
1556
+ }
1557
+ }
1558
+ function nodeGlyph(kind, x, y, c) {
1559
+ const k = (kind ?? "").toLowerCase();
1560
+ if (k === "store" || k === "db" || k === "database") {
1561
+ return `<g stroke="${c}" stroke-width="1.3" fill="none"><ellipse cx="${x + 8}" cy="${y + 3}" rx="7" ry="3"/><path d="M${x + 1} ${y + 3} V ${y + 13}"/><path d="M${x + 15} ${y + 3} V ${y + 13}"/><path d="M${x + 1} ${y + 13} a 7 3 0 0 0 14 0"/></g>`;
1562
+ }
1563
+ if (k === "bucket" || k === "blob" || k === "object") {
1564
+ return `<g stroke="${c}" stroke-width="1.3" fill="none"><ellipse cx="${x + 8}" cy="${y + 3}" rx="7" ry="2.5"/><path d="M${x + 1.5} ${y + 3} L ${x + 3.5} ${y + 14} L ${x + 12.5} ${y + 14} L ${x + 14.5} ${y + 3}"/></g>`;
1565
+ }
1566
+ if (k === "queue" || k === "topic") {
1567
+ return `<g stroke="${c}" stroke-width="1.6"><path d="M${x + 2} ${y + 1} V ${y + 14}"/><path d="M${x + 8} ${y + 1} V ${y + 14}"/><path d="M${x + 14} ${y + 1} V ${y + 14}"/></g>`;
1568
+ }
1569
+ if (k === "firewall") {
1570
+ return `<path d="M${x + 8} ${y} L ${x + 15} ${y + 3} V ${y + 9} Q ${x + 15} ${y + 14} ${x + 8} ${y + 16} Q ${x + 1} ${y + 14} ${x + 1} ${y + 9} V ${y + 3} Z" fill="none" stroke="${c}" stroke-width="1.3"/>`;
1571
+ }
1572
+ if (k === "cache") {
1573
+ const slab = (dy) => `<path d="M${x + 1} ${y + 5 + dy} L${x + 8} ${y + 2 + dy} L${x + 15} ${y + 5 + dy} L${x + 8} ${y + 8 + dy} Z" fill="none" stroke="${c}" stroke-width="1.2"/>`;
1574
+ return `<g>${slab(0)}${slab(3.5)}${slab(7)}</g>`;
1575
+ }
1576
+ if (k === "function" || k === "lambda") {
1577
+ return `<text x="${x + 7}" y="${y + 14}" font-family="Georgia, serif" font-size="17" font-style="italic" font-weight="700" fill="${c}" text-anchor="middle">\u0192</text>`;
1578
+ }
1579
+ if (k === "cdn" || k === "external") {
1580
+ return `<path d="M${x + 3} ${y + 13} a 4 4 0 0 1 0.5 -8 a 5 5 0 0 1 9.5 1.2 a 3 3 0 0 1 1 6.8 z" fill="none" stroke="${c}" stroke-width="1.3"/>`;
1581
+ }
1582
+ if (k === "gateway" || k === "lb") {
1583
+ return `<g stroke="${c}" stroke-width="1.5" fill="none"><path d="M${x + 2} ${y + 1} L ${x + 8} ${y + 7} L ${x + 2} ${y + 13}"/><path d="M${x + 8} ${y + 1} L ${x + 14} ${y + 7} L ${x + 8} ${y + 13}"/></g>`;
1584
+ }
1585
+ if (k === "service" || k === "microservice" || k === "compute" || k === "container") {
1586
+ return `<g stroke="${c}" stroke-width="1.3"><rect x="${x + 4}" y="${y + 1}" width="11" height="11" rx="1.5" fill="none"/><rect x="${x + 1}" y="${y + 4}" width="11" height="11" rx="1.5" fill="#fff"/></g>`;
1587
+ }
1588
+ return "";
1589
+ }
1590
+ var GEDGE = {
1591
+ solid: { stroke: "var(--charcoal)", sw: 1.4, dash: "", marker: "gArrow", err: false },
1592
+ dashed: { stroke: "var(--gray)", sw: 1.4, dash: "5 4", marker: "gSoft", err: false },
1593
+ forbidden: { stroke: "#991b1b", sw: 2, dash: "", marker: "gErr", err: true },
1594
+ error: { stroke: "#991b1b", sw: 1.6, dash: "", marker: "gErr", err: true }
1595
+ };
1596
+
1597
+ // src/blocks/c4.ts
1598
+ function c4Style(n) {
1599
+ const f = (n.family ?? "").toLowerCase();
1600
+ switch (n.kind) {
1601
+ case "person":
1602
+ return {
1603
+ accent: "#0e54a1",
1604
+ fill: "#0e54a1",
1605
+ text: "#fff",
1606
+ sub: "#cfe0f3",
1607
+ chip: "Person",
1608
+ solid: true
1609
+ };
1610
+ case "system":
1611
+ return {
1612
+ accent: "#1f9747",
1613
+ fill: "#dcf1e2",
1614
+ text: "#0f3d22",
1615
+ sub: "#356b49",
1616
+ chip: "Software System"
1617
+ };
1618
+ case "external":
1619
+ return {
1620
+ accent: "#6b7280",
1621
+ fill: "#f3f4f6",
1622
+ text: "#374151",
1623
+ sub: "#6b7280",
1624
+ chip: "External System"
1625
+ };
1626
+ case "store":
1627
+ return {
1628
+ accent: "#f7952c",
1629
+ fill: "#fde7cd",
1630
+ text: "#7a3d00",
1631
+ sub: "#9a5a12",
1632
+ chip: "Database"
1633
+ };
1634
+ case "component":
1635
+ if (f === "repo")
1636
+ return {
1637
+ accent: "#374151",
1638
+ fill: "#f3f4f6",
1639
+ text: "#1a1a2e",
1640
+ sub: "#374151",
1641
+ chip: "Component"
1642
+ };
1643
+ if (f === "external")
1644
+ return {
1645
+ accent: "#6b7280",
1646
+ fill: "#fff",
1647
+ text: "#374151",
1648
+ sub: "#6b7280",
1649
+ chip: "External",
1650
+ dash: "4 3"
1651
+ };
1652
+ if (f === "controller")
1653
+ return {
1654
+ accent: "#0e54a1",
1655
+ fill: "#e5eff8",
1656
+ text: "#0a3a6e",
1657
+ sub: "#365f86",
1658
+ chip: "Component"
1659
+ };
1660
+ return {
1661
+ accent: "#1f9747",
1662
+ fill: "#dcf1e2",
1663
+ text: "#0f3d22",
1664
+ sub: "#356b49",
1665
+ chip: "Component"
1666
+ };
1667
+ default:
1668
+ if (f === "data")
1669
+ return {
1670
+ accent: "#6b21a8",
1671
+ fill: "#ede9fe",
1672
+ text: "#4a1772",
1673
+ sub: "#6b21a8",
1674
+ chip: "Container"
1675
+ };
1676
+ if (f === "service")
1677
+ return {
1678
+ accent: "#1f9747",
1679
+ fill: "#dcf1e2",
1680
+ text: "#0f3d22",
1681
+ sub: "#356b49",
1682
+ chip: "Container"
1683
+ };
1684
+ if (f === "client")
1685
+ return {
1686
+ accent: "#0e54a1",
1687
+ fill: "#e5eff8",
1688
+ text: "#0a3a6e",
1689
+ sub: "#365f86",
1690
+ chip: "Container"
1691
+ };
1692
+ if (f === "store")
1693
+ return {
1694
+ accent: "#f7952c",
1695
+ fill: "#fde7cd",
1696
+ text: "#7a3d00",
1697
+ sub: "#9a5a12",
1698
+ chip: "Database"
1699
+ };
1700
+ return {
1701
+ accent: "#0e54a1",
1702
+ fill: "#e5eff8",
1703
+ text: "#0a3a6e",
1704
+ sub: "#365f86",
1705
+ chip: "Container"
1706
+ };
1707
+ }
1708
+ }
1709
+ var LEGEND = [
1710
+ { sw: "#0e54a1", label: "Person" },
1711
+ { sw: "#dcf1e2", label: "System / service" },
1712
+ { sw: "#e5eff8", label: "Container (client)" },
1713
+ { sw: "#ede9fe", label: "Container (data)" },
1714
+ { sw: "#fde7cd", label: "Database" },
1715
+ { sw: "#f3f4f6", label: "External" }
1716
+ ];
1717
+ function renderC4(data) {
1718
+ const nodes = data.nodes ?? [];
1719
+ const edges = data.edges ?? [];
1720
+ const byId = new Map(nodes.map((n) => [n.id, n]));
1721
+ const cellW = 212;
1722
+ const cellH = 102;
1723
+ const gapX = 56;
1724
+ const gapY = 64;
1725
+ const padX = 26;
1726
+ const padTop = 46;
1727
+ const padBot = 24;
1728
+ const cols = Math.max(1, ...nodes.map((n) => n.col + ((n.w ?? 1) - 1)));
1729
+ const rows = Math.max(1, ...nodes.map((n) => n.row));
1730
+ const rectFor2 = (n) => ({
1731
+ x: padX + (n.col - 1) * (cellW + gapX),
1732
+ y: padTop + (n.row - 1) * (cellH + gapY),
1733
+ w: (n.w ?? 1) * cellW + ((n.w ?? 1) - 1) * gapX,
1734
+ h: cellH
1735
+ });
1736
+ const width = padX * 2 + cols * cellW + (cols - 1) * gapX;
1737
+ const height = padTop + rows * cellH + (rows - 1) * gapY + padBot;
1738
+ let boundarySvg = "";
1739
+ if (data.boundary !== void 0) {
1740
+ const internals = nodes.filter((n) => n.kind === "container" || n.kind === "component" || n.kind === "store").map(rectFor2);
1741
+ if (internals.length > 0) {
1742
+ const minX = Math.min(...internals.map((r) => r.x)) - 16;
1743
+ const minY = Math.min(...internals.map((r) => r.y)) - 26;
1744
+ const maxX = Math.max(...internals.map((r) => r.x + r.w)) + 16;
1745
+ const maxY = Math.max(...internals.map((r) => r.y + r.h)) + 16;
1746
+ const w = Math.max(120, data.boundary.label.length * 6.2);
1747
+ boundarySvg = `<g><rect x="${minX}" y="${minY}" width="${maxX - minX}" height="${maxY - minY}" rx="12" class="c4-boundary"/><rect x="${minX + 12}" y="${minY - 8}" width="${w}" height="16" fill="#fff"/><text x="${minX + 16}" y="${minY + 4}" class="c4-boundary-label">${escapeHtml(data.boundary.label)}</text></g>`;
1748
+ }
1749
+ }
1750
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>C4 diagram</title>${boundarySvg}`;
1751
+ for (const e of edges) {
1752
+ const A = byId.get(e.from);
1753
+ const B = byId.get(e.to);
1754
+ if (!A || !B) continue;
1755
+ const p = ortho(rectFor2(A), rectFor2(B));
1756
+ const st = GEDGE[e.kind ?? "solid"] ?? GEDGE["solid"] ?? {
1757
+ stroke: "#1a1a2e",
1758
+ sw: 1.4,
1759
+ dash: "",
1760
+ marker: "gArrow",
1761
+ err: false
1762
+ };
1763
+ s += `<g><path d="${p.d}" fill="none" stroke="${st.stroke}" stroke-width="${st.sw}" stroke-dasharray="${st.dash}" marker-end="url(#${st.marker})"/>` + edgePill(p, e.label, st.err) + `</g>`;
1764
+ }
1765
+ for (const n of nodes) {
1766
+ const r = rectFor2(n);
1767
+ const st = c4Style(n);
1768
+ const px = r.x + 16;
1769
+ const desc = wrapText(n.desc, 30, 2);
1770
+ const strokeWidth = st.solid === true ? 0 : 1.2;
1771
+ const strokeAttr = st.solid === true ? "none" : st.accent;
1772
+ const dashAttr = st.dash !== void 0 ? ` stroke-dasharray="${st.dash}"` : "";
1773
+ const stripe = st.solid === true ? "" : `<rect x="${r.x}" y="${r.y}" width="5" height="${r.h}" rx="2" fill="${st.accent}"/>`;
1774
+ const personGlyph = n.kind === "person" ? `<g fill="${st.text}"><circle cx="${px + 6}" cy="${r.y + 19}" r="6"/><path d="M ${px} ${r.y + 34} a 6 7 0 0 1 12 0 z"/></g>` : "";
1775
+ const chipX = n.kind === "person" ? px + 22 : px;
1776
+ const chipFill = st.solid === true ? st.sub : st.accent;
1777
+ const techLine = n.tech !== void 0 ? `<text x="${px}" y="${r.y + 60}" class="c4-tech" fill="${st.sub}">${escapeHtml(n.tech)}</text>` : "";
1778
+ const descLines = desc.map(
1779
+ (ln, j) => `<text x="${px}" y="${r.y + 77 + j * 13}" class="c4-desc" fill="${st.sub}">${escapeHtml(ln)}</text>`
1780
+ ).join("");
1781
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="10" fill="${st.fill}" stroke="${strokeAttr}" stroke-width="${strokeWidth}"${dashAttr}/>` + stripe + personGlyph + `<text x="${chipX}" y="${r.y + 22}" class="c4-chip" fill="${chipFill}">${escapeHtml(st.chip)}</text><text x="${px}" y="${r.y + 44}" class="c4-name" fill="${st.text}">${escapeHtml(n.name)}</text>` + techLine + descLines + `</g>`;
1782
+ }
1783
+ s += `</svg>`;
1784
+ const legend = `<div class="legend">` + LEGEND.map(
1785
+ (l) => `<span class="item"><span class="sw" style="background:${l.sw};border:1px solid #d1d5db"></span>${escapeHtml(l.label)}</span>`
1786
+ ).join("") + `</div>`;
1787
+ return diagramFrame(
1788
+ {
1789
+ tag: "C4",
1790
+ tagClass: "c4",
1791
+ ...data.title !== void 0 ? { title: data.title } : {},
1792
+ ...data.description !== void 0 ? { desc: data.description } : {}
1793
+ },
1794
+ s + legend
1795
+ );
1796
+ }
1797
+
1798
+ // src/blocks/uml.ts
1799
+ function umlRel(kind) {
1800
+ switch ((kind ?? "association").toLowerCase()) {
1801
+ case "inheritance":
1802
+ case "extends":
1803
+ return { dash: "", end: "umlTri" };
1804
+ case "implementation":
1805
+ case "implements":
1806
+ return { dash: "6 4", end: "umlTri" };
1807
+ case "composition":
1808
+ return { dash: "", start: "umlDiaF" };
1809
+ case "aggregation":
1810
+ return { dash: "", start: "umlDiaH" };
1811
+ case "dependency":
1812
+ return { dash: "6 4", end: "umlOpen" };
1813
+ default:
1814
+ return { dash: "", end: "umlOpen" };
1815
+ }
1816
+ }
1817
+ function renderUml(data) {
1818
+ const classes = data.classes ?? [];
1819
+ const rels = data.rels ?? [];
1820
+ const colW = 204;
1821
+ const gapX = 64;
1822
+ const gapY = 50;
1823
+ const padX = 26;
1824
+ const padTop = 30;
1825
+ const padBot = 22;
1826
+ const rowH = 17;
1827
+ const headH = (c) => 28 + (c.stereotype !== void 0 ? 12 : 0);
1828
+ const compH = (list) => (list !== void 0 && list.length > 0 ? list.length * rowH : 6) + 8;
1829
+ const clsH = (c) => headH(c) + compH(c.attrs) + compH(c.methods);
1830
+ const cols = Math.max(1, ...classes.map((c) => c.col));
1831
+ const rows = Math.max(1, ...classes.map((c) => c.row));
1832
+ const bandH = /* @__PURE__ */ new Map();
1833
+ const bandTop = /* @__PURE__ */ new Map();
1834
+ let acc = padTop;
1835
+ for (let r = 1; r <= rows; r++) {
1836
+ const hs = classes.filter((c) => c.row === r).map(clsH);
1837
+ const h = hs.length > 0 ? Math.max(...hs) : 60;
1838
+ bandH.set(r, h);
1839
+ bandTop.set(r, acc);
1840
+ acc += h + gapY;
1841
+ }
1842
+ const xOf = (c) => padX + (c - 1) * (colW + gapX);
1843
+ const rectFor2 = (c) => ({
1844
+ x: xOf(c.col),
1845
+ y: bandTop.get(c.row) ?? padTop,
1846
+ w: colW,
1847
+ h: clsH(c)
1848
+ });
1849
+ const byId = new Map(classes.map((c) => [c.id, c]));
1850
+ const width = padX * 2 + cols * colW + (cols - 1) * gapX;
1851
+ const height = acc - gapY + padBot;
1852
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>UML class diagram</title><defs><marker id="umlTri" viewBox="0 0 14 14" refX="13" refY="7" markerWidth="15" markerHeight="15" orient="auto-start-reverse"><path d="M1,1 L13,7 L1,13 z" fill="#fff" stroke="#1a1a2e" stroke-width="1.2"/></marker><marker id="umlDiaF" viewBox="0 0 20 12" refX="19" refY="6" markerWidth="20" markerHeight="12" orient="auto-start-reverse"><path d="M1,6 L10,1 L19,6 L10,11 z" fill="#1a1a2e"/></marker><marker id="umlDiaH" viewBox="0 0 20 12" refX="19" refY="6" markerWidth="20" markerHeight="12" orient="auto-start-reverse"><path d="M1,6 L10,1 L19,6 L10,11 z" fill="#fff" stroke="#1a1a2e" stroke-width="1.2"/></marker><marker id="umlOpen" viewBox="0 0 12 12" refX="10" refY="6" markerWidth="12" markerHeight="12" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11" fill="none" stroke="#1a1a2e" stroke-width="1.3"/></marker></defs>`;
1853
+ for (const rl of rels) {
1854
+ const A = byId.get(rl.from);
1855
+ const B = byId.get(rl.to);
1856
+ if (!A || !B) continue;
1857
+ const p = ortho(rectFor2(A), rectFor2(B));
1858
+ const st = umlRel(rl.kind);
1859
+ const start = st.start !== void 0 ? ` marker-start="url(#${st.start})"` : "";
1860
+ const end = st.end !== void 0 ? ` marker-end="url(#${st.end})"` : "";
1861
+ s += `<g><path d="${p.d}" fill="none" stroke="#1a1a2e" stroke-width="1.3" stroke-dasharray="${st.dash}"${start}${end}/>` + edgePill(p, rl.label) + `</g>`;
1862
+ }
1863
+ for (const c of classes) {
1864
+ const r = rectFor2(c);
1865
+ const hh = headH(c);
1866
+ const aH = compH(c.attrs);
1867
+ const nameY = r.y + (c.stereotype !== void 0 ? 24 : 19);
1868
+ const stereo = c.stereotype !== void 0 ? `<text x="${r.x + r.w / 2}" y="${r.y + 13}" class="uml-stereo">\xAB${escapeHtml(c.stereotype)}\xBB</text>` : "";
1869
+ const attrs = (c.attrs ?? []).map(
1870
+ (a, j) => `<text x="${r.x + 10}" y="${r.y + hh + 14 + j * rowH}" class="uml-row">${escapeHtml(a)}</text>`
1871
+ ).join("");
1872
+ const methods = (c.methods ?? []).map(
1873
+ (m, j) => `<text x="${r.x + 10}" y="${r.y + hh + aH + 14 + j * rowH}" class="uml-row">${escapeHtml(m)}</text>`
1874
+ ).join("");
1875
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="3" fill="#fff" stroke="#0e54a1" stroke-width="1.3"/>` + stereo + `<text x="${r.x + r.w / 2}" y="${nameY}" class="uml-name">${escapeHtml(c.name)}</text><line x1="${r.x}" y1="${r.y + hh}" x2="${r.x + r.w}" y2="${r.y + hh}" class="uml-sep"/>` + attrs + `<line x1="${r.x}" y1="${r.y + hh + aH}" x2="${r.x + r.w}" y2="${r.y + hh + aH}" class="uml-sep"/>` + methods + `</g>`;
1876
+ }
1877
+ s += `</svg>`;
1878
+ return diagramFrame(
1879
+ {
1880
+ tag: "UML",
1881
+ tagBg: "#6b21a8",
1882
+ ...data.title !== void 0 ? { title: data.title } : {},
1883
+ ...data.description !== void 0 ? { desc: data.description } : {}
1884
+ },
1885
+ s
1886
+ );
1887
+ }
1888
+
1889
+ // src/blocks/mece.ts
1890
+ var CHART_COLORS4 = ["#0e54a1", "#1a6dbe", "#0f766e", "#1f9747", "#6b21a8", "#f7952c"];
1891
+ function meceStyle(d) {
1892
+ if (d === 0) return { fill: "#0e54a1", text: "#fff", accent: "#0e54a1", solid: true };
1893
+ const c = CHART_COLORS4[d % CHART_COLORS4.length] ?? "#0e54a1";
1894
+ return { fill: "#fff", text: c, accent: c };
1895
+ }
1896
+ function renderMece(data) {
1897
+ const nodes = data.nodes ?? [];
1898
+ const byId = /* @__PURE__ */ new Map();
1899
+ const children = /* @__PURE__ */ new Map();
1900
+ for (const n of nodes) {
1901
+ byId.set(n.id, n);
1902
+ children.set(n.id, []);
1903
+ }
1904
+ const roots = [];
1905
+ for (const n of nodes) {
1906
+ if (n.parent !== void 0 && byId.has(n.parent)) children.get(n.parent)?.push(n.id);
1907
+ else roots.push(n.id);
1908
+ }
1909
+ const pos = /* @__PURE__ */ new Map();
1910
+ const depth = /* @__PURE__ */ new Map();
1911
+ const seen = /* @__PURE__ */ new Set();
1912
+ let leaf = 0;
1913
+ let maxDepth = 0;
1914
+ const dfs = (id, d) => {
1915
+ if (seen.has(id)) return;
1916
+ seen.add(id);
1917
+ depth.set(id, d);
1918
+ if (d > maxDepth) maxDepth = d;
1919
+ const ch = children.get(id) ?? [];
1920
+ if (ch.length === 0) {
1921
+ pos.set(id, leaf);
1922
+ leaf += 1;
1923
+ } else {
1924
+ for (const c of ch) dfs(c, d + 1);
1925
+ const first = pos.get(ch[0] ?? "") ?? 0;
1926
+ const last = pos.get(ch[ch.length - 1] ?? "") ?? 0;
1927
+ pos.set(id, (first + last) / 2);
1928
+ }
1929
+ };
1930
+ for (const r of roots) dfs(r, 0);
1931
+ const nodeW = 168;
1932
+ const nodeH = 50;
1933
+ const gapY = 16;
1934
+ const colGap = 56;
1935
+ const padX = 24;
1936
+ const padTop = 18;
1937
+ const padBot = 18;
1938
+ const xOf = (id) => padX + (depth.get(id) ?? 0) * (nodeW + colGap);
1939
+ const yOf = (id) => padTop + (pos.get(id) ?? 0) * (nodeH + gapY);
1940
+ const width = padX * 2 + (maxDepth + 1) * nodeW + maxDepth * colGap;
1941
+ const height = padTop + Math.max(leaf, 1) * (nodeH + gapY) - gapY + padBot;
1942
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Issue tree</title>`;
1943
+ for (const n of nodes) {
1944
+ if (n.parent === void 0 || !byId.has(n.parent) || !pos.has(n.id)) continue;
1945
+ const px = xOf(n.parent) + nodeW;
1946
+ const pcy = yOf(n.parent) + nodeH / 2;
1947
+ const cx = xOf(n.id);
1948
+ const ccy = yOf(n.id) + nodeH / 2;
1949
+ const midX = (px + cx) / 2;
1950
+ s += `<path class="tree-link" d="M ${px} ${pcy} H ${midX} V ${ccy} H ${cx}"/>`;
1951
+ }
1952
+ for (const n of nodes) {
1953
+ if (!pos.has(n.id)) continue;
1954
+ const x = xOf(n.id);
1955
+ const y = yOf(n.id);
1956
+ const st = meceStyle(depth.get(n.id) ?? 0);
1957
+ const stroke = st.solid === true ? "none" : st.accent;
1958
+ const stripe = st.solid === true ? "" : `<rect x="${x}" y="${y}" width="5" height="${nodeH}" rx="2" fill="${st.accent}"/>`;
1959
+ const labelX = x + (st.solid === true ? nodeW / 2 : 14);
1960
+ const anchor = st.solid === true ? "middle" : "start";
1961
+ const lines = wrapText(n.label, st.solid === true ? 22 : 20, n.note !== void 0 ? 1 : 2);
1962
+ const startY = lines.length === 2 ? y + 22 : y + (n.note !== void 0 ? 22 : 30);
1963
+ const labelTexts = lines.map(
1964
+ (ln, j) => `<text x="${labelX}" y="${startY + j * 14}" class="blk-name" fill="${st.text}" text-anchor="${anchor}">${escapeHtml(ln)}</text>`
1965
+ ).join("");
1966
+ const note = n.note !== void 0 ? `<text x="${labelX}" y="${y + 38}" class="ft-note" fill="${st.solid === true ? "#cfe0f3" : st.accent}" text-anchor="${anchor}">${escapeHtml(n.note)}</text>` : "";
1967
+ s += `<g filter="url(#gshadow)"><rect x="${x}" y="${y}" width="${nodeW}" height="${nodeH}" rx="6" fill="${st.fill}" stroke="${stroke}" stroke-width="1.3"/>` + stripe + labelTexts + note + `</g>`;
1968
+ }
1969
+ s += `</svg>`;
1970
+ return diagramFrame(
1971
+ {
1972
+ tag: "MECE",
1973
+ tagBg: "#0f766e",
1974
+ ...data.title !== void 0 ? { title: data.title } : {},
1975
+ ...data.description !== void 0 ? { desc: data.description } : {}
1976
+ },
1977
+ s
1978
+ );
1979
+ }
1980
+
1981
+ // src/blocks/frontend.ts
1982
+ function ftStyle(kind) {
1983
+ switch ((kind ?? "component").toLowerCase()) {
1984
+ case "root":
1985
+ return { accent: "#0e54a1", fill: "#0e54a1", text: "#fff", solid: true };
1986
+ case "layout":
1987
+ return { accent: "#0f766e", fill: "#ccfbf1", text: "#0f4f49" };
1988
+ case "page":
1989
+ return { accent: "#0e54a1", fill: "#e5eff8", text: "#0a3a6e" };
1990
+ case "component":
1991
+ return { accent: "#1f9747", fill: "#dcf1e2", text: "#0f3d22" };
1992
+ case "leaf":
1993
+ return { accent: "#6b7280", fill: "#f3f4f6", text: "#374151" };
1994
+ case "provider":
1995
+ case "context":
1996
+ return { accent: "#6b21a8", fill: "#ede9fe", text: "#4a1772" };
1997
+ case "hook":
1998
+ return { accent: "#7c3aed", fill: "#ede9fe", text: "#4a1772" };
1999
+ case "store":
2000
+ case "state":
2001
+ return { accent: "#f7952c", fill: "#fde7cd", text: "#7a3d00" };
2002
+ default:
2003
+ return { accent: "#1f9747", fill: "#dcf1e2", text: "#0f3d22" };
2004
+ }
2005
+ }
2006
+ function renderFrontend(data) {
2007
+ const nodes = data.nodes ?? [];
2008
+ const byId = /* @__PURE__ */ new Map();
2009
+ const children = /* @__PURE__ */ new Map();
2010
+ for (const n of nodes) {
2011
+ byId.set(n.id, n);
2012
+ children.set(n.id, []);
2013
+ }
2014
+ const roots = [];
2015
+ for (const n of nodes) {
2016
+ if (n.parent !== void 0 && byId.has(n.parent)) children.get(n.parent)?.push(n.id);
2017
+ else roots.push(n.id);
2018
+ }
2019
+ const pos = /* @__PURE__ */ new Map();
2020
+ const depth = /* @__PURE__ */ new Map();
2021
+ const seen = /* @__PURE__ */ new Set();
2022
+ let leaf = 0;
2023
+ let maxDepth = 0;
2024
+ const dfs = (id, d) => {
2025
+ if (seen.has(id)) return;
2026
+ seen.add(id);
2027
+ depth.set(id, d);
2028
+ if (d > maxDepth) maxDepth = d;
2029
+ const ch = children.get(id) ?? [];
2030
+ if (ch.length === 0) {
2031
+ pos.set(id, leaf);
2032
+ leaf += 1;
2033
+ } else {
2034
+ for (const c of ch) dfs(c, d + 1);
2035
+ const first = pos.get(ch[0] ?? "") ?? 0;
2036
+ const last = pos.get(ch[ch.length - 1] ?? "") ?? 0;
2037
+ pos.set(id, (first + last) / 2);
2038
+ }
2039
+ };
2040
+ for (const r of roots) dfs(r, 0);
2041
+ const nodeW = 158;
2042
+ const nodeH = 56;
2043
+ const gapX = 24;
2044
+ const levelGap = 96;
2045
+ const padX = 24;
2046
+ const padTop = 18;
2047
+ const padBot = 18;
2048
+ const slot = Math.max(leaf, 1);
2049
+ const xOf = (id) => padX + (pos.get(id) ?? 0) * (nodeW + gapX);
2050
+ const yOf = (id) => padTop + (depth.get(id) ?? 0) * levelGap;
2051
+ const width = padX * 2 + slot * (nodeW + gapX) - gapX;
2052
+ const height = padTop + maxDepth * levelGap + nodeH + padBot;
2053
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Component tree</title>`;
2054
+ for (const n of nodes) {
2055
+ if (n.parent === void 0 || !byId.has(n.parent) || !pos.has(n.id)) continue;
2056
+ const pcx = xOf(n.parent) + nodeW / 2;
2057
+ const pby = yOf(n.parent) + nodeH;
2058
+ const ccx = xOf(n.id) + nodeW / 2;
2059
+ const cty = yOf(n.id);
2060
+ const midY = (pby + cty) / 2;
2061
+ s += `<path class="tree-link" d="M ${pcx} ${pby} V ${midY} H ${ccx} V ${cty}"/>`;
2062
+ }
2063
+ for (const n of nodes) {
2064
+ if (!pos.has(n.id)) continue;
2065
+ const x = xOf(n.id);
2066
+ const y = yOf(n.id);
2067
+ const st = ftStyle(n.kind);
2068
+ const stroke = st.solid === true ? "none" : st.accent;
2069
+ const stripe = st.solid === true ? "" : `<rect x="${x}" y="${y}" width="5" height="${nodeH}" rx="2" fill="${st.accent}"/>`;
2070
+ const labelX = x + (st.solid === true ? nodeW / 2 : 14);
2071
+ const anchor = st.solid === true ? "middle" : "start";
2072
+ const lines = wrapText(n.name, st.solid === true ? 20 : 18, n.note !== void 0 ? 1 : 2);
2073
+ const startY = lines.length === 2 ? y + 25 : y + (n.note !== void 0 ? 25 : 33);
2074
+ const labelTexts = lines.map(
2075
+ (ln, j) => `<text x="${labelX}" y="${startY + j * 14}" class="blk-name" fill="${st.text}" text-anchor="${anchor}">${escapeHtml(ln)}</text>`
2076
+ ).join("");
2077
+ const note = n.note !== void 0 ? `<text x="${labelX}" y="${y + 41}" class="ft-note" fill="${st.solid === true ? "#cfe0f3" : st.accent}" text-anchor="${anchor}">${escapeHtml(n.note)}</text>` : "";
2078
+ s += `<g filter="url(#gshadow)"><rect x="${x}" y="${y}" width="${nodeW}" height="${nodeH}" rx="8" fill="${st.fill}" stroke="${stroke}" stroke-width="1.2"/>` + stripe + labelTexts + note + `</g>`;
2079
+ }
2080
+ s += `</svg>`;
2081
+ return diagramFrame(
2082
+ {
2083
+ tag: "FE",
2084
+ tagBg: "#0f766e",
2085
+ ...data.title !== void 0 ? { title: data.title } : {},
2086
+ ...data.description !== void 0 ? { desc: data.description } : {}
2087
+ },
2088
+ s
2089
+ );
2090
+ }
2091
+
2092
+ // src/blocks/cluster.ts
2093
+ function renderCluster(data) {
2094
+ const clusters = data.clusters ?? [];
2095
+ const services = data.services ?? [];
2096
+ const edges = data.edges ?? [];
2097
+ const svcByCluster = /* @__PURE__ */ new Map();
2098
+ for (const c of clusters) svcByCluster.set(c.id, []);
2099
+ for (const sv of services) {
2100
+ const list = svcByCluster.get(sv.cluster);
2101
+ if (list !== void 0) list.push(sv);
2102
+ }
2103
+ const serviceW = 158;
2104
+ const serviceH = 78;
2105
+ const gapS = 22;
2106
+ const cPadX = 26;
2107
+ const cHeader = 38;
2108
+ const cPadTop = 18;
2109
+ const cPadBot = 22;
2110
+ const cGap = 28;
2111
+ const outerPad = 26;
2112
+ let maxPerCluster = 1;
2113
+ for (const list of svcByCluster.values()) {
2114
+ if (list.length > maxPerCluster) maxPerCluster = list.length;
2115
+ }
2116
+ const cols = Math.min(4, Math.max(1, maxPerCluster));
2117
+ const clusterW = cols * serviceW + (cols - 1) * gapS + cPadX * 2;
2118
+ const width = outerPad * 2 + clusterW;
2119
+ const rects = /* @__PURE__ */ new Map();
2120
+ const clusterBoxes = [];
2121
+ let y = outerPad;
2122
+ for (const c of clusters) {
2123
+ const list = svcByCluster.get(c.id) ?? [];
2124
+ const rows = Math.max(1, Math.ceil(list.length / cols));
2125
+ const h = cHeader + cPadTop + rows * serviceH + (rows - 1) * gapS + cPadBot;
2126
+ const cx = outerPad;
2127
+ clusterBoxes.push({ c, x: cx, y, w: clusterW, h });
2128
+ list.forEach((sv, i) => {
2129
+ const r = Math.floor(i / cols);
2130
+ const col = i % cols;
2131
+ rects.set(sv.id, {
2132
+ x: cx + cPadX + col * (serviceW + gapS),
2133
+ y: y + cHeader + cPadTop + r * (serviceH + gapS),
2134
+ w: serviceW,
2135
+ h: serviceH
2136
+ });
2137
+ });
2138
+ y += h + cGap;
2139
+ }
2140
+ const height = y - cGap + outerPad;
2141
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Cluster diagram</title>`;
2142
+ for (const cb of clusterBoxes) {
2143
+ const kindLabel = cb.c.kind !== void 0 ? `<text x="${cb.x + cb.w - 14}" y="${cb.y + 21}" class="cl-kind">${escapeHtml(cb.c.kind)}</text>` : "";
2144
+ s += `<g><rect x="${cb.x}" y="${cb.y}" width="${cb.w}" height="${cb.h}" rx="12" fill="#0e54a1" fill-opacity="0.035" stroke="#0e54a1" stroke-width="1.4" stroke-dasharray="8 5"/><rect x="${cb.x}" y="${cb.y}" width="${cb.w}" height="${cHeader}" rx="12" fill="#0e54a1"/><rect x="${cb.x}" y="${cb.y + cHeader - 12}" width="${cb.w}" height="12" fill="#0e54a1"/><text x="${cb.x + 16}" y="${cb.y + 21}" class="cl-head">${escapeHtml(cb.c.label)}</text>` + kindLabel + `</g>`;
2145
+ }
2146
+ for (const e of edges) {
2147
+ const A = rects.get(e.from);
2148
+ const B = rects.get(e.to);
2149
+ if (!A || !B) continue;
2150
+ const p = ortho(A, B);
2151
+ const st = GEDGE[e.kind ?? "solid"] ?? GEDGE["solid"] ?? {
2152
+ stroke: "#1a1a2e",
2153
+ sw: 1.4,
2154
+ dash: "",
2155
+ marker: "gArrow",
2156
+ err: false
2157
+ };
2158
+ s += `<g><path d="${p.d}" fill="none" stroke="${st.stroke}" stroke-width="${st.sw}" stroke-dasharray="${st.dash}" marker-end="url(#${st.marker})"/>` + edgePill(p, e.label, st.err) + `</g>`;
2159
+ }
2160
+ for (const sv of services) {
2161
+ const r = rects.get(sv.id);
2162
+ if (r === void 0) continue;
2163
+ const st = blockStyle(sv.kind);
2164
+ const gl = nodeGlyph(sv.kind, r.x + 14, r.y + 14, st.accent);
2165
+ const nx = gl.length > 0 ? r.x + 38 : r.x + 14;
2166
+ const reps = sv.replicas ?? 0;
2167
+ const techLine = sv.tech !== void 0 ? `<text x="${nx}" y="${r.y + 42}" class="blk-tech" fill="${st.accent}">${escapeHtml(sv.tech)}</text>` : "";
2168
+ const repIndicator = reps > 0 ? (() => {
2169
+ const shown = Math.min(reps, 5);
2170
+ let bars = "";
2171
+ for (let j = 0; j < shown; j++) {
2172
+ bars += `<rect x="${r.x + 12 + j * 8}" y="${r.y + r.h - 14}" width="5" height="8" rx="1" fill="${st.accent}" opacity="0.7"/>`;
2173
+ }
2174
+ return `<g>` + bars + `<text x="${r.x + 12 + shown * 8 + 4}" y="${r.y + r.h - 7}" class="blk-tech" fill="${st.accent}">\xD7${reps}</text></g>`;
2175
+ })() : "";
2176
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="8" fill="${st.fill}" stroke="${st.accent}" stroke-width="1.2"/><rect x="${r.x}" y="${r.y}" width="5" height="${r.h}" rx="2" fill="${st.accent}"/>` + gl + `<text x="${nx}" y="${r.y + (sv.tech !== void 0 ? 26 : 30)}" class="blk-name" fill="${st.text}" style="font-size:12px">${escapeHtml(sv.label)}</text>` + techLine + repIndicator + `</g>`;
2177
+ }
2178
+ s += `</svg>`;
2179
+ return diagramFrame(
2180
+ {
2181
+ tag: "CLUSTER",
2182
+ tagBg: "#0e54a1",
2183
+ ...data.title !== void 0 ? { title: data.title } : {},
2184
+ ...data.description !== void 0 ? { desc: data.description } : {}
2185
+ },
2186
+ s
2187
+ );
2188
+ }
2189
+
2190
+ // src/blocks/blockGraph.ts
2191
+ var FALLBACK_EDGE = {
2192
+ stroke: "#1a1a2e",
2193
+ sw: 1.4,
2194
+ dash: "",
2195
+ marker: "gArrow",
2196
+ err: false
2197
+ };
2198
+ function renderGrid(data) {
2199
+ const groups = data.groups ?? [];
2200
+ const nodes = data.nodes ?? [];
2201
+ const edges = data.edges ?? [];
2202
+ const cellW = 184;
2203
+ const cellH = 82;
2204
+ const gapX = 56;
2205
+ const gapY = 58;
2206
+ const padX = 26;
2207
+ const padTop = 30;
2208
+ const padBot = 20;
2209
+ const cols = Math.max(
2210
+ 1,
2211
+ ...nodes.map((n) => (n.col ?? 1) + ((n.w ?? 1) - 1)),
2212
+ ...groups.map((g) => g.col + (g.cols ?? 1) - 1)
2213
+ );
2214
+ const rows = Math.max(
2215
+ 1,
2216
+ ...nodes.map((n) => n.row ?? 1),
2217
+ ...groups.map((g) => g.row + (g.rows ?? 1) - 1)
2218
+ );
2219
+ const xOf = (c) => padX + (c - 1) * (cellW + gapX);
2220
+ const yOf = (r) => padTop + (r - 1) * (cellH + gapY);
2221
+ const rectFor2 = (n) => ({
2222
+ x: xOf(n.col ?? 1),
2223
+ y: yOf(n.row ?? 1),
2224
+ w: (n.w ?? 1) * cellW + ((n.w ?? 1) - 1) * gapX,
2225
+ h: cellH
2226
+ });
2227
+ const groupRect = (g) => ({
2228
+ x: xOf(g.col) - 16,
2229
+ y: yOf(g.row) - 22,
2230
+ w: (g.cols ?? 1) * cellW + ((g.cols ?? 1) - 1) * gapX + 32,
2231
+ h: (g.rows ?? 1) * cellH + ((g.rows ?? 1) - 1) * gapY + 38
2232
+ });
2233
+ const byId = new Map(nodes.map((n) => [n.id, n]));
2234
+ const width = padX * 2 + cols * cellW + (cols - 1) * gapX;
2235
+ const height = padTop + rows * cellH + (rows - 1) * gapY + padBot;
2236
+ const sortedGroups = [...groups].sort(
2237
+ (a, b) => (b.cols ?? 1) * (b.rows ?? 1) - (a.cols ?? 1) * (a.rows ?? 1)
2238
+ );
2239
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Block diagram</title>`;
2240
+ for (const g of sortedGroups) {
2241
+ const r = groupRect(g);
2242
+ const col = safeColor(g.color, "#0e54a1");
2243
+ const badgeW = 16 + g.label.length * 6.4;
2244
+ const isContainer = (g.cols ?? 1) >= 2 && (g.rows ?? 1) >= 2;
2245
+ const zone = `<rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="10" fill="${col}" fill-opacity="0.06" stroke="${col}" stroke-opacity="0.55" stroke-width="1.3" stroke-dasharray="7 5"/>`;
2246
+ const label = isContainer ? `<rect x="${r.x + (r.w - badgeW) / 2}" y="${r.y - 1}" width="${badgeW}" height="20" rx="10" fill="${col}"/><text x="${r.x + r.w / 2}" y="${r.y + 13}" class="grp-label" fill="#fff" text-anchor="middle">${escapeHtml(g.label)}</text>` : `<path d="M${r.x} ${r.y + 20} L${r.x} ${r.y + 10} a10 10 0 0 1 10 -10 h${badgeW - 10} v20 z" fill="${col}"/><text x="${r.x + badgeW / 2}" y="${r.y + 14}" class="grp-label" fill="#fff" text-anchor="middle">${escapeHtml(g.label)}</text>`;
2247
+ s += `<g>${zone}${label}</g>`;
2248
+ }
2249
+ for (const e of edges) {
2250
+ const A = byId.get(e.from);
2251
+ const B = byId.get(e.to);
2252
+ if (!A || !B) continue;
2253
+ const p = ortho(rectFor2(A), rectFor2(B));
2254
+ const st = GEDGE[e.kind ?? "solid"] ?? GEDGE["solid"] ?? FALLBACK_EDGE;
2255
+ s += `<g><path d="${p.d}" fill="none" stroke="${st.stroke}" stroke-width="${st.sw}" stroke-dasharray="${st.dash}" marker-end="url(#${st.marker})"/>` + edgePill(p, e.label, st.err) + `</g>`;
2256
+ }
2257
+ for (const n of nodes) {
2258
+ const r = rectFor2(n);
2259
+ const st = blockStyle(n.kind);
2260
+ const gl = nodeGlyph(n.kind, r.x + 16, r.y + 16, st.accent);
2261
+ const nx = gl.length > 0 ? r.x + 42 : r.x + 16;
2262
+ const chip = gl.length === 0 && n.kind !== void 0 ? `<text x="${r.x + 16}" y="${r.y + 22}" class="blk-chip" fill="${st.accent}">${escapeHtml(n.kind)}</text>` : "";
2263
+ const tech = n.tech !== void 0 ? `<text x="${nx}" y="${r.y + (gl.length > 0 ? 50 : 60)}" class="blk-tech" fill="${st.accent}">${escapeHtml(n.tech)}</text>` : "";
2264
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="9" fill="${st.fill}" stroke="${st.accent}" stroke-width="1.2"/><rect x="${r.x}" y="${r.y}" width="5" height="${r.h}" rx="2" fill="${st.accent}"/>` + gl + chip + `<text x="${nx}" y="${r.y + (gl.length > 0 ? 34 : 44)}" class="blk-name" fill="${st.text}">${escapeHtml(n.name)}</text>` + tech + `</g>`;
2265
+ }
2266
+ s += `</svg>`;
2267
+ return s;
2268
+ }
2269
+ function renderLayered(data) {
2270
+ const layers = data.layers ?? [];
2271
+ const nodes = data.nodes ?? [];
2272
+ const edges = data.edges ?? [];
2273
+ const byLayer = layers.map(
2274
+ (_, i) => nodes.filter((n) => (n.layer ?? 0) === i)
2275
+ );
2276
+ const outerPad = 28;
2277
+ const titleH = data.systemLabel !== void 0 ? 32 : 16;
2278
+ const labelW = 132;
2279
+ const bandPadX = 16;
2280
+ const bandPadY = 14;
2281
+ const bandGap = 12;
2282
+ const nodeW = 158;
2283
+ const nodeH = 56;
2284
+ const nodeGap = 22;
2285
+ const rowW = (c) => c * nodeW + (c - 1) * nodeGap;
2286
+ const contentW = Math.max(220, ...byLayer.map((a) => rowW(Math.max(a.length, 1))));
2287
+ const bandInnerW = contentW + bandPadX * 2;
2288
+ const bandH = nodeH + bandPadY * 2;
2289
+ const innerX = outerPad + 14;
2290
+ const contentX = innerX + labelW;
2291
+ const width = contentX + bandInnerW + 14 + outerPad;
2292
+ const top = outerPad + titleH;
2293
+ const bandY = (i) => top + i * (bandH + bandGap);
2294
+ const height = bandY(layers.length) - bandGap + outerPad;
2295
+ const rects = /* @__PURE__ */ new Map();
2296
+ byLayer.forEach((arr, i) => {
2297
+ const startX = contentX + (bandInnerW - rowW(arr.length)) / 2;
2298
+ arr.forEach((n, j) => {
2299
+ rects.set(n.id, {
2300
+ x: startX + j * (nodeW + nodeGap),
2301
+ y: bandY(i) + bandPadY,
2302
+ w: nodeW,
2303
+ h: nodeH
2304
+ });
2305
+ });
2306
+ });
2307
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Layered architecture</title><rect x="${outerPad}" y="${outerPad}" width="${width - outerPad * 2}" height="${height - outerPad * 2}" rx="12" fill="none" stroke="#0e54a1" stroke-width="1.5"/>`;
2308
+ if (data.systemLabel !== void 0) {
2309
+ s += `<text x="${outerPad + 14}" y="${outerPad + 18}" class="grp-label" fill="#0e54a1">${escapeHtml(data.systemLabel)}</text>`;
2310
+ }
2311
+ for (let i = 0; i < layers.length; i++) {
2312
+ const L = layers[i];
2313
+ if (L === void 0) continue;
2314
+ s += `<g><rect x="${innerX}" y="${bandY(i)}" width="${labelW + bandInnerW}" height="${bandH}" rx="6" fill="#f3f4f6" stroke="#d1d5db"/><rect x="${innerX}" y="${bandY(i)}" width="${labelW}" height="${bandH}" rx="6" fill="#0e54a1"/><rect x="${innerX + labelW - 8}" y="${bandY(i)}" width="8" height="${bandH}" fill="#0e54a1"/><text x="${innerX + 14}" y="${bandY(i) + bandH / 2 + 4}" class="layer-label">${escapeHtml(L.label)}</text></g>`;
2315
+ }
2316
+ for (const e of edges) {
2317
+ const A = rects.get(e.from);
2318
+ const B = rects.get(e.to);
2319
+ if (!A || !B) continue;
2320
+ const p = ortho(A, B);
2321
+ const st = GEDGE[e.kind ?? "solid"] ?? GEDGE["solid"] ?? FALLBACK_EDGE;
2322
+ s += `<g><path d="${p.d}" fill="none" stroke="${st.stroke}" stroke-width="${st.sw}" stroke-dasharray="${st.dash}" marker-end="url(#${st.marker})"/>` + edgePill(p, e.label, st.err) + `</g>`;
2323
+ }
2324
+ for (const n of nodes) {
2325
+ const r = rects.get(n.id);
2326
+ if (r === void 0) continue;
2327
+ const st = blockStyle(n.kind);
2328
+ const gl = nodeGlyph(n.kind, r.x + 12, r.y + 12, st.accent);
2329
+ const nx = gl.length > 0 ? r.x + 34 : r.x + 14;
2330
+ const tech = n.tech !== void 0 ? `<text x="${nx}" y="${r.y + 42}" class="blk-tech" fill="${st.accent}">${escapeHtml(n.tech)}</text>` : "";
2331
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="8" fill="${st.fill}" stroke="${st.accent}" stroke-width="1.2"/><rect x="${r.x}" y="${r.y}" width="5" height="${r.h}" rx="2" fill="${st.accent}"/>` + gl + `<text x="${nx}" y="${r.y + (n.tech !== void 0 ? 26 : 33)}" class="blk-name" fill="${st.text}">${escapeHtml(n.name)}</text>` + tech + `</g>`;
2332
+ }
2333
+ s += `</svg>`;
2334
+ return s;
2335
+ }
2336
+ function renderBlockGraph(data, frame) {
2337
+ const svg = data.layers !== void 0 && data.layers.length > 0 ? renderLayered(data) : renderGrid(data);
2338
+ const opts = {
2339
+ tag: frame.tag,
2340
+ ...frame.tagClass !== void 0 ? { tagClass: frame.tagClass } : {},
2341
+ ...frame.tagBg !== void 0 ? { tagBg: frame.tagBg } : {},
2342
+ ...data.title !== void 0 ? { title: data.title } : {},
2343
+ ...data.description !== void 0 ? { desc: data.description } : {}
2344
+ };
2345
+ return diagramFrame(opts, svg);
2346
+ }
2347
+ function renderBlock(data) {
2348
+ return renderBlockGraph(data, { tag: "ARCH", tagBg: "#0f766e" });
2349
+ }
2350
+ function renderInfra(data) {
2351
+ return renderBlockGraph(data, { tag: "INFRA", tagBg: "#0078d4" });
2352
+ }
2353
+ function renderEvent(data) {
2354
+ return renderBlockGraph(data, { tag: "EVENT", tagBg: "#0f766e" });
2355
+ }
2356
+ function renderDdd(data) {
2357
+ return renderBlockGraph(data, { tag: "DDD", tagBg: "#6b21a8" });
2358
+ }
2359
+ function renderNetwork(data) {
2360
+ return renderBlockGraph(data, { tag: "ZONES", tagBg: "#991b1b" });
2361
+ }
2362
+
2363
+ // src/blocks/felogic.ts
2364
+ function feStyle(kind) {
2365
+ switch ((kind ?? "component").toLowerCase()) {
2366
+ case "engine":
2367
+ case "core":
2368
+ return { accent: "#0e54a1", fill: "#0e54a1", text: "#fff", solid: true };
2369
+ case "interface":
2370
+ return { accent: "#6b21a8", fill: "#fff", text: "#4a1772", dash: "5 4", stereo: "interface" };
2371
+ case "strategy":
2372
+ case "adapter":
2373
+ case "impl":
2374
+ return { accent: "#7c3aed", fill: "#ede9fe", text: "#4a1772" };
2375
+ case "controller":
2376
+ case "handler":
2377
+ case "route":
2378
+ return { accent: "#0e54a1", fill: "#cfe0f3", text: "#0a3a6e" };
2379
+ case "service":
2380
+ case "usecase":
2381
+ case "apiclient":
2382
+ case "client":
2383
+ return { accent: "#1a6dbe", fill: "#e5eff8", text: "#0a3a6e" };
2384
+ case "repository":
2385
+ case "repo":
2386
+ case "dao":
2387
+ return { accent: "#0f766e", fill: "#ccfbf1", text: "#0f4f49" };
2388
+ case "worker":
2389
+ case "consumer":
2390
+ return { accent: "#1f9747", fill: "#dcf1e2", text: "#0f3d22" };
2391
+ case "middleware":
2392
+ return { accent: "#6b7280", fill: "#f3f4f6", text: "#374151" };
2393
+ case "model":
2394
+ case "entity":
2395
+ return { accent: "#6b21a8", fill: "#ede9fe", text: "#4a1772" };
2396
+ case "db":
2397
+ case "store":
2398
+ case "database":
2399
+ return { accent: "#f7952c", fill: "#fde7cd", text: "#7a3d00" };
2400
+ case "cache":
2401
+ return { accent: "#0891b2", fill: "#cffafe", text: "#0e4f5c" };
2402
+ case "queue":
2403
+ case "bus":
2404
+ case "broker":
2405
+ return { accent: "#0f766e", fill: "#ccfbf1", text: "#0f4f49" };
2406
+ case "state":
2407
+ case "store_state":
2408
+ return { accent: "#f7952c", fill: "#fde7cd", text: "#7a3d00" };
2409
+ case "hook":
2410
+ return { accent: "#7c3aed", fill: "#ede9fe", text: "#4a1772" };
2411
+ case "external":
2412
+ case "backend":
2413
+ case "egress":
2414
+ case "api":
2415
+ case "thirdparty":
2416
+ return { accent: "#6b7280", fill: "#f3f4f6", text: "#374151", cloud: true };
2417
+ default:
2418
+ return { accent: "#1f9747", fill: "#dcf1e2", text: "#0f3d22" };
2419
+ }
2420
+ }
2421
+ function feEdge(kind) {
2422
+ switch ((kind ?? "uses").toLowerCase()) {
2423
+ case "implements":
2424
+ return { stroke: "#6b21a8", sw: 1.4, dash: "5 4", marker: "gTri" };
2425
+ case "egress":
2426
+ case "https":
2427
+ case "api":
2428
+ return { stroke: "#0e54a1", sw: 2, dash: "", marker: "gArrow" };
2429
+ case "reads":
2430
+ case "dashed":
2431
+ case "async":
2432
+ return { stroke: "#6b7280", sw: 1.4, dash: "5 4", marker: "gSoft" };
2433
+ default:
2434
+ return { stroke: "#1a1a2e", sw: 1.4, dash: "", marker: "gArrow" };
2435
+ }
2436
+ }
2437
+ var GLYPH_KINDS = /* @__PURE__ */ new Set([
2438
+ "db",
2439
+ "store",
2440
+ "database",
2441
+ "bucket",
2442
+ "blob",
2443
+ "object",
2444
+ "queue",
2445
+ "bus",
2446
+ "broker",
2447
+ "cache",
2448
+ "external",
2449
+ "backend",
2450
+ "api",
2451
+ "thirdparty",
2452
+ "function"
2453
+ ]);
2454
+ var GLYPH_REMAP = {
2455
+ database: "db",
2456
+ store: "db",
2457
+ bus: "queue",
2458
+ broker: "queue",
2459
+ backend: "external",
2460
+ api: "external",
2461
+ thirdparty: "external"
2462
+ };
2463
+ function renderFelogicGraph(data, frame) {
2464
+ const groups = data.groups ?? [];
2465
+ const nodes = data.nodes ?? [];
2466
+ const edges = data.edges ?? [];
2467
+ const cellW = 178;
2468
+ const cellH = 80;
2469
+ const gapX = 54;
2470
+ const gapY = 60;
2471
+ const padX = 26;
2472
+ const padTop = 30;
2473
+ const padBot = 20;
2474
+ const cols = Math.max(
2475
+ 1,
2476
+ ...nodes.map((n) => n.col + ((n.w ?? 1) - 1)),
2477
+ ...groups.map((g) => g.col + (g.cols ?? 1) - 1)
2478
+ );
2479
+ const rows = Math.max(
2480
+ 1,
2481
+ ...nodes.map((n) => n.row),
2482
+ ...groups.map((g) => g.row + (g.rows ?? 1) - 1)
2483
+ );
2484
+ const xOf = (c) => padX + (c - 1) * (cellW + gapX);
2485
+ const yOf = (r) => padTop + (r - 1) * (cellH + gapY);
2486
+ const rectFor2 = (n) => ({
2487
+ x: xOf(n.col),
2488
+ y: yOf(n.row),
2489
+ w: (n.w ?? 1) * cellW + ((n.w ?? 1) - 1) * gapX,
2490
+ h: cellH
2491
+ });
2492
+ const groupRect = (g) => ({
2493
+ x: xOf(g.col) - 16,
2494
+ y: yOf(g.row) - 22,
2495
+ w: (g.cols ?? 1) * cellW + ((g.cols ?? 1) - 1) * gapX + 32,
2496
+ h: (g.rows ?? 1) * cellH + ((g.rows ?? 1) - 1) * gapY + 38
2497
+ });
2498
+ const byId = new Map(nodes.map((n) => [n.id, n]));
2499
+ const width = padX * 2 + cols * cellW + (cols - 1) * gapX;
2500
+ const height = padTop + rows * cellH + (rows - 1) * gapY + padBot;
2501
+ const sortedGroups = [...groups].sort(
2502
+ (a, b) => (b.cols ?? 1) * (b.rows ?? 1) - (a.cols ?? 1) * (a.rows ?? 1)
2503
+ );
2504
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>Module graph</title>`;
2505
+ for (const g of sortedGroups) {
2506
+ const r = groupRect(g);
2507
+ const col = safeColor(g.color, "#0e54a1");
2508
+ s += `<g><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="10" fill="${col}" fill-opacity="0.05" stroke="${col}" stroke-opacity="0.5" stroke-width="1.2" stroke-dasharray="7 5"/><text x="${r.x + 14}" y="${r.y + 15}" class="grp-label" fill="${col}">${escapeHtml(g.label)}</text></g>`;
2509
+ }
2510
+ for (const e of edges) {
2511
+ const A = byId.get(e.from);
2512
+ const B = byId.get(e.to);
2513
+ if (!A || !B) continue;
2514
+ const p = ortho(rectFor2(A), rectFor2(B));
2515
+ const st = feEdge(e.kind);
2516
+ s += `<g><path d="${p.d}" fill="none" stroke="${st.stroke}" stroke-width="${st.sw}" stroke-dasharray="${st.dash}" marker-end="url(#${st.marker})"/>` + edgePill(p, e.label) + `</g>`;
2517
+ }
2518
+ for (const n of nodes) {
2519
+ const r = rectFor2(n);
2520
+ const st = feStyle(n.kind);
2521
+ const k = (n.kind ?? "").toLowerCase();
2522
+ const gl = GLYPH_KINDS.has(k) ? nodeGlyph(GLYPH_REMAP[k] ?? k, r.x + 16, r.y + 16, st.accent) : "";
2523
+ const nx = st.solid === true ? r.x + r.w / 2 : gl.length > 0 ? r.x + 42 : r.x + 16;
2524
+ const anchor = st.solid === true ? "middle" : "start";
2525
+ const nameY = r.y + (st.stereo !== void 0 ? 38 : n.note !== void 0 ? 36 : 44);
2526
+ const noteY = r.y + (st.stereo !== void 0 ? 56 : 52);
2527
+ const stroke = st.solid === true ? "none" : st.accent;
2528
+ const dashAttr = st.dash !== void 0 ? ` stroke-dasharray="${st.dash}"` : "";
2529
+ const stripe = st.solid === true ? "" : `<rect x="${r.x}" y="${r.y}" width="5" height="${r.h}" rx="2" fill="${st.accent}"/>`;
2530
+ const stereo = st.stereo !== void 0 ? `<text x="${r.x + r.w / 2}" y="${r.y + 20}" class="uml-stereo">\xAB${escapeHtml(st.stereo)}\xBB</text>` : "";
2531
+ const note = n.note !== void 0 ? `<text x="${nx}" y="${noteY}" class="blk-tech" fill="${st.solid === true ? "#cfe0f3" : st.accent}" text-anchor="${anchor}">${escapeHtml(n.note)}</text>` : "";
2532
+ s += `<g filter="url(#gshadow)"><rect x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="9" fill="${st.fill}" stroke="${stroke}" stroke-width="1.2"${dashAttr}/>` + stripe + gl + stereo + `<text x="${nx}" y="${nameY}" class="blk-name" fill="${st.text}" text-anchor="${anchor}">${escapeHtml(n.name)}</text>` + note + `</g>`;
2533
+ }
2534
+ s += `</svg>`;
2535
+ return diagramFrame(
2536
+ {
2537
+ tag: frame.tag,
2538
+ ...frame.tagBg !== void 0 ? { tagBg: frame.tagBg } : {},
2539
+ ...data.title !== void 0 ? { title: data.title } : {},
2540
+ ...data.description !== void 0 ? { desc: data.description } : {}
2541
+ },
2542
+ s
2543
+ );
2544
+ }
2545
+ function renderFelogic(data) {
2546
+ return renderFelogicGraph(data, { tag: "LOGIC", tagBg: "#6b21a8" });
2547
+ }
2548
+ function renderBelogic(data) {
2549
+ return renderFelogicGraph(data, { tag: "LOGIC", tagBg: "#0e54a1" });
2550
+ }
2551
+
2552
+ // src/blocks/wireframe.ts
2553
+ function contentWidth(device) {
2554
+ if (device === "phone") return 200;
2555
+ return 380;
2556
+ }
2557
+ function elementHeight(el) {
2558
+ const rows = Math.max(1, el.rows ?? 1);
2559
+ switch (el.type) {
2560
+ case "header":
2561
+ return 34;
2562
+ case "subheader":
2563
+ return 26;
2564
+ case "text":
2565
+ return 16 + (rows - 1) * 12;
2566
+ case "button":
2567
+ return 42;
2568
+ case "input":
2569
+ case "search":
2570
+ return 42;
2571
+ case "image":
2572
+ return 96;
2573
+ case "avatar":
2574
+ return 52;
2575
+ case "card":
2576
+ return rows * 64 + (rows - 1) * 10;
2577
+ case "list":
2578
+ return rows * 40;
2579
+ case "nav":
2580
+ return 34;
2581
+ case "tabs":
2582
+ return 52;
2583
+ case "divider":
2584
+ return 14;
2585
+ case "badge":
2586
+ return 26;
2587
+ case "toggle":
2588
+ return 30;
2589
+ case "spacer":
2590
+ return 18 * rows;
2591
+ default:
2592
+ return 24;
2593
+ }
2594
+ }
2595
+ var PH = 'fill="var(--light-gray)" stroke="var(--rule)" stroke-width="1"';
2596
+ function drawElement(el, x, y, w) {
2597
+ const rows = Math.max(1, el.rows ?? 1);
2598
+ const label = el.label ?? "";
2599
+ const accent = el.tone === "danger" ? "var(--negative)" : el.tone === "muted" ? "var(--gray)" : "var(--navy)";
2600
+ const anchorX = el.align === "c" ? x + w / 2 : el.align === "r" ? x + w : x;
2601
+ const anchor = el.align === "c" ? "middle" : el.align === "r" ? "end" : "start";
2602
+ switch (el.type) {
2603
+ case "header":
2604
+ return `<text x="${anchorX}" y="${y + 22}" class="wf-h" fill="var(--charcoal)" text-anchor="${anchor}">${escapeHtml(label || "Heading")}</text>`;
2605
+ case "subheader":
2606
+ return `<text x="${anchorX}" y="${y + 17}" class="wf-sub" fill="var(--gray)" text-anchor="${anchor}">${escapeHtml(label || "Subheading")}</text>`;
2607
+ case "text": {
2608
+ let s = "";
2609
+ for (let i = 0; i < rows; i++) {
2610
+ const lw = i === rows - 1 ? w * 0.66 : w;
2611
+ s += `<rect x="${x}" y="${y + i * 12}" width="${lw}" height="6" rx="3" fill="var(--rule)"/>`;
2612
+ }
2613
+ return s;
2614
+ }
2615
+ case "button":
2616
+ return `<rect x="${x}" y="${y}" width="${w}" height="34" rx="8" fill="${accent}"/><text x="${x + w / 2}" y="${y + 22}" class="wf-btn" text-anchor="middle">${escapeHtml(label || "Button")}</text>`;
2617
+ case "input":
2618
+ case "search": {
2619
+ const icon = el.type === "search" ? `<circle cx="${x + 16}" cy="${y + 17}" r="5" fill="none" stroke="var(--gray)" stroke-width="1.4"/><path d="M${x + 20} ${y + 21} l4 4" stroke="var(--gray)" stroke-width="1.4"/>` : "";
2620
+ const tx = el.type === "search" ? x + 30 : x + 12;
2621
+ return `<rect x="${x}" y="${y}" width="${w}" height="34" rx="8" fill="var(--white)" stroke="var(--rule)" stroke-width="1.2"/>` + icon + `<text x="${tx}" y="${y + 21}" class="wf-ph-text">${escapeHtml(label || "Type here\u2026")}</text>`;
2622
+ }
2623
+ case "image":
2624
+ return `<rect x="${x}" y="${y}" width="${w}" height="88" rx="8" ${PH}/><path d="M${x} ${y + 88} L${x + w * 0.4} ${y + 40} L${x + w * 0.62} ${y + 66} L${x + w * 0.78} ${y + 50} L${x + w} ${y + 88}" fill="none" stroke="var(--gray)" stroke-width="1.3"/><circle cx="${x + w * 0.74} " cy="${y + 26}" r="7" fill="none" stroke="var(--gray)" stroke-width="1.3"/>`;
2625
+ case "avatar":
2626
+ return `<circle cx="${x + 22}" cy="${y + 22}" r="20" ${PH}/><circle cx="${x + 22}" cy="${y + 17}" r="7" fill="var(--gray)"/><path d="M${x + 9} ${y + 40} a13 11 0 0 1 26 0" fill="var(--gray)"/>` + (label ? `<text x="${x + 52}" y="${y + 27}" class="wf-sub" fill="var(--charcoal)">${escapeHtml(label)}</text>` : "");
2627
+ case "card": {
2628
+ let s = "";
2629
+ for (let i = 0; i < rows; i++) {
2630
+ const cy = y + i * 74;
2631
+ s += `<rect x="${x}" y="${cy}" width="${w}" height="64" rx="10" fill="var(--white)" stroke="var(--rule)" stroke-width="1.2"/><rect x="${x + 12}" y="${cy + 12}" width="40" height="40" rx="8" ${PH}/><rect x="${x + 64}" y="${cy + 16}" width="${w - 92}" height="7" rx="3.5" fill="var(--rule)"/><rect x="${x + 64}" y="${cy + 34}" width="${(w - 92) * 0.6}" height="6" rx="3" fill="var(--rule)"/>`;
2632
+ }
2633
+ const cap = label ? `<text x="${x + 64}" y="${y + 30}" class="wf-ph-text"></text>` : "";
2634
+ return s + cap;
2635
+ }
2636
+ case "list": {
2637
+ let s = "";
2638
+ for (let i = 0; i < rows; i++) {
2639
+ const ly = y + i * 40;
2640
+ s += `<circle cx="${x + 16}" cy="${ly + 20}" r="12" ${PH}/><rect x="${x + 38}" y="${ly + 12}" width="${w - 76}" height="6" rx="3" fill="var(--rule)"/><rect x="${x + 38}" y="${ly + 24}" width="${(w - 76) * 0.55}" height="5" rx="2.5" fill="var(--rule)"/><path d="M${x + w - 14} ${ly + 15} l5 5 l-5 5" fill="none" stroke="var(--gray)" stroke-width="1.4"/>` + (i < rows - 1 ? `<line x1="${x + 38}" y1="${ly + 40}" x2="${x + w}" y2="${ly + 40}" stroke="var(--light-gray)" stroke-width="1"/>` : "");
2641
+ }
2642
+ return s;
2643
+ }
2644
+ case "nav": {
2645
+ const items = (label || "Home, Docs, Pricing, About").split(",").map((t) => t.trim());
2646
+ let s = "";
2647
+ let nx = x;
2648
+ for (const it of items) {
2649
+ const pw = 16 + it.length * 6.2;
2650
+ s += `<rect x="${nx}" y="${y + 4}" width="${pw}" height="22" rx="11" fill="var(--light-gray)"/><text x="${nx + pw / 2}" y="${y + 19}" class="wf-ph-text" text-anchor="middle">${escapeHtml(it)}</text>`;
2651
+ nx += pw + 8;
2652
+ }
2653
+ return s;
2654
+ }
2655
+ case "tabs": {
2656
+ const items = (label || "Home, Search, Bell, Profile").split(",").map((t) => t.trim());
2657
+ const seg = w / items.length;
2658
+ let s = `<line x1="${x}" y1="${y}" x2="${x + w}" y2="${y}" stroke="var(--rule)" stroke-width="1"/>`;
2659
+ items.forEach((it, i) => {
2660
+ const cx = x + seg * i + seg / 2;
2661
+ s += `<circle cx="${cx}" cy="${y + 18}" r="8" fill="none" stroke="${i === 0 ? accent : "var(--gray)"}" stroke-width="1.6"/><text x="${cx}" y="${y + 42}" class="wf-tab" text-anchor="middle" fill="${i === 0 ? accent : "var(--gray)"}">${escapeHtml(it)}</text>`;
2662
+ });
2663
+ return s;
2664
+ }
2665
+ case "divider":
2666
+ return `<line x1="${x}" y1="${y + 7}" x2="${x + w}" y2="${y + 7}" stroke="var(--rule)" stroke-width="1"/>`;
2667
+ case "badge": {
2668
+ const pw = 22 + label.length * 6.4;
2669
+ return `<rect x="${anchorX - (anchor === "middle" ? pw / 2 : anchor === "end" ? pw : 0)}" y="${y}" width="${pw}" height="22" rx="11" fill="${accent}"/><text x="${anchorX - (anchor === "middle" ? 0 : anchor === "end" ? pw / 2 : -pw / 2)}" y="${y + 15}" class="wf-btn" text-anchor="middle">${escapeHtml(label || "New")}</text>`;
2670
+ }
2671
+ case "toggle":
2672
+ return (label ? `<text x="${x}" y="${y + 19}" class="wf-sub" fill="var(--charcoal)">${escapeHtml(label)}</text>` : "") + `<rect x="${x + w - 44}" y="${y + 6}" width="44" height="22" rx="11" fill="${accent}"/><circle cx="${x + w - 16}" cy="${y + 17}" r="8" fill="#fff"/>`;
2673
+ case "spacer":
2674
+ return "";
2675
+ default:
2676
+ return "";
2677
+ }
2678
+ }
2679
+ function drawScreen(screen, idx) {
2680
+ const device = screen.device ?? "browser";
2681
+ const cw = contentWidth(device);
2682
+ const pad = 16;
2683
+ const els = screen.elements ?? [];
2684
+ let contentH = 0;
2685
+ for (const el of els) contentH += elementHeight(el) + 10;
2686
+ contentH = Math.max(contentH, 80);
2687
+ const isPhone = device === "phone";
2688
+ const isBrowser = device === "browser";
2689
+ const titleBarH = 30;
2690
+ const addressBarH = isBrowser ? 26 : 0;
2691
+ const chromeTop = titleBarH + addressBarH;
2692
+ const homeBarH = isPhone ? 24 : 0;
2693
+ const frameW = cw + pad * 2;
2694
+ const screenH = contentH + pad;
2695
+ const frameH = chromeTop + screenH + homeBarH;
2696
+ const rx = isPhone ? 30 : 14;
2697
+ const sw = isPhone ? 2.4 : 1.8;
2698
+ const clip = `wfclip${idx}`;
2699
+ let inner = "";
2700
+ if (isPhone) {
2701
+ inner += `<rect x="0" y="0" width="${frameW}" height="${titleBarH}" fill="var(--light-gray)"/>`;
2702
+ inner += `<rect x="${frameW / 2 - 26}" y="6" width="52" height="9" rx="4.5" fill="var(--charcoal)"/>`;
2703
+ if (screen.title)
2704
+ inner += `<text x="16" y="20" class="wf-status">${escapeHtml(screen.title)}</text>`;
2705
+ inner += `<text x="${frameW - 16}" y="20" class="wf-status" text-anchor="end">100%</text>`;
2706
+ } else {
2707
+ inner += `<rect x="0" y="0" width="${frameW}" height="${titleBarH}" fill="var(--light-gray)"/>`;
2708
+ inner += `<line x1="0" y1="${titleBarH}" x2="${frameW}" y2="${titleBarH}" stroke="var(--rule)" stroke-width="1"/>`;
2709
+ inner += `<circle cx="18" cy="15" r="5" fill="var(--negative)"/><circle cx="34" cy="15" r="5" fill="var(--highlight)"/><circle cx="50" cy="15" r="5" fill="var(--positive)"/>`;
2710
+ if (screen.title && !isBrowser)
2711
+ inner += `<text x="${frameW / 2}" y="20" class="wf-status" text-anchor="middle">${escapeHtml(screen.title)}</text>`;
2712
+ if (isBrowser) {
2713
+ const url = screen.url ?? screen.title ?? "example.com";
2714
+ inner += `<rect x="68" y="${titleBarH + 5}" width="${frameW - 84}" height="16" rx="8" fill="var(--white)" stroke="var(--rule)" stroke-width="1"/>`;
2715
+ inner += `<text x="78" y="${titleBarH + 16}" class="wf-url">${escapeHtml(url)}</text>`;
2716
+ }
2717
+ }
2718
+ let cy = chromeTop + pad;
2719
+ for (const el of els) {
2720
+ inner += drawElement(el, pad, cy, cw);
2721
+ cy += elementHeight(el) + 10;
2722
+ }
2723
+ if (isPhone) {
2724
+ inner += `<rect x="${frameW / 2 - 30}" y="${frameH - 15}" width="60" height="5" rx="2.5" fill="var(--charcoal)" opacity="0.55"/>`;
2725
+ }
2726
+ const s = `<g filter="url(#gshadow)"><defs><clipPath id="${clip}"><rect x="0" y="0" width="${frameW}" height="${frameH}" rx="${rx}"/></clipPath></defs><rect x="0" y="0" width="${frameW}" height="${frameH}" rx="${rx}" fill="var(--white)"/><g clip-path="url(#${clip})">${inner}</g><rect x="0" y="0" width="${frameW}" height="${frameH}" rx="${rx}" fill="none" stroke="var(--charcoal)" stroke-width="${sw}"/></g>`;
2727
+ return { svg: s, width: frameW, height: frameH };
2728
+ }
2729
+ function renderWireframe(data) {
2730
+ const screens = data.screens ?? [];
2731
+ const gap = 36;
2732
+ const capH = 22;
2733
+ const padX = 8;
2734
+ const padY = 8;
2735
+ const drawn = screens.map((screen, i) => drawScreen(screen, i));
2736
+ const totalW = drawn.reduce((a, d) => a + d.width, 0) + gap * Math.max(0, drawn.length - 1);
2737
+ const maxH = drawn.reduce((a, d) => Math.max(a, d.height), 0);
2738
+ const width = totalW + padX * 2;
2739
+ const height = maxH + capH + padY * 2;
2740
+ let s = `<svg viewBox="0 0 ${width} ${height}" role="img"><title>${escapeHtml(data.title ?? "UI mockup")}</title>`;
2741
+ let x = padX;
2742
+ drawn.forEach((d, i) => {
2743
+ const screen = screens[i];
2744
+ s += `<g transform="translate(${x}, ${padY})">${d.svg}</g>`;
2745
+ const cap = screen?.label;
2746
+ if (cap !== void 0 && cap.length > 0) {
2747
+ const lines = wrapText(cap, Math.floor(d.width / 6), 2);
2748
+ lines.forEach((ln, j) => {
2749
+ s += `<text x="${x + d.width / 2}" y="${padY + maxH + 16 + j * 12}" class="wf-caption" text-anchor="middle">${escapeHtml(ln)}</text>`;
2750
+ });
2751
+ }
2752
+ x += d.width + gap;
2753
+ });
2754
+ s += `</svg>`;
2755
+ return diagramFrame(
2756
+ {
2757
+ tag: "UI",
2758
+ tagBg: "#6b21a8",
2759
+ ...data.title !== void 0 ? { title: data.title } : {},
2760
+ ...data.description !== void 0 ? { desc: data.description } : {}
2761
+ },
2762
+ s
2763
+ );
2764
+ }
2765
+
2766
+ // src/registry.ts
2767
+ var htmlRenderers = {
2768
+ meta: renderMetaBlock,
2769
+ callout: renderCallout,
2770
+ table: renderTable,
2771
+ sequence: renderSequence,
2772
+ erd: renderErd,
2773
+ userstory: renderUserStory,
2774
+ timeline: renderTimeline,
2775
+ kanban: renderKanban,
2776
+ tracker: renderTracker,
2777
+ prose: renderProseBlock,
2778
+ glossary: renderGlossary,
2779
+ proscons: renderProsCons,
2780
+ cvt: renderCvt,
2781
+ stats: renderStats,
2782
+ code: renderCode,
2783
+ agenda: renderAgenda,
2784
+ tree: renderTree,
2785
+ pyramid: renderPyramid,
2786
+ funnel: renderFunnel,
2787
+ flow: renderFlow,
2788
+ state: renderState,
2789
+ dfd: renderDfd,
2790
+ journey: renderJourney,
2791
+ gantt: renderGantt,
2792
+ graph: renderGraph,
2793
+ quadrant: renderQuadrant,
2794
+ swimlane: renderSwimlane,
2795
+ c4: renderC4,
2796
+ uml: renderUml,
2797
+ mece: renderMece,
2798
+ frontend: renderFrontend,
2799
+ cluster: renderCluster,
2800
+ block: renderBlock,
2801
+ infra: renderInfra,
2802
+ event: renderEvent,
2803
+ ddd: renderDdd,
2804
+ network: renderNetwork,
2805
+ felogic: renderFelogic,
2806
+ belogic: renderBelogic,
2807
+ dag: renderDag,
2808
+ wireframe: renderWireframe
2809
+ };
2810
+
2811
+ // src/svg/defs.ts
2812
+ function globalDefsSvg() {
2813
+ return `<svg width="0" height="0" style="position:absolute" aria-hidden="true"><defs><marker id="gArrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="var(--charcoal)"/></marker><marker id="gSoft" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="var(--gray)"/></marker><marker id="gErr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#991b1b"/></marker><marker id="gTri" viewBox="0 0 14 14" refX="13" refY="7" markerWidth="14" markerHeight="14" orient="auto-start-reverse"><path d="M1,1 L13,7 L1,13 z" fill="#fff" stroke="#6b21a8" stroke-width="1.2"/></marker><filter id="gshadow" x="-20%" y="-20%" width="140%" height="170%"><feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#0e54a1" flood-opacity="0.13"/></filter></defs></svg>`;
2814
+ }
2815
+
2816
+ // src/themes.ts
2817
+ var DEFAULT_THEME = "navy";
2818
+ var themes = {
2819
+ navy: {
2820
+ label: "Editorial",
2821
+ vars: {}
2822
+ },
2823
+ teal: {
2824
+ label: "Teal",
2825
+ vars: {
2826
+ "--navy": "#0f766e",
2827
+ "--blue": "#0e7490",
2828
+ "--highlight": "#f59e0b"
2829
+ }
2830
+ },
2831
+ plum: {
2832
+ label: "Plum",
2833
+ vars: {
2834
+ "--navy": "#6b21a8",
2835
+ "--blue": "#7c3aed",
2836
+ "--highlight": "#db2777"
2837
+ }
2838
+ },
2839
+ slate: {
2840
+ label: "Slate sans",
2841
+ vars: {
2842
+ "--navy": "#334155",
2843
+ "--blue": "#475569",
2844
+ "--highlight": "#0d9488",
2845
+ "--font-display": '"Helvetica Neue", Arial, sans-serif'
2846
+ }
2847
+ },
2848
+ // Full dark mode. Surfaces (--white) and ink (--charcoal) flip; neutrals are
2849
+ // remapped so hairlines/edges read as light-on-dark. Accent hues are brightened
2850
+ // for contrast. Node "chip" pastels (in the SVG palette) stay light by design —
2851
+ // they read as colored cards on the dark canvas.
2852
+ dark: {
2853
+ label: "Dark",
2854
+ vars: {
2855
+ "--white": "#161b26",
2856
+ // surfaces: page + cards + diagram bg
2857
+ "--charcoal": "#e6e9f2",
2858
+ // primary ink + structural strokes
2859
+ "--slate": "#c2c9d6",
2860
+ // secondary text
2861
+ "--gray": "#94a0b4",
2862
+ // muted text / dashed edges
2863
+ "--light-gray": "#222a39",
2864
+ // subtle panels / zone fills / bars
2865
+ "--rule": "#333f54",
2866
+ // hairlines / borders
2867
+ "--navy": "#5b9cff",
2868
+ // primary accent (headings, links, primary nodes)
2869
+ "--navy-tint": "#1e2a44",
2870
+ "--blue": "#7fb0ff",
2871
+ "--light-blue": "#16233a",
2872
+ "--highlight": "#f7a64a",
2873
+ "--highlight-soft": "#3a2c17",
2874
+ "--positive": "#3ecf7a",
2875
+ "--positive-soft": "#16301f",
2876
+ "--negative": "#ff6b6b",
2877
+ "--negative-soft": "#3a1d1d",
2878
+ "--purple": "#b78bff",
2879
+ "--purple-soft": "#271d3a",
2880
+ "--teal": "#4fd1c5",
2881
+ "--teal-soft": "#13302d",
2882
+ "--radius": "14px"
2883
+ }
2884
+ },
2885
+ // Soft modern light theme: rounded surfaces, indigo accent, warm-gray ink.
2886
+ soft: {
2887
+ label: "Soft",
2888
+ vars: {
2889
+ "--navy": "#4f46e5",
2890
+ "--blue": "#6366f1",
2891
+ "--charcoal": "#1f2433",
2892
+ "--slate": "#4b5366",
2893
+ "--gray": "#8b93a7",
2894
+ "--rule": "#e6e8ef",
2895
+ "--light-gray": "#f5f6fa",
2896
+ "--highlight": "#f59e0b",
2897
+ "--radius": "16px",
2898
+ "--font-display": '"Helvetica Neue", Arial, sans-serif'
2899
+ }
2900
+ }
2901
+ };
2902
+ function themeStyle(name) {
2903
+ const vars = themes[name].vars;
2904
+ const parts = [];
2905
+ for (const k of Object.keys(vars)) parts.push(`${k}:${vars[k]};`);
2906
+ return parts.join("");
2907
+ }
2908
+
2909
+ // src/parts.ts
2910
+ function pad2(n) {
2911
+ return n < 10 ? `0${n}` : String(n);
2912
+ }
2913
+ function readTitleAndLede(data) {
2914
+ if (data === null || typeof data !== "object") return {};
2915
+ const d = data;
2916
+ const out = {};
2917
+ if (typeof d.title === "string" && d.title.length > 0) out.title = d.title;
2918
+ if (typeof d.lede === "string" && d.lede.length > 0) out.lede = d.lede;
2919
+ return out;
2920
+ }
2921
+ function renderSectionHead(num, kind, title, lede) {
2922
+ const label = SECTION_LABEL[kind];
2923
+ const titleHtml = title !== void 0 ? `<h2 class="section-title">${escapeHtml(title)}</h2>` : "";
2924
+ const ledeHtml = lede !== void 0 ? `<p class="section-lede">${escapeHtml(lede)}</p>` : "";
2925
+ return `<div class="section-head"><div class="section-num">SECTION ${pad2(num)} \xB7 ${escapeHtml(label)}</div>` + titleHtml + ledeHtml + `</div>`;
2926
+ }
2927
+ function dispatchBlock(kind, data) {
2928
+ const fn = htmlRenderers[kind];
2929
+ return fn(data);
2930
+ }
2931
+ function renderTypedSegment(seg, ctx) {
2932
+ if (seg.kind === "meta") return "";
2933
+ if (seg.parseError !== void 0) {
2934
+ return `<div class="err">${escapeHtml(seg.kind)} block \u2014 parse error:
2935
+ ${escapeHtml(seg.parseError)}</div>`;
2936
+ }
2937
+ if (seg.data === null || seg.data === void 0) return "";
2938
+ const body = dispatchBlock(seg.kind, seg.data);
2939
+ ctx.sectionNum += 1;
2940
+ const num = ctx.sectionNum;
2941
+ const { title, lede } = readTitleAndLede(seg.data);
2942
+ const id = `section-${pad2(num)}`;
2943
+ ctx.sections.push({
2944
+ id,
2945
+ num,
2946
+ label: SECTION_LABEL[seg.kind],
2947
+ ...title !== void 0 ? { title } : {}
2948
+ });
2949
+ const head = renderSectionHead(num, seg.kind, title, lede);
2950
+ return `<section id="${id}" class="section-block">${head}${body}</section>`;
2951
+ }
2952
+ function renderSegment(seg, ctx) {
2953
+ if (seg.kind === "markdown") return renderProse(seg.text);
2954
+ return renderTypedSegment(seg, ctx);
2955
+ }
2956
+ function buildThemeVars(theme, vars) {
2957
+ let css = themeStyle(theme);
2958
+ if (vars !== void 0) {
2959
+ for (const k of Object.keys(vars)) css += `${k}:${vars[k]};`;
2960
+ }
2961
+ return css;
2962
+ }
2963
+ function renderDocumentParts(doc, opts = {}) {
2964
+ const title = doc.meta?.title ?? "Untitled";
2965
+ const theme = opts.theme ?? DEFAULT_THEME;
2966
+ const themeVars = buildThemeVars(theme, opts.themeVars);
2967
+ const ctx = { sectionNum: 0, sections: [] };
2968
+ const body = globalDefsSvg() + renderCover(doc.meta) + doc.segments.map((s) => renderSegment(s, ctx)).join("");
2969
+ return { css: houseCss, themeVars, body, title, sections: ctx.sections };
2970
+ }
2971
+
2972
+ // src/document.ts
2973
+ function renderDocument(doc, opts = {}) {
2974
+ const parts = renderDocumentParts(doc, opts);
2975
+ const themeBlock = parts.themeVars.length > 0 ? `
2976
+ <style>:root{${parts.themeVars}}</style>` : "";
2977
+ return `<!doctype html>
2978
+ <html lang="en">
2979
+ <head>
2980
+ <meta charset="utf-8">
2981
+ <meta name="viewport" content="width=device-width, initial-scale=1">
2982
+ <title>${escapeHtml(parts.title)}</title>
2983
+ <style>${parts.css}</style>` + themeBlock + `
2984
+ </head>
2985
+ <body>
2986
+ <div class="docskin">
2987
+ ` + parts.body + `</div>
2988
+ </body>
2989
+ </html>
2990
+ `;
2991
+ }
2992
+
2993
+ export { DEFAULT_THEME, buildThemeVars, edgePill, escapeHtml, globalDefsSvg, houseCss, htmlRenderers, ortho, renderDocument, renderDocumentParts, renderProse, safeColor, safeUrl, themeStyle, themes, wrapText };
2994
+ //# sourceMappingURL=index.js.map
2995
+ //# sourceMappingURL=index.js.map