living-ai-documentation 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 (203) hide show
  1. package/LICENSE +661 -0
  2. package/README.fr.md +344 -0
  3. package/README.md +344 -0
  4. package/dist/bin/cli.d.ts +3 -0
  5. package/dist/bin/cli.d.ts.map +1 -0
  6. package/dist/bin/cli.js +262 -0
  7. package/dist/bin/cli.js.map +1 -0
  8. package/dist/src/frontend/accuracy-gauge.js +70 -0
  9. package/dist/src/frontend/admin.html +1532 -0
  10. package/dist/src/frontend/annotations.js +585 -0
  11. package/dist/src/frontend/boot.js +101 -0
  12. package/dist/src/frontend/config.js +29 -0
  13. package/dist/src/frontend/confirm-modal.js +82 -0
  14. package/dist/src/frontend/context.html +1252 -0
  15. package/dist/src/frontend/dark-mode.js +20 -0
  16. package/dist/src/frontend/diagram/alignment.js +161 -0
  17. package/dist/src/frontend/diagram/clipboard.js +187 -0
  18. package/dist/src/frontend/diagram/constants.js +109 -0
  19. package/dist/src/frontend/diagram/custom-shapes.js +104 -0
  20. package/dist/src/frontend/diagram/debug.js +43 -0
  21. package/dist/src/frontend/diagram/drawio-export.js +649 -0
  22. package/dist/src/frontend/diagram/edge-panel.js +293 -0
  23. package/dist/src/frontend/diagram/edge-rendering.js +12 -0
  24. package/dist/src/frontend/diagram/evidence.js +146 -0
  25. package/dist/src/frontend/diagram/grid.js +78 -0
  26. package/dist/src/frontend/diagram/groups.js +102 -0
  27. package/dist/src/frontend/diagram/history.js +157 -0
  28. package/dist/src/frontend/diagram/image-name-modal.js +48 -0
  29. package/dist/src/frontend/diagram/image-upload.js +36 -0
  30. package/dist/src/frontend/diagram/label-editor.js +115 -0
  31. package/dist/src/frontend/diagram/link-panel.js +144 -0
  32. package/dist/src/frontend/diagram/main.js +364 -0
  33. package/dist/src/frontend/diagram/network.js +2214 -0
  34. package/dist/src/frontend/diagram/node-panel.js +389 -0
  35. package/dist/src/frontend/diagram/node-rendering.js +964 -0
  36. package/dist/src/frontend/diagram/persistence.js +168 -0
  37. package/dist/src/frontend/diagram/ports.js +421 -0
  38. package/dist/src/frontend/diagram/selection-overlay.js +387 -0
  39. package/dist/src/frontend/diagram/state.js +43 -0
  40. package/dist/src/frontend/diagram/t.js +3 -0
  41. package/dist/src/frontend/diagram/toast.js +21 -0
  42. package/dist/src/frontend/diagram/unlock-hold.js +206 -0
  43. package/dist/src/frontend/diagram/zoom.js +20 -0
  44. package/dist/src/frontend/diagram-link-modal.js +137 -0
  45. package/dist/src/frontend/diagram.html +1494 -0
  46. package/dist/src/frontend/documents.js +479 -0
  47. package/dist/src/frontend/export.js +338 -0
  48. package/dist/src/frontend/file-attach.js +178 -0
  49. package/dist/src/frontend/files-modal.js +243 -0
  50. package/dist/src/frontend/i18n/en.json +624 -0
  51. package/dist/src/frontend/i18n/fr.json +624 -0
  52. package/dist/src/frontend/i18n.js +32 -0
  53. package/dist/src/frontend/image-paste.js +126 -0
  54. package/dist/src/frontend/index.html +2806 -0
  55. package/dist/src/frontend/local-search.js +476 -0
  56. package/dist/src/frontend/metadata.js +318 -0
  57. package/dist/src/frontend/misc.js +92 -0
  58. package/dist/src/frontend/new-doc-modal.js +285 -0
  59. package/dist/src/frontend/new-folder-modal.js +169 -0
  60. package/dist/src/frontend/search.js +194 -0
  61. package/dist/src/frontend/shape-editor.html +685 -0
  62. package/dist/src/frontend/sidebar-helpers.js +96 -0
  63. package/dist/src/frontend/sidebar-resize.js +98 -0
  64. package/dist/src/frontend/sidebar.js +351 -0
  65. package/dist/src/frontend/snippet-detect.js +25 -0
  66. package/dist/src/frontend/snippet-table.js +85 -0
  67. package/dist/src/frontend/snippet-tree.js +94 -0
  68. package/dist/src/frontend/snippets.js +1146 -0
  69. package/dist/src/frontend/state.js +46 -0
  70. package/dist/src/frontend/utils.js +21 -0
  71. package/dist/src/frontend/validate.js +107 -0
  72. package/dist/src/frontend/vendor/wordcloud2.js +1187 -0
  73. package/dist/src/frontend/wordcloud.js +693 -0
  74. package/dist/src/lib/config.d.ts +26 -0
  75. package/dist/src/lib/config.d.ts.map +1 -0
  76. package/dist/src/lib/config.js +195 -0
  77. package/dist/src/lib/config.js.map +1 -0
  78. package/dist/src/lib/hash.d.ts +2 -0
  79. package/dist/src/lib/hash.d.ts.map +1 -0
  80. package/dist/src/lib/hash.js +18 -0
  81. package/dist/src/lib/hash.js.map +1 -0
  82. package/dist/src/lib/metadata.d.ts +31 -0
  83. package/dist/src/lib/metadata.d.ts.map +1 -0
  84. package/dist/src/lib/metadata.js +128 -0
  85. package/dist/src/lib/metadata.js.map +1 -0
  86. package/dist/src/lib/parser.d.ts +11 -0
  87. package/dist/src/lib/parser.d.ts.map +1 -0
  88. package/dist/src/lib/parser.js +111 -0
  89. package/dist/src/lib/parser.js.map +1 -0
  90. package/dist/src/lib/status.d.ts +9 -0
  91. package/dist/src/lib/status.d.ts.map +1 -0
  92. package/dist/src/lib/status.js +72 -0
  93. package/dist/src/lib/status.js.map +1 -0
  94. package/dist/src/mcp/server.d.ts +3 -0
  95. package/dist/src/mcp/server.d.ts.map +1 -0
  96. package/dist/src/mcp/server.js +2046 -0
  97. package/dist/src/mcp/server.js.map +1 -0
  98. package/dist/src/mcp/tools/diagrams.d.ts +82 -0
  99. package/dist/src/mcp/tools/diagrams.d.ts.map +1 -0
  100. package/dist/src/mcp/tools/diagrams.js +594 -0
  101. package/dist/src/mcp/tools/diagrams.js.map +1 -0
  102. package/dist/src/mcp/tools/documents.d.ts +44 -0
  103. package/dist/src/mcp/tools/documents.d.ts.map +1 -0
  104. package/dist/src/mcp/tools/documents.js +186 -0
  105. package/dist/src/mcp/tools/documents.js.map +1 -0
  106. package/dist/src/mcp/tools/git.d.ts +10 -0
  107. package/dist/src/mcp/tools/git.d.ts.map +1 -0
  108. package/dist/src/mcp/tools/git.js +217 -0
  109. package/dist/src/mcp/tools/git.js.map +1 -0
  110. package/dist/src/mcp/tools/metadata.d.ts +57 -0
  111. package/dist/src/mcp/tools/metadata.d.ts.map +1 -0
  112. package/dist/src/mcp/tools/metadata.js +222 -0
  113. package/dist/src/mcp/tools/metadata.js.map +1 -0
  114. package/dist/src/mcp/tools/source.d.ts +29 -0
  115. package/dist/src/mcp/tools/source.d.ts.map +1 -0
  116. package/dist/src/mcp/tools/source.js +196 -0
  117. package/dist/src/mcp/tools/source.js.map +1 -0
  118. package/dist/src/routes/annotations.d.ts +3 -0
  119. package/dist/src/routes/annotations.d.ts.map +1 -0
  120. package/dist/src/routes/annotations.js +83 -0
  121. package/dist/src/routes/annotations.js.map +1 -0
  122. package/dist/src/routes/browse-source.d.ts +3 -0
  123. package/dist/src/routes/browse-source.d.ts.map +1 -0
  124. package/dist/src/routes/browse-source.js +79 -0
  125. package/dist/src/routes/browse-source.js.map +1 -0
  126. package/dist/src/routes/browse.d.ts +3 -0
  127. package/dist/src/routes/browse.d.ts.map +1 -0
  128. package/dist/src/routes/browse.js +91 -0
  129. package/dist/src/routes/browse.js.map +1 -0
  130. package/dist/src/routes/config.d.ts +3 -0
  131. package/dist/src/routes/config.d.ts.map +1 -0
  132. package/dist/src/routes/config.js +145 -0
  133. package/dist/src/routes/config.js.map +1 -0
  134. package/dist/src/routes/context.d.ts +3 -0
  135. package/dist/src/routes/context.d.ts.map +1 -0
  136. package/dist/src/routes/context.js +287 -0
  137. package/dist/src/routes/context.js.map +1 -0
  138. package/dist/src/routes/diagrams.d.ts +3 -0
  139. package/dist/src/routes/diagrams.d.ts.map +1 -0
  140. package/dist/src/routes/diagrams.js +69 -0
  141. package/dist/src/routes/diagrams.js.map +1 -0
  142. package/dist/src/routes/documents.d.ts +11 -0
  143. package/dist/src/routes/documents.d.ts.map +1 -0
  144. package/dist/src/routes/documents.js +450 -0
  145. package/dist/src/routes/documents.js.map +1 -0
  146. package/dist/src/routes/export.d.ts +3 -0
  147. package/dist/src/routes/export.d.ts.map +1 -0
  148. package/dist/src/routes/export.js +280 -0
  149. package/dist/src/routes/export.js.map +1 -0
  150. package/dist/src/routes/files.d.ts +3 -0
  151. package/dist/src/routes/files.d.ts.map +1 -0
  152. package/dist/src/routes/files.js +180 -0
  153. package/dist/src/routes/files.js.map +1 -0
  154. package/dist/src/routes/images.d.ts +3 -0
  155. package/dist/src/routes/images.d.ts.map +1 -0
  156. package/dist/src/routes/images.js +49 -0
  157. package/dist/src/routes/images.js.map +1 -0
  158. package/dist/src/routes/metadata.d.ts +3 -0
  159. package/dist/src/routes/metadata.d.ts.map +1 -0
  160. package/dist/src/routes/metadata.js +131 -0
  161. package/dist/src/routes/metadata.js.map +1 -0
  162. package/dist/src/routes/shape-libraries.d.ts +3 -0
  163. package/dist/src/routes/shape-libraries.d.ts.map +1 -0
  164. package/dist/src/routes/shape-libraries.js +118 -0
  165. package/dist/src/routes/shape-libraries.js.map +1 -0
  166. package/dist/src/routes/wordcloud.d.ts +3 -0
  167. package/dist/src/routes/wordcloud.d.ts.map +1 -0
  168. package/dist/src/routes/wordcloud.js +95 -0
  169. package/dist/src/routes/wordcloud.js.map +1 -0
  170. package/dist/src/server.d.ts +7 -0
  171. package/dist/src/server.d.ts.map +1 -0
  172. package/dist/src/server.js +93 -0
  173. package/dist/src/server.js.map +1 -0
  174. package/dist/starter-doc/.living-doc.json +52 -0
  175. package/dist/starter-doc/ADRS/2026_01_01_[ADR]_example_architecture_decision.md +59 -0
  176. package/dist/starter-doc/AI/2026_01_01_how_to.md +112 -0
  177. package/dist/starter-doc/AI/PROJECT-INSTRUCTIONS.md +172 -0
  178. package/dist/starter-doc/AI/PROJECT-STACK.md +77 -0
  179. package/dist/starter-doc/AI/PROJECT-USEFUL-COMMANDS.md +80 -0
  180. package/dist/starter-doc/AI/default/AGENTS.md +31 -0
  181. package/dist/starter-doc/AI/default/CLAUDE.md +31 -0
  182. package/dist/starter-doc/AI/default/MEMORY.md +24 -0
  183. package/dist/starter-doc/AI/rules/no-magic-numbers.md +18 -0
  184. package/dist/starter-doc/AI/rules/track-current-work.md +23 -0
  185. package/dist/starter-doc/WORKLOG/current-task.md +57 -0
  186. package/dist/starter-doc-fr/.living-doc.json +52 -0
  187. package/dist/starter-doc-fr/ADRS/2026_01_01_[ADR]_example_architecture_decision.md +59 -0
  188. package/dist/starter-doc-fr/AI/2026_01_01_how_to.md +100 -0
  189. package/dist/starter-doc-fr/AI/PROJECT-INSTRUCTIONS.md +172 -0
  190. package/dist/starter-doc-fr/AI/PROJECT-STACK.md +77 -0
  191. package/dist/starter-doc-fr/AI/PROJECT-USEFUL-COMMANDS.md +80 -0
  192. package/dist/starter-doc-fr/AI/default/AGENTS.md +31 -0
  193. package/dist/starter-doc-fr/AI/default/CLAUDE.md +31 -0
  194. package/dist/starter-doc-fr/AI/default/MEMORY.md +24 -0
  195. package/dist/starter-doc-fr/AI/rules/no-magic-numbers.md +18 -0
  196. package/dist/starter-doc-fr/AI/rules/track-current-work.md +23 -0
  197. package/dist/starter-doc-fr/WORKLOG/current-task.md +57 -0
  198. package/images/living_documentation.jpg +0 -0
  199. package/images/readme-extra-files.png +0 -0
  200. package/images/readme-filename-pattern.png +0 -0
  201. package/images/readme-intelligent-search-demo.jpg +0 -0
  202. package/images/readme-sidebar.png +0 -0
  203. package/package.json +72 -0
