@hugobatist/smartcode 0.1.0
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/LICENSE +21 -0
- package/README.md +292 -0
- package/dist/cli.js +4324 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +374 -0
- package/dist/index.js +1167 -0
- package/dist/index.js.map +1 -0
- package/dist/static/annotations-panel.js +133 -0
- package/dist/static/annotations-svg.js +108 -0
- package/dist/static/annotations.css +367 -0
- package/dist/static/annotations.js +367 -0
- package/dist/static/app-init.js +497 -0
- package/dist/static/breakpoints.css +69 -0
- package/dist/static/breakpoints.js +197 -0
- package/dist/static/clipboard.js +94 -0
- package/dist/static/collapse-ui.js +325 -0
- package/dist/static/command-history.js +89 -0
- package/dist/static/context-menu.js +334 -0
- package/dist/static/custom-renderer.js +201 -0
- package/dist/static/dagre-layout.js +291 -0
- package/dist/static/diagram-dom.js +241 -0
- package/dist/static/diagram-editor.js +368 -0
- package/dist/static/editor-panel.js +107 -0
- package/dist/static/editor-popovers.js +187 -0
- package/dist/static/event-bus.js +57 -0
- package/dist/static/export.js +181 -0
- package/dist/static/file-tree.js +470 -0
- package/dist/static/ghost-paths.js +397 -0
- package/dist/static/heatmap.css +116 -0
- package/dist/static/heatmap.js +308 -0
- package/dist/static/icons.js +66 -0
- package/dist/static/inline-edit.js +294 -0
- package/dist/static/interaction-state.js +155 -0
- package/dist/static/interaction-tracker.js +93 -0
- package/dist/static/live.html +239 -0
- package/dist/static/main-layout.css +220 -0
- package/dist/static/main.css +334 -0
- package/dist/static/mcp-sessions.js +202 -0
- package/dist/static/modal.css +109 -0
- package/dist/static/modal.js +171 -0
- package/dist/static/node-drag.js +293 -0
- package/dist/static/pan-zoom.js +199 -0
- package/dist/static/renderer.js +280 -0
- package/dist/static/search.css +103 -0
- package/dist/static/search.js +304 -0
- package/dist/static/selection.js +353 -0
- package/dist/static/session-player.css +137 -0
- package/dist/static/session-player.js +411 -0
- package/dist/static/sidebar.css +248 -0
- package/dist/static/svg-renderer.js +313 -0
- package/dist/static/svg-shapes.js +218 -0
- package/dist/static/tokens.css +76 -0
- package/dist/static/vendor/dagre-bundle.js +43 -0
- package/dist/static/vendor/dagre.min.js +3 -0
- package/dist/static/vendor/graphlib.min.js +2 -0
- package/dist/static/viewport-transform.js +107 -0
- package/dist/static/workspace-switcher.js +202 -0
- package/dist/static/ws-client.js +71 -0
- package/dist/static/ws-handler.js +125 -0
- package/package.json +74 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SmartCode SVG Renderer -- assembles complete SVG DOM from a LayoutResult.
|
|
3
|
+
* Renders nodes (with shapes), edges (with arrows), and subgraphs.
|
|
4
|
+
*
|
|
5
|
+
* Dependencies: svg-shapes.js (SmartCodeSvgShapes)
|
|
6
|
+
* Dependents: (future) custom-renderer-bridge.js
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* var svgEl = SmartCodeSvgRenderer.createSVG(layout);
|
|
10
|
+
* container.appendChild(svgEl);
|
|
11
|
+
*/
|
|
12
|
+
(function() {
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
var NS = 'http://www.w3.org/2000/svg';
|
|
16
|
+
|
|
17
|
+
// ── Theme constants (matching renderer.js MERMAID_CONFIG themeVariables) ──
|
|
18
|
+
var THEME = {
|
|
19
|
+
nodeFill: '#eff6ff',
|
|
20
|
+
nodeStroke: '#2563eb',
|
|
21
|
+
nodeStrokeWidth: '2',
|
|
22
|
+
nodeTextColor: '#18181b',
|
|
23
|
+
nodeFontSize: '15',
|
|
24
|
+
nodeFontWeight: '600',
|
|
25
|
+
nodeFontFamily: "'Inter', sans-serif",
|
|
26
|
+
edgeStroke: '#52525b',
|
|
27
|
+
edgeStrokeWidth: '1.5',
|
|
28
|
+
edgeLabelBg: '#ffffff',
|
|
29
|
+
edgeLabelColor: '#52525b',
|
|
30
|
+
edgeLabelFontSize: '13',
|
|
31
|
+
subgraphFill: '#f4f4f5',
|
|
32
|
+
subgraphStroke: '#a1a1aa',
|
|
33
|
+
subgraphStrokeWidth: '1',
|
|
34
|
+
subgraphLabelColor: '#18181b',
|
|
35
|
+
subgraphLabelFontSize: '14',
|
|
36
|
+
subgraphLabelFontWeight: '700'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function el(tag) { return document.createElementNS(NS, tag); }
|
|
40
|
+
|
|
41
|
+
function attrs(element, map) {
|
|
42
|
+
for (var key in map) {
|
|
43
|
+
if (map.hasOwnProperty(key)) element.setAttribute(key, String(map[key]));
|
|
44
|
+
}
|
|
45
|
+
return element;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── Arrow Markers ──
|
|
49
|
+
function createArrowMarkers(defs) {
|
|
50
|
+
var sizes = { 'arrow-normal': 8, 'arrow-thick': 10, 'arrow-dotted': 8 };
|
|
51
|
+
for (var id in sizes) {
|
|
52
|
+
if (!sizes.hasOwnProperty(id)) continue;
|
|
53
|
+
var marker = attrs(el('marker'), {
|
|
54
|
+
id: id, viewBox: '0 0 10 10', refX: 10, refY: 5,
|
|
55
|
+
markerWidth: sizes[id], markerHeight: sizes[id],
|
|
56
|
+
orient: 'auto-start-reverse'
|
|
57
|
+
});
|
|
58
|
+
var arrow = el('polygon');
|
|
59
|
+
arrow.setAttribute('points', '0 0, 10 5, 0 10');
|
|
60
|
+
arrow.setAttribute('fill', THEME.edgeStroke);
|
|
61
|
+
marker.appendChild(arrow);
|
|
62
|
+
defs.appendChild(marker);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── Edge path construction ──
|
|
67
|
+
function edgePointsToPath(points) {
|
|
68
|
+
if (!points || points.length === 0) return '';
|
|
69
|
+
// Filter out points with NaN coordinates (defensive against dagre bugs)
|
|
70
|
+
var valid = [];
|
|
71
|
+
for (var vi = 0; vi < points.length; vi++) {
|
|
72
|
+
if (isFinite(points[vi].x) && isFinite(points[vi].y)) {
|
|
73
|
+
valid.push(points[vi]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (valid.length === 0) return '';
|
|
77
|
+
var d = 'M ' + valid[0].x + ' ' + valid[0].y;
|
|
78
|
+
if (valid.length === 2) {
|
|
79
|
+
d += ' L ' + valid[1].x + ' ' + valid[1].y;
|
|
80
|
+
} else if (valid.length >= 3) {
|
|
81
|
+
var idx = 1;
|
|
82
|
+
while (idx + 2 < valid.length) {
|
|
83
|
+
d += ' C ' + valid[idx].x + ' ' + valid[idx].y
|
|
84
|
+
+ ', ' + valid[idx + 1].x + ' ' + valid[idx + 1].y
|
|
85
|
+
+ ', ' + valid[idx + 2].x + ' ' + valid[idx + 2].y;
|
|
86
|
+
idx += 3;
|
|
87
|
+
}
|
|
88
|
+
while (idx < valid.length) {
|
|
89
|
+
d += ' L ' + valid[idx].x + ' ' + valid[idx].y;
|
|
90
|
+
idx++;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return d;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── Helper: clamp endpoint to rectangle boundary ──
|
|
97
|
+
function clampToRect(from, cx, cy, hw, hh) {
|
|
98
|
+
var dx = cx - from.x;
|
|
99
|
+
var dy = cy - from.y;
|
|
100
|
+
if (dx === 0 && dy === 0) return { x: cx, y: cy - hh };
|
|
101
|
+
var absDx = Math.abs(dx);
|
|
102
|
+
var absDy = Math.abs(dy);
|
|
103
|
+
var t = (absDx * hh > absDy * hw) ? hw / absDx : hh / absDy;
|
|
104
|
+
return { x: cx - dx * t, y: cy - dy * t };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── Shorten path to node boundary ──
|
|
108
|
+
function shortenPathToNodeBoundary(points, targetNode) {
|
|
109
|
+
if (!points || points.length < 2 || !targetNode) return points;
|
|
110
|
+
var result = [];
|
|
111
|
+
for (var i = 0; i < points.length; i++) {
|
|
112
|
+
result.push({ x: points[i].x, y: points[i].y });
|
|
113
|
+
}
|
|
114
|
+
var prev = result[result.length - 2];
|
|
115
|
+
var dx = result[result.length - 1].x - prev.x;
|
|
116
|
+
var dy = result[result.length - 1].y - prev.y;
|
|
117
|
+
var dist = Math.sqrt(dx * dx + dy * dy);
|
|
118
|
+
if (dist === 0) return result;
|
|
119
|
+
|
|
120
|
+
var nx = targetNode.x, ny = targetNode.y;
|
|
121
|
+
var nw = targetNode.width / 2, nh = targetNode.height / 2;
|
|
122
|
+
var shape = targetNode.shape || 'rect';
|
|
123
|
+
|
|
124
|
+
if (shape === 'circle') {
|
|
125
|
+
var r = Math.max(targetNode.width, targetNode.height) / 2;
|
|
126
|
+
result[result.length - 1] = { x: nx - (dx / dist) * r, y: ny - (dy / dist) * r };
|
|
127
|
+
} else if (shape === 'diamond') {
|
|
128
|
+
result[result.length - 1] = clampToRect(prev, nx, ny, nw * 0.7, nh * 0.7);
|
|
129
|
+
} else {
|
|
130
|
+
result[result.length - 1] = clampToRect(prev, nx, ny, nw, nh);
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── Render a single node ──
|
|
136
|
+
function renderNode(node) {
|
|
137
|
+
var g = el('g');
|
|
138
|
+
g.setAttribute('data-node-id', node.id);
|
|
139
|
+
g.setAttribute('class', 'smartcode-node');
|
|
140
|
+
var nx = isFinite(node.x) ? node.x : 0;
|
|
141
|
+
var ny = isFinite(node.y) ? node.y : 0;
|
|
142
|
+
g.setAttribute('transform', 'translate(' + nx + ',' + ny + ')');
|
|
143
|
+
|
|
144
|
+
var shapeEl = window.SmartCodeSvgShapes.render(node.shape, node.width, node.height);
|
|
145
|
+
attrs(shapeEl, { fill: THEME.nodeFill, stroke: THEME.nodeStroke, 'stroke-width': THEME.nodeStrokeWidth });
|
|
146
|
+
|
|
147
|
+
// For subroutine <g>, apply styles to child elements individually
|
|
148
|
+
if (node.shape === 'subroutine' && shapeEl.tagName === 'g') {
|
|
149
|
+
var children = shapeEl.childNodes;
|
|
150
|
+
for (var c = 0; c < children.length; c++) {
|
|
151
|
+
if (children[c].nodeType === 1) {
|
|
152
|
+
attrs(children[c], {
|
|
153
|
+
fill: children[c].tagName === 'rect' ? THEME.nodeFill : 'none',
|
|
154
|
+
stroke: THEME.nodeStroke, 'stroke-width': THEME.nodeStrokeWidth
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
g.appendChild(shapeEl);
|
|
160
|
+
|
|
161
|
+
var text = el('text');
|
|
162
|
+
attrs(text, {
|
|
163
|
+
'text-anchor': 'middle',
|
|
164
|
+
fill: THEME.nodeTextColor, 'font-family': THEME.nodeFontFamily,
|
|
165
|
+
'font-size': THEME.nodeFontSize, 'font-weight': THEME.nodeFontWeight
|
|
166
|
+
});
|
|
167
|
+
// Handle multiline labels: split on \n and use <tspan> per line
|
|
168
|
+
var labelStr = (node.label || '').replace(/\\n/g, '\n');
|
|
169
|
+
var lines = labelStr.split('\n');
|
|
170
|
+
var lineHeight = parseFloat(THEME.nodeFontSize) * 1.3;
|
|
171
|
+
var totalHeight = lines.length * lineHeight;
|
|
172
|
+
var startY = -(totalHeight - lineHeight) / 2;
|
|
173
|
+
for (var li = 0; li < lines.length; li++) {
|
|
174
|
+
var tspan = el('tspan');
|
|
175
|
+
tspan.setAttribute('x', '0');
|
|
176
|
+
tspan.setAttribute('dy', li === 0 ? String(startY) : String(lineHeight));
|
|
177
|
+
tspan.setAttribute('dominant-baseline', 'central');
|
|
178
|
+
tspan.textContent = lines[li];
|
|
179
|
+
text.appendChild(tspan);
|
|
180
|
+
}
|
|
181
|
+
g.appendChild(text);
|
|
182
|
+
return g;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── Edge type styling ──
|
|
186
|
+
function applyEdgeStyle(path, type) {
|
|
187
|
+
var base = { stroke: THEME.edgeStroke, fill: 'none' };
|
|
188
|
+
if (type === 'thick') {
|
|
189
|
+
attrs(path, { stroke: base.stroke, 'stroke-width': '3', 'marker-end': 'url(#arrow-thick)', fill: 'none' });
|
|
190
|
+
} else if (type === 'dotted') {
|
|
191
|
+
attrs(path, { stroke: base.stroke, 'stroke-width': THEME.edgeStrokeWidth, 'stroke-dasharray': '5,5', 'marker-end': 'url(#arrow-normal)', fill: 'none' });
|
|
192
|
+
} else if (type === 'open') {
|
|
193
|
+
attrs(path, { stroke: base.stroke, 'stroke-width': THEME.edgeStrokeWidth, fill: 'none' });
|
|
194
|
+
} else if (type === 'invisible') {
|
|
195
|
+
attrs(path, { stroke: 'none', fill: 'none' });
|
|
196
|
+
} else {
|
|
197
|
+
// Default: arrow
|
|
198
|
+
attrs(path, { stroke: base.stroke, 'stroke-width': THEME.edgeStrokeWidth, 'marker-end': 'url(#arrow-normal)', fill: 'none' });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ── Render a single edge ──
|
|
203
|
+
function renderEdge(edge, nodesMap) {
|
|
204
|
+
var g = el('g');
|
|
205
|
+
g.setAttribute('data-edge-id', edge.id);
|
|
206
|
+
g.setAttribute('class', 'smartcode-edge');
|
|
207
|
+
|
|
208
|
+
var targetNode = nodesMap[edge.to];
|
|
209
|
+
var points = targetNode ? shortenPathToNodeBoundary(edge.points, targetNode) : edge.points;
|
|
210
|
+
|
|
211
|
+
var path = el('path');
|
|
212
|
+
path.setAttribute('d', edgePointsToPath(points));
|
|
213
|
+
applyEdgeStyle(path, edge.type);
|
|
214
|
+
g.appendChild(path);
|
|
215
|
+
|
|
216
|
+
// Optional label at midpoint
|
|
217
|
+
if (edge.label) {
|
|
218
|
+
var midPt = points[Math.floor(points.length / 2)] || points[0];
|
|
219
|
+
var labelGroup = el('g');
|
|
220
|
+
labelGroup.setAttribute('transform', 'translate(' + midPt.x + ',' + midPt.y + ')');
|
|
221
|
+
|
|
222
|
+
var textLen = edge.label.length * 7 + 12;
|
|
223
|
+
labelGroup.appendChild(attrs(el('rect'), {
|
|
224
|
+
x: -textLen / 2, y: -10, width: textLen, height: 20,
|
|
225
|
+
rx: 4, fill: THEME.edgeLabelBg, stroke: 'none'
|
|
226
|
+
}));
|
|
227
|
+
|
|
228
|
+
var labelText = el('text');
|
|
229
|
+
attrs(labelText, {
|
|
230
|
+
'text-anchor': 'middle', 'dominant-baseline': 'central',
|
|
231
|
+
fill: THEME.edgeLabelColor, 'font-family': THEME.nodeFontFamily,
|
|
232
|
+
'font-size': THEME.edgeLabelFontSize
|
|
233
|
+
});
|
|
234
|
+
labelText.textContent = edge.label;
|
|
235
|
+
labelGroup.appendChild(labelText);
|
|
236
|
+
g.appendChild(labelGroup);
|
|
237
|
+
}
|
|
238
|
+
return g;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ── Render a subgraph ──
|
|
242
|
+
function renderSubgraph(sg) {
|
|
243
|
+
var g = el('g');
|
|
244
|
+
g.setAttribute('data-subgraph-id', sg.id);
|
|
245
|
+
g.setAttribute('class', 'smartcode-subgraph');
|
|
246
|
+
|
|
247
|
+
// Guard against NaN/undefined positions from dagre compound layout
|
|
248
|
+
var sx = isFinite(sg.x) ? sg.x : 0;
|
|
249
|
+
var sy = isFinite(sg.y) ? sg.y : 0;
|
|
250
|
+
var sw = sg.width > 0 ? sg.width : 100;
|
|
251
|
+
var sh = sg.height > 0 ? sg.height : 60;
|
|
252
|
+
|
|
253
|
+
g.appendChild(attrs(el('rect'), {
|
|
254
|
+
x: sx - sw / 2, y: sy - sh / 2,
|
|
255
|
+
width: sw, height: sh, rx: 8,
|
|
256
|
+
fill: THEME.subgraphFill, stroke: THEME.subgraphStroke,
|
|
257
|
+
'stroke-width': THEME.subgraphStrokeWidth
|
|
258
|
+
}));
|
|
259
|
+
|
|
260
|
+
var text = el('text');
|
|
261
|
+
attrs(text, {
|
|
262
|
+
x: sx, y: sy - sh / 2 + 20,
|
|
263
|
+
'text-anchor': 'middle', 'dominant-baseline': 'central',
|
|
264
|
+
fill: THEME.subgraphLabelColor, 'font-family': THEME.nodeFontFamily,
|
|
265
|
+
'font-size': THEME.subgraphLabelFontSize, 'font-weight': THEME.subgraphLabelFontWeight
|
|
266
|
+
});
|
|
267
|
+
text.textContent = sg.label;
|
|
268
|
+
g.appendChild(text);
|
|
269
|
+
return g;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ── Main: create complete SVG from LayoutResult ──
|
|
273
|
+
function createSVG(layout) {
|
|
274
|
+
var svg = attrs(el('svg'), {
|
|
275
|
+
xmlns: NS,
|
|
276
|
+
width: '100%',
|
|
277
|
+
height: '100%',
|
|
278
|
+
viewBox: '0 0 ' + layout.width + ' ' + layout.height
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
var defs = el('defs');
|
|
282
|
+
createArrowMarkers(defs);
|
|
283
|
+
svg.appendChild(defs);
|
|
284
|
+
|
|
285
|
+
var root = el('g');
|
|
286
|
+
root.setAttribute('class', 'smartcode-diagram');
|
|
287
|
+
|
|
288
|
+
// Build nodes map for edge boundary calculations
|
|
289
|
+
var nodesMap = {};
|
|
290
|
+
for (var n = 0; n < layout.nodes.length; n++) {
|
|
291
|
+
nodesMap[layout.nodes[n].id] = layout.nodes[n];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 1. Subgraphs first (behind everything)
|
|
295
|
+
for (var s = 0; s < layout.subgraphs.length; s++) {
|
|
296
|
+
root.appendChild(renderSubgraph(layout.subgraphs[s]));
|
|
297
|
+
}
|
|
298
|
+
// 2. Edges next (middle layer)
|
|
299
|
+
for (var e = 0; e < layout.edges.length; e++) {
|
|
300
|
+
root.appendChild(renderEdge(layout.edges[e], nodesMap));
|
|
301
|
+
}
|
|
302
|
+
// 3. Nodes last (on top)
|
|
303
|
+
for (var i = 0; i < layout.nodes.length; i++) {
|
|
304
|
+
root.appendChild(renderNode(layout.nodes[i]));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
svg.appendChild(root);
|
|
308
|
+
return svg;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ── Public API ──
|
|
312
|
+
window.SmartCodeSvgRenderer = { createSVG: createSVG };
|
|
313
|
+
})();
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SmartCode SVG Shapes -- SVG element factories for all 13 Mermaid node shapes.
|
|
3
|
+
* Each shape function takes (w, h) and returns an SVG element centered at (0, 0).
|
|
4
|
+
*
|
|
5
|
+
* Dependencies: none (standalone)
|
|
6
|
+
* Dependents: svg-renderer.js (calls SmartCodeSvgShapes.render)
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* var el = SmartCodeSvgShapes.render('diamond', 120, 60);
|
|
10
|
+
* // Returns an SVG element ready for translate(x,y) positioning
|
|
11
|
+
*/
|
|
12
|
+
(function() {
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
var NS = 'http://www.w3.org/2000/svg';
|
|
16
|
+
|
|
17
|
+
// ── Helper: create SVG element ──
|
|
18
|
+
function el(tag) {
|
|
19
|
+
return document.createElementNS(NS, tag);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ── Helper: set multiple attributes ──
|
|
23
|
+
function attrs(element, map) {
|
|
24
|
+
for (var key in map) {
|
|
25
|
+
if (map.hasOwnProperty(key)) {
|
|
26
|
+
element.setAttribute(key, String(map[key]));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return element;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── Helper: polygon from points array ──
|
|
33
|
+
function polygon(points) {
|
|
34
|
+
var poly = el('polygon');
|
|
35
|
+
var str = '';
|
|
36
|
+
for (var i = 0; i < points.length; i++) {
|
|
37
|
+
if (i > 0) str += ' ';
|
|
38
|
+
str += points[i][0] + ',' + points[i][1];
|
|
39
|
+
}
|
|
40
|
+
poly.setAttribute('points', str);
|
|
41
|
+
return poly;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── Shape: rect ──
|
|
45
|
+
function rect(w, h) {
|
|
46
|
+
return attrs(el('rect'), {
|
|
47
|
+
x: -w / 2, y: -h / 2, width: w, height: h, rx: 0
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Shape: rounded ──
|
|
52
|
+
function rounded(w, h) {
|
|
53
|
+
return attrs(el('rect'), {
|
|
54
|
+
x: -w / 2, y: -h / 2, width: w, height: h, rx: 8
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Shape: stadium ──
|
|
59
|
+
function stadium(w, h) {
|
|
60
|
+
return attrs(el('rect'), {
|
|
61
|
+
x: -w / 2, y: -h / 2, width: w, height: h, rx: h / 2
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Shape: diamond ──
|
|
66
|
+
function diamond(w, h) {
|
|
67
|
+
return polygon([
|
|
68
|
+
[0, -h / 2],
|
|
69
|
+
[w / 2, 0],
|
|
70
|
+
[0, h / 2],
|
|
71
|
+
[-w / 2, 0]
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── Shape: circle ──
|
|
76
|
+
function circle(w, h) {
|
|
77
|
+
var r = Math.max(w, h) / 2;
|
|
78
|
+
return attrs(el('circle'), { cx: 0, cy: 0, r: r });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Shape: hexagon ──
|
|
82
|
+
function hexagon(w, h) {
|
|
83
|
+
var dx = h / 4;
|
|
84
|
+
return polygon([
|
|
85
|
+
[-w / 2 + dx, -h / 2],
|
|
86
|
+
[w / 2 - dx, -h / 2],
|
|
87
|
+
[w / 2, 0],
|
|
88
|
+
[w / 2 - dx, h / 2],
|
|
89
|
+
[-w / 2 + dx, h / 2],
|
|
90
|
+
[-w / 2, 0]
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Shape: subroutine ──
|
|
95
|
+
function subroutine(w, h) {
|
|
96
|
+
var g = el('g');
|
|
97
|
+
var mainRect = attrs(el('rect'), {
|
|
98
|
+
x: -w / 2, y: -h / 2, width: w, height: h, rx: 0
|
|
99
|
+
});
|
|
100
|
+
g.appendChild(mainRect);
|
|
101
|
+
|
|
102
|
+
// Left inner vertical line
|
|
103
|
+
var lineLeft = attrs(el('line'), {
|
|
104
|
+
x1: -w / 2 + 8, y1: -h / 2,
|
|
105
|
+
x2: -w / 2 + 8, y2: h / 2
|
|
106
|
+
});
|
|
107
|
+
g.appendChild(lineLeft);
|
|
108
|
+
|
|
109
|
+
// Right inner vertical line
|
|
110
|
+
var lineRight = attrs(el('line'), {
|
|
111
|
+
x1: w / 2 - 8, y1: -h / 2,
|
|
112
|
+
x2: w / 2 - 8, y2: h / 2
|
|
113
|
+
});
|
|
114
|
+
g.appendChild(lineRight);
|
|
115
|
+
|
|
116
|
+
return g;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Shape: cylinder ──
|
|
120
|
+
function cylinder(w, h) {
|
|
121
|
+
var rx = w / 2;
|
|
122
|
+
var ry = 8; // ellipse vertical radius for cap
|
|
123
|
+
var bodyTop = -h / 2 + ry;
|
|
124
|
+
var bodyBottom = h / 2 - ry;
|
|
125
|
+
|
|
126
|
+
// Path: top ellipse, right side, bottom ellipse, left side, close with top back-arc
|
|
127
|
+
var d = 'M ' + (-rx) + ' ' + bodyTop
|
|
128
|
+
+ ' A ' + rx + ' ' + ry + ' 0 0 1 ' + rx + ' ' + bodyTop
|
|
129
|
+
+ ' L ' + rx + ' ' + bodyBottom
|
|
130
|
+
+ ' A ' + rx + ' ' + ry + ' 0 0 1 ' + (-rx) + ' ' + bodyBottom
|
|
131
|
+
+ ' L ' + (-rx) + ' ' + bodyTop
|
|
132
|
+
+ ' A ' + rx + ' ' + ry + ' 0 0 0 ' + rx + ' ' + bodyTop;
|
|
133
|
+
|
|
134
|
+
return attrs(el('path'), { d: d });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── Shape: asymmetric (flag/pennant) ──
|
|
138
|
+
function asymmetric(w, h) {
|
|
139
|
+
var notch = h / 4;
|
|
140
|
+
return polygon([
|
|
141
|
+
[-w / 2, -h / 2],
|
|
142
|
+
[w / 2, -h / 2],
|
|
143
|
+
[w / 2 - notch, 0],
|
|
144
|
+
[w / 2, h / 2],
|
|
145
|
+
[-w / 2, h / 2]
|
|
146
|
+
]);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Shape: parallelogram (slanting right) ──
|
|
150
|
+
function parallelogram(w, h) {
|
|
151
|
+
var skew = h * 0.3;
|
|
152
|
+
return polygon([
|
|
153
|
+
[-w / 2 + skew, -h / 2],
|
|
154
|
+
[w / 2, -h / 2],
|
|
155
|
+
[w / 2 - skew, h / 2],
|
|
156
|
+
[-w / 2, h / 2]
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── Shape: parallelogram-alt (slanting left, mirror) ──
|
|
161
|
+
function parallelogramAlt(w, h) {
|
|
162
|
+
var skew = h * 0.3;
|
|
163
|
+
return polygon([
|
|
164
|
+
[-w / 2, -h / 2],
|
|
165
|
+
[w / 2 - skew, -h / 2],
|
|
166
|
+
[w / 2, h / 2],
|
|
167
|
+
[-w / 2 + skew, h / 2]
|
|
168
|
+
]);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── Shape: trapezoid (wider at bottom) ──
|
|
172
|
+
function trapezoid(w, h) {
|
|
173
|
+
var dx = w * 0.15;
|
|
174
|
+
return polygon([
|
|
175
|
+
[-w / 2 + dx, -h / 2],
|
|
176
|
+
[w / 2 - dx, -h / 2],
|
|
177
|
+
[w / 2, h / 2],
|
|
178
|
+
[-w / 2, h / 2]
|
|
179
|
+
]);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── Shape: trapezoid-alt (wider at top) ──
|
|
183
|
+
function trapezoidAlt(w, h) {
|
|
184
|
+
var dx = w * 0.15;
|
|
185
|
+
return polygon([
|
|
186
|
+
[-w / 2, -h / 2],
|
|
187
|
+
[w / 2, -h / 2],
|
|
188
|
+
[w / 2 - dx, h / 2],
|
|
189
|
+
[-w / 2 + dx, h / 2]
|
|
190
|
+
]);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Shape Registry ──
|
|
194
|
+
var SHAPE_RENDERERS = {
|
|
195
|
+
'rect': rect,
|
|
196
|
+
'rounded': rounded,
|
|
197
|
+
'stadium': stadium,
|
|
198
|
+
'diamond': diamond,
|
|
199
|
+
'circle': circle,
|
|
200
|
+
'hexagon': hexagon,
|
|
201
|
+
'subroutine': subroutine,
|
|
202
|
+
'cylinder': cylinder,
|
|
203
|
+
'asymmetric': asymmetric,
|
|
204
|
+
'parallelogram': parallelogram,
|
|
205
|
+
'parallelogram-alt': parallelogramAlt,
|
|
206
|
+
'trapezoid': trapezoid,
|
|
207
|
+
'trapezoid-alt': trapezoidAlt
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// ── Render function ──
|
|
211
|
+
function render(shape, w, h) {
|
|
212
|
+
var fn = SHAPE_RENDERERS[shape] || SHAPE_RENDERERS['rect'];
|
|
213
|
+
return fn(w, h);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ── Public API ──
|
|
217
|
+
window.SmartCodeSvgShapes = { render: render };
|
|
218
|
+
})();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* SmartCode — Carbon Design Tokens */
|
|
2
|
+
/* Single source of truth for all colors, spacing, and radius */
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
/* Surface colors (dark chrome) */
|
|
6
|
+
--surface-0: #111113;
|
|
7
|
+
--surface-1: #18181b;
|
|
8
|
+
--surface-2: #1f1f23;
|
|
9
|
+
--surface-3: #27272b;
|
|
10
|
+
--surface-4: #303036;
|
|
11
|
+
|
|
12
|
+
/* Preview canvas (light, for Mermaid readability) */
|
|
13
|
+
--preview-bg: #fafafa;
|
|
14
|
+
|
|
15
|
+
/* Borders */
|
|
16
|
+
--border-subtle: #27272b;
|
|
17
|
+
--border-default: #303036;
|
|
18
|
+
--border-focus: #3b82f6;
|
|
19
|
+
|
|
20
|
+
/* Text */
|
|
21
|
+
--text-primary: #e4e4e7;
|
|
22
|
+
--text-secondary: #a1a1aa;
|
|
23
|
+
--text-tertiary: #71717a;
|
|
24
|
+
--text-on-accent: #ffffff;
|
|
25
|
+
|
|
26
|
+
/* Accent (blue) */
|
|
27
|
+
--accent: #3b82f6;
|
|
28
|
+
--accent-hover: #2563eb;
|
|
29
|
+
--accent-muted: rgba(59, 130, 246, 0.15);
|
|
30
|
+
|
|
31
|
+
/* Status — semantically identical colors */
|
|
32
|
+
--status-ok: #22c55e;
|
|
33
|
+
--status-ok-stroke: #16a34a;
|
|
34
|
+
--status-problem: #ef4444;
|
|
35
|
+
--status-problem-stroke: #dc2626;
|
|
36
|
+
--status-warning: #eab308;
|
|
37
|
+
--status-warning-stroke: #ca8a04;
|
|
38
|
+
--status-neutral: #71717a;
|
|
39
|
+
--status-neutral-stroke: #52525b;
|
|
40
|
+
|
|
41
|
+
/* Utility */
|
|
42
|
+
--ghost-path: #8b5cf6;
|
|
43
|
+
--search-match: #f59e0b;
|
|
44
|
+
--breakpoint: #ef4444;
|
|
45
|
+
--diff-added: #22c55e;
|
|
46
|
+
--diff-removed: #ef4444;
|
|
47
|
+
--diff-modified: #eab308;
|
|
48
|
+
|
|
49
|
+
/* Mermaid theme (light canvas) */
|
|
50
|
+
--mermaid-primary: #3b82f6;
|
|
51
|
+
--mermaid-primary-text: #18181b;
|
|
52
|
+
--mermaid-primary-border: #2563eb;
|
|
53
|
+
--mermaid-secondary: #dbeafe;
|
|
54
|
+
--mermaid-tertiary: #f0fdf4;
|
|
55
|
+
--mermaid-line: #52525b;
|
|
56
|
+
--mermaid-main-bg: #eff6ff;
|
|
57
|
+
--mermaid-node-border: #2563eb;
|
|
58
|
+
--mermaid-cluster-bg: #f4f4f5;
|
|
59
|
+
--mermaid-cluster-border: #a1a1aa;
|
|
60
|
+
--mermaid-title: #18181b;
|
|
61
|
+
--mermaid-edge-label-bg: #ffffff;
|
|
62
|
+
|
|
63
|
+
/* Spacing scale */
|
|
64
|
+
--space-1: 4px;
|
|
65
|
+
--space-2: 8px;
|
|
66
|
+
--space-3: 12px;
|
|
67
|
+
--space-4: 16px;
|
|
68
|
+
--space-6: 24px;
|
|
69
|
+
--space-8: 32px;
|
|
70
|
+
|
|
71
|
+
/* Radius scale */
|
|
72
|
+
--radius-sm: 2px;
|
|
73
|
+
--radius-md: 4px;
|
|
74
|
+
--radius-lg: 6px;
|
|
75
|
+
--radius-xl: 8px;
|
|
76
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dagre-bundle.js — loads graphlib then dagre for browser use.
|
|
3
|
+
*
|
|
4
|
+
* @dagrejs/dagre@2 internally calls require("@dagrejs/graphlib") which
|
|
5
|
+
* breaks in the browser. We load graphlib first (it self-registers as
|
|
6
|
+
* window.graphlib) and then monkey-patch the bundler's require so dagre
|
|
7
|
+
* can find it.
|
|
8
|
+
*/
|
|
9
|
+
(function () {
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
/* ── 1. Inject graphlib ──────────────────────────────────────────── */
|
|
13
|
+
var s1 = document.createElement('script');
|
|
14
|
+
s1.src = 'vendor/graphlib.min.js';
|
|
15
|
+
s1.async = false; // keep execution order
|
|
16
|
+
document.head.appendChild(s1);
|
|
17
|
+
|
|
18
|
+
/* ── 2. Once graphlib is ready, patch require and inject dagre ──── */
|
|
19
|
+
s1.onload = function () {
|
|
20
|
+
// The dagre CJS bundle calls __require("@dagrejs/graphlib").
|
|
21
|
+
// We intercept that by shimming a global require that resolves it.
|
|
22
|
+
var _origRequire = window.require; // may be undefined — that's fine
|
|
23
|
+
window.require = function (id) {
|
|
24
|
+
if (id === '@dagrejs/graphlib') return window.graphlib;
|
|
25
|
+
if (typeof _origRequire === 'function') return _origRequire(id);
|
|
26
|
+
throw new Error('Cannot find module "' + id + '"');
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
var s2 = document.createElement('script');
|
|
30
|
+
s2.src = 'vendor/dagre.min.js';
|
|
31
|
+
s2.async = false;
|
|
32
|
+
document.head.appendChild(s2);
|
|
33
|
+
|
|
34
|
+
s2.onload = function () {
|
|
35
|
+
// Restore original require (or remove shim)
|
|
36
|
+
if (_origRequire) {
|
|
37
|
+
window.require = _origRequire;
|
|
38
|
+
} else {
|
|
39
|
+
delete window.require;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
})();
|