@bovinphang/craft-common 0.0.0-stage → 1.0.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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -2
  3. package/dist/content/catalog.d.ts +4 -0
  4. package/dist/content/resources.d.ts +2 -0
  5. package/dist/content/types.d.ts +21 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.js +668 -0
  8. package/dist/install/identity.d.ts +5 -0
  9. package/dist/install/planner.d.ts +6 -0
  10. package/dist/install/transaction.d.ts +7 -0
  11. package/dist/install/types.d.ts +44 -0
  12. package/docs/consuming-common.md +33 -0
  13. package/localized/zh-CN/methods/debug.md +13 -0
  14. package/localized/zh-CN/methods/doc-sync.md +12 -0
  15. package/localized/zh-CN/methods/refactoring.md +16 -0
  16. package/localized/zh-CN/methods/source-driven-development.md +19 -0
  17. package/localized/zh-CN/methods/tdd.md +13 -0
  18. package/localized/zh-CN/skills/cc-debug-methodology/SKILL.md +8 -0
  19. package/localized/zh-CN/skills/cc-debug-methodology/references/method.md +13 -0
  20. package/localized/zh-CN/skills/cc-doc-sync-core/SKILL.md +8 -0
  21. package/localized/zh-CN/skills/cc-doc-sync-core/references/method.md +12 -0
  22. package/localized/zh-CN/skills/cc-drawio-studio/SKILL.md +73 -0
  23. package/localized/zh-CN/skills/cc-drawio-studio/data/THIRD_PARTY_NOTICES.md +10 -0
  24. package/localized/zh-CN/skills/cc-drawio-studio/data/brand-icons.json +878 -0
  25. package/localized/zh-CN/skills/cc-drawio-studio/data/shape-index.json +83570 -0
  26. package/localized/zh-CN/skills/cc-drawio-studio/references/data-residency.md +27 -0
  27. package/localized/zh-CN/skills/cc-drawio-studio/references/diagram-patterns.md +60 -0
  28. package/localized/zh-CN/skills/cc-drawio-studio/references/flowchart-quality.md +49 -0
  29. package/localized/zh-CN/skills/cc-drawio-studio/references/layout-and-export.md +72 -0
  30. package/localized/zh-CN/skills/cc-drawio-studio/references/troubleshooting.md +29 -0
  31. package/localized/zh-CN/skills/cc-drawio-studio/references/xml-and-mermaid.md +58 -0
  32. package/localized/zh-CN/skills/cc-drawio-studio/scripts/brand-symbols.mjs +99 -0
  33. package/localized/zh-CN/skills/cc-drawio-studio/scripts/diagram-lint.mjs +205 -0
  34. package/localized/zh-CN/skills/cc-drawio-studio/scripts/diagram-url.mjs +102 -0
  35. package/localized/zh-CN/skills/cc-drawio-studio/scripts/drawio-export.mjs +134 -0
  36. package/localized/zh-CN/skills/cc-drawio-studio/scripts/layout-graph.mjs +107 -0
  37. package/localized/zh-CN/skills/cc-drawio-studio/scripts/png-embed-fix.mjs +26 -0
  38. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-go-packages.mjs +36 -0
  39. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-js-modules.mjs +52 -0
  40. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-python-classes.mjs +30 -0
  41. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-python-modules.mjs +55 -0
  42. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-rust-modules.mjs +35 -0
  43. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-ts-modules.mjs +7 -0
  44. package/localized/zh-CN/skills/cc-drawio-studio/scripts/shape-query.mjs +121 -0
  45. package/localized/zh-CN/skills/cc-drawio-studio/scripts/studio-core.mjs +139 -0
  46. package/localized/zh-CN/skills/cc-drawio-studio/styles/built-in/corporate.json +49 -0
  47. package/localized/zh-CN/skills/cc-drawio-studio/styles/built-in/default.json +49 -0
  48. package/localized/zh-CN/skills/cc-drawio-studio/styles/built-in/handdrawn.json +49 -0
  49. package/localized/zh-CN/skills/cc-drawio-studio/styles/schema.json +112 -0
  50. package/localized/zh-CN/skills/cc-refactoring-core/SKILL.md +8 -0
  51. package/localized/zh-CN/skills/cc-refactoring-core/references/method.md +16 -0
  52. package/localized/zh-CN/skills/cc-source-driven-development/SKILL.md +8 -0
  53. package/localized/zh-CN/skills/cc-source-driven-development/references/method.md +19 -0
  54. package/localized/zh-CN/skills/cc-tdd-methodology/SKILL.md +8 -0
  55. package/localized/zh-CN/skills/cc-tdd-methodology/references/method.md +13 -0
  56. package/localized/zh-CN/skills/cc-technical-diagrams/SKILL.md +34 -0
  57. package/localized/zh-CN/skills/cc-technical-diagrams/assets/interactive-diagram.html +371 -0
  58. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/README.md +14 -0
  59. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/architecture.json +121 -0
  60. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/invalid-explicit.json +41 -0
  61. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/sequence.json +53 -0
  62. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/sequence.mmd +21 -0
  63. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/workflow.json +98 -0
  64. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/workflow.mmd +7 -0
  65. package/localized/zh-CN/skills/cc-technical-diagrams/references/diagram-workflows.md +81 -0
  66. package/localized/zh-CN/skills/cc-technical-diagrams/references/html-technical-diagrams.md +243 -0
  67. package/localized/zh-CN/skills/cc-technical-diagrams/references/png-qa-autofix.md +37 -0
  68. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/diagram-browser.mjs +153 -0
  69. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/diagram-layout.mjs +176 -0
  70. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/export-diagram.mjs +332 -0
  71. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/interactive-diagram-server.mjs +654 -0
  72. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/mermaid-render.mjs +64 -0
  73. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/png-qa.mjs +462 -0
  74. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/tech-diagram-render.mjs +1537 -0
  75. package/localized/zh-CN/skills/eval_queries.json +160 -0
  76. package/localized/zh-CN/skills/metadata.json +223 -0
  77. package/localized/zh-CN/skills/relations.json +53 -0
  78. package/methods/debug.md +13 -0
  79. package/methods/doc-sync.md +12 -0
  80. package/methods/refactoring.md +16 -0
  81. package/methods/source-driven-development.md +19 -0
  82. package/methods/tdd.md +13 -0
  83. package/package.json +51 -4
  84. package/skills/cc-debug-methodology/SKILL.md +8 -0
  85. package/skills/cc-debug-methodology/references/method.md +13 -0
  86. package/skills/cc-doc-sync-core/SKILL.md +8 -0
  87. package/skills/cc-doc-sync-core/references/method.md +12 -0
  88. package/skills/cc-drawio-studio/SKILL.md +73 -0
  89. package/skills/cc-drawio-studio/data/THIRD_PARTY_NOTICES.md +10 -0
  90. package/skills/cc-drawio-studio/data/brand-icons.json +878 -0
  91. package/skills/cc-drawio-studio/data/shape-index.json +83570 -0
  92. package/skills/cc-drawio-studio/references/data-residency.md +27 -0
  93. package/skills/cc-drawio-studio/references/diagram-patterns.md +60 -0
  94. package/skills/cc-drawio-studio/references/flowchart-quality.md +49 -0
  95. package/skills/cc-drawio-studio/references/layout-and-export.md +72 -0
  96. package/skills/cc-drawio-studio/references/troubleshooting.md +29 -0
  97. package/skills/cc-drawio-studio/references/xml-and-mermaid.md +58 -0
  98. package/skills/cc-drawio-studio/scripts/brand-symbols.mjs +99 -0
  99. package/skills/cc-drawio-studio/scripts/diagram-lint.mjs +205 -0
  100. package/skills/cc-drawio-studio/scripts/diagram-url.mjs +102 -0
  101. package/skills/cc-drawio-studio/scripts/drawio-export.mjs +134 -0
  102. package/skills/cc-drawio-studio/scripts/layout-graph.mjs +107 -0
  103. package/skills/cc-drawio-studio/scripts/png-embed-fix.mjs +26 -0
  104. package/skills/cc-drawio-studio/scripts/scan-go-packages.mjs +36 -0
  105. package/skills/cc-drawio-studio/scripts/scan-js-modules.mjs +52 -0
  106. package/skills/cc-drawio-studio/scripts/scan-python-classes.mjs +30 -0
  107. package/skills/cc-drawio-studio/scripts/scan-python-modules.mjs +55 -0
  108. package/skills/cc-drawio-studio/scripts/scan-rust-modules.mjs +35 -0
  109. package/skills/cc-drawio-studio/scripts/scan-ts-modules.mjs +7 -0
  110. package/skills/cc-drawio-studio/scripts/shape-query.mjs +121 -0
  111. package/skills/cc-drawio-studio/scripts/studio-core.mjs +139 -0
  112. package/skills/cc-drawio-studio/styles/built-in/corporate.json +49 -0
  113. package/skills/cc-drawio-studio/styles/built-in/default.json +49 -0
  114. package/skills/cc-drawio-studio/styles/built-in/handdrawn.json +49 -0
  115. package/skills/cc-drawio-studio/styles/schema.json +112 -0
  116. package/skills/cc-refactoring-core/SKILL.md +8 -0
  117. package/skills/cc-refactoring-core/references/method.md +16 -0
  118. package/skills/cc-source-driven-development/SKILL.md +8 -0
  119. package/skills/cc-source-driven-development/references/method.md +19 -0
  120. package/skills/cc-tdd-methodology/SKILL.md +8 -0
  121. package/skills/cc-tdd-methodology/references/method.md +13 -0
  122. package/skills/cc-technical-diagrams/SKILL.md +34 -0
  123. package/skills/cc-technical-diagrams/assets/interactive-diagram.html +371 -0
  124. package/skills/cc-technical-diagrams/assets/quality-examples/README.md +14 -0
  125. package/skills/cc-technical-diagrams/assets/quality-examples/architecture.json +121 -0
  126. package/skills/cc-technical-diagrams/assets/quality-examples/invalid-explicit.json +41 -0
  127. package/skills/cc-technical-diagrams/assets/quality-examples/sequence.json +53 -0
  128. package/skills/cc-technical-diagrams/assets/quality-examples/sequence.mmd +21 -0
  129. package/skills/cc-technical-diagrams/assets/quality-examples/workflow.json +98 -0
  130. package/skills/cc-technical-diagrams/assets/quality-examples/workflow.mmd +7 -0
  131. package/skills/cc-technical-diagrams/references/diagram-workflows.md +81 -0
  132. package/skills/cc-technical-diagrams/references/html-technical-diagrams.md +243 -0
  133. package/skills/cc-technical-diagrams/references/png-qa-autofix.md +37 -0
  134. package/skills/cc-technical-diagrams/scripts/diagram-browser.mjs +153 -0
  135. package/skills/cc-technical-diagrams/scripts/diagram-layout.mjs +176 -0
  136. package/skills/cc-technical-diagrams/scripts/export-diagram.mjs +332 -0
  137. package/skills/cc-technical-diagrams/scripts/interactive-diagram-server.mjs +654 -0
  138. package/skills/cc-technical-diagrams/scripts/mermaid-render.mjs +64 -0
  139. package/skills/cc-technical-diagrams/scripts/png-qa.mjs +462 -0
  140. package/skills/cc-technical-diagrams/scripts/tech-diagram-render.mjs +1537 -0
  141. package/skills/eval_queries.json +160 -0
  142. package/skills/metadata.json +223 -0
  143. package/skills/relations.json +53 -0