@@ -0,0 +1,1494 @@
1
+ <!doctype html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Diagram — Living Documentation</title>
7
+ <script src="/i18n.js"></script>
8
+ <script src="https://cdn.tailwindcss.com?plugins=typography"></script>
9
+ <script>
10
+ tailwind.config = { darkMode: "class", theme: { extend: {} } };
11
+ </script>
12
+ <script src="https://unpkg.com/vis-network@9.1.9/standalone/umd/vis-network.min.js"></script>
13
+ <style>
14
+ #vis-canvas > div {
15
+ border: none !important;
16
+ }
17
+ .tool-btn {
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ width: 2rem;
22
+ height: 2rem;
23
+ border-radius: 0.5rem;
24
+ font-size: 0.875rem;
25
+ cursor: pointer;
26
+ flex-shrink: 0;
27
+ transition:
28
+ background-color 0.15s,
29
+ color 0.15s;
30
+ color: #4b5563;
31
+ background: none;
32
+ border: none;
33
+ }
34
+ .dark .tool-btn {
35
+ color: #9ca3af;
36
+ }
37
+ .tool-btn:hover {
38
+ background: #f3f4f6;
39
+ }
40
+ .dark .tool-btn:hover {
41
+ background: #1f2937;
42
+ }
43
+ .tool-active {
44
+ background: #fff7ed !important;
45
+ color: #ea580c !important;
46
+ }
47
+ .dark .tool-active {
48
+ background: rgba(124, 45, 18, 0.3) !important;
49
+ color: #fb923c !important;
50
+ }
51
+ #vis-canvas.cursor-crosshair canvas {
52
+ cursor: crosshair !important;
53
+ }
54
+
55
+ /* Floating panels */
56
+ .float-panel {
57
+ position: absolute;
58
+ top: 0.75rem;
59
+ left: 50%;
60
+ transform: translateX(-50%);
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 0.25rem;
64
+ padding: 0.375rem 0.5rem;
65
+ background: white;
66
+ border: 1px solid #e5e7eb;
67
+ border-radius: 0.5rem;
68
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
69
+ z-index: 10;
70
+ }
71
+ .dark .float-panel {
72
+ background: #1f2937;
73
+ border-color: #374151;
74
+ }
75
+ .float-panel.hidden {
76
+ display: none !important;
77
+ }
78
+ .panel-sep {
79
+ width: 1px;
80
+ height: 1rem;
81
+ background: #e5e7eb;
82
+ margin: 0 0.125rem;
83
+ flex-shrink: 0;
84
+ }
85
+ .dark .panel-sep {
86
+ background: #374151;
87
+ }
88
+ #customShapeLabelPlacementControls {
89
+ display: contents;
90
+ }
91
+ #customShapeLabelPlacementControls.hidden {
92
+ display: none;
93
+ }
94
+ .edge-btn-active {
95
+ background: #fff7ed !important;
96
+ color: #ea580c !important;
97
+ }
98
+ .dark .edge-btn-active {
99
+ background: rgba(124, 45, 18, 0.3) !important;
100
+ color: #fb923c !important;
101
+ }
102
+
103
+ /* Floating label textarea */
104
+ #labelInput {
105
+ position: absolute;
106
+ z-index: 20;
107
+ padding: 4px 8px;
108
+ font-size: 13px;
109
+ font-family:
110
+ system-ui,
111
+ -apple-system,
112
+ sans-serif;
113
+ font-weight: 500;
114
+ text-align: center;
115
+ background: rgba(255, 255, 255, 0.95);
116
+ color: #1f2937;
117
+ border: 2px solid #3b82f6;
118
+ border-radius: 0.5rem;
119
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
120
+ outline: none;
121
+ resize: none;
122
+ overflow: hidden;
123
+ line-height: 1.4;
124
+ min-width: 80px;
125
+ min-height: 28px;
126
+ }
127
+ .dark #labelInput {
128
+ background: rgba(17, 24, 39, 0.95);
129
+ color: #f3f4f6;
130
+ }
131
+ #labelInput.hidden {
132
+ display: none;
133
+ }
134
+
135
+ /* Debug overlay layer */
136
+ #debugLayer {
137
+ position: absolute;
138
+ inset: 0;
139
+ pointer-events: none;
140
+ z-index: 12;
141
+ overflow: hidden;
142
+ }
143
+ .debug-box {
144
+ position: absolute;
145
+ pointer-events: auto;
146
+ font: 10px/1.4 monospace;
147
+ white-space: pre;
148
+ user-select: text;
149
+ cursor: text;
150
+ padding: 3px 6px;
151
+ border: 1px solid #f97316;
152
+ border-radius: 3px;
153
+ color: #c2410c;
154
+ background: rgba(255, 255, 255, 0.9);
155
+ }
156
+ .dark .debug-box {
157
+ color: #fbbf24;
158
+ background: rgba(0, 0, 0, 0.78);
159
+ }
160
+
161
+ /* Resize / rotation / selection overlay */
162
+ #selectionOverlay {
163
+ position: absolute;
164
+ display: none;
165
+ border: 2px dashed #f97316;
166
+ border-radius: 3px;
167
+ pointer-events: none;
168
+ z-index: 15;
169
+ box-sizing: border-box;
170
+ }
171
+ .resize-handle {
172
+ position: absolute;
173
+ width: 10px;
174
+ height: 10px;
175
+ background: white;
176
+ border: 2px solid #f97316;
177
+ border-radius: 2px;
178
+ pointer-events: all;
179
+ z-index: 1;
180
+ }
181
+ .dark .resize-handle {
182
+ background: #374151;
183
+ }
184
+ #rh-rotate {
185
+ position: absolute;
186
+ width: 16px;
187
+ height: 16px;
188
+ background: white;
189
+ border: 2px solid #f97316;
190
+ border-radius: 50%;
191
+ pointer-events: all;
192
+ z-index: 1;
193
+ cursor: grab;
194
+ display: flex;
195
+ align-items: center;
196
+ justify-content: center;
197
+ font-size: 10px;
198
+ color: #f97316;
199
+ }
200
+ #rh-rotate:active { cursor: grabbing; }
201
+ .dark #rh-rotate { background: #374151; }
202
+ #rh-label-rotate {
203
+ position: absolute;
204
+ width: 16px;
205
+ height: 16px;
206
+ background: white;
207
+ border: 2px solid #6366f1;
208
+ border-radius: 50%;
209
+ pointer-events: all;
210
+ z-index: 1;
211
+ cursor: grab;
212
+ display: flex;
213
+ align-items: center;
214
+ justify-content: center;
215
+ font-size: 10px;
216
+ color: #6366f1;
217
+ }
218
+ #rh-label-rotate:active { cursor: grabbing; }
219
+ .dark #rh-label-rotate { background: #374151; }
220
+
221
+ /* Toast notifications */
222
+ #toastContainer {
223
+ position: fixed;
224
+ bottom: 1.5rem;
225
+ right: 1.5rem;
226
+ display: flex;
227
+ flex-direction: column-reverse;
228
+ gap: 0.5rem;
229
+ z-index: 1000;
230
+ pointer-events: none;
231
+ }
232
+ .ld-toast {
233
+ pointer-events: all;
234
+ padding: 0.6rem 1rem;
235
+ border-radius: 0.5rem;
236
+ font-size: 0.8rem;
237
+ font-weight: 500;
238
+ box-shadow: 0 4px 16px rgba(0,0,0,0.18);
239
+ cursor: pointer;
240
+ opacity: 0;
241
+ transform: translateY(0.5rem);
242
+ transition: opacity 0.2s ease, transform 0.2s ease;
243
+ background: #1c1917;
244
+ color: #fafaf9;
245
+ border: 1px solid #292524;
246
+ }
247
+ .dark .ld-toast {
248
+ background: #fafaf9;
249
+ color: #1c1917;
250
+ border-color: #e7e5e4;
251
+ }
252
+ .ld-toast--error {
253
+ background: #7f1d1d;
254
+ color: #fef2f2;
255
+ border-color: #991b1b;
256
+ }
257
+ .ld-toast--visible {
258
+ opacity: 1;
259
+ transform: translateY(0);
260
+ }
261
+
262
+ #evidenceLayer {
263
+ position: absolute;
264
+ inset: 0;
265
+ pointer-events: none;
266
+ z-index: 18;
267
+ overflow: hidden;
268
+ }
269
+ #evidenceLayer.hidden {
270
+ display: none;
271
+ }
272
+ .evidence-marker {
273
+ position: absolute;
274
+ width: 1.25rem;
275
+ height: 1.25rem;
276
+ transform: translate(-50%, -50%);
277
+ display: flex;
278
+ align-items: center;
279
+ justify-content: center;
280
+ border-radius: 999px;
281
+ border: 1px solid #0284c7;
282
+ background: #e0f2fe;
283
+ color: #0369a1;
284
+ font-size: 0.7rem;
285
+ font-weight: 700;
286
+ line-height: 1;
287
+ box-shadow: 0 2px 8px rgba(2, 132, 199, 0.28);
288
+ pointer-events: auto;
289
+ }
290
+ .dark .evidence-marker {
291
+ border-color: #38bdf8;
292
+ background: #082f49;
293
+ color: #e0f2fe;
294
+ }
295
+ #customShapeBar {
296
+ position: absolute;
297
+ left: 0.75rem;
298
+ bottom: 0.75rem;
299
+ z-index: 9;
300
+ display: flex;
301
+ align-items: center;
302
+ max-width: min(42rem, calc(100% - 1.5rem));
303
+ padding: 0.35rem;
304
+ gap: 0.25rem;
305
+ border: 1px solid #e5e7eb;
306
+ border-radius: 0.5rem;
307
+ background: rgba(255, 255, 255, 0.96);
308
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.10);
309
+ }
310
+ .dark #customShapeBar {
311
+ border-color: #374151;
312
+ background: rgba(31, 41, 55, 0.96);
313
+ }
314
+ #customShapeBar.hidden {
315
+ display: none;
316
+ }
317
+ #customShapeBarBody {
318
+ display: flex;
319
+ gap: 0.25rem;
320
+ overflow-x: auto;
321
+ scrollbar-width: thin;
322
+ }
323
+ .custom-shape-btn {
324
+ display: flex;
325
+ align-items: center;
326
+ justify-content: center;
327
+ width: 2.25rem;
328
+ height: 2.25rem;
329
+ border-radius: 0.375rem;
330
+ border: 1px solid transparent;
331
+ background: transparent;
332
+ flex: 0 0 auto;
333
+ }
334
+ .custom-shape-btn:hover {
335
+ background: #f3f4f6;
336
+ }
337
+ .dark .custom-shape-btn:hover {
338
+ background: #111827;
339
+ }
340
+ .custom-shape-btn img {
341
+ max-width: 1.7rem;
342
+ max-height: 1.7rem;
343
+ object-fit: contain;
344
+ pointer-events: none;
345
+ }
346
+ </style>
347
+ </head>
348
+ <body
349
+ class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 flex flex-col h-screen overflow-hidden"
350
+ >
351
+ <script>
352
+ const _d = localStorage.getItem("ld-dark") === "true";
353
+ document.documentElement.classList.toggle("dark", _d);
354
+ </script>
355
+
356
+ <!-- ── Top bar ── -->
357
+ <header
358
+ class="flex items-center gap-1 px-2 h-12 shrink-0 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm z-10"
359
+ >
360
+ <button
361
+ onclick="history.back()"
362
+ class="tool-btn !w-auto px-2 text-xs font-medium text-gray-500 dark:text-gray-400"
363
+ data-i18n="diagram.back_btn">← Back</button
364
+ >
365
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
366
+ <button id="btnSidebar" class="tool-btn" data-i18n-title="diagram.diagrams_list_title">
367
+
368
+ </button>
369
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
370
+
371
+ <button
372
+ id="btnCopyDiagramId"
373
+ class="tool-btn"
374
+ data-i18n-title="diagram.toolbar.copy_mcp_id"
375
+ >
376
+ <svg
377
+ width="15"
378
+ height="15"
379
+ viewBox="0 0 16 16"
380
+ fill="none"
381
+ stroke="currentColor"
382
+ stroke-width="1.6"
383
+ stroke-linecap="round"
384
+ stroke-linejoin="round"
385
+ >
386
+ <rect x="5" y="5" width="8" height="9" rx="1.5" />
387
+ <path d="M3 11V3a1 1 0 0 1 1-1h7" />
388
+ </svg>
389
+ </button>
390
+ <button
391
+ id="toolSelect"
392
+ class="tool-btn tool-active"
393
+ data-i18n-title="diagram.toolbar.select"
394
+ >
395
+ <svg
396
+ width="11"
397
+ height="13"
398
+ viewBox="0 0 11 13"
399
+ fill="currentColor"
400
+ stroke="none"
401
+ >
402
+ <path d="M1 1 L1 12 L4 9 L6.5 13 L8 12.2 L5.5 8.2 L10 8.2 Z" />
403
+ </svg>
404
+ </button>
405
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
406
+
407
+ <button
408
+ id="toolBox"
409
+ class="tool-btn"
410
+ data-i18n-title="diagram.toolbar.box"
411
+ >
412
+ <svg
413
+ width="15"
414
+ height="10"
415
+ viewBox="0 0 15 10"
416
+ fill="none"
417
+ stroke="currentColor"
418
+ stroke-width="1.5"
419
+ >
420
+ <rect x="1" y="1" width="13" height="8" rx="1" />
421
+ </svg>
422
+ </button>
423
+ <button
424
+ id="toolEllipse"
425
+ class="tool-btn"
426
+ data-i18n-title="diagram.toolbar.ellipse"
427
+ >
428
+ <svg
429
+ width="16"
430
+ height="10"
431
+ viewBox="0 0 16 10"
432
+ fill="none"
433
+ stroke="currentColor"
434
+ stroke-width="1.5"
435
+ >
436
+ <ellipse cx="8" cy="5" rx="7" ry="4" />
437
+ </svg>
438
+ </button>
439
+ <button
440
+ id="toolDatabase"
441
+ class="tool-btn"
442
+ data-i18n-title="diagram.toolbar.database"
443
+ >
444
+ <svg
445
+ width="12"
446
+ height="16"
447
+ viewBox="0 0 12 16"
448
+ fill="none"
449
+ stroke="currentColor"
450
+ stroke-width="1.5"
451
+ stroke-linecap="round"
452
+ >
453
+ <ellipse cx="6" cy="3" rx="5" ry="2" />
454
+ <path d="M1 3v10c0 1.1 2.2 2 5 2s5-.9 5-2V3" />
455
+ <path d="M11 7.5c0 1.1-2.2 2-5 2s-5-.9-5-2" />
456
+ </svg>
457
+ </button>
458
+ <button
459
+ id="toolCircle"
460
+ class="tool-btn"
461
+ data-i18n-title="diagram.toolbar.circle"
462
+ >
463
+ <svg
464
+ width="13"
465
+ height="13"
466
+ viewBox="0 0 13 13"
467
+ fill="none"
468
+ stroke="currentColor"
469
+ stroke-width="1.5"
470
+ >
471
+ <circle cx="6.5" cy="6.5" r="5.5" />
472
+ </svg>
473
+ </button>
474
+ <button
475
+ id="toolActor"
476
+ class="tool-btn"
477
+ data-i18n-title="diagram.toolbar.actor"
478
+ >
479
+ <svg
480
+ width="12"
481
+ height="17"
482
+ viewBox="0 0 12 17"
483
+ fill="none"
484
+ stroke="currentColor"
485
+ stroke-width="1.5"
486
+ stroke-linecap="round"
487
+ >
488
+ <circle cx="6" cy="3" r="2.2" />
489
+ <line x1="6" y1="5.2" x2="6" y2="10" />
490
+ <line x1="1.5" y1="7.5" x2="10.5" y2="7.5" />
491
+ <line x1="6" y1="10" x2="2.5" y2="15" />
492
+ <line x1="6" y1="10" x2="9.5" y2="15" />
493
+ </svg>
494
+ </button>
495
+ <button
496
+ id="toolPostIt"
497
+ class="tool-btn"
498
+ data-i18n-title="diagram.toolbar.postit"
499
+ >
500
+ <svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.4">
501
+ <path d="M1 1 H9 L12 4 V12 H1 Z" />
502
+ <path d="M9 1 V4 H12" stroke-opacity="0.5"/>
503
+ </svg>
504
+ </button>
505
+ <button
506
+ id="toolTextFree"
507
+ class="tool-btn"
508
+ data-i18n-title="diagram.toolbar.text_free"
509
+ style="font-size:11px; font-weight:600;"
510
+ >
511
+ T
512
+ </button>
513
+ <button
514
+ id="toolImage"
515
+ class="tool-btn"
516
+ data-i18n-title="diagram.toolbar.image"
517
+ >
518
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
519
+ <rect x="1" y="1" width="12" height="12" rx="1.5"/>
520
+ <circle cx="4.5" cy="4.5" r="1.2"/>
521
+ <path d="M1 9.5 L4 6.5 L6.5 9 L9 7 L13 10.5"/>
522
+ </svg>
523
+ </button>
524
+ <a
525
+ href="/shape-editor"
526
+ class="tool-btn"
527
+ data-i18n-title="diagram.toolbar.shape_editor"
528
+ >
529
+
530
+ </a>
531
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
532
+
533
+ <button
534
+ id="toolArrow"
535
+ class="tool-btn"
536
+ data-i18n-title="diagram.toolbar.arrow"
537
+ >
538
+
539
+ </button>
540
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
541
+
542
+ <button
543
+ id="btnDelete"
544
+ class="tool-btn"
545
+ data-i18n-title="diagram.toolbar.delete"
546
+ >
547
+ <svg
548
+ width="11"
549
+ height="11"
550
+ viewBox="0 0 11 11"
551
+ fill="none"
552
+ stroke="currentColor"
553
+ stroke-width="1.8"
554
+ stroke-linecap="round"
555
+ >
556
+ <line x1="1" y1="1" x2="10" y2="10" />
557
+ <line x1="10" y1="1" x2="1" y2="10" />
558
+ </svg>
559
+ </button>
560
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
561
+
562
+ <button
563
+ id="btnAlign"
564
+ class="tool-btn"
565
+ data-i18n-title="diagram.toolbar.align_guides"
566
+ >
567
+ <svg
568
+ width="14"
569
+ height="14"
570
+ viewBox="0 0 14 14"
571
+ fill="none"
572
+ stroke="currentColor"
573
+ stroke-width="1.4"
574
+ >
575
+ <line x1="0.5" y1="7.5" x2="13.5" y2="7.5" />
576
+ <line x1="3.5" y1="0.5" x2="3.5" y2="13.5" stroke-dasharray="1.5 1.5" />
577
+ <line x1="10.5" y1="0.5" x2="10.5" y2="13.5" stroke-dasharray="1.5 1.5" />
578
+ </svg>
579
+ </button>
580
+ <button
581
+ id="btnGrid"
582
+ class="tool-btn"
583
+ data-i18n-title="diagram.toolbar.grid"
584
+ >
585
+ <svg
586
+ width="14"
587
+ height="14"
588
+ viewBox="0 0 14 14"
589
+ fill="none"
590
+ stroke="currentColor"
591
+ stroke-width="1.2"
592
+ >
593
+ <line x1="5" y1="1" x2="5" y2="13" />
594
+ <line x1="9" y1="1" x2="9" y2="13" />
595
+ <line x1="1" y1="5" x2="13" y2="5" />
596
+ <line x1="1" y1="9" x2="13" y2="9" />
597
+ <rect x="1" y="1" width="12" height="12" rx="1" />
598
+ </svg>
599
+ </button>
600
+ <button
601
+ id="btnEdgeStraight"
602
+ class="tool-btn"
603
+ data-i18n-title="diagram.toolbar.edge_style"
604
+ >
605
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
606
+ <line x1="2" y1="12" x2="12" y2="2" />
607
+ <polyline points="8,2 12,2 12,6" />
608
+ </svg>
609
+ </button>
610
+ <button
611
+ id="btnEvidenceMode"
612
+ class="tool-btn"
613
+ data-i18n-title="diagram.toolbar.evidence_mode"
614
+ >
615
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
616
+ <path d="M4 1.5 H8.5 L11 4 V12.5 H4 Z" />
617
+ <path d="M8.5 1.5 V4 H11" />
618
+ <path d="M1.5 5.5 H6.5" />
619
+ <path d="M1.5 8 H6.5" />
620
+ <path d="M1.5 10.5 H5" />
621
+ </svg>
622
+ </button>
623
+ <button
624
+ id="btnResizeMode"
625
+ class="tool-btn active-tool"
626
+ data-i18n-title="diagram.toolbar.resize_corner"
627
+ >
628
+ <!-- icon-resize-corner: dashed square + single arrow top-right → bottom-left -->
629
+ <svg id="icon-resize-corner" width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
630
+ <rect x="1" y="1" width="12" height="12" stroke-dasharray="2.5 2" />
631
+ <line x1="4" y1="4" x2="10" y2="10" />
632
+ <polyline points="7,10 10,10 10,7" />
633
+ </svg>
634
+ <!-- icon-resize-center: dashed square + two double-headed diagonal arrows -->
635
+ <svg id="icon-resize-center" width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="hidden">
636
+ <rect x="1" y="1" width="12" height="12" stroke-dasharray="2.5 2" />
637
+ <line x1="4" y1="4" x2="10" y2="10" />
638
+ <polyline points="4,6.5 4,4 6.5,4" />
639
+ <polyline points="10,7.5 10,10 7.5,10" />
640
+ <line x1="10" y1="4" x2="4" y2="10" />
641
+ <polyline points="7.5,4 10,4 10,6.5" />
642
+ <polyline points="4,7.5 4,10 6.5,10" />
643
+ </svg>
644
+ </button>
645
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
646
+
647
+ <input
648
+ id="diagramTitle"
649
+ type="text"
650
+ data-i18n-placeholder="diagram.toolbar.title_placeholder" placeholder="Diagram title"
651
+ class="flex-1 min-w-0 px-2 py-1 text-sm bg-transparent border-0 focus:outline-none focus:ring-1 focus:ring-blue-500 rounded text-gray-700 dark:text-gray-300 placeholder:text-gray-400"
652
+ />
653
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
654
+
655
+ <button id="btnZoomOut" class="tool-btn" data-i18n-title="diagram.toolbar.zoom_out">
656
+
657
+ </button>
658
+ <span
659
+ id="zoomLevel"
660
+ class="text-xs text-gray-500 dark:text-gray-400 w-10 text-center tabular-nums select-none"
661
+ >100%</span
662
+ >
663
+ <button id="btnZoomIn" class="tool-btn" data-i18n-title="diagram.toolbar.zoom_in">
664
+ +
665
+ </button>
666
+ <button id="btnZoomReset" class="tool-btn" data-i18n-title="diagram.toolbar.fit">
667
+
668
+ </button>
669
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
670
+
671
+ <button id="btnDark" class="tool-btn">
672
+ <span id="darkIcon">☽</span>
673
+ </button>
674
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
675
+
676
+ <button
677
+ id="btnDebug"
678
+ class="hidden tool-btn text-xs font-mono"
679
+ data-i18n-title="diagram.toolbar.debug"
680
+ >
681
+ dbg
682
+ </button>
683
+ <div
684
+ id="sepDebug"
685
+ class="hidden w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"
686
+ ></div>
687
+
688
+ <button
689
+ id="btnExportDrawio"
690
+ class="tool-btn"
691
+ data-i18n-title="diagram.toolbar.export_drawio"
692
+ >
693
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
694
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
695
+ <polyline points="7 10 12 15 17 10"/>
696
+ <line x1="12" y1="15" x2="12" y2="3"/>
697
+ </svg>
698
+ </button>
699
+ <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
700
+
701
+ <button
702
+ id="btnSave"
703
+ disabled
704
+ class="px-3 py-1.5 text-xs font-semibold rounded-lg bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-40 disabled:cursor-not-allowed shrink-0 transition-colors"
705
+ >
706
+ <span data-i18n="diagram.toolbar.save">Save</span>
707
+ </button>
708
+ </header>
709
+
710
+ <!-- ── Body ── -->
711
+ <div class="flex flex-1 overflow-hidden">
712
+ <!-- Sidebar -->
713
+ <div
714
+ id="sidebar"
715
+ class="w-56 shrink-0 border-r border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 flex flex-col overflow-hidden"
716
+ style="transition: width 0.2s ease"
717
+ >
718
+ <div
719
+ class="flex items-center justify-between px-3 py-2 border-b border-gray-200 dark:border-gray-700 shrink-0"
720
+ >
721
+ <span
722
+ class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider"
723
+ data-i18n="diagram.sidebar.title">Diagrams</span
724
+ >
725
+ <button
726
+ id="btnNewDiagram"
727
+ class="text-xs font-medium text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 transition-colors"
728
+ data-i18n="diagram.sidebar.new_btn"
729
+ >
730
+ + New
731
+ </button>
732
+ </div>
733
+ <div id="diagramList" class="flex-1 overflow-y-auto py-1"></div>
734
+ </div>
735
+
736
+ <!-- Canvas area -->
737
+ <div class="relative flex-1 overflow-hidden bg-gray-50 dark:bg-gray-950">
738
+ <div id="vis-canvas" class="w-full h-full"></div>
739
+
740
+ <div id="customShapeBar" class="hidden">
741
+ <div id="customShapeBarBody"></div>
742
+ </div>
743
+
744
+ <!-- Debug overlay layer -->
745
+ <div id="debugLayer"></div>
746
+
747
+ <!-- Evidence markers layer -->
748
+ <div id="evidenceLayer" class="hidden"></div>
749
+
750
+ <!-- Stamp overlay: intercepts canvas clicks during stamp mode -->
751
+ <div id="stampOverlay" style="position:absolute;inset:0;display:none;z-index:11;cursor:crosshair;"></div>
752
+
753
+ <!-- Selection / resize overlay -->
754
+ <div id="selectionOverlay">
755
+ <div
756
+ id="rh-tl"
757
+ class="resize-handle"
758
+ style="top: -5px; left: -5px; cursor: nw-resize"
759
+ ></div>
760
+ <div
761
+ id="rh-tr"
762
+ class="resize-handle"
763
+ style="top: -5px; right: -5px; cursor: ne-resize"
764
+ ></div>
765
+ <div
766
+ id="rh-bl"
767
+ class="resize-handle"
768
+ style="bottom: -5px; left: -5px; cursor: sw-resize"
769
+ ></div>
770
+ <div
771
+ id="rh-br"
772
+ class="resize-handle"
773
+ style="bottom: -5px; right: -5px; cursor: se-resize"
774
+ ></div>
775
+ <div id="rh-rotate" data-i18n-title="diagram.selection.rotate_shape">↻</div>
776
+ <div id="rh-label-rotate" data-i18n-title="diagram.selection.rotate_text" style="left: 0; top: -28px;">
777
+ <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
778
+ <g transform="rotate(25,5,5)">
779
+ <line x1="2.5" y1="3" x2="7.5" y2="3"/>
780
+ <line x1="5" y1="3" x2="5" y2="8"/>
781
+ </g>
782
+ </svg>
783
+ </div>
784
+ </div>
785
+
786
+ <!-- Evidence panel -->
787
+ <aside
788
+ id="evidencePanel"
789
+ class="hidden absolute top-0 right-0 bottom-0 w-80 max-w-[85vw] z-30 bg-gray-50 dark:bg-gray-900 border-l border-gray-200 dark:border-gray-700 shadow-xl flex flex-col"
790
+ >
791
+ <div class="flex items-center justify-between gap-2 px-4 py-3 border-b border-gray-200 dark:border-gray-700">
792
+ <h2
793
+ id="evidencePanelTitle"
794
+ class="text-sm font-semibold text-gray-800 dark:text-gray-100"
795
+ data-i18n="diagram.evidence.panel_title"
796
+ >Sources</h2>
797
+ <button
798
+ id="btnEvidenceClose"
799
+ class="tool-btn !w-7 !h-7"
800
+ data-i18n-title="diagram.evidence.close"
801
+ >×</button>
802
+ </div>
803
+ <div id="evidencePanelBody" class="flex-1 overflow-y-auto p-3 space-y-3"></div>
804
+ </aside>
805
+
806
+ <!-- Node panel -->
807
+ <div id="nodePanel" class="float-panel hidden">
808
+ <button
809
+ id="btnNodeLock"
810
+ class="tool-btn !w-6 !h-6 text-sm"
811
+ data-i18n-title="diagram.node_panel.lock"
812
+ >🔒</button>
813
+ <div class="panel-sep"></div>
814
+ <div id="nodePanelControls" class="contents">
815
+ <div id="nodePaletteContainer" class="contents"></div>
816
+ <div class="panel-sep"></div>
817
+ <input
818
+ id="nodeBgOpacity"
819
+ type="range"
820
+ min="0"
821
+ max="100"
822
+ step="5"
823
+ value="100"
824
+ class="w-16 h-1 accent-orange-500 cursor-pointer"
825
+ data-i18n-title="diagram.node_panel.bg_opacity"
826
+ />
827
+ <div class="panel-sep"></div>
828
+ <button
829
+ id="btnNodeLabelEdit"
830
+ class="tool-btn !w-6 !h-6"
831
+ data-i18n-title="diagram.node_panel.edit_label"
832
+ >
833
+
834
+ </button>
835
+ <button
836
+ id="btnNodeLink"
837
+ class="tool-btn !w-6 !h-6"
838
+ data-i18n-title="diagram.node_panel.edit_link"
839
+ >
840
+ <svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
841
+ <path d="M5 7.5a3.5 3.5 0 0 0 5 0l1.5-1.5a3.5 3.5 0 0 0-5-5L5 2.5"/>
842
+ <path d="M8 5.5a3.5 3.5 0 0 0-5 0L1.5 7a3.5 3.5 0 0 0 5 5L8 10.5"/>
843
+ </svg>
844
+ </button>
845
+ <div class="panel-sep"></div>
846
+ <button
847
+ id="btnNodeFontDecrease"
848
+ class="tool-btn !w-8 !h-6"
849
+ style="font-size: 10px"
850
+ data-i18n-title="diagram.node_panel.font_decrease"
851
+ >
852
+ Aa−
853
+ </button>
854
+ <span
855
+ id="nodeFontSizeValue"
856
+ class="inline-flex items-center justify-center min-w-[2.25rem] h-6 px-1 text-[11px] font-mono text-gray-700 dark:text-gray-200 border border-gray-300 dark:border-gray-600 rounded bg-white/80 dark:bg-gray-800/80"
857
+ data-i18n-title="diagram.node_panel.font_size_value"
858
+ >13</span>
859
+ <button
860
+ id="btnNodeFontIncrease"
861
+ class="tool-btn !w-8 !h-6"
862
+ style="font-size: 10px"
863
+ data-i18n-title="diagram.node_panel.font_increase"
864
+ >
865
+ Aa+
866
+ </button>
867
+ <div class="panel-sep"></div>
868
+ <!-- Horizontal align -->
869
+ <button
870
+ id="btnAlignLeft"
871
+ class="tool-btn !w-6 !h-6"
872
+ data-i18n-title="diagram.node_panel.align_left"
873
+ >
874
+ <svg
875
+ width="12"
876
+ height="10"
877
+ viewBox="0 0 12 10"
878
+ fill="none"
879
+ stroke="currentColor"
880
+ stroke-width="1.4"
881
+ stroke-linecap="round"
882
+ >
883
+ <line x1="1" y1="2" x2="11" y2="2" />
884
+ <line x1="1" y1="5" x2="7" y2="5" />
885
+ <line x1="1" y1="8" x2="9" y2="8" />
886
+ </svg>
887
+ </button>
888
+ <button
889
+ id="btnAlignCenter"
890
+ class="tool-btn !w-6 !h-6"
891
+ data-i18n-title="diagram.node_panel.align_center"
892
+ >
893
+ <svg
894
+ width="12"
895
+ height="10"
896
+ viewBox="0 0 12 10"
897
+ fill="none"
898
+ stroke="currentColor"
899
+ stroke-width="1.4"
900
+ stroke-linecap="round"
901
+ >
902
+ <line x1="1" y1="2" x2="11" y2="2" />
903
+ <line x1="2.5" y1="5" x2="9.5" y2="5" />
904
+ <line x1="1.5" y1="8" x2="10.5" y2="8" />
905
+ </svg>
906
+ </button>
907
+ <button
908
+ id="btnAlignRight"
909
+ class="tool-btn !w-6 !h-6"
910
+ data-i18n-title="diagram.node_panel.align_right"
911
+ >
912
+ <svg
913
+ width="12"
914
+ height="10"
915
+ viewBox="0 0 12 10"
916
+ fill="none"
917
+ stroke="currentColor"
918
+ stroke-width="1.4"
919
+ stroke-linecap="round"
920
+ >
921
+ <line x1="1" y1="2" x2="11" y2="2" />
922
+ <line x1="5" y1="5" x2="11" y2="5" />
923
+ <line x1="3" y1="8" x2="11" y2="8" />
924
+ </svg>
925
+ </button>
926
+ <div class="panel-sep"></div>
927
+ <!-- Vertical align -->
928
+ <button
929
+ id="btnValignTop"
930
+ class="tool-btn !w-6 !h-6"
931
+ data-i18n-title="diagram.node_panel.align_top"
932
+ >
933
+ <svg
934
+ width="10"
935
+ height="12"
936
+ viewBox="0 0 10 12"
937
+ fill="none"
938
+ stroke="currentColor"
939
+ stroke-linecap="round"
940
+ >
941
+ <line x1="1" y1="1.5" x2="9" y2="1.5" stroke-width="2" />
942
+ <line x1="1" y1="5" x2="8" y2="5" stroke-width="1.3" />
943
+ <line x1="1" y1="8.5" x2="6" y2="8.5" stroke-width="1.3" />
944
+ </svg>
945
+ </button>
946
+ <button
947
+ id="btnValignMiddle"
948
+ class="tool-btn !w-6 !h-6"
949
+ data-i18n-title="diagram.node_panel.align_middle"
950
+ >
951
+ <svg
952
+ width="10"
953
+ height="12"
954
+ viewBox="0 0 10 12"
955
+ fill="none"
956
+ stroke="currentColor"
957
+ stroke-linecap="round"
958
+ >
959
+ <line x1="1" y1="2.5" x2="8" y2="2.5" stroke-width="1.3" />
960
+ <line x1="1" y1="6" x2="9" y2="6" stroke-width="2" />
961
+ <line x1="1" y1="9.5" x2="6" y2="9.5" stroke-width="1.3" />
962
+ </svg>
963
+ </button>
964
+ <button
965
+ id="btnValignBottom"
966
+ class="tool-btn !w-6 !h-6"
967
+ data-i18n-title="diagram.node_panel.align_bottom"
968
+ >
969
+ <svg
970
+ width="10"
971
+ height="12"
972
+ viewBox="0 0 10 12"
973
+ fill="none"
974
+ stroke="currentColor"
975
+ stroke-linecap="round"
976
+ >
977
+ <line x1="1" y1="3.5" x2="8" y2="3.5" stroke-width="1.3" />
978
+ <line x1="1" y1="7" x2="6" y2="7" stroke-width="1.3" />
979
+ <line x1="1" y1="10.5" x2="9" y2="10.5" stroke-width="2" />
980
+ </svg>
981
+ </button>
982
+ <div class="panel-sep"></div>
983
+ <div id="customShapeLabelPlacementControls" class="hidden">
984
+ <button
985
+ class="tool-btn !w-7 !h-6 font-mono text-[10px]"
986
+ data-label-placement="above"
987
+ data-i18n-title="diagram.node_panel.custom_label_above"
988
+ >T↑</button>
989
+ <button
990
+ class="tool-btn !w-7 !h-6 font-mono text-[10px]"
991
+ data-label-placement="below"
992
+ data-i18n-title="diagram.node_panel.custom_label_below"
993
+ >T↓</button>
994
+ <button
995
+ class="tool-btn !w-7 !h-6 font-mono text-[10px]"
996
+ data-label-placement="left"
997
+ data-i18n-title="diagram.node_panel.custom_label_left"
998
+ >←T</button>
999
+ <button
1000
+ class="tool-btn !w-7 !h-6 font-mono text-[10px]"
1001
+ data-label-placement="right"
1002
+ data-i18n-title="diagram.node_panel.custom_label_right"
1003
+ >T→</button>
1004
+ <button
1005
+ class="tool-btn !w-7 !h-6 font-mono text-[10px]"
1006
+ data-label-placement="center"
1007
+ data-i18n-title="diagram.node_panel.custom_label_center"
1008
+ >T□</button>
1009
+ <div class="panel-sep"></div>
1010
+ </div>
1011
+ <button
1012
+ id="btnZOrderBack"
1013
+ class="tool-btn !w-7 !h-6"
1014
+ data-i18n-title="diagram.node_panel.send_back"
1015
+ >
1016
+ <svg
1017
+ width="14"
1018
+ height="14"
1019
+ viewBox="0 0 14 14"
1020
+ fill="none"
1021
+ stroke="currentColor"
1022
+ stroke-width="1.4"
1023
+ stroke-linecap="round"
1024
+ stroke-linejoin="round"
1025
+ >
1026
+ <rect x="1" y="5" width="8" height="8" rx="1" />
1027
+ <rect
1028
+ x="5"
1029
+ y="1"
1030
+ width="8"
1031
+ height="8"
1032
+ rx="1"
1033
+ stroke-opacity="0.35"
1034
+ />
1035
+ </svg>
1036
+ </button>
1037
+ <button
1038
+ id="btnZOrderFront"
1039
+ class="tool-btn !w-7 !h-6"
1040
+ data-i18n-title="diagram.node_panel.bring_front"
1041
+ >
1042
+ <svg
1043
+ width="14"
1044
+ height="14"
1045
+ viewBox="0 0 14 14"
1046
+ fill="none"
1047
+ stroke="currentColor"
1048
+ stroke-width="1.4"
1049
+ stroke-linecap="round"
1050
+ stroke-linejoin="round"
1051
+ >
1052
+ <rect
1053
+ x="1"
1054
+ y="5"
1055
+ width="8"
1056
+ height="8"
1057
+ rx="1"
1058
+ stroke-opacity="0.35"
1059
+ />
1060
+ <rect x="5" y="1" width="8" height="8" rx="1" />
1061
+ </svg>
1062
+ </button>
1063
+
1064
+ <div class="panel-sep"></div>
1065
+
1066
+ <!-- Stamp: copy color (goutte) -->
1067
+ <button
1068
+ id="btnStampColor"
1069
+ class="tool-btn !w-7 !h-6"
1070
+ data-i18n-title="diagram.node_panel.stamp_color"
1071
+ >
1072
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
1073
+ <path d="M7 2 C7 2 3 6.5 3 9 a4 4 0 0 0 8 0 C11 6.5 7 2 7 2 Z" fill="currentColor" fill-opacity="0.25"/>
1074
+ </svg>
1075
+ </button>
1076
+
1077
+ <!-- Stamp: copy font size -->
1078
+ <button
1079
+ id="btnStampFontSize"
1080
+ class="tool-btn !w-7 !h-6 font-mono text-xs font-bold"
1081
+ data-i18n-title="diagram.node_panel.stamp_font"
1082
+ >Aa</button>
1083
+
1084
+ <!-- Stamp: copy size -->
1085
+ <button
1086
+ id="btnStampSize"
1087
+ class="tool-btn !w-7 !h-6"
1088
+ data-i18n-title="diagram.node_panel.stamp_size"
1089
+ >
1090
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
1091
+ <rect x="2" y="4" width="6" height="6" rx="0.8" fill="currentColor" fill-opacity="0.25"/>
1092
+ <path d="M10 2 L12 2 L12 4"/>
1093
+ <path d="M12 2 L9 5"/>
1094
+ <path d="M10 12 L12 12 L12 10"/>
1095
+ <path d="M12 12 L9 9"/>
1096
+ </svg>
1097
+ </button>
1098
+
1099
+ <div class="panel-sep"></div>
1100
+
1101
+ <!-- Rotation anti-horaire 10° -->
1102
+ <button
1103
+ id="btnRotateCCW"
1104
+ class="tool-btn !w-7 !h-6"
1105
+ data-i18n-title="diagram.node_panel.rotate_ccw"
1106
+ style="font-size:15px; line-height:1;"
1107
+ >↺</button>
1108
+
1109
+ <!-- Rotation horaire 10° -->
1110
+ <button
1111
+ id="btnRotateCW"
1112
+ class="tool-btn !w-7 !h-6"
1113
+ data-i18n-title="diagram.node_panel.rotate_cw"
1114
+ style="font-size:15px; line-height:1;"
1115
+ >↻</button>
1116
+
1117
+ <div class="panel-sep"></div>
1118
+
1119
+ <!-- Copy as PNG -->
1120
+ <button
1121
+ id="btnCopyPng"
1122
+ class="tool-btn !h-6 px-1.5 font-mono text-xs font-semibold flex items-center gap-0.5"
1123
+ data-i18n-title="diagram.node_panel.copy_png"
1124
+ >
1125
+ <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
1126
+ <line x1="5" y1="7" x2="5" y2="1"/>
1127
+ <polyline points="2,4 5,1 8,4"/>
1128
+ <line x1="1" y1="9" x2="9" y2="9"/>
1129
+ </svg>
1130
+ PNG
1131
+ </button>
1132
+
1133
+ <div class="panel-sep"></div>
1134
+
1135
+ <!-- Group -->
1136
+ <button
1137
+ id="btnGroup"
1138
+ class="tool-btn !w-8 !h-7"
1139
+ data-i18n-title="diagram.node_panel.group"
1140
+ >
1141
+ <svg width="22" height="14" viewBox="0 0 22 14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round">
1142
+ <rect x="0.7" y="1" width="20.6" height="12" rx="2" stroke-dasharray="2.5,1.5"/>
1143
+ <rect x="4" y="4.5" width="3" height="5" rx="1" fill="currentColor"/>
1144
+ <rect x="15" y="4.5" width="3" height="5" rx="1" fill="currentColor"/>
1145
+ </svg>
1146
+ </button>
1147
+
1148
+ <!-- Ungroup -->
1149
+ <button
1150
+ id="btnUngroup"
1151
+ class="tool-btn !w-8 !h-7"
1152
+ data-i18n-title="diagram.node_panel.ungroup"
1153
+ >
1154
+ <svg width="26" height="14" viewBox="0 0 26 14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round">
1155
+ <rect x="0.7" y="1" width="9" height="12" rx="2" stroke-dasharray="2.5,1.5"/>
1156
+ <rect x="3" y="4.5" width="3" height="5" rx="1" fill="currentColor"/>
1157
+ <rect x="16.3" y="1" width="9" height="12" rx="2" stroke-dasharray="2.5,1.5"/>
1158
+ <rect x="19" y="4.5" width="3" height="5" rx="1" fill="currentColor"/>
1159
+ </svg>
1160
+ </button>
1161
+ </div><!-- /nodePanelControls -->
1162
+ </div>
1163
+
1164
+ <!-- Link panel -->
1165
+ <div id="linkPanel" class="float-panel hidden" style="min-width:260px; flex-direction:column; align-items:stretch; gap:0.5rem; padding:0.75rem;">
1166
+ <div class="text-xs font-semibold text-gray-500 dark:text-gray-400 mb-1" data-i18n="diagram.link_panel.title">Link on this shape</div>
1167
+
1168
+ <label class="flex items-center gap-2 text-xs cursor-pointer">
1169
+ <input type="radio" name="linkType" id="linkTypeUrl" value="url" class="accent-orange-500"/>
1170
+ <span data-i18n="diagram.link_panel.external">External URL</span>
1171
+ </label>
1172
+ <div id="linkUrlRow">
1173
+ <input id="linkUrlInput" type="url" data-i18n-placeholder="diagram.link_panel.url_placeholder" placeholder="https://…"
1174
+ class="w-full text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-1 focus:ring-orange-400"/>
1175
+ </div>
1176
+
1177
+ <label class="flex items-center gap-2 text-xs cursor-pointer">
1178
+ <input type="radio" name="linkType" id="linkTypeDiagram" value="diagram" class="accent-orange-500"/>
1179
+ <span data-i18n="diagram.link_panel.existing_diagram">Existing diagram</span>
1180
+ </label>
1181
+ <div id="linkDiagramRow" class="hidden">
1182
+ <select id="linkDiagramSelect"
1183
+ class="w-full text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-1 focus:ring-orange-400">
1184
+ </select>
1185
+ </div>
1186
+
1187
+ <label class="flex items-center gap-2 text-xs cursor-pointer">
1188
+ <input type="radio" name="linkType" id="linkTypeNew" value="new" class="accent-orange-500"/>
1189
+ <span data-i18n="diagram.link_panel.new_diagram">New diagram</span>
1190
+ </label>
1191
+ <div id="linkNewRow" class="hidden">
1192
+ <input id="linkNewName" type="text" data-i18n-placeholder="diagram.link_panel.diagram_name_placeholder" placeholder="Diagram name…"
1193
+ class="w-full text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-1 focus:ring-orange-400"/>
1194
+ </div>
1195
+
1196
+ <div class="flex gap-1 mt-1">
1197
+ <button id="btnLinkSave" class="flex-1 text-xs bg-orange-500 hover:bg-orange-600 text-white rounded px-2 py-1" data-i18n="diagram.link_panel.save_btn">Save</button>
1198
+ <button id="btnLinkRemove" class="text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 hover:bg-red-50 dark:hover:bg-red-900/20 hover:text-red-600" data-i18n="diagram.link_panel.remove_btn">Remove</button>
1199
+ <button id="btnLinkCancel" class="text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 hover:bg-gray-100 dark:hover:bg-gray-700">✕</button>
1200
+ </div>
1201
+ </div>
1202
+
1203
+ <!-- Edge panel -->
1204
+ <div id="edgePanel" class="float-panel hidden">
1205
+ <button
1206
+ id="btnEdgeLock"
1207
+ class="tool-btn !w-6 !h-6 text-sm"
1208
+ data-i18n-title="diagram.edge_panel.lock"
1209
+ >🔒</button>
1210
+ <div class="panel-sep"></div>
1211
+ <div id="edgePanelControls" class="contents">
1212
+ <button
1213
+ id="edgeBtnNone"
1214
+ class="tool-btn !w-7 !h-6 font-mono text-xs"
1215
+ data-i18n-title="diagram.edge_panel.no_arrow"
1216
+ >
1217
+
1218
+ </button>
1219
+ <button
1220
+ id="edgeBtnFrom"
1221
+ class="tool-btn !w-7 !h-6 text-xs"
1222
+ data-i18n-title="diagram.edge_panel.arrow_from"
1223
+ >
1224
+
1225
+ </button>
1226
+ <button
1227
+ id="edgeBtnTo"
1228
+ class="tool-btn !w-7 !h-6 text-xs"
1229
+ data-i18n-title="diagram.edge_panel.arrow_to"
1230
+ >
1231
+
1232
+ </button>
1233
+ <button
1234
+ id="edgeBtnBoth"
1235
+ class="tool-btn !w-8 !h-6 text-xs"
1236
+ data-i18n-title="diagram.edge_panel.arrow_both"
1237
+ >
1238
+ ←→
1239
+ </button>
1240
+ <div class="panel-sep"></div>
1241
+ <button
1242
+ id="edgeBtnSolid"
1243
+ class="tool-btn !w-8 !h-6"
1244
+ data-i18n-title="diagram.edge_panel.solid"
1245
+ >
1246
+ <svg width="22" height="4" viewBox="0 0 22 4">
1247
+ <line
1248
+ x1="1"
1249
+ y1="2"
1250
+ x2="21"
1251
+ y2="2"
1252
+ stroke="currentColor"
1253
+ stroke-width="2"
1254
+ stroke-linecap="round"
1255
+ />
1256
+ </svg>
1257
+ </button>
1258
+ <button
1259
+ id="edgeBtnDashed"
1260
+ class="tool-btn !w-8 !h-6"
1261
+ data-i18n-title="diagram.edge_panel.dashed"
1262
+ >
1263
+ <svg width="22" height="4" viewBox="0 0 22 4">
1264
+ <line
1265
+ x1="1"
1266
+ y1="2"
1267
+ x2="21"
1268
+ y2="2"
1269
+ stroke="currentColor"
1270
+ stroke-width="2"
1271
+ stroke-linecap="round"
1272
+ stroke-dasharray="4,3"
1273
+ />
1274
+ </svg>
1275
+ </button>
1276
+ <div class="panel-sep"></div>
1277
+ <button
1278
+ id="btnEdgeFontDecrease"
1279
+ class="tool-btn !w-8 !h-6"
1280
+ style="font-size: 10px"
1281
+ data-i18n-title="diagram.edge_panel.font_decrease"
1282
+ >
1283
+ Aa−
1284
+ </button>
1285
+ <button
1286
+ id="btnEdgeFontIncrease"
1287
+ class="tool-btn !w-8 !h-6"
1288
+ style="font-size: 10px"
1289
+ data-i18n-title="diagram.edge_panel.font_increase"
1290
+ >
1291
+ Aa+
1292
+ </button>
1293
+ <div class="panel-sep"></div>
1294
+ <button
1295
+ id="btnEdgeLabelEdit"
1296
+ class="tool-btn !w-6 !h-6"
1297
+ data-i18n-title="diagram.edge_panel.edit_label"
1298
+ >
1299
+
1300
+ </button>
1301
+ <button
1302
+ id="btnEdgeLabelWidthReset"
1303
+ class="tool-btn !w-6 !h-6 text-xs"
1304
+ data-i18n-title="diagram.edge_panel.reset_label_width"
1305
+ >
1306
+
1307
+ </button>
1308
+ <div class="panel-sep"></div>
1309
+ <button
1310
+ id="btnEdgeLabelRotateCCW"
1311
+ class="tool-btn !w-7 !h-6 text-sm"
1312
+ data-i18n-title="diagram.edge_panel.rotate_label_ccw"
1313
+ >
1314
+
1315
+ </button>
1316
+ <button
1317
+ id="btnEdgeLabelRotateCW"
1318
+ class="tool-btn !w-7 !h-6 text-sm"
1319
+ data-i18n-title="diagram.edge_panel.rotate_label_cw"
1320
+ >
1321
+
1322
+ </button>
1323
+ <div class="panel-sep"></div>
1324
+ <button
1325
+ id="btnEdgeLabelOffsetLeft"
1326
+ class="tool-btn !w-6 !h-6 text-sm"
1327
+ data-i18n-title="diagram.edge_panel.offset_label_left"
1328
+ >←</button>
1329
+ <button
1330
+ id="btnEdgeLabelOffsetRight"
1331
+ class="tool-btn !w-6 !h-6 text-sm"
1332
+ data-i18n-title="diagram.edge_panel.offset_label_right"
1333
+ >→</button>
1334
+ <button
1335
+ id="btnEdgeLabelOffsetUp"
1336
+ class="tool-btn !w-6 !h-6 text-sm"
1337
+ data-i18n-title="diagram.edge_panel.offset_label_up"
1338
+ >↑</button>
1339
+ <button
1340
+ id="btnEdgeLabelOffsetDown"
1341
+ class="tool-btn !w-6 !h-6 text-sm"
1342
+ data-i18n-title="diagram.edge_panel.offset_label_down"
1343
+ >↓</button>
1344
+ <button
1345
+ id="btnEdgeLabelOffsetReset"
1346
+ class="tool-btn !w-6 !h-6 text-xs"
1347
+ data-i18n-title="diagram.edge_panel.offset_label_reset"
1348
+ >⊙</button>
1349
+ <div class="panel-sep"></div>
1350
+ <button id="btnEdgeWidthDecrease" class="tool-btn !w-7 !h-6" style="font-size:10px" data-i18n-title="diagram.edge_panel.width_decrease">W−</button>
1351
+ <button id="btnEdgeWidthIncrease" class="tool-btn !w-7 !h-6" style="font-size:10px" data-i18n-title="diagram.edge_panel.width_increase">W+</button>
1352
+ <div class="panel-sep"></div>
1353
+ <div id="edgePaletteContainer" class="contents"></div>
1354
+ <div class="panel-sep"></div>
1355
+ <button
1356
+ id="btnEdgeClearPorts"
1357
+ class="tool-btn !w-8 !h-6 text-xs"
1358
+ data-i18n-title="diagram.edge_panel.clear_ports"
1359
+ >
1360
+
1361
+ </button>
1362
+ </div><!-- /edgePanelControls -->
1363
+ </div>
1364
+
1365
+ <!-- Floating label textarea -->
1366
+ <textarea
1367
+ id="labelInput"
1368
+ class="hidden"
1369
+ rows="1"
1370
+ data-i18n-placeholder="diagram.label_input.placeholder" placeholder="Label…"
1371
+ ></textarea>
1372
+
1373
+ <!-- Empty state -->
1374
+ <div
1375
+ id="emptyState"
1376
+ class="absolute inset-0 flex flex-col items-center justify-center text-gray-400 dark:text-gray-600 pointer-events-none select-none"
1377
+ >
1378
+ <svg
1379
+ width="52"
1380
+ height="44"
1381
+ viewBox="0 0 52 44"
1382
+ fill="none"
1383
+ stroke="currentColor"
1384
+ stroke-width="1.5"
1385
+ class="mb-3 opacity-50"
1386
+ >
1387
+ <rect x="2" y="4" width="18" height="12" rx="2" />
1388
+ <rect x="32" y="28" width="18" height="12" rx="2" />
1389
+ <line x1="20" y1="10" x2="32" y2="34" stroke-dasharray="4,3" />
1390
+ <rect x="17" y="16" width="18" height="12" rx="2" />
1391
+ <line x1="26" y1="16" x2="26" y2="10" stroke-dasharray="4,3" />
1392
+ </svg>
1393
+ <p class="text-sm" data-i18n="diagram.empty_state">Select or create a diagram</p>
1394
+ </div>
1395
+ </div>
1396
+ </div>
1397
+
1398
+ <div id="toastContainer"></div>
1399
+
1400
+ <!-- Image name modal -->
1401
+ <div id="imageNameModal" style="display:none;position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,0.5);"
1402
+ class="flex items-center justify-center">
1403
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow-2xl p-6 w-80 flex flex-col gap-3">
1404
+ <p class="text-sm font-semibold text-gray-700 dark:text-gray-200" data-i18n="diagram.image_modal.title">Image filename</p>
1405
+ <p class="text-xs text-gray-400 dark:text-gray-500">
1406
+ Lettres, chiffres, <code class="bg-gray-100 dark:bg-gray-700 px-0.5 rounded">_</code> et
1407
+ <code class="bg-gray-100 dark:bg-gray-700 px-0.5 rounded">-</code> uniquement.
1408
+ Laisser vide pour un nom automatique.
1409
+ </p>
1410
+ <div class="flex items-center gap-1">
1411
+ <input id="imageNameInput" type="text" autocomplete="off" spellcheck="false"
1412
+ class="flex-1 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700
1413
+ text-gray-900 dark:text-gray-100 text-sm px-2 py-1 outline-none focus:ring-2 focus:ring-blue-400"
1414
+ data-i18n-placeholder="diagram.image_modal.placeholder" placeholder="image_name" />
1415
+ <span class="text-xs text-gray-400 dark:text-gray-500 whitespace-nowrap">.png</span>
1416
+ </div>
1417
+ <p id="imageNameError" class="text-xs text-red-500 hidden" data-i18n="diagram.image_modal.error_chars">Allowed characters: letters, numbers, _ and -</p>
1418
+ <div class="flex gap-2 justify-end">
1419
+ <button id="imageNameCancel"
1420
+ class="px-3 py-1 text-xs rounded border border-gray-300 dark:border-gray-600
1421
+ text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
1422
+ data-i18n="diagram.image_modal.cancel_btn">
1423
+ Cancel
1424
+ </button>
1425
+ <button id="imageNameConfirm"
1426
+ class="px-3 py-1 text-xs rounded bg-blue-500 text-white hover:bg-blue-600"
1427
+ data-i18n="diagram.image_modal.paste_btn">
1428
+ Paste
1429
+ </button>
1430
+ </div>
1431
+ </div>
1432
+ </div>
1433
+
1434
+ <script type="module" src="/diagram/main.js"></script>
1435
+ <script type="module">
1436
+ import { NODE_COLORS, NODE_L_RATIOS, DEFAULT_NODE_PALETTE, DEFAULT_EDGE_PALETTE, deriveNodeColors } from '/diagram/constants.js';
1437
+ import { st } from '/diagram/state.js';
1438
+ import { loadDiagramList } from '/diagram/persistence.js';
1439
+ import { loadCustomShapeLibraries, renderCustomShapeBar } from '/diagram/custom-shapes.js';
1440
+ (async () => {
1441
+ // diagramNodePalette: array of 15 bg hex strings (positional, matching DEFAULT_NODE_PALETTE)
1442
+ // diagramEdgePalette: array of hex strings
1443
+ let nodeBgOverrides = null; // array of 15 hex | null
1444
+ let edgePalette = DEFAULT_EDGE_PALETTE;
1445
+ try {
1446
+ const cfg = await fetch('/api/config').then((r) => r.json());
1447
+ await window.initI18n(cfg.language);
1448
+ if (Array.isArray(cfg.diagramNodePalette) && cfg.diagramNodePalette.length) nodeBgOverrides = cfg.diagramNodePalette;
1449
+ if (Array.isArray(cfg.diagramEdgePalette) && cfg.diagramEdgePalette.length) edgePalette = cfg.diagramEdgePalette;
1450
+ } catch { await window.initI18n('en'); /* config unavailable — use defaults */ }
1451
+
1452
+ // Apply node color overrides to shared state so renderers pick them up.
1453
+ DEFAULT_NODE_PALETTE.forEach((key, i) => {
1454
+ const customBg = nodeBgOverrides && nodeBgOverrides[i];
1455
+ if (customBg && customBg !== NODE_COLORS[key]?.bg) {
1456
+ st.nodeColorOverrides[key] = deriveNodeColors(customBg, NODE_L_RATIOS[key] || 0.60);
1457
+ }
1458
+ });
1459
+
1460
+ // Build node palette buttons.
1461
+ const nc = document.getElementById('nodePaletteContainer');
1462
+ DEFAULT_NODE_PALETTE.forEach((key, i) => {
1463
+ if (!nc) return;
1464
+ const customBg = nodeBgOverrides && nodeBgOverrides[i];
1465
+ const c = (customBg ? deriveNodeColors(customBg, NODE_L_RATIOS[key] || 0.60) : null) || NODE_COLORS[key];
1466
+ const btn = document.createElement('button');
1467
+ btn.dataset.color = key;
1468
+ btn.className = 'w-5 h-5 rounded-full border-2 hover:scale-125 transition-transform';
1469
+ btn.style.background = c.bg;
1470
+ btn.style.borderColor = c.border;
1471
+ btn.title = key.replace('c-', '');
1472
+ nc.appendChild(btn);
1473
+ });
1474
+
1475
+ // Build edge palette buttons.
1476
+ const ec = document.getElementById('edgePaletteContainer');
1477
+ edgePalette.forEach((hex) => {
1478
+ if (!ec) return;
1479
+ const btn = document.createElement('button');
1480
+ btn.dataset.edgeColor = hex;
1481
+ btn.className = 'edge-color-btn w-4 h-4 rounded-full hover:scale-125 transition-transform';
1482
+ btn.style.background = hex;
1483
+ btn.style.border = '2px solid rgba(0,0,0,0.2)';
1484
+ btn.title = hex;
1485
+ ec.appendChild(btn);
1486
+ });
1487
+ await loadCustomShapeLibraries();
1488
+ renderCustomShapeBar();
1489
+ window.applyI18n();
1490
+ loadDiagramList();
1491
+ })();
1492
+ </script>
1493
+ </body>
1494
+ </html>