doc-survival-kit 2.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of doc-survival-kit might be problematic. Click here for more details.

@@ -0,0 +1,603 @@
1
+ // ══════════════════════════════════════
2
+ // events.js — Événements souris/clavier, hit-tests, création de flèches et initialisation
3
+ // ══════════════════════════════════════
4
+
5
+ // ── Hit-test ──
6
+ function shapeAt(x, y) {
7
+ var diag = getCurrentDiagram();
8
+ if (!diag) return null;
9
+ for (var i = diag.shapes.length - 1; i >= 0; i--) {
10
+ var s = diag.shapes[i];
11
+ var lx = x, ly = y;
12
+ if (s.rotation) {
13
+ var scx = s.x + s.w / 2, scy = s.y + s.h / 2;
14
+ var rad = -s.rotation * Math.PI / 180;
15
+ var cos = Math.cos(rad), sin = Math.sin(rad);
16
+ var ddx = x - scx, ddy = y - scy;
17
+ lx = scx + ddx * cos - ddy * sin;
18
+ ly = scy + ddx * sin + ddy * cos;
19
+ }
20
+ if (lx >= s.x && lx <= s.x + s.w && ly >= s.y && ly <= s.y + s.h) return s;
21
+ }
22
+ return null;
23
+ }
24
+
25
+ function arrowIdAt(x, y) {
26
+ var groups = document.querySelectorAll(".arrow-group");
27
+ for (var i = 0; i < groups.length; i++) {
28
+ var lines = groups[i].querySelectorAll("line");
29
+ if (lines.length < 2) continue;
30
+ var hit = lines[lines.length - 1];
31
+ var x1 = +hit.getAttribute("x1"), y1 = +hit.getAttribute("y1");
32
+ var x2 = +hit.getAttribute("x2"), y2 = +hit.getAttribute("y2");
33
+ if (distToSeg(x, y, x1, y1, x2, y2) < 8) {
34
+ return groups[i].getAttribute("data-id");
35
+ }
36
+ }
37
+ return null;
38
+ }
39
+
40
+ function distToSeg(px, py, x1, y1, x2, y2) {
41
+ var dx = x2 - x1, dy = y2 - y1;
42
+ var lenSq = dx * dx + dy * dy;
43
+ if (lenSq === 0) return Math.hypot(px - x1, py - y1);
44
+ var t = Math.max(0, Math.min(1, ((px - x1) * dx + (py - y1) * dy) / lenSq));
45
+ return Math.hypot(px - (x1 + t * dx), py - (y1 + t * dy));
46
+ }
47
+
48
+ // ── Créer une flèche ──
49
+ function createArrow(fromId, toId) {
50
+ var diag = getCurrentDiagram();
51
+ if (diag.arrows.some(function (a) { return a.from === fromId && a.to === toId; })) return;
52
+ pushHistory();
53
+ var arrow = { id: "a" + Date.now(), from: fromId, to: toId, label: "" };
54
+ diag.arrows.push(arrow);
55
+ saveDiagrammes();
56
+ selectedId = arrow.id; selectedType = "arrow";
57
+ selectedIds = [];
58
+ renderAll();
59
+ startArrowTextEdit(arrow.id);
60
+ }
61
+
62
+ // ── Événements souris ──
63
+ function onMouseDown(e) {
64
+ if (e.button !== 0) return;
65
+ if (editingShapeId || editingArrowId) { confirmTextEdit(); return; }
66
+
67
+ // Mode verrouillé : pan uniquement
68
+ if (boardLocked) {
69
+ panStart = { cx: e.clientX, cy: e.clientY, px: viewTransform.x, py: viewTransform.y };
70
+ return;
71
+ }
72
+
73
+ var pt = svgPoint(e.clientX, e.clientY);
74
+
75
+ // Mode pick (copie de style)
76
+ if (pickMode) {
77
+ var srcShape = shapeAt(pt.x, pt.y);
78
+ if (srcShape) applyPickMode(srcShape);
79
+ cancelPickMode();
80
+ return;
81
+ }
82
+
83
+ // Clic sur un point de connexion → début de flèche
84
+ var connDot = e.target.closest(".conn-dot");
85
+ if (connDot) {
86
+ arrowSrcId = connDot.getAttribute("data-shape-id");
87
+ var srcShape = getCurrentDiagram().shapes.find(function (s) { return s.id === arrowSrcId; });
88
+ var ta = document.getElementById("tempArrow");
89
+ ta.setAttribute("x1", srcShape.x + srcShape.w / 2);
90
+ ta.setAttribute("y1", srcShape.y + srcShape.h / 2);
91
+ ta.setAttribute("x2", pt.x); ta.setAttribute("y2", pt.y);
92
+ ta.style.display = "";
93
+ return;
94
+ }
95
+
96
+ // Clic sur un chevron de redimensionnement de colonne
97
+ var colSepEl = e.target.closest("[data-col-sep]");
98
+ if (colSepEl) {
99
+ var csColIdx = parseInt(colSepEl.getAttribute("data-col-sep"), 10);
100
+ var csSid = colSepEl.getAttribute("data-shape-id");
101
+ var csShape = getCurrentDiagram().shapes.find(function (s) { return s.id === csSid; });
102
+ if (csShape) {
103
+ var csCw = getColWidths(csShape);
104
+ dragState = { type: "col-resize", id: csSid, colIdx: csColIdx, sx: pt.x, origWidths: csCw.slice(), snapshot: JSON.stringify(diagramsList) };
105
+ }
106
+ return;
107
+ }
108
+
109
+ // Clic sur la poignée de resize
110
+ var grip = e.target.closest(".resize-grip");
111
+ if (grip) {
112
+ var sid = grip.getAttribute("data-shape-id");
113
+ var shape = getCurrentDiagram().shapes.find(function (s) { return s.id === sid; });
114
+ if (shape) {
115
+ dragState = { type: "resize", id: sid, sx: pt.x, sy: pt.y, ow: shape.w, oh: shape.h, snapshot: JSON.stringify(diagramsList) };
116
+ }
117
+ return;
118
+ }
119
+
120
+ var shape = shapeAt(pt.x, pt.y);
121
+
122
+ if (currentTool === "select") {
123
+ if (shape) {
124
+ // Détection du double-clic par horodatage (avant tout renderAll)
125
+ var now = Date.now();
126
+ if (now - lastClickTime < 350 && lastClickShapeId === shape.id) {
127
+ lastClickTime = 0; lastClickShapeId = null;
128
+ if (shape.type === "table") {
129
+ var tDblRows = shape.rows || 3;
130
+ var tDblOffsets = getColOffsets(shape);
131
+ var tDblCw = getColWidths(shape);
132
+ var tDblRx = pt.x - shape.x;
133
+ var tDblC = tDblCw.length - 1;
134
+ for (var tci3 = 0; tci3 < tDblCw.length; tci3++) {
135
+ if (tDblRx < tDblOffsets[tci3] + tDblCw[tci3]) { tDblC = tci3; break; }
136
+ }
137
+ var tDblR = Math.min(tDblRows - 1, Math.max(0, Math.floor((pt.y - shape.y) / (shape.h / tDblRows))));
138
+ startTableCellEdit(shape.id, tDblR, tDblC);
139
+ } else {
140
+ startTextEdit(shape.id);
141
+ }
142
+ return;
143
+ }
144
+ lastClickTime = now; lastClickShapeId = shape.id; lastClickArrowId = null;
145
+
146
+ if (e.shiftKey) {
147
+ // Shift+clic : ajouter/retirer de la multi-sélection
148
+ var idx = selectedIds.indexOf(shape.id);
149
+ if (idx === -1) {
150
+ selectedIds.push(shape.id);
151
+ selectedId = shape.id; selectedType = "shape";
152
+ } else {
153
+ selectedIds.splice(idx, 1);
154
+ selectedId = selectedIds.length > 0 ? selectedIds[selectedIds.length - 1] : null;
155
+ selectedType = selectedId ? "shape" : null;
156
+ }
157
+ document.getElementById("colorPanel").style.display = selectedIds.length > 0 ? "flex" : "none";
158
+ renderAll();
159
+ } else if (selectedIds.indexOf(shape.id) !== -1 && selectedIds.length > 1) {
160
+ // Clic sur une forme déjà dans la multi-sélection → multi-déplacement
161
+ var diag = getCurrentDiagram();
162
+ dragState = {
163
+ type: "multi-move",
164
+ sx: pt.x, sy: pt.y,
165
+ origPositions: selectedIds.map(function (id) {
166
+ var s = diag.shapes.find(function (sh) { return sh.id === id; });
167
+ return { id: id, ox: s ? s.x : 0, oy: s ? s.y : 0 };
168
+ }),
169
+ snapshot: JSON.stringify(diagramsList),
170
+ };
171
+ } else {
172
+ // Clic simple → sélection unique
173
+ selectedIds = [shape.id];
174
+ selectedId = shape.id; selectedType = "shape";
175
+ dragState = { type: "move", id: shape.id, sx: pt.x, sy: pt.y, ox: shape.x, oy: shape.y, snapshot: JSON.stringify(diagramsList) };
176
+ document.getElementById("colorPanel").style.display = "flex";
177
+ renderAll();
178
+ }
179
+ } else {
180
+ var aid = arrowIdAt(pt.x, pt.y);
181
+ if (aid) {
182
+ // Détection du double-clic sur une flèche
183
+ var now2 = Date.now();
184
+ if (now2 - lastClickTime < 350 && lastClickArrowId === aid) {
185
+ lastClickTime = 0; lastClickArrowId = null;
186
+ selectedId = aid; selectedType = "arrow";
187
+ selectedIds = [];
188
+ renderAll();
189
+ startArrowTextEdit(aid);
190
+ return;
191
+ }
192
+ lastClickTime = now2; lastClickArrowId = aid;
193
+
194
+ selectedId = aid; selectedType = "arrow";
195
+ selectedIds = [];
196
+ document.getElementById("colorPanel").style.display = "none";
197
+ renderAll();
198
+ } else if (e.shiftKey) {
199
+ // Shift+drag sur fond vide → lasso de sélection
200
+ rubberBandState = { sx: pt.x, sy: pt.y };
201
+ } else {
202
+ // Pan
203
+ panStart = { cx: e.clientX, cy: e.clientY, px: viewTransform.x, py: viewTransform.y };
204
+ selectedId = null; selectedType = null;
205
+ selectedIds = [];
206
+ document.getElementById("colorPanel").style.display = "none";
207
+ renderAll();
208
+ }
209
+ }
210
+
211
+ } else if (currentTool === "arrow") {
212
+ if (shape && shape.type !== "postit") {
213
+ if (!arrowSrcId) {
214
+ arrowSrcId = shape.id;
215
+ var ta = document.getElementById("tempArrow");
216
+ ta.setAttribute("x1", shape.x + shape.w / 2);
217
+ ta.setAttribute("y1", shape.y + shape.h / 2);
218
+ ta.setAttribute("x2", pt.x); ta.setAttribute("y2", pt.y);
219
+ ta.style.display = "";
220
+ } else if (arrowSrcId !== shape.id) {
221
+ createArrow(arrowSrcId, shape.id);
222
+ arrowSrcId = null;
223
+ document.getElementById("tempArrow").style.display = "none";
224
+ }
225
+ } else {
226
+ arrowSrcId = null;
227
+ document.getElementById("tempArrow").style.display = "none";
228
+ }
229
+
230
+ } else {
231
+ // Outils forme : placer au clic
232
+ addShape(currentTool, pt.x, pt.y);
233
+ setTool("select");
234
+ }
235
+ }
236
+
237
+ function onMouseMove(e) {
238
+ var pt = svgPoint(e.clientX, e.clientY);
239
+
240
+ // Mise à jour de la flèche temporaire
241
+ if (arrowSrcId) {
242
+ var ta = document.getElementById("tempArrow");
243
+ if (ta.style.display !== "none") {
244
+ ta.setAttribute("x2", pt.x); ta.setAttribute("y2", pt.y);
245
+ }
246
+ }
247
+
248
+ if (rubberBandState) {
249
+ var rb = document.getElementById("rubberBand");
250
+ var rbX = Math.min(rubberBandState.sx, pt.x);
251
+ var rbY = Math.min(rubberBandState.sy, pt.y);
252
+ rb.setAttribute("x", rbX); rb.setAttribute("y", rbY);
253
+ rb.setAttribute("width", Math.abs(pt.x - rubberBandState.sx));
254
+ rb.setAttribute("height", Math.abs(pt.y - rubberBandState.sy));
255
+ rb.style.display = "";
256
+ } else if (dragState) {
257
+ dragState.moved = true;
258
+ var diag = getCurrentDiagram();
259
+ if (dragState.type === "multi-move") {
260
+ var dx = pt.x - dragState.sx;
261
+ var dy = pt.y - dragState.sy;
262
+ dragState.origPositions.forEach(function (op) {
263
+ var s = diag.shapes.find(function (sh) { return sh.id === op.id; });
264
+ if (s) { s.x = Math.round(op.ox + dx); s.y = Math.round(op.oy + dy); }
265
+ });
266
+ renderAll();
267
+ } else {
268
+ var shape = diag.shapes.find(function (s) { return s.id === dragState.id; });
269
+ if (!shape) return;
270
+ if (dragState.type === "move") {
271
+ shape.x = Math.round(dragState.ox + pt.x - dragState.sx);
272
+ shape.y = Math.round(dragState.oy + pt.y - dragState.sy);
273
+ } else if (dragState.type === "resize") {
274
+ var newW = Math.max(60, Math.round(dragState.ow + pt.x - dragState.sx));
275
+ var newH = Math.max(30, Math.round(dragState.oh + pt.y - dragState.sy));
276
+ if (shape.type === "table" && shape.colWidths && shape.colWidths.length === (shape.cols || 3)) {
277
+ var oldW2 = shape.w;
278
+ shape.colWidths = shape.colWidths.map(function (cw) { return cw * newW / oldW2; });
279
+ }
280
+ shape.w = newW;
281
+ shape.h = newH;
282
+ } else if (dragState.type === "col-resize") {
283
+ var crCw = dragState.origWidths.slice();
284
+ var crDx = pt.x - dragState.sx;
285
+ var crCi = dragState.colIdx;
286
+ var MIN_COL = 20;
287
+ var crLeft = Math.max(MIN_COL, crCw[crCi] + crDx);
288
+ var crRight = Math.max(MIN_COL, crCw[crCi + 1] - crDx);
289
+ var crTotal = crCw[crCi] + crCw[crCi + 1];
290
+ crLeft = Math.min(crTotal - MIN_COL, crLeft);
291
+ crRight = crTotal - crLeft;
292
+ crCw[crCi] = crLeft;
293
+ crCw[crCi + 1] = crRight;
294
+ shape.colWidths = crCw;
295
+ }
296
+ renderAll();
297
+ }
298
+ } else if (panStart) {
299
+ viewTransform.x = panStart.px + (e.clientX - panStart.cx);
300
+ viewTransform.y = panStart.py + (e.clientY - panStart.cy);
301
+ updateViewport();
302
+ }
303
+ }
304
+
305
+ function onMouseUp(e) {
306
+ var pt = svgPoint(e.clientX, e.clientY);
307
+
308
+ // En mode verrouillé : détecter un clic (sans déplacement) sur une forme liée
309
+ if (boardLocked && panStart) {
310
+ var movedPx = Math.abs(e.clientX - panStart.cx) + Math.abs(e.clientY - panStart.cy);
311
+ if (movedPx < 5 && !e.shiftKey) {
312
+ var shape = shapeAt(pt.x, pt.y);
313
+ if (shape && shape.linkedDiagramId) {
314
+ var target = findDiagramById(shape.linkedDiagramId, diagramsList);
315
+ if (target) {
316
+ hideLinkPicker();
317
+ diagNavStack.push(currentDiagramId);
318
+ if (diagNavStack.length > 30) diagNavStack.shift();
319
+ panStart = null;
320
+ selectDiagramme(shape.linkedDiagramId);
321
+ return;
322
+ }
323
+ } else if (shape && shape.externalUrl) {
324
+ panStart = null;
325
+ window.open(shape.externalUrl, "_blank");
326
+ return;
327
+ }
328
+ }
329
+ panStart = null;
330
+ return;
331
+ }
332
+
333
+ // Fin de tracé de flèche via conn-dot
334
+ if (arrowSrcId && !dragState) {
335
+ document.getElementById("tempArrow").style.display = "none";
336
+ if (currentTool !== "arrow") {
337
+ var target = shapeAt(pt.x, pt.y);
338
+ if (target && target.id !== arrowSrcId && target.type !== "postit") {
339
+ createArrow(arrowSrcId, target.id);
340
+ }
341
+ arrowSrcId = null;
342
+ }
343
+ }
344
+
345
+ if (rubberBandState) {
346
+ document.getElementById("rubberBand").style.display = "none";
347
+ var minX = Math.min(rubberBandState.sx, pt.x);
348
+ var minY = Math.min(rubberBandState.sy, pt.y);
349
+ var maxX = Math.max(rubberBandState.sx, pt.x);
350
+ var maxY = Math.max(rubberBandState.sy, pt.y);
351
+ if (maxX - minX > 4 || maxY - minY > 4) {
352
+ var diag = getCurrentDiagram();
353
+ selectedIds = (diag.shapes || []).filter(function (s) {
354
+ return s.x < maxX && s.x + s.w > minX && s.y < maxY && s.y + s.h > minY;
355
+ }).map(function (s) { return s.id; });
356
+ selectedId = selectedIds.length > 0 ? selectedIds[0] : null;
357
+ selectedType = selectedIds.length > 0 ? "shape" : null;
358
+ document.getElementById("colorPanel").style.display = selectedIds.length > 0 ? "flex" : "none";
359
+ }
360
+ rubberBandState = null;
361
+ renderAll();
362
+ }
363
+
364
+ if (dragState) {
365
+ var wasMoved = dragState.moved;
366
+ var draggedId = dragState.id;
367
+ var dragType = dragState.type;
368
+ if (dragState.moved && dragState.snapshot) {
369
+ historyStack.push(dragState.snapshot);
370
+ if (historyStack.length > MAX_HISTORY) historyStack.shift();
371
+ }
372
+ saveDiagrammes();
373
+ dragState = null;
374
+ renderAll();
375
+ // Naviguer vers le diagramme lié si clic sans déplacement
376
+ if (!wasMoved && dragType === "move" && draggedId && !e.shiftKey) {
377
+ var diag = getCurrentDiagram();
378
+ var clickedShape = diag ? diag.shapes.find(function (s) { return s.id === draggedId; }) : null;
379
+ if (clickedShape && clickedShape.linkedDiagramId) {
380
+ var target = findDiagramById(clickedShape.linkedDiagramId, diagramsList);
381
+ if (target) {
382
+ hideLinkPicker();
383
+ diagNavStack.push(currentDiagramId);
384
+ if (diagNavStack.length > 30) diagNavStack.shift();
385
+ selectDiagramme(clickedShape.linkedDiagramId);
386
+ return;
387
+ }
388
+ } else if (clickedShape && clickedShape.externalUrl) {
389
+ window.open(clickedShape.externalUrl, "_blank");
390
+ return;
391
+ }
392
+ }
393
+ }
394
+ panStart = null;
395
+ }
396
+
397
+
398
+ function onWheel(e) {
399
+ e.preventDefault();
400
+ var svg = document.getElementById("canvas");
401
+ var r = svg.getBoundingClientRect();
402
+ applyZoom(e.deltaY < 0 ? 1.12 : 1 / 1.12, e.clientX - r.left, e.clientY - r.top);
403
+ }
404
+
405
+
406
+ // ── Init ──
407
+ document.addEventListener("DOMContentLoaded", function () {
408
+ diagramsList = loadDiagrammes();
409
+ if (!localStorage.getItem("mes_diagrammes")) saveDiagrammes();
410
+ var savedDiagId = localStorage.getItem("current_diagram_id");
411
+ if (savedDiagId && findDiagramById(savedDiagId, diagramsList)) {
412
+ currentDiagramId = savedDiagId;
413
+ } else {
414
+ var savedIdx = parseInt(localStorage.getItem("current_diagram_idx"), 10);
415
+ if (!isNaN(savedIdx) && savedIdx >= 0 && savedIdx < diagramsList.length) {
416
+ currentDiagramId = String(diagramsList[savedIdx].id);
417
+ } else {
418
+ currentDiagramId = diagramsList[0] ? String(diagramsList[0].id) : null;
419
+ }
420
+ }
421
+ restoreLockForDiagram(currentDiagramId);
422
+ checkDiffDiagrammes();
423
+ renderAll();
424
+ updateLockBtn();
425
+ updateBackBtn();
426
+
427
+ var canvas = document.getElementById("canvas");
428
+ canvas.addEventListener("mousedown", onMouseDown);
429
+ canvas.addEventListener("mousemove", onMouseMove);
430
+ canvas.addEventListener("mouseup", onMouseUp);
431
+ canvas.addEventListener("wheel", onWheel, { passive: false });
432
+
433
+ var titleInput = document.getElementById("diagramTitle");
434
+ titleInput.addEventListener("change", onTitleChange);
435
+ titleInput.addEventListener("blur", onTitleChange);
436
+ titleInput.addEventListener("keydown", function (e) {
437
+ if (e.key === "Enter") {
438
+ e.preventDefault();
439
+ if (pendingNewDiagram) confirmerNouveauDiagramme();
440
+ else titleInput.blur();
441
+ }
442
+ if (e.key === "Escape" && pendingNewDiagram) annulerNouveauDiagramme();
443
+ });
444
+
445
+ var textInput = document.getElementById("shapeTextInput");
446
+ textInput.addEventListener("keydown", function (e) {
447
+ if (e.key === "Enter") { e.preventDefault(); confirmTextEdit(); }
448
+ if (e.key === "Escape") {
449
+ document.getElementById("textOverlay").style.display = "none";
450
+ editingShapeId = null;
451
+ }
452
+ });
453
+ textInput.addEventListener("blur", function () {
454
+ // Small delay to allow click on modal-confirm etc.
455
+ setTimeout(confirmTextEdit, 100);
456
+ });
457
+
458
+ var postitInput = document.getElementById("postitTextInput");
459
+ postitInput.addEventListener("keydown", function (e) {
460
+ if (e.key === "Escape") {
461
+ document.getElementById("textOverlay").style.display = "none";
462
+ postitInput.style.display = "none";
463
+ document.getElementById("shapeTextInput").style.display = "block";
464
+ editingShapeId = null;
465
+ renderAll(); // restaure la visibilité du texte SVG
466
+ }
467
+ if (e.key === "Tab" && editingTableCell !== null) {
468
+ e.preventDefault();
469
+ var diag = getCurrentDiagram();
470
+ var shape = diag.shapes.find(function (s) { return s.id === editingShapeId; });
471
+ if (!shape) return;
472
+ // Sauvegarder la cellule courante
473
+ if (!shape.cells) shape.cells = [];
474
+ while (shape.cells.length <= editingTableCell.row) shape.cells.push([]);
475
+ shape.cells[editingTableCell.row][editingTableCell.col] = postitInput.value;
476
+ var rows = shape.rows || 3;
477
+ var cols = shape.cols || 3;
478
+ var nextRow = editingTableCell.row;
479
+ var nextCol = editingTableCell.col;
480
+ if (e.shiftKey) {
481
+ // Shift+Tab : cellule précédente, s'arrête à la première
482
+ nextCol -= 1;
483
+ if (nextCol < 0) {
484
+ if (nextRow > 0) { nextRow--; nextCol = cols - 1; }
485
+ else { nextCol = 0; }
486
+ }
487
+ } else {
488
+ // Tab : cellule suivante, ajoute une ligne à la fin
489
+ nextCol += 1;
490
+ if (nextCol >= cols) { nextRow++; nextCol = 0; }
491
+ if (nextRow >= rows) {
492
+ pushHistory();
493
+ shape.rows = rows + 1;
494
+ if (!shape.cells) shape.cells = [];
495
+ while (shape.cells.length < shape.rows) shape.cells.push([]);
496
+ shape.cells[shape.rows - 1] = new Array(cols).fill("");
497
+ }
498
+ }
499
+ var shapeId = editingShapeId;
500
+ saveDiagrammes();
501
+ renderAll();
502
+ startTableCellEdit(shapeId, nextRow, nextCol);
503
+ }
504
+ // Enter ajoute un saut de ligne (comportement natif textarea — pas de preventDefault)
505
+ });
506
+ postitInput.addEventListener("blur", function () {
507
+ setTimeout(confirmTextEdit, 100);
508
+ });
509
+
510
+ document.addEventListener("keydown", function (e) {
511
+ if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") return;
512
+ if ((e.ctrlKey || e.metaKey) && e.key === "z") { e.preventDefault(); undoAction(); return; }
513
+ if ((e.ctrlKey || e.metaKey) && e.key === "a") {
514
+ e.preventDefault();
515
+ var diag = getCurrentDiagram();
516
+ if (diag && diag.shapes.length > 0) {
517
+ selectedIds = diag.shapes.map(function (s) { return s.id; });
518
+ selectedType = "shape";
519
+ selectedId = selectedIds[selectedIds.length - 1];
520
+ renderAll();
521
+ document.getElementById("colorPanel").style.display = "flex";
522
+ syncColorPanel();
523
+ }
524
+ return;
525
+ }
526
+ if (boardLocked) return;
527
+ if (e.key === "Delete" || e.key === "Backspace") deleteSelected();
528
+ if (e.key === "Escape") {
529
+ hideLinkPicker();
530
+ arrowSrcId = null;
531
+ document.getElementById("tempArrow").style.display = "none";
532
+ setTool("select");
533
+ selectedIds = []; selectedId = null; selectedType = null;
534
+ document.getElementById("colorPanel").style.display = "none";
535
+ renderAll();
536
+ }
537
+ if ((e.ctrlKey || e.metaKey) && e.key === "x") {
538
+ e.preventDefault();
539
+ if (selectedIds.length === 0) return;
540
+ var diag = getCurrentDiagram();
541
+ clipboard = selectedIds.map(function (id) {
542
+ var s = diag.shapes.find(function (sh) { return sh.id === id; });
543
+ return s ? JSON.parse(JSON.stringify(s)) : null;
544
+ }).filter(Boolean);
545
+ deleteSelected();
546
+ return;
547
+ }
548
+ if ((e.ctrlKey || e.metaKey) && e.key === "c") {
549
+ e.preventDefault();
550
+ if (selectedIds.length === 0) return;
551
+ var diag = getCurrentDiagram();
552
+ clipboard = selectedIds.map(function (id) {
553
+ var s = diag.shapes.find(function (sh) { return sh.id === id; });
554
+ return s ? JSON.parse(JSON.stringify(s)) : null;
555
+ }).filter(Boolean);
556
+ }
557
+ if ((e.ctrlKey || e.metaKey) && e.key === "v") {
558
+ e.preventDefault();
559
+ if (clipboard.length > 0) {
560
+ pasteShapes();
561
+ return;
562
+ }
563
+ if (!navigator.clipboard || !navigator.clipboard.read) return;
564
+ navigator.clipboard.read().then(function (clipItems) {
565
+ for (var i = 0; i < clipItems.length; i++) {
566
+ for (var j = 0; j < clipItems[i].types.length; j++) {
567
+ if (clipItems[i].types[j].indexOf("image") !== -1) {
568
+ clipItems[i].getType(clipItems[i].types[j]).then(function (blob) {
569
+ handleImagePaste(blob);
570
+ });
571
+ return;
572
+ }
573
+ }
574
+ }
575
+ }).catch(function () {});
576
+ }
577
+ });
578
+
579
+ // Quand la fenêtre reprend le focus, l'utilisateur revient d'une autre app
580
+ // où il a peut-être copié une image → vider le clipboard interne
581
+ window.addEventListener("focus", function () {
582
+ clipboard = [];
583
+ });
584
+
585
+ document.getElementById("modalPremiereSauvegardeDiag").addEventListener("click", function (e) {
586
+ if (e.target === this) this.classList.remove("open");
587
+ });
588
+
589
+
590
+ document.addEventListener("mousedown", function (e) {
591
+ var lp = document.getElementById("linkPickerPanel");
592
+ if (lp && lp.style.display !== "none") {
593
+ var lbtn = document.getElementById("btnShapeLink");
594
+ if (!lp.contains(e.target) && e.target !== lbtn) hideLinkPicker();
595
+ }
596
+ var panel = document.getElementById("diagramListPanel");
597
+ if (!panel.classList.contains("open")) return;
598
+ var burgerBtn = document.querySelector(".diagram-tool[onclick=\"toggleDiagramList()\"]");
599
+ if (!panel.contains(e.target) && (!burgerBtn || !burgerBtn.contains(e.target))) {
600
+ panel.classList.remove("open");
601
+ }
602
+ });
603
+ });