@@ -0,0 +1,371 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>frontend-craft Interactive Diagram</title>
7
+ <style>
8
+ :root {
9
+ color-scheme: light;
10
+ --bg: #f5f7fb;
11
+ --panel: #ffffff;
12
+ --ink: #172033;
13
+ --muted: #61708a;
14
+ --line: #d9e0eb;
15
+ --accent: #3157d5;
16
+ --edge: #728096;
17
+ --shadow: 0 12px 32px rgba(20, 32, 54, 0.12);
18
+ }
19
+ * { box-sizing: border-box; }
20
+ body {
21
+ margin: 0;
22
+ min-height: 100vh;
23
+ display: grid;
24
+ grid-template-rows: auto 1fr;
25
+ font-family: Arial, Helvetica, sans-serif;
26
+ color: var(--ink);
27
+ background: var(--bg);
28
+ }
29
+ header {
30
+ display: flex;
31
+ gap: 12px;
32
+ align-items: center;
33
+ justify-content: space-between;
34
+ padding: 12px 16px;
35
+ border-bottom: 1px solid var(--line);
36
+ background: var(--panel);
37
+ }
38
+ h1 { margin: 0; font-size: 16px; line-height: 1.2; }
39
+ .toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
40
+ button {
41
+ border: 1px solid var(--line);
42
+ background: #fff;
43
+ color: var(--ink);
44
+ border-radius: 6px;
45
+ padding: 7px 10px;
46
+ font: inherit;
47
+ cursor: pointer;
48
+ }
49
+ button:hover { border-color: var(--accent); color: var(--accent); }
50
+ main { position: relative; min-height: 0; overflow: hidden; }
51
+ #status {
52
+ position: absolute;
53
+ left: 16px;
54
+ bottom: 14px;
55
+ z-index: 2;
56
+ padding: 6px 9px;
57
+ border: 1px solid var(--line);
58
+ border-radius: 6px;
59
+ background: rgba(255, 255, 255, 0.9);
60
+ color: var(--muted);
61
+ font-size: 12px;
62
+ }
63
+ svg { display: block; width: 100%; height: 100%; min-height: calc(100vh - 58px); background: #fbfcff; }
64
+ .grid { stroke: #eef2f8; stroke-width: 1; }
65
+ .edge { stroke: var(--edge); stroke-width: 2; fill: none; }
66
+ .edge-label-bg { fill: #fff; stroke: var(--line); }
67
+ .edge-label { fill: var(--muted); font-size: 12px; }
68
+ .node text { pointer-events: none; font-size: 13px; fill: var(--ink); }
69
+ .node-shape { fill: #f8fbff; stroke: var(--accent); stroke-width: 2; filter: drop-shadow(0 3px 4px rgba(20, 32, 54, 0.08)); }
70
+ .node-terminal { stroke: #2f9e44; fill: #f1fbf3; }
71
+ .node-terminal-end { stroke: #d9480f; fill: #fff4ef; }
72
+ .node-decision { stroke: #d69e2e; fill: #fff8dc; }
73
+ .node-service { stroke: #805ad5; fill: #faf5ff; }
74
+ .node-database { stroke: #0c8599; fill: #edfaff; }
75
+ .node-success { stroke: #2f9e44; fill: #f1fbf3; }
76
+ .node-error { stroke: #e03131; fill: #fff5f5; }
77
+ .container-shape { fill: rgba(248, 251, 255, 0.72); stroke: #7a8aa0; stroke-width: 2; stroke-dasharray: 7 5; }
78
+ .selected .node-shape, .selected .container-shape { stroke-width: 3; }
79
+ </style>
80
+ </head>
81
+ <body>
82
+ <header>
83
+ <h1 id="title">Interactive Diagram</h1>
84
+ <div class="toolbar">
85
+ <button type="button" data-action="fit">Fit</button>
86
+ <button type="button" data-action="zoom-in">Zoom In</button>
87
+ <button type="button" data-action="zoom-out">Zoom Out</button>
88
+ <button type="button" data-action="export-svg">SVG</button>
89
+ <button type="button" data-action="export-png">PNG</button>
90
+ <button type="button" data-action="export-json">JSON</button>
91
+ </div>
92
+ </header>
93
+ <main>
94
+ <svg id="canvas" role="img" aria-labelledby="title">
95
+ <defs>
96
+ <marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
97
+ <path d="M0,0 L0,6 L9,3 z" fill="#728096"></path>
98
+ </marker>
99
+ <pattern id="grid" width="24" height="24" patternUnits="userSpaceOnUse">
100
+ <path class="grid" d="M24 0H0V24"></path>
101
+ </pattern>
102
+ </defs>
103
+ <rect width="100%" height="100%" fill="url(#grid)"></rect>
104
+ <g id="viewport"></g>
105
+ </svg>
106
+ <div id="status">Connecting</div>
107
+ </main>
108
+ <script>
109
+ const svg = document.getElementById('canvas');
110
+ const viewport = document.getElementById('viewport');
111
+ const titleEl = document.getElementById('title');
112
+ const statusEl = document.getElementById('status');
113
+ const session = new URLSearchParams(location.search).get('s') || 'default';
114
+ const state = { title: 'Interactive Diagram', direction: 'TB', nodes: new Map(), edges: [], scale: 1, tx: 24, ty: 24, selected: null };
115
+ const ns = 'http://www.w3.org/2000/svg';
116
+
117
+ function setStatus(text) { statusEl.textContent = `${session}: ${text}`; }
118
+ function sizeFor(node) {
119
+ const text = String(node.label || node.id || '');
120
+ const width = Number(node.width) || Math.min(240, Math.max(node.type === 'decision' ? 132 : 150, text.length * 8 + 42));
121
+ const height = Number(node.height) || (node.type === 'decision' ? 88 : 62);
122
+ return { width, height };
123
+ }
124
+ function layout() {
125
+ const normalNodes = [...state.nodes.values()].filter(node => node.type !== 'container');
126
+ const incoming = new Map(normalNodes.map(node => [node.id, 0]));
127
+ const outgoing = new Map(normalNodes.map(node => [node.id, []]));
128
+ for (const edge of state.edges) {
129
+ if (!incoming.has(edge.to) || !outgoing.has(edge.from)) continue;
130
+ incoming.set(edge.to, incoming.get(edge.to) + 1);
131
+ outgoing.get(edge.from).push(edge.to);
132
+ }
133
+ const layer = new Map();
134
+ const queue = normalNodes.filter(node => incoming.get(node.id) === 0);
135
+ queue.forEach(node => layer.set(node.id, 0));
136
+ while (queue.length) {
137
+ const node = queue.shift();
138
+ for (const next of outgoing.get(node.id) || []) {
139
+ layer.set(next, Math.max(layer.get(next) || 0, (layer.get(node.id) || 0) + 1));
140
+ const nextNode = state.nodes.get(next);
141
+ if (nextNode) queue.push(nextNode);
142
+ }
143
+ }
144
+ normalNodes.forEach(node => {
145
+ if (!layer.has(node.id)) layer.set(node.id, 0);
146
+ });
147
+ const horizontal = state.direction === 'LR' || state.direction === 'RL';
148
+ const groups = new Map();
149
+ normalNodes.forEach(node => {
150
+ if (Number.isFinite(node.x) && Number.isFinite(node.y)) return;
151
+ const rank = layer.get(node.id) || 0;
152
+ const group = groups.get(rank) || [];
153
+ group.push(node);
154
+ groups.set(rank, group);
155
+ });
156
+ for (const [rank, nodes] of groups) {
157
+ nodes.forEach((node, index) => {
158
+ const main = 80 + rank * 190;
159
+ const cross = 80 + index * 120;
160
+ node.x = horizontal ? main : cross;
161
+ node.y = horizontal ? cross : main;
162
+ });
163
+ }
164
+ for (const node of [...state.nodes.values()].filter(item => item.type === 'container')) {
165
+ const children = Array.isArray(node.children) ? node.children.map(id => state.nodes.get(id)).filter(Boolean) : [];
166
+ if (!children.length) continue;
167
+ const left = Math.min(...children.map(child => child.x || 0));
168
+ const top = Math.min(...children.map(child => child.y || 0));
169
+ const right = Math.max(...children.map(child => (child.x || 0) + sizeFor(child).width));
170
+ const bottom = Math.max(...children.map(child => (child.y || 0) + sizeFor(child).height));
171
+ node.x = left - 36;
172
+ node.y = top - 54;
173
+ node.width = right - left + 72;
174
+ node.height = bottom - top + 90;
175
+ }
176
+ }
177
+ function render() {
178
+ layout();
179
+ viewport.innerHTML = '';
180
+ viewport.setAttribute('transform', `translate(${state.tx} ${state.ty}) scale(${state.scale})`);
181
+ renderEdges();
182
+ [...state.nodes.values()].filter(node => node.type === 'container').forEach(renderNode);
183
+ [...state.nodes.values()].filter(node => node.type !== 'container').forEach(renderNode);
184
+ titleEl.textContent = state.title;
185
+ setStatus(`${state.nodes.size} nodes, ${state.edges.length} edges`);
186
+ }
187
+ function pointFor(id, bottom) {
188
+ const node = state.nodes.get(id);
189
+ if (!node) return null;
190
+ const { width, height } = sizeFor(node);
191
+ return { x: (node.x || 0) + width / 2, y: (node.y || 0) + (bottom ? height : 0) };
192
+ }
193
+ function renderEdges() {
194
+ for (const edge of state.edges) {
195
+ const from = pointFor(edge.from, true);
196
+ const to = pointFor(edge.to, false);
197
+ if (!from || !to) continue;
198
+ const group = el('g', {});
199
+ const midY = (from.y + to.y) / 2;
200
+ group.append(el('path', { class: 'edge', markerEnd: 'url(#arrow)', d: `M${from.x},${from.y} C${from.x},${midY} ${to.x},${midY} ${to.x},${to.y}` }));
201
+ if (edge.label) {
202
+ const x = (from.x + to.x) / 2;
203
+ const y = midY - 8;
204
+ group.append(el('rect', { class: 'edge-label-bg', x: x - 42, y: y - 15, width: 84, height: 20, rx: 5 }));
205
+ const text = el('text', { class: 'edge-label', x, y, textAnchor: 'middle' });
206
+ text.textContent = edge.label;
207
+ group.append(text);
208
+ }
209
+ viewport.append(group);
210
+ }
211
+ }
212
+ function renderNode(node) {
213
+ const { width, height } = sizeFor(node);
214
+ const group = el('g', { class: `node ${state.selected === node.id ? 'selected' : ''}`, transform: `translate(${node.x || 0} ${node.y || 0})`, tabindex: '0' });
215
+ group.dataset.id = node.id;
216
+ if (node.type === 'container') {
217
+ group.append(el('rect', { class: 'container-shape', width, height, rx: 8 }));
218
+ const label = el('text', { x: 14, y: 24, fontWeight: '700' });
219
+ label.textContent = node.label || node.id;
220
+ group.append(label);
221
+ } else if (node.type === 'decision') {
222
+ group.append(el('polygon', { class: 'node-shape node-decision', points: `${width / 2},0 ${width},${height / 2} ${width / 2},${height} 0,${height / 2}` }));
223
+ group.append(centerText(node.label || node.id, width / 2, height / 2 + 5));
224
+ } else if (node.type === 'terminal' || node.type === 'terminal-end') {
225
+ group.append(el('ellipse', { class: `node-shape node-${node.type}`, cx: width / 2, cy: height / 2, rx: width / 2, ry: height / 2 }));
226
+ group.append(centerText(node.label || node.id, width / 2, height / 2 + 5));
227
+ } else {
228
+ group.append(el('rect', { class: `node-shape node-${node.type || 'process'}`, width, height, rx: 8 }));
229
+ group.append(centerText(node.label || node.id, width / 2, height / 2 + 5));
230
+ }
231
+ group.addEventListener('pointerdown', event => startDrag(event, node));
232
+ group.addEventListener('dblclick', () => editNode(node));
233
+ viewport.append(group);
234
+ }
235
+ function centerText(value, x, y) {
236
+ const text = el('text', { x, y, textAnchor: 'middle' });
237
+ text.textContent = value;
238
+ return text;
239
+ }
240
+ function el(name, attrs) {
241
+ const node = document.createElementNS(ns, name);
242
+ for (const [key, value] of Object.entries(attrs)) {
243
+ node.setAttribute(key === 'markerEnd' ? 'marker-end' : key === 'textAnchor' ? 'text-anchor' : key, value);
244
+ }
245
+ return node;
246
+ }
247
+ function startDrag(event, node) {
248
+ event.preventDefault();
249
+ state.selected = node.id;
250
+ const start = toGraphPoint(event);
251
+ const ox = node.x || 0;
252
+ const oy = node.y || 0;
253
+ const move = e => {
254
+ const next = toGraphPoint(e);
255
+ node.x = ox + next.x - start.x;
256
+ node.y = oy + next.y - start.y;
257
+ render();
258
+ };
259
+ const up = () => {
260
+ window.removeEventListener('pointermove', move);
261
+ window.removeEventListener('pointerup', up);
262
+ };
263
+ window.addEventListener('pointermove', move);
264
+ window.addEventListener('pointerup', up);
265
+ render();
266
+ }
267
+ function toGraphPoint(event) {
268
+ const rect = svg.getBoundingClientRect();
269
+ return { x: (event.clientX - rect.left - state.tx) / state.scale, y: (event.clientY - rect.top - state.ty) / state.scale };
270
+ }
271
+ function editNode(node) {
272
+ const next = prompt('Node label', node.label || node.id);
273
+ if (next !== null) {
274
+ node.label = next;
275
+ render();
276
+ }
277
+ }
278
+ function handleCommand(cmd) {
279
+ if (cmd.cmd === 'init') {
280
+ state.title = cmd.title || 'Interactive Diagram';
281
+ state.direction = cmd.direction || 'TB';
282
+ state.nodes.clear();
283
+ state.edges = [];
284
+ } else if (cmd.cmd === 'title') {
285
+ state.title = cmd.text || state.title;
286
+ } else if (cmd.cmd === 'node' || cmd.cmd === 'container') {
287
+ const id = cmd.id;
288
+ if (!id) return;
289
+ state.nodes.set(id, { ...state.nodes.get(id), ...cmd, id, type: cmd.cmd === 'container' ? 'container' : (cmd.type || 'process'), label: cmd.label || id });
290
+ } else if (cmd.cmd === 'edge') {
291
+ if (cmd.from && cmd.to) state.edges.push({ id: cmd.id || `${cmd.from}-${cmd.to}-${state.edges.length}`, ...cmd });
292
+ } else if (cmd.cmd === 'remove') {
293
+ state.nodes.delete(cmd.id);
294
+ state.edges = state.edges.filter(edge => edge.from !== cmd.id && edge.to !== cmd.id);
295
+ } else if (cmd.cmd === 'clear') {
296
+ state.nodes.clear();
297
+ state.edges = [];
298
+ } else if (cmd.cmd === 'layout') {
299
+ [...state.nodes.values()].forEach(node => { delete node.x; delete node.y; });
300
+ } else if (cmd.cmd === 'export') {
301
+ exportFile(cmd.format || 'json');
302
+ }
303
+ render();
304
+ }
305
+ async function restore() {
306
+ const response = await fetch(`/state?s=${encodeURIComponent(session)}`);
307
+ const commands = await response.json();
308
+ commands.forEach(handleCommand);
309
+ }
310
+ function connect() {
311
+ const events = new EventSource(`/events?s=${encodeURIComponent(session)}`);
312
+ events.onopen = () => setStatus('connected');
313
+ events.onerror = () => setStatus('reconnecting');
314
+ events.onmessage = event => handleCommand(JSON.parse(event.data));
315
+ }
316
+ function exportFile(format) {
317
+ if (format === 'svg') {
318
+ download('diagram.svg', new XMLSerializer().serializeToString(svg), 'image/svg+xml');
319
+ } else if (format === 'png') {
320
+ const image = new Image();
321
+ const data = new XMLSerializer().serializeToString(svg);
322
+ image.onload = () => {
323
+ const canvas = document.createElement('canvas');
324
+ canvas.width = svg.clientWidth * 2;
325
+ canvas.height = svg.clientHeight * 2;
326
+ const ctx = canvas.getContext('2d');
327
+ ctx.fillStyle = '#ffffff';
328
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
329
+ ctx.scale(2, 2);
330
+ ctx.drawImage(image, 0, 0);
331
+ downloadUrl('diagram.png', canvas.toDataURL('image/png'));
332
+ };
333
+ image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(data)}`;
334
+ } else {
335
+ const graph = { title: state.title, direction: state.direction, nodes: [...state.nodes.values()], edges: state.edges };
336
+ download('diagram.json', JSON.stringify(graph, null, 2), 'application/json');
337
+ }
338
+ }
339
+ function download(name, body, type) {
340
+ const url = URL.createObjectURL(new Blob([body], { type }));
341
+ downloadUrl(name, url);
342
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
343
+ }
344
+ function downloadUrl(name, url) {
345
+ const a = document.createElement('a');
346
+ a.href = url;
347
+ a.download = name;
348
+ a.click();
349
+ }
350
+ document.querySelector('.toolbar').addEventListener('click', event => {
351
+ const action = event.target.dataset.action;
352
+ if (action === 'fit') { state.scale = 1; state.tx = 24; state.ty = 24; render(); }
353
+ if (action === 'zoom-in') { state.scale = Math.min(3, state.scale + 0.15); render(); }
354
+ if (action === 'zoom-out') { state.scale = Math.max(0.35, state.scale - 0.15); render(); }
355
+ if (action === 'export-svg') exportFile('svg');
356
+ if (action === 'export-png') exportFile('png');
357
+ if (action === 'export-json') exportFile('json');
358
+ });
359
+ window.addEventListener('keydown', event => {
360
+ if ((event.key === 'Delete' || event.key === 'Backspace') && state.selected) {
361
+ const id = state.selected;
362
+ state.nodes.delete(id);
363
+ state.edges = state.edges.filter(edge => edge.from !== id && edge.to !== id);
364
+ state.selected = null;
365
+ render();
366
+ }
367
+ });
368
+ restore().then(connect).catch(error => setStatus(error.message));
369
+ </script>
370
+ </body>
371
+ </html>
@@ -0,0 +1,14 @@
1
+ # Diagram quality examples
2
+
3
+ The JSON fixtures cover sequence self calls, workflow decisions and loops, and architecture groups with reverse/data/async edges. The Mermaid sources are equivalent text-first examples for standard sequence/workflow rendering. Preserve their labels and relationships when changing layout.
4
+
5
+ ```sh
6
+ node skills/cc-technical-diagrams/scripts/tech-diagram-render.mjs --input skills/cc-technical-diagrams/assets/quality-examples/workflow.json --type workflow --output workflow.html --manifest workflow.layout.json
7
+ node skills/cc-technical-diagrams/scripts/export-diagram.mjs --input workflow.html --format png --output workflow.png --theme light --scale 2 --manifest workflow.layout.json --output-manifest workflow.actual.json
8
+ node skills/cc-technical-diagrams/scripts/png-qa.mjs --png workflow.png --manifest workflow.actual.json --format json
9
+ node skills/cc-technical-diagrams/scripts/mermaid-render.mjs --input skills/cc-technical-diagrams/assets/quality-examples/sequence.mmd --output sequence.svg --report sequence.render.json
10
+ ```
11
+
12
+ Use a local Chromium browser for PNG; set FEC_BROWSER_PATH to its executable when detection fails. Mermaid needs an already installed mmdc; no automatic install or CDN is used. Missing Mermaid must be recorded as unavailable and the HTML route selected. Architecture editing uses the existing cc-drawio-studio skill when available, otherwise the JSON/HTML fixture. Graphviz is optional for dense topology and is not installed automatically.
13
+
14
+ Inspect final PNGs at intended reading size. Check node text against shape interiors, edge labels against arrowheads, branch/return directions, loop visibility, group boundaries and light/dark contrast. Run at 1x and 2x and inspect both. Geometry QA is evidence, not a substitute for visual review. Small explicit node sizes, overlapping explicit coordinates or obstructed explicit waypoints are reported; change the source with user intent preserved. Limit automatic source repairs to two rounds (five only when requested), and report partial acceptance if unresolved.
@@ -0,0 +1,121 @@
1
+ {
2
+ "schema_version": 1,
3
+ "diagram_type": "architecture",
4
+ "meta": {
5
+ "title": "Platform layers and trust boundaries"
6
+ },
7
+ "visual": {
8
+ "style": "minimal"
9
+ },
10
+ "groups": [
11
+ {
12
+ "id": "private",
13
+ "label": "Trusted network / business and data services",
14
+ "x": 360,
15
+ "y": 70,
16
+ "width": 920,
17
+ "height": 470,
18
+ "type": "security"
19
+ }
20
+ ],
21
+ "nodes": [
22
+ {
23
+ "id": "web",
24
+ "label": "Browser application",
25
+ "sublabel": "External entry",
26
+ "type": "browser",
27
+ "x": 70,
28
+ "y": 160,
29
+ "width": 200,
30
+ "height": 112
31
+ },
32
+ {
33
+ "id": "gateway",
34
+ "label": "Identity and request routing gateway",
35
+ "type": "gateway",
36
+ "x": 420,
37
+ "y": 150,
38
+ "width": 220,
39
+ "height": 112,
40
+ "group": "private"
41
+ },
42
+ {
43
+ "id": "service",
44
+ "label": "Order query and approval service",
45
+ "type": "backend",
46
+ "x": 760,
47
+ "y": 150,
48
+ "width": 220,
49
+ "height": 112,
50
+ "group": "private"
51
+ },
52
+ {
53
+ "id": "cache",
54
+ "label": "Short-lived query cache",
55
+ "type": "memory",
56
+ "x": 420,
57
+ "y": 370,
58
+ "width": 220,
59
+ "height": 112,
60
+ "group": "private"
61
+ },
62
+ {
63
+ "id": "db",
64
+ "label": "Order and approval database",
65
+ "type": "database",
66
+ "x": 760,
67
+ "y": 370,
68
+ "width": 220,
69
+ "height": 112,
70
+ "group": "private"
71
+ },
72
+ {
73
+ "id": "queue",
74
+ "label": "Async tasks and notification queue",
75
+ "type": "queue",
76
+ "x": 1060,
77
+ "y": 370,
78
+ "width": 180,
79
+ "height": 112,
80
+ "group": "private"
81
+ }
82
+ ],
83
+ "connections": [
84
+ {
85
+ "from": "web",
86
+ "to": "gateway",
87
+ "label": "Authentication request",
88
+ "flow": "control"
89
+ },
90
+ {
91
+ "from": "gateway",
92
+ "to": "service",
93
+ "label": "Validated request",
94
+ "flow": "control"
95
+ },
96
+ {
97
+ "from": "service",
98
+ "to": "gateway",
99
+ "label": "Query response",
100
+ "flow": "data"
101
+ },
102
+ {
103
+ "from": "service",
104
+ "to": "cache",
105
+ "label": "Read cache",
106
+ "flow": "read"
107
+ },
108
+ {
109
+ "from": "service",
110
+ "to": "db",
111
+ "label": "Query records",
112
+ "flow": "read"
113
+ },
114
+ {
115
+ "from": "service",
116
+ "to": "queue",
117
+ "label": "Submit notification task",
118
+ "flow": "async"
119
+ }
120
+ ]
121
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "schema_version": 1,
3
+ "diagram_type": "architecture",
4
+ "meta": {
5
+ "title": "Expected diagnostics"
6
+ },
7
+ "nodes": [
8
+ {
9
+ "id": "tiny",
10
+ "label": "A complete long label that cannot fit explicit size",
11
+ "x": -12,
12
+ "y": 40,
13
+ "width": 48,
14
+ "height": 24
15
+ },
16
+ {
17
+ "id": "overlap",
18
+ "label": "Overlap",
19
+ "x": 20,
20
+ "y": 44,
21
+ "width": 100,
22
+ "height": 60
23
+ }
24
+ ],
25
+ "connections": [
26
+ {
27
+ "from": "tiny",
28
+ "to": "overlap",
29
+ "waypoints": [
30
+ [
31
+ 30,
32
+ 50
33
+ ],
34
+ [
35
+ 800,
36
+ -20
37
+ ]
38
+ ]
39
+ }
40
+ ]
41
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "schema_version": 1,
3
+ "diagram_type": "sequence",
4
+ "meta": {
5
+ "title": "Order query and cache refill"
6
+ },
7
+ "participants": [
8
+ {
9
+ "id": "client",
10
+ "label": "Browser client",
11
+ "type": "frontend"
12
+ },
13
+ {
14
+ "id": "api",
15
+ "label": "Order service API",
16
+ "type": "backend"
17
+ },
18
+ {
19
+ "id": "db",
20
+ "label": "Persistent database",
21
+ "type": "database"
22
+ }
23
+ ],
24
+ "messages": [
25
+ {
26
+ "from": "client",
27
+ "to": "api",
28
+ "label": "Query order details and current payment status"
29
+ },
30
+ {
31
+ "from": "api",
32
+ "to": "api",
33
+ "label": "Validate identity and order access"
34
+ },
35
+ {
36
+ "from": "api",
37
+ "to": "db",
38
+ "label": "Cache miss: read order records"
39
+ },
40
+ {
41
+ "from": "db",
42
+ "to": "api",
43
+ "label": "Return order records and payment status",
44
+ "variant": "return"
45
+ },
46
+ {
47
+ "from": "api",
48
+ "to": "client",
49
+ "label": "Return results and update the page",
50
+ "variant": "return"
51
+ }
52
+ ]
53
+ }
@@ -0,0 +1,21 @@
1
+ sequenceDiagram
2
+ participant Web as Browser client
3
+ participant API as Order service API
4
+ participant DB as Persistent database
5
+ Web->>API: Query order details and current payment status
6
+ API->>API: Validate identity and order access
7
+ API->>DB: Cache miss: read order records
8
+ DB-->>API: Return order records and payment status
9
+ API-->>Web: Return results and update the page
10
+
11
+ activate API
12
+ alt Cache hit
13
+ API-->>Web: Return cached result
14
+ else Cache miss
15
+ loop Retry failed query
16
+ API->>DB: Read records
17
+ DB-->>API: Return result
18
+ end
19
+ end
20
+ API-)Web: Async update notification
21
+ deactivate API