@checkflow/sdk 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vue.js CHANGED
@@ -210,11 +210,1122 @@ var init_network_interceptor = __esm({
210
210
  }
211
211
  });
212
212
 
213
+ // src/annotation/types.ts
214
+ var DEFAULT_STYLE, HIGHLIGHT_STYLE, BLUR_STYLE, COLOR_PALETTE, STROKE_WIDTHS;
215
+ var init_types = __esm({
216
+ "src/annotation/types.ts"() {
217
+ "use strict";
218
+ DEFAULT_STYLE = {
219
+ strokeColor: "#FF3B30",
220
+ strokeWidth: 3,
221
+ fillColor: "transparent",
222
+ opacity: 1,
223
+ fontSize: 16,
224
+ fontFamily: "system-ui, -apple-system, sans-serif"
225
+ };
226
+ HIGHLIGHT_STYLE = {
227
+ strokeColor: "transparent",
228
+ fillColor: "#FFEB3B",
229
+ opacity: 0.4
230
+ };
231
+ BLUR_STYLE = {
232
+ strokeColor: "#666666",
233
+ fillColor: "#666666",
234
+ opacity: 0.8
235
+ };
236
+ COLOR_PALETTE = [
237
+ "#FF3B30",
238
+ // Red
239
+ "#FF9500",
240
+ // Orange
241
+ "#FFCC00",
242
+ // Yellow
243
+ "#34C759",
244
+ // Green
245
+ "#007AFF",
246
+ // Blue
247
+ "#5856D6",
248
+ // Purple
249
+ "#AF52DE",
250
+ // Pink
251
+ "#000000",
252
+ // Black
253
+ "#FFFFFF"
254
+ // White
255
+ ];
256
+ STROKE_WIDTHS = [1, 2, 3, 5, 8];
257
+ }
258
+ });
259
+
260
+ // src/annotation/toolbar.ts
261
+ var TOOL_ICONS, TOOL_LABELS, AnnotationToolbar;
262
+ var init_toolbar = __esm({
263
+ "src/annotation/toolbar.ts"() {
264
+ "use strict";
265
+ init_types();
266
+ TOOL_ICONS = {
267
+ select: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"/><path d="M13 13l6 6"/></svg>`,
268
+ rectangle: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>`,
269
+ ellipse: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="12" rx="9" ry="6"/></svg>`,
270
+ arrow: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>`,
271
+ line: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="5" y1="19" x2="19" y2="5"/></svg>`,
272
+ highlight: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>`,
273
+ blur: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3 .5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm15 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-11 10c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-17c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 5.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm8 .5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm3 8.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM14 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-4-12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0 8.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm4-4.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-4c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"/></svg>`,
274
+ text: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
275
+ freehand: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/></svg>`
276
+ };
277
+ TOOL_LABELS = {
278
+ select: "S\xE9lection",
279
+ rectangle: "Rectangle",
280
+ ellipse: "Ellipse",
281
+ arrow: "Fl\xE8che",
282
+ line: "Ligne",
283
+ highlight: "Surbrillance",
284
+ blur: "Floutage",
285
+ text: "Texte",
286
+ freehand: "Dessin libre"
287
+ };
288
+ AnnotationToolbar = class {
289
+ constructor(config2) {
290
+ this.colorPicker = null;
291
+ this.strokePicker = null;
292
+ this.config = config2;
293
+ this.element = this.createToolbar();
294
+ }
295
+ getElement() {
296
+ return this.element;
297
+ }
298
+ setActiveTool(tool) {
299
+ this.config.activeTool = tool;
300
+ this.updateActiveState();
301
+ }
302
+ setStyle(style) {
303
+ this.config.style = style;
304
+ this.updateStyleDisplay();
305
+ }
306
+ createToolbar() {
307
+ const toolbar = document.createElement("div");
308
+ toolbar.className = "cf-toolbar";
309
+ const toolsSection = document.createElement("div");
310
+ toolsSection.className = "cf-toolbar-section cf-toolbar-tools";
311
+ for (const tool of this.config.tools) {
312
+ const button = this.createToolButton(tool);
313
+ toolsSection.appendChild(button);
314
+ }
315
+ const separator1 = document.createElement("div");
316
+ separator1.className = "cf-toolbar-separator";
317
+ const styleSection = document.createElement("div");
318
+ styleSection.className = "cf-toolbar-section cf-toolbar-style";
319
+ const colorBtn = document.createElement("button");
320
+ colorBtn.className = "cf-toolbar-btn cf-color-btn";
321
+ colorBtn.title = "Couleur";
322
+ colorBtn.innerHTML = `<span class="cf-color-preview" style="background: ${this.config.style.strokeColor}"></span>`;
323
+ colorBtn.addEventListener("click", () => this.toggleColorPicker());
324
+ styleSection.appendChild(colorBtn);
325
+ const strokeBtn = document.createElement("button");
326
+ strokeBtn.className = "cf-toolbar-btn cf-stroke-btn";
327
+ strokeBtn.title = "\xC9paisseur";
328
+ strokeBtn.innerHTML = `<span class="cf-stroke-preview">${this.config.style.strokeWidth}px</span>`;
329
+ strokeBtn.addEventListener("click", () => this.toggleStrokePicker());
330
+ styleSection.appendChild(strokeBtn);
331
+ const separator2 = document.createElement("div");
332
+ separator2.className = "cf-toolbar-separator";
333
+ const actionsSection = document.createElement("div");
334
+ actionsSection.className = "cf-toolbar-section cf-toolbar-actions";
335
+ const undoBtn = document.createElement("button");
336
+ undoBtn.className = "cf-toolbar-btn";
337
+ undoBtn.title = "Annuler (Ctrl+Z)";
338
+ undoBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 00-9-9 9 9 0 00-6 2.3L3 13"/></svg>`;
339
+ undoBtn.addEventListener("click", () => this.config.onUndo());
340
+ actionsSection.appendChild(undoBtn);
341
+ const clearBtn = document.createElement("button");
342
+ clearBtn.className = "cf-toolbar-btn";
343
+ clearBtn.title = "Tout effacer";
344
+ clearBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/></svg>`;
345
+ clearBtn.addEventListener("click", () => this.config.onClear());
346
+ actionsSection.appendChild(clearBtn);
347
+ toolbar.appendChild(toolsSection);
348
+ toolbar.appendChild(separator1);
349
+ toolbar.appendChild(styleSection);
350
+ toolbar.appendChild(separator2);
351
+ toolbar.appendChild(actionsSection);
352
+ return toolbar;
353
+ }
354
+ createToolButton(tool) {
355
+ const button = document.createElement("button");
356
+ button.className = `cf-toolbar-btn cf-tool-btn ${tool === this.config.activeTool ? "active" : ""}`;
357
+ button.dataset.tool = tool;
358
+ button.title = TOOL_LABELS[tool];
359
+ button.innerHTML = TOOL_ICONS[tool];
360
+ button.addEventListener("click", () => {
361
+ this.config.onToolChange(tool);
362
+ this.setActiveTool(tool);
363
+ });
364
+ return button;
365
+ }
366
+ updateActiveState() {
367
+ const buttons = this.element.querySelectorAll(".cf-tool-btn");
368
+ buttons.forEach((btn) => {
369
+ const button = btn;
370
+ button.classList.toggle("active", button.dataset.tool === this.config.activeTool);
371
+ });
372
+ }
373
+ updateStyleDisplay() {
374
+ const colorPreview = this.element.querySelector(".cf-color-preview");
375
+ if (colorPreview) {
376
+ colorPreview.style.background = this.config.style.strokeColor;
377
+ }
378
+ const strokePreview = this.element.querySelector(".cf-stroke-preview");
379
+ if (strokePreview) {
380
+ strokePreview.textContent = `${this.config.style.strokeWidth}px`;
381
+ }
382
+ }
383
+ toggleColorPicker() {
384
+ if (this.colorPicker) {
385
+ this.colorPicker.remove();
386
+ this.colorPicker = null;
387
+ return;
388
+ }
389
+ if (this.strokePicker) {
390
+ this.strokePicker.remove();
391
+ this.strokePicker = null;
392
+ }
393
+ this.colorPicker = document.createElement("div");
394
+ this.colorPicker.className = "cf-picker cf-color-picker";
395
+ for (const color of COLOR_PALETTE) {
396
+ const swatch = document.createElement("button");
397
+ swatch.className = `cf-color-swatch ${color === this.config.style.strokeColor ? "active" : ""}`;
398
+ swatch.style.background = color;
399
+ swatch.addEventListener("click", () => {
400
+ this.config.onStyleChange({ strokeColor: color });
401
+ this.setStyle({ ...this.config.style, strokeColor: color });
402
+ this.colorPicker?.remove();
403
+ this.colorPicker = null;
404
+ });
405
+ this.colorPicker.appendChild(swatch);
406
+ }
407
+ const colorBtn = this.element.querySelector(".cf-color-btn");
408
+ colorBtn?.appendChild(this.colorPicker);
409
+ }
410
+ toggleStrokePicker() {
411
+ if (this.strokePicker) {
412
+ this.strokePicker.remove();
413
+ this.strokePicker = null;
414
+ return;
415
+ }
416
+ if (this.colorPicker) {
417
+ this.colorPicker.remove();
418
+ this.colorPicker = null;
419
+ }
420
+ this.strokePicker = document.createElement("div");
421
+ this.strokePicker.className = "cf-picker cf-stroke-picker";
422
+ for (const width of STROKE_WIDTHS) {
423
+ const option = document.createElement("button");
424
+ option.className = `cf-stroke-option ${width === this.config.style.strokeWidth ? "active" : ""}`;
425
+ option.innerHTML = `<span style="height: ${width}px"></span> ${width}px`;
426
+ option.addEventListener("click", () => {
427
+ this.config.onStyleChange({ strokeWidth: width });
428
+ this.setStyle({ ...this.config.style, strokeWidth: width });
429
+ this.strokePicker?.remove();
430
+ this.strokePicker = null;
431
+ });
432
+ this.strokePicker.appendChild(option);
433
+ }
434
+ const strokeBtn = this.element.querySelector(".cf-stroke-btn");
435
+ strokeBtn?.appendChild(this.strokePicker);
436
+ }
437
+ };
438
+ }
439
+ });
440
+
441
+ // src/annotation/styles.ts
442
+ function injectAnnotationStyles() {
443
+ if (stylesInjected) return;
444
+ const styleElement = document.createElement("style");
445
+ styleElement.id = "cf-annotation-styles";
446
+ styleElement.textContent = STYLES;
447
+ document.head.appendChild(styleElement);
448
+ stylesInjected = true;
449
+ }
450
+ var STYLES, stylesInjected;
451
+ var init_styles = __esm({
452
+ "src/annotation/styles.ts"() {
453
+ "use strict";
454
+ STYLES = `
455
+ /* Annotation Overlay */
456
+ .cf-annotation-overlay {
457
+ position: fixed;
458
+ top: 0;
459
+ left: 0;
460
+ right: 0;
461
+ bottom: 0;
462
+ z-index: 999999;
463
+ background: rgba(0, 0, 0, 0.85);
464
+ display: flex;
465
+ align-items: center;
466
+ justify-content: center;
467
+ animation: cf-fade-in 0.2s ease-out;
468
+ }
469
+
470
+ @keyframes cf-fade-in {
471
+ from { opacity: 0; }
472
+ to { opacity: 1; }
473
+ }
474
+
475
+ /* Editor Wrapper */
476
+ .cf-annotation-wrapper {
477
+ display: flex;
478
+ flex-direction: column;
479
+ max-width: 95vw;
480
+ max-height: 95vh;
481
+ background: #1a1a1a;
482
+ border-radius: 12px;
483
+ overflow: hidden;
484
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
485
+ }
486
+
487
+ /* Canvas Container */
488
+ .cf-annotation-canvas-container {
489
+ flex: 1;
490
+ display: flex;
491
+ align-items: center;
492
+ justify-content: center;
493
+ overflow: auto;
494
+ padding: 16px;
495
+ background: #0d0d0d;
496
+ }
497
+
498
+ .cf-annotation-canvas {
499
+ max-width: 100%;
500
+ max-height: calc(95vh - 140px);
501
+ border-radius: 4px;
502
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
503
+ cursor: crosshair;
504
+ }
505
+
506
+ /* Toolbar */
507
+ .cf-toolbar {
508
+ display: flex;
509
+ align-items: center;
510
+ gap: 8px;
511
+ padding: 12px 16px;
512
+ background: #2a2a2a;
513
+ border-bottom: 1px solid #3a3a3a;
514
+ }
515
+
516
+ .cf-toolbar-section {
517
+ display: flex;
518
+ align-items: center;
519
+ gap: 4px;
520
+ }
521
+
522
+ .cf-toolbar-separator {
523
+ width: 1px;
524
+ height: 24px;
525
+ background: #4a4a4a;
526
+ margin: 0 8px;
527
+ }
528
+
529
+ .cf-toolbar-btn {
530
+ display: flex;
531
+ align-items: center;
532
+ justify-content: center;
533
+ width: 36px;
534
+ height: 36px;
535
+ padding: 0;
536
+ border: none;
537
+ border-radius: 8px;
538
+ background: transparent;
539
+ color: #999;
540
+ cursor: pointer;
541
+ transition: all 0.15s ease;
542
+ }
543
+
544
+ .cf-toolbar-btn:hover {
545
+ background: #3a3a3a;
546
+ color: #fff;
547
+ }
548
+
549
+ .cf-toolbar-btn.active {
550
+ background: #007AFF;
551
+ color: #fff;
552
+ }
553
+
554
+ .cf-toolbar-btn svg {
555
+ width: 20px;
556
+ height: 20px;
557
+ }
558
+
559
+ /* Color Button */
560
+ .cf-color-btn {
561
+ position: relative;
562
+ }
563
+
564
+ .cf-color-preview {
565
+ width: 20px;
566
+ height: 20px;
567
+ border-radius: 50%;
568
+ border: 2px solid #fff;
569
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.2);
570
+ }
571
+
572
+ /* Stroke Button */
573
+ .cf-stroke-btn {
574
+ width: auto;
575
+ padding: 0 12px;
576
+ }
577
+
578
+ .cf-stroke-preview {
579
+ font-size: 12px;
580
+ font-weight: 500;
581
+ color: inherit;
582
+ }
583
+
584
+ /* Pickers */
585
+ .cf-picker {
586
+ position: absolute;
587
+ top: 100%;
588
+ left: 50%;
589
+ transform: translateX(-50%);
590
+ margin-top: 8px;
591
+ padding: 8px;
592
+ background: #2a2a2a;
593
+ border-radius: 8px;
594
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
595
+ z-index: 10;
596
+ }
597
+
598
+ .cf-color-picker {
599
+ display: grid;
600
+ grid-template-columns: repeat(5, 1fr);
601
+ gap: 4px;
602
+ width: 140px;
603
+ }
604
+
605
+ .cf-color-swatch {
606
+ width: 24px;
607
+ height: 24px;
608
+ border-radius: 50%;
609
+ border: 2px solid transparent;
610
+ cursor: pointer;
611
+ transition: transform 0.15s ease;
612
+ }
613
+
614
+ .cf-color-swatch:hover {
615
+ transform: scale(1.15);
616
+ }
617
+
618
+ .cf-color-swatch.active {
619
+ border-color: #fff;
620
+ }
621
+
622
+ .cf-stroke-picker {
623
+ display: flex;
624
+ flex-direction: column;
625
+ gap: 4px;
626
+ min-width: 80px;
627
+ }
628
+
629
+ .cf-stroke-option {
630
+ display: flex;
631
+ align-items: center;
632
+ gap: 8px;
633
+ padding: 6px 10px;
634
+ border: none;
635
+ border-radius: 4px;
636
+ background: transparent;
637
+ color: #999;
638
+ font-size: 12px;
639
+ cursor: pointer;
640
+ transition: all 0.15s ease;
641
+ }
642
+
643
+ .cf-stroke-option:hover {
644
+ background: #3a3a3a;
645
+ color: #fff;
646
+ }
647
+
648
+ .cf-stroke-option.active {
649
+ background: #007AFF;
650
+ color: #fff;
651
+ }
652
+
653
+ .cf-stroke-option span {
654
+ width: 24px;
655
+ background: currentColor;
656
+ border-radius: 2px;
657
+ }
658
+
659
+ /* Action Buttons */
660
+ .cf-annotation-actions {
661
+ display: flex;
662
+ justify-content: flex-end;
663
+ gap: 12px;
664
+ padding: 12px 16px;
665
+ background: #2a2a2a;
666
+ border-top: 1px solid #3a3a3a;
667
+ }
668
+
669
+ .cf-btn {
670
+ display: flex;
671
+ align-items: center;
672
+ justify-content: center;
673
+ padding: 10px 20px;
674
+ border: none;
675
+ border-radius: 8px;
676
+ font-size: 14px;
677
+ font-weight: 500;
678
+ cursor: pointer;
679
+ transition: all 0.15s ease;
680
+ }
681
+
682
+ .cf-btn-primary {
683
+ background: #007AFF;
684
+ color: #fff;
685
+ }
686
+
687
+ .cf-btn-primary:hover {
688
+ background: #0066DD;
689
+ }
690
+
691
+ .cf-btn-secondary {
692
+ background: #3a3a3a;
693
+ color: #fff;
694
+ }
695
+
696
+ .cf-btn-secondary:hover {
697
+ background: #4a4a4a;
698
+ }
699
+
700
+ /* Text Input */
701
+ .cf-annotation-text-input {
702
+ position: fixed;
703
+ z-index: 1000000;
704
+ padding: 4px 8px;
705
+ border: 2px solid #007AFF;
706
+ border-radius: 4px;
707
+ background: rgba(255, 255, 255, 0.95);
708
+ font-size: 16px;
709
+ font-family: system-ui, -apple-system, sans-serif;
710
+ outline: none;
711
+ min-width: 150px;
712
+ }
713
+
714
+ /* Tool-specific cursors */
715
+ .cf-annotation-canvas[data-tool="select"] { cursor: default; }
716
+ .cf-annotation-canvas[data-tool="rectangle"] { cursor: crosshair; }
717
+ .cf-annotation-canvas[data-tool="ellipse"] { cursor: crosshair; }
718
+ .cf-annotation-canvas[data-tool="arrow"] { cursor: crosshair; }
719
+ .cf-annotation-canvas[data-tool="line"] { cursor: crosshair; }
720
+ .cf-annotation-canvas[data-tool="highlight"] { cursor: crosshair; }
721
+ .cf-annotation-canvas[data-tool="blur"] { cursor: crosshair; }
722
+ .cf-annotation-canvas[data-tool="text"] { cursor: text; }
723
+ .cf-annotation-canvas[data-tool="freehand"] { cursor: crosshair; }
724
+
725
+ /* Responsive */
726
+ @media (max-width: 768px) {
727
+ .cf-toolbar {
728
+ flex-wrap: wrap;
729
+ justify-content: center;
730
+ }
731
+
732
+ .cf-toolbar-separator {
733
+ display: none;
734
+ }
735
+
736
+ .cf-annotation-actions {
737
+ justify-content: stretch;
738
+ }
739
+
740
+ .cf-annotation-actions .cf-btn {
741
+ flex: 1;
742
+ }
743
+ }
744
+ `;
745
+ stylesInjected = false;
746
+ }
747
+ });
748
+
749
+ // src/annotation/editor.ts
750
+ var AnnotationEditor;
751
+ var init_editor = __esm({
752
+ "src/annotation/editor.ts"() {
753
+ "use strict";
754
+ init_types();
755
+ init_toolbar();
756
+ init_styles();
757
+ AnnotationEditor = class {
758
+ constructor(config2) {
759
+ this.container = null;
760
+ this.canvas = null;
761
+ this.ctx = null;
762
+ this.toolbar = null;
763
+ this.annotations = [];
764
+ this.backgroundImage = null;
765
+ this.state = {
766
+ activeTool: "rectangle",
767
+ style: { ...DEFAULT_STYLE },
768
+ isDrawing: false,
769
+ currentAnnotation: null
770
+ };
771
+ this.startPoint = null;
772
+ this.freehandPoints = [];
773
+ this.textInput = null;
774
+ this.config = {
775
+ ...config2,
776
+ tools: config2.tools || ["select", "rectangle", "arrow", "highlight", "blur", "text", "freehand"],
777
+ defaultStyle: config2.defaultStyle || DEFAULT_STYLE
778
+ };
779
+ this.state.style = { ...this.config.defaultStyle };
780
+ }
781
+ /**
782
+ * Open the annotation editor with a screenshot
783
+ */
784
+ async open(screenshotDataUrl2) {
785
+ injectAnnotationStyles();
786
+ this.backgroundImage = await this.loadImage(screenshotDataUrl2);
787
+ this.createEditorUI();
788
+ this.setupEventListeners();
789
+ this.render();
790
+ }
791
+ /**
792
+ * Close the editor
793
+ */
794
+ close() {
795
+ if (this.container) {
796
+ this.container.remove();
797
+ this.container = null;
798
+ }
799
+ this.canvas = null;
800
+ this.ctx = null;
801
+ this.toolbar = null;
802
+ this.annotations = [];
803
+ this.backgroundImage = null;
804
+ }
805
+ /**
806
+ * Get the annotated image as data URL
807
+ */
808
+ getAnnotatedImage() {
809
+ if (!this.canvas || !this.ctx) return "";
810
+ const tempCanvas = document.createElement("canvas");
811
+ tempCanvas.width = this.canvas.width;
812
+ tempCanvas.height = this.canvas.height;
813
+ const tempCtx = tempCanvas.getContext("2d");
814
+ if (this.backgroundImage) {
815
+ tempCtx.drawImage(this.backgroundImage, 0, 0);
816
+ }
817
+ this.drawAnnotations(tempCtx);
818
+ return tempCanvas.toDataURL("image/png");
819
+ }
820
+ /**
821
+ * Get annotations data
822
+ */
823
+ getAnnotations() {
824
+ return [...this.annotations];
825
+ }
826
+ // Private methods
827
+ loadImage(src) {
828
+ return new Promise((resolve, reject) => {
829
+ const img = new Image();
830
+ img.onload = () => resolve(img);
831
+ img.onerror = reject;
832
+ img.src = src;
833
+ });
834
+ }
835
+ createEditorUI() {
836
+ this.container = document.createElement("div");
837
+ this.container.className = "cf-annotation-overlay";
838
+ const wrapper = document.createElement("div");
839
+ wrapper.className = "cf-annotation-wrapper";
840
+ const canvasContainer = document.createElement("div");
841
+ canvasContainer.className = "cf-annotation-canvas-container";
842
+ this.canvas = document.createElement("canvas");
843
+ this.canvas.className = "cf-annotation-canvas";
844
+ this.canvas.width = this.backgroundImage?.width || 1920;
845
+ this.canvas.height = this.backgroundImage?.height || 1080;
846
+ this.ctx = this.canvas.getContext("2d");
847
+ canvasContainer.appendChild(this.canvas);
848
+ this.toolbar = new AnnotationToolbar({
849
+ tools: this.config.tools,
850
+ activeTool: this.state.activeTool,
851
+ style: this.state.style,
852
+ onToolChange: (tool) => this.setActiveTool(tool),
853
+ onStyleChange: (style) => this.setStyle(style),
854
+ onUndo: () => this.undo(),
855
+ onClear: () => this.clearAll(),
856
+ onSave: () => this.save(),
857
+ onCancel: () => this.cancel()
858
+ });
859
+ const actions = document.createElement("div");
860
+ actions.className = "cf-annotation-actions";
861
+ actions.innerHTML = `
862
+ <button class="cf-btn cf-btn-secondary" data-action="cancel">Annuler</button>
863
+ <button class="cf-btn cf-btn-primary" data-action="save">Enregistrer</button>
864
+ `;
865
+ wrapper.appendChild(this.toolbar.getElement());
866
+ wrapper.appendChild(canvasContainer);
867
+ wrapper.appendChild(actions);
868
+ this.container.appendChild(wrapper);
869
+ document.body.appendChild(this.container);
870
+ actions.querySelector('[data-action="cancel"]')?.addEventListener("click", () => this.cancel());
871
+ actions.querySelector('[data-action="save"]')?.addEventListener("click", () => this.save());
872
+ }
873
+ setupEventListeners() {
874
+ if (!this.canvas) return;
875
+ this.canvas.addEventListener("mousedown", this.handleMouseDown.bind(this));
876
+ this.canvas.addEventListener("mousemove", this.handleMouseMove.bind(this));
877
+ this.canvas.addEventListener("mouseup", this.handleMouseUp.bind(this));
878
+ this.canvas.addEventListener("mouseleave", this.handleMouseUp.bind(this));
879
+ this.canvas.addEventListener("touchstart", this.handleTouchStart.bind(this));
880
+ this.canvas.addEventListener("touchmove", this.handleTouchMove.bind(this));
881
+ this.canvas.addEventListener("touchend", this.handleTouchEnd.bind(this));
882
+ document.addEventListener("keydown", this.handleKeyDown.bind(this));
883
+ }
884
+ handleMouseDown(e) {
885
+ const point = this.getCanvasPoint(e);
886
+ this.startDrawing(point);
887
+ }
888
+ handleMouseMove(e) {
889
+ const point = this.getCanvasPoint(e);
890
+ this.continueDrawing(point);
891
+ }
892
+ handleMouseUp(_e) {
893
+ this.finishDrawing();
894
+ }
895
+ handleTouchStart(e) {
896
+ e.preventDefault();
897
+ const touch = e.touches[0];
898
+ const point = this.getCanvasPointFromTouch(touch);
899
+ this.startDrawing(point);
900
+ }
901
+ handleTouchMove(e) {
902
+ e.preventDefault();
903
+ const touch = e.touches[0];
904
+ const point = this.getCanvasPointFromTouch(touch);
905
+ this.continueDrawing(point);
906
+ }
907
+ handleTouchEnd(e) {
908
+ e.preventDefault();
909
+ this.finishDrawing();
910
+ }
911
+ handleKeyDown(e) {
912
+ if ((e.ctrlKey || e.metaKey) && e.key === "z") {
913
+ e.preventDefault();
914
+ this.undo();
915
+ }
916
+ if (e.key === "Escape") {
917
+ if (this.state.isDrawing) {
918
+ this.state.isDrawing = false;
919
+ this.state.currentAnnotation = null;
920
+ this.render();
921
+ } else {
922
+ this.cancel();
923
+ }
924
+ }
925
+ if (e.key === "Enter" && !this.state.isDrawing) {
926
+ this.save();
927
+ }
928
+ }
929
+ getCanvasPoint(e) {
930
+ const rect = this.canvas.getBoundingClientRect();
931
+ const scaleX = this.canvas.width / rect.width;
932
+ const scaleY = this.canvas.height / rect.height;
933
+ return {
934
+ x: (e.clientX - rect.left) * scaleX,
935
+ y: (e.clientY - rect.top) * scaleY
936
+ };
937
+ }
938
+ getCanvasPointFromTouch(touch) {
939
+ const rect = this.canvas.getBoundingClientRect();
940
+ const scaleX = this.canvas.width / rect.width;
941
+ const scaleY = this.canvas.height / rect.height;
942
+ return {
943
+ x: (touch.clientX - rect.left) * scaleX,
944
+ y: (touch.clientY - rect.top) * scaleY
945
+ };
946
+ }
947
+ startDrawing(point) {
948
+ if (this.state.activeTool === "select") return;
949
+ this.state.isDrawing = true;
950
+ this.startPoint = point;
951
+ if (this.state.activeTool === "text") {
952
+ this.showTextInput(point);
953
+ return;
954
+ }
955
+ if (this.state.activeTool === "freehand") {
956
+ this.freehandPoints = [point];
957
+ }
958
+ }
959
+ continueDrawing(point) {
960
+ if (!this.state.isDrawing || !this.startPoint) return;
961
+ if (this.state.activeTool === "freehand") {
962
+ this.freehandPoints.push(point);
963
+ }
964
+ this.state.currentAnnotation = this.createAnnotation(this.startPoint, point);
965
+ this.render();
966
+ }
967
+ finishDrawing() {
968
+ if (!this.state.isDrawing || !this.state.currentAnnotation) {
969
+ this.state.isDrawing = false;
970
+ return;
971
+ }
972
+ if (this.isValidAnnotation(this.state.currentAnnotation)) {
973
+ this.annotations.push(this.state.currentAnnotation);
974
+ }
975
+ this.state.isDrawing = false;
976
+ this.state.currentAnnotation = null;
977
+ this.startPoint = null;
978
+ this.freehandPoints = [];
979
+ this.render();
980
+ }
981
+ isValidAnnotation(annotation) {
982
+ switch (annotation.type) {
983
+ case "rectangle":
984
+ case "ellipse":
985
+ case "highlight":
986
+ case "blur":
987
+ const bounds = annotation.bounds;
988
+ return Math.abs(bounds.width) > 5 && Math.abs(bounds.height) > 5;
989
+ case "arrow":
990
+ case "line":
991
+ const start = annotation.start;
992
+ const end = annotation.end;
993
+ const dist = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
994
+ return dist > 10;
995
+ case "freehand":
996
+ return annotation.points.length > 2;
997
+ case "text":
998
+ return !!annotation.text?.trim();
999
+ default:
1000
+ return true;
1001
+ }
1002
+ }
1003
+ createAnnotation(start, end) {
1004
+ const id = this.generateId();
1005
+ const baseStyle = { ...this.state.style };
1006
+ switch (this.state.activeTool) {
1007
+ case "rectangle":
1008
+ return {
1009
+ id,
1010
+ type: "rectangle",
1011
+ bounds: this.createBounds(start, end),
1012
+ style: baseStyle,
1013
+ timestamp: Date.now()
1014
+ };
1015
+ case "ellipse":
1016
+ return {
1017
+ id,
1018
+ type: "ellipse",
1019
+ bounds: this.createBounds(start, end),
1020
+ style: baseStyle,
1021
+ timestamp: Date.now()
1022
+ };
1023
+ case "arrow":
1024
+ return {
1025
+ id,
1026
+ type: "arrow",
1027
+ start: { ...start },
1028
+ end: { ...end },
1029
+ style: baseStyle,
1030
+ timestamp: Date.now()
1031
+ };
1032
+ case "line":
1033
+ return {
1034
+ id,
1035
+ type: "line",
1036
+ start: { ...start },
1037
+ end: { ...end },
1038
+ style: baseStyle,
1039
+ timestamp: Date.now()
1040
+ };
1041
+ case "highlight":
1042
+ return {
1043
+ id,
1044
+ type: "highlight",
1045
+ bounds: this.createBounds(start, end),
1046
+ style: { ...baseStyle, ...HIGHLIGHT_STYLE },
1047
+ timestamp: Date.now()
1048
+ };
1049
+ case "blur":
1050
+ return {
1051
+ id,
1052
+ type: "blur",
1053
+ bounds: this.createBounds(start, end),
1054
+ style: { ...baseStyle, ...BLUR_STYLE },
1055
+ blurAmount: 10,
1056
+ timestamp: Date.now()
1057
+ };
1058
+ case "freehand":
1059
+ return {
1060
+ id,
1061
+ type: "freehand",
1062
+ points: [...this.freehandPoints],
1063
+ style: baseStyle,
1064
+ timestamp: Date.now()
1065
+ };
1066
+ default:
1067
+ return {
1068
+ id,
1069
+ type: "rectangle",
1070
+ bounds: this.createBounds(start, end),
1071
+ style: baseStyle,
1072
+ timestamp: Date.now()
1073
+ };
1074
+ }
1075
+ }
1076
+ createBounds(start, end) {
1077
+ return {
1078
+ x: Math.min(start.x, end.x),
1079
+ y: Math.min(start.y, end.y),
1080
+ width: Math.abs(end.x - start.x),
1081
+ height: Math.abs(end.y - start.y)
1082
+ };
1083
+ }
1084
+ showTextInput(point) {
1085
+ if (this.textInput) {
1086
+ this.textInput.remove();
1087
+ }
1088
+ const rect = this.canvas.getBoundingClientRect();
1089
+ const scaleX = rect.width / this.canvas.width;
1090
+ const scaleY = rect.height / this.canvas.height;
1091
+ this.textInput = document.createElement("input");
1092
+ this.textInput.type = "text";
1093
+ this.textInput.className = "cf-annotation-text-input";
1094
+ this.textInput.style.left = `${rect.left + point.x * scaleX}px`;
1095
+ this.textInput.style.top = `${rect.top + point.y * scaleY}px`;
1096
+ this.textInput.style.color = this.state.style.strokeColor;
1097
+ this.textInput.style.fontSize = `${(this.state.style.fontSize || 16) * scaleY}px`;
1098
+ this.textInput.placeholder = "Tapez votre texte...";
1099
+ document.body.appendChild(this.textInput);
1100
+ this.textInput.focus();
1101
+ const handleTextSubmit = () => {
1102
+ if (this.textInput && this.textInput.value.trim()) {
1103
+ const textAnnotation = {
1104
+ id: this.generateId(),
1105
+ type: "text",
1106
+ position: point,
1107
+ text: this.textInput.value.trim(),
1108
+ style: { ...this.state.style },
1109
+ timestamp: Date.now()
1110
+ };
1111
+ this.annotations.push(textAnnotation);
1112
+ this.render();
1113
+ }
1114
+ this.textInput?.remove();
1115
+ this.textInput = null;
1116
+ this.state.isDrawing = false;
1117
+ };
1118
+ this.textInput.addEventListener("blur", handleTextSubmit);
1119
+ this.textInput.addEventListener("keydown", (e) => {
1120
+ if (e.key === "Enter") {
1121
+ handleTextSubmit();
1122
+ } else if (e.key === "Escape") {
1123
+ this.textInput?.remove();
1124
+ this.textInput = null;
1125
+ this.state.isDrawing = false;
1126
+ }
1127
+ });
1128
+ }
1129
+ generateId() {
1130
+ return `ann_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1131
+ }
1132
+ setActiveTool(tool) {
1133
+ this.state.activeTool = tool;
1134
+ this.toolbar?.setActiveTool(tool);
1135
+ if (this.canvas) {
1136
+ this.canvas.style.cursor = tool === "select" ? "default" : "crosshair";
1137
+ }
1138
+ }
1139
+ setStyle(style) {
1140
+ this.state.style = { ...this.state.style, ...style };
1141
+ this.toolbar?.setStyle(this.state.style);
1142
+ }
1143
+ undo() {
1144
+ if (this.annotations.length > 0) {
1145
+ this.annotations.pop();
1146
+ this.render();
1147
+ }
1148
+ }
1149
+ clearAll() {
1150
+ this.annotations = [];
1151
+ this.render();
1152
+ }
1153
+ save() {
1154
+ const imageData = this.getAnnotatedImage();
1155
+ this.config.onSave?.(this.annotations, imageData);
1156
+ this.close();
1157
+ }
1158
+ cancel() {
1159
+ this.config.onCancel?.();
1160
+ this.close();
1161
+ }
1162
+ render() {
1163
+ if (!this.ctx || !this.canvas) return;
1164
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
1165
+ if (this.backgroundImage) {
1166
+ this.ctx.drawImage(this.backgroundImage, 0, 0);
1167
+ }
1168
+ this.drawAnnotations(this.ctx);
1169
+ if (this.state.currentAnnotation) {
1170
+ this.drawAnnotation(this.ctx, this.state.currentAnnotation);
1171
+ }
1172
+ }
1173
+ drawAnnotations(ctx) {
1174
+ for (const annotation of this.annotations) {
1175
+ this.drawAnnotation(ctx, annotation);
1176
+ }
1177
+ }
1178
+ drawAnnotation(ctx, annotation) {
1179
+ ctx.save();
1180
+ ctx.globalAlpha = annotation.style.opacity;
1181
+ ctx.strokeStyle = annotation.style.strokeColor;
1182
+ ctx.fillStyle = annotation.style.fillColor;
1183
+ ctx.lineWidth = annotation.style.strokeWidth;
1184
+ ctx.lineCap = "round";
1185
+ ctx.lineJoin = "round";
1186
+ switch (annotation.type) {
1187
+ case "rectangle":
1188
+ this.drawRectangle(ctx, annotation.bounds, annotation.style);
1189
+ break;
1190
+ case "ellipse":
1191
+ this.drawEllipse(ctx, annotation.bounds, annotation.style);
1192
+ break;
1193
+ case "arrow":
1194
+ this.drawArrow(ctx, annotation.start, annotation.end, annotation.style);
1195
+ break;
1196
+ case "line":
1197
+ this.drawLine(ctx, annotation.start, annotation.end);
1198
+ break;
1199
+ case "highlight":
1200
+ this.drawHighlight(ctx, annotation.bounds, annotation.style);
1201
+ break;
1202
+ case "blur":
1203
+ this.drawBlur(ctx, annotation.bounds, annotation.blurAmount);
1204
+ break;
1205
+ case "text":
1206
+ this.drawText(ctx, annotation.position, annotation.text, annotation.style);
1207
+ break;
1208
+ case "freehand":
1209
+ this.drawFreehand(ctx, annotation.points);
1210
+ break;
1211
+ }
1212
+ ctx.restore();
1213
+ }
1214
+ drawRectangle(ctx, bounds, style) {
1215
+ if (style.fillColor !== "transparent") {
1216
+ ctx.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
1217
+ }
1218
+ ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
1219
+ }
1220
+ drawEllipse(ctx, bounds, style) {
1221
+ const centerX = bounds.x + bounds.width / 2;
1222
+ const centerY = bounds.y + bounds.height / 2;
1223
+ const radiusX = bounds.width / 2;
1224
+ const radiusY = bounds.height / 2;
1225
+ ctx.beginPath();
1226
+ ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);
1227
+ if (style.fillColor !== "transparent") {
1228
+ ctx.fill();
1229
+ }
1230
+ ctx.stroke();
1231
+ }
1232
+ drawArrow(ctx, start, end, style) {
1233
+ const headLength = 15 + style.strokeWidth * 2;
1234
+ const angle = Math.atan2(end.y - start.y, end.x - start.x);
1235
+ ctx.beginPath();
1236
+ ctx.moveTo(start.x, start.y);
1237
+ ctx.lineTo(end.x, end.y);
1238
+ ctx.stroke();
1239
+ ctx.beginPath();
1240
+ ctx.moveTo(end.x, end.y);
1241
+ ctx.lineTo(
1242
+ end.x - headLength * Math.cos(angle - Math.PI / 6),
1243
+ end.y - headLength * Math.sin(angle - Math.PI / 6)
1244
+ );
1245
+ ctx.lineTo(
1246
+ end.x - headLength * Math.cos(angle + Math.PI / 6),
1247
+ end.y - headLength * Math.sin(angle + Math.PI / 6)
1248
+ );
1249
+ ctx.closePath();
1250
+ ctx.fillStyle = style.strokeColor;
1251
+ ctx.fill();
1252
+ }
1253
+ drawLine(ctx, start, end) {
1254
+ ctx.beginPath();
1255
+ ctx.moveTo(start.x, start.y);
1256
+ ctx.lineTo(end.x, end.y);
1257
+ ctx.stroke();
1258
+ }
1259
+ drawHighlight(ctx, bounds, style) {
1260
+ ctx.fillStyle = style.fillColor;
1261
+ ctx.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
1262
+ }
1263
+ drawBlur(ctx, bounds, blurAmount) {
1264
+ const pixelSize = Math.max(blurAmount, 5);
1265
+ if (this.backgroundImage) {
1266
+ const tempCanvas = document.createElement("canvas");
1267
+ tempCanvas.width = bounds.width;
1268
+ tempCanvas.height = bounds.height;
1269
+ const tempCtx = tempCanvas.getContext("2d");
1270
+ tempCtx.drawImage(
1271
+ this.backgroundImage,
1272
+ bounds.x,
1273
+ bounds.y,
1274
+ bounds.width,
1275
+ bounds.height,
1276
+ 0,
1277
+ 0,
1278
+ bounds.width,
1279
+ bounds.height
1280
+ );
1281
+ const w = tempCanvas.width;
1282
+ const h = tempCanvas.height;
1283
+ tempCtx.imageSmoothingEnabled = false;
1284
+ tempCtx.drawImage(tempCanvas, 0, 0, w, h, 0, 0, w / pixelSize, h / pixelSize);
1285
+ tempCtx.drawImage(tempCanvas, 0, 0, w / pixelSize, h / pixelSize, 0, 0, w, h);
1286
+ ctx.drawImage(tempCanvas, bounds.x, bounds.y);
1287
+ }
1288
+ ctx.strokeStyle = "#999";
1289
+ ctx.lineWidth = 1;
1290
+ ctx.setLineDash([5, 5]);
1291
+ ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
1292
+ ctx.setLineDash([]);
1293
+ }
1294
+ drawText(ctx, position, text, style) {
1295
+ ctx.font = `${style.fontSize || 16}px ${style.fontFamily || "system-ui"}`;
1296
+ ctx.fillStyle = style.strokeColor;
1297
+ ctx.textBaseline = "top";
1298
+ const metrics = ctx.measureText(text);
1299
+ const padding = 4;
1300
+ ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
1301
+ ctx.fillRect(
1302
+ position.x - padding,
1303
+ position.y - padding,
1304
+ metrics.width + padding * 2,
1305
+ (style.fontSize || 16) + padding * 2
1306
+ );
1307
+ ctx.fillStyle = style.strokeColor;
1308
+ ctx.fillText(text, position.x, position.y);
1309
+ }
1310
+ drawFreehand(ctx, points) {
1311
+ if (points.length < 2) return;
1312
+ ctx.beginPath();
1313
+ ctx.moveTo(points[0].x, points[0].y);
1314
+ for (let i = 1; i < points.length; i++) {
1315
+ ctx.lineTo(points[i].x, points[i].y);
1316
+ }
1317
+ ctx.stroke();
1318
+ }
1319
+ };
1320
+ }
1321
+ });
1322
+
213
1323
  // src/widget.ts
214
1324
  function mountWidget(config2 = {}, onSubmit, opts) {
215
1325
  if (container) return;
216
1326
  onSubmitCallback = onSubmit;
217
1327
  const cfg = { ...DEFAULT_CONFIG, ...config2 };
1328
+ currentCfg = cfg;
218
1329
  container = document.createElement("div");
219
1330
  container.id = "checkflow-widget";
220
1331
  injectStyles(cfg);
@@ -223,15 +1334,22 @@ function mountWidget(config2 = {}, onSubmit, opts) {
223
1334
  const btn = container.querySelector("#cf-trigger");
224
1335
  btn?.addEventListener("click", () => openModal(cfg, opts));
225
1336
  }
1337
+ function getBackdropClass(cfg, expanded) {
1338
+ if (expanded) return "cf-backdrop--center";
1339
+ if (cfg.position === "center") return "cf-backdrop--center";
1340
+ return "cf-backdrop--corner cf-pos-" + cfg.position;
1341
+ }
226
1342
  function openModal(cfg, opts) {
227
1343
  if (!container) return;
228
1344
  const existing = document.getElementById("cf-modal-backdrop");
229
1345
  if (existing) existing.remove();
230
1346
  isExpanded = false;
231
1347
  screenshotDataUrl = null;
1348
+ annotatedImageUrl = null;
232
1349
  annotationsData = [];
233
1350
  const backdrop = document.createElement("div");
234
1351
  backdrop.id = "cf-modal-backdrop";
1352
+ backdrop.className = getBackdropClass(cfg, false);
235
1353
  backdrop.innerHTML = getModalHTML(cfg, false);
236
1354
  document.body.appendChild(backdrop);
237
1355
  backdrop.addEventListener("click", (e) => {
@@ -244,6 +1362,7 @@ function closeModal() {
244
1362
  if (backdrop) backdrop.remove();
245
1363
  isExpanded = false;
246
1364
  screenshotDataUrl = null;
1365
+ annotatedImageUrl = null;
247
1366
  annotationsData = [];
248
1367
  }
249
1368
  function expandModal(cfg, opts) {
@@ -253,6 +1372,7 @@ function expandModal(cfg, opts) {
253
1372
  const email = backdrop.querySelector("#cf-email")?.value || "";
254
1373
  const desc = backdrop.querySelector("#cf-desc")?.value || "";
255
1374
  isExpanded = true;
1375
+ backdrop.className = getBackdropClass(cfg, true);
256
1376
  backdrop.innerHTML = getModalHTML(cfg, true);
257
1377
  bindModalEvents(cfg, opts);
258
1378
  const nameEl = backdrop.querySelector("#cf-name");
@@ -270,7 +1390,9 @@ function collapseModal(cfg, opts) {
270
1390
  const desc = backdrop.querySelector("#cf-desc")?.value || "";
271
1391
  isExpanded = false;
272
1392
  screenshotDataUrl = null;
1393
+ annotatedImageUrl = null;
273
1394
  annotationsData = [];
1395
+ backdrop.className = getBackdropClass(cfg, false);
274
1396
  backdrop.innerHTML = getModalHTML(cfg, false);
275
1397
  bindModalEvents(cfg, opts);
276
1398
  const nameEl = backdrop.querySelector("#cf-name");
@@ -280,6 +1402,30 @@ function collapseModal(cfg, opts) {
280
1402
  if (emailEl) emailEl.value = email;
281
1403
  if (descEl) descEl.value = desc;
282
1404
  }
1405
+ function openAnnotationEditor(cfg, opts) {
1406
+ if (!screenshotDataUrl) return;
1407
+ const backdrop = document.getElementById("cf-modal-backdrop");
1408
+ if (backdrop) backdrop.style.display = "none";
1409
+ const editor = new AnnotationEditor({
1410
+ tools: ["rectangle", "ellipse", "arrow", "highlight", "blur", "text", "freehand"],
1411
+ defaultStyle: { strokeColor: "#FF3B30", strokeWidth: 3, fillColor: "transparent", opacity: 1, fontSize: 16, fontFamily: "system-ui, -apple-system, sans-serif" },
1412
+ onSave: (annotations, imageData) => {
1413
+ annotationsData = annotations;
1414
+ annotatedImageUrl = imageData;
1415
+ if (backdrop) {
1416
+ backdrop.style.display = "flex";
1417
+ const img = backdrop.querySelector("#cf-screenshot-img");
1418
+ if (img) img.src = annotatedImageUrl;
1419
+ const annotBtn = backdrop.querySelector("#cf-annotate-btn");
1420
+ if (annotBtn) annotBtn.textContent = `Annoter (${annotationsData.length})`;
1421
+ }
1422
+ },
1423
+ onCancel: () => {
1424
+ if (backdrop) backdrop.style.display = "flex";
1425
+ }
1426
+ });
1427
+ editor.open(screenshotDataUrl);
1428
+ }
283
1429
  function bindModalEvents(cfg, opts) {
284
1430
  const backdrop = document.getElementById("cf-modal-backdrop");
285
1431
  if (!backdrop) return;
@@ -302,33 +1448,8 @@ function bindModalEvents(cfg, opts) {
302
1448
  backdrop.querySelector("#cf-remove-screenshot")?.addEventListener("click", () => {
303
1449
  collapseModal(cfg, opts);
304
1450
  });
305
- backdrop.querySelector("#cf-highlight-btn")?.addEventListener("click", async () => {
306
- if (!opts?.onHighlight) return;
307
- backdrop.style.display = "none";
308
- try {
309
- const annotations = await opts.onHighlight();
310
- if (annotations && annotations.length > 0) {
311
- annotationsData = annotations;
312
- }
313
- } catch {
314
- }
315
- backdrop.style.display = "flex";
316
- const hlBtn = backdrop.querySelector("#cf-highlight-btn");
317
- if (hlBtn && annotationsData.length > 0) {
318
- hlBtn.textContent = `Surligner (${annotationsData.length})`;
319
- }
320
- });
321
- backdrop.querySelector("#cf-mask-btn")?.addEventListener("click", async () => {
322
- if (!opts?.onHighlight) return;
323
- backdrop.style.display = "none";
324
- try {
325
- const annotations = await opts.onHighlight();
326
- if (annotations && annotations.length > 0) {
327
- annotationsData = [...annotationsData, ...annotations];
328
- }
329
- } catch {
330
- }
331
- backdrop.style.display = "flex";
1451
+ backdrop.querySelector("#cf-annotate-btn")?.addEventListener("click", () => {
1452
+ openAnnotationEditor(cfg, opts);
332
1453
  });
333
1454
  backdrop.querySelector("#cf-submit")?.addEventListener("click", () => {
334
1455
  const desc = backdrop.querySelector("#cf-desc")?.value;
@@ -347,7 +1468,7 @@ function bindModalEvents(cfg, opts) {
347
1468
  description: desc.trim(),
348
1469
  type: "BUG",
349
1470
  priority: "MEDIUM",
350
- screenshot: screenshotDataUrl || void 0,
1471
+ screenshot: annotatedImageUrl || screenshotDataUrl || void 0,
351
1472
  annotations: annotationsData.length > 0 ? annotationsData : void 0,
352
1473
  reporter_name: name?.trim() || void 0,
353
1474
  reporter_email: email?.trim() || void 0
@@ -409,9 +1530,30 @@ function injectStyles(cfg) {
409
1530
  background:rgba(0,0,0,.45);
410
1531
  z-index:100001;
411
1532
  display:flex;
1533
+ font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
1534
+ }
1535
+ #cf-modal-backdrop.cf-backdrop--center {
412
1536
  align-items:center;
413
1537
  justify-content:center;
414
- font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
1538
+ }
1539
+ #cf-modal-backdrop.cf-backdrop--corner {
1540
+ padding:20px;
1541
+ }
1542
+ #cf-modal-backdrop.cf-pos-bottom-right {
1543
+ align-items:flex-end;
1544
+ justify-content:flex-end;
1545
+ }
1546
+ #cf-modal-backdrop.cf-pos-bottom-left {
1547
+ align-items:flex-end;
1548
+ justify-content:flex-start;
1549
+ }
1550
+ #cf-modal-backdrop.cf-pos-top-right {
1551
+ align-items:flex-start;
1552
+ justify-content:flex-end;
1553
+ }
1554
+ #cf-modal-backdrop.cf-pos-top-left {
1555
+ align-items:flex-start;
1556
+ justify-content:flex-start;
415
1557
  }
416
1558
 
417
1559
  .cf-modal {
@@ -620,6 +1762,8 @@ function getPositionCSS(pos) {
620
1762
  return "top:20px;right:20px;";
621
1763
  case "top-left":
622
1764
  return "top:20px;left:20px;";
1765
+ case "center":
1766
+ return "bottom:20px;left:50%;transform:translateX(-50%);";
623
1767
  default:
624
1768
  return "bottom:20px;right:20px;";
625
1769
  }
@@ -652,6 +1796,8 @@ function getModalHTML(cfg, expanded) {
652
1796
  </div>
653
1797
  `;
654
1798
  if (expanded && screenshotDataUrl) {
1799
+ const displayImg = annotatedImageUrl || screenshotDataUrl;
1800
+ const annotLabel = annotationsData.length > 0 ? `Annoter (${annotationsData.length})` : "Annoter";
655
1801
  return `
656
1802
  <div class="${modalClass}">
657
1803
  <div class="cf-modal-header">
@@ -660,10 +1806,9 @@ function getModalHTML(cfg, expanded) {
660
1806
  </div>
661
1807
  <div class="cf-modal-body">
662
1808
  <div class="cf-screenshot-panel">
663
- <img src="${screenshotDataUrl}" alt="Capture d'\xE9cran" />
1809
+ <img id="cf-screenshot-img" src="${displayImg}" alt="Capture d'\xE9cran" />
664
1810
  <div class="cf-screenshot-tools">
665
- <button class="cf-tool-highlight" id="cf-highlight-btn">Surligner</button>
666
- <button class="cf-tool-mask" id="cf-mask-btn">Masquer</button>
1811
+ <button class="cf-tool-highlight" id="cf-annotate-btn">${annotLabel}</button>
667
1812
  </div>
668
1813
  </div>
669
1814
  <div class="cf-form-panel">
@@ -693,10 +1838,11 @@ function getModalHTML(cfg, expanded) {
693
1838
  </div>
694
1839
  `;
695
1840
  }
696
- var DEFAULT_CONFIG, container, onSubmitCallback, screenshotDataUrl, annotationsData, isExpanded;
1841
+ var DEFAULT_CONFIG, container, onSubmitCallback, screenshotDataUrl, annotatedImageUrl, annotationsData, isExpanded, currentCfg;
697
1842
  var init_widget = __esm({
698
1843
  "src/widget.ts"() {
699
1844
  "use strict";
1845
+ init_editor();
700
1846
  DEFAULT_CONFIG = {
701
1847
  position: "bottom-right",
702
1848
  color: "#1e3a5f",
@@ -706,8 +1852,10 @@ var init_widget = __esm({
706
1852
  container = null;
707
1853
  onSubmitCallback = null;
708
1854
  screenshotDataUrl = null;
1855
+ annotatedImageUrl = null;
709
1856
  annotationsData = [];
710
1857
  isExpanded = false;
1858
+ currentCfg = { ...DEFAULT_CONFIG };
711
1859
  }
712
1860
  });
713
1861
 
@@ -777,6 +1925,143 @@ var init_screenshot = __esm({
777
1925
  }
778
1926
  });
779
1927
 
1928
+ // src/index.ts
1929
+ var index_exports = {};
1930
+ __export(index_exports, {
1931
+ captureScreenshot: () => captureScreenshot,
1932
+ destroy: () => destroy,
1933
+ hideWidget: () => hideWidget,
1934
+ init: () => init,
1935
+ sendFeedback: () => sendFeedback,
1936
+ showWidget: () => showWidget
1937
+ });
1938
+ function init(cfg) {
1939
+ config = {
1940
+ enabled: true,
1941
+ endpoint: DEFAULT_ENDPOINT,
1942
+ environment: "production",
1943
+ ...cfg
1944
+ };
1945
+ if (!config.enabled) return;
1946
+ installInterceptors();
1947
+ installNetworkInterceptor();
1948
+ if (typeof window !== "undefined" && config.widget?.showOnInit !== false) {
1949
+ mountWidget(
1950
+ config.widget,
1951
+ (data) => {
1952
+ sendFeedback({
1953
+ title: data.title,
1954
+ description: data.description,
1955
+ type: data.type,
1956
+ priority: data.priority,
1957
+ screenshot_data: data.screenshot,
1958
+ annotations: data.annotations,
1959
+ reporter_name: data.reporter_name,
1960
+ reporter_email: data.reporter_email
1961
+ });
1962
+ },
1963
+ {
1964
+ onScreenshot: () => captureScreenshot()
1965
+ }
1966
+ );
1967
+ }
1968
+ }
1969
+ async function sendFeedback(data) {
1970
+ if (!config) {
1971
+ return { success: false, error: { message: "SDK not initialized. Call init() first.", code: "NOT_INITIALIZED" } };
1972
+ }
1973
+ const context = typeof window !== "undefined" ? collectContext() : {};
1974
+ const perf = typeof window !== "undefined" ? collectPerformance() : void 0;
1975
+ const consoleLogs = getConsoleLogs();
1976
+ const jsErrors = getJavascriptErrors();
1977
+ const networkLogs = getNetworkLogs();
1978
+ const payload = {
1979
+ ...data,
1980
+ ...context,
1981
+ environment: config.environment,
1982
+ performance_metrics: perf,
1983
+ console_logs: consoleLogs.length > 0 ? consoleLogs : void 0,
1984
+ javascript_errors: jsErrors.length > 0 ? jsErrors : void 0,
1985
+ network_logs: networkLogs.length > 0 ? networkLogs : void 0,
1986
+ sdk_version: SDK_VERSION
1987
+ };
1988
+ if (data.screenshot_data) {
1989
+ payload.screenshot_data = data.screenshot_data;
1990
+ }
1991
+ if (data.annotations && data.annotations.length > 0) {
1992
+ payload.annotations = data.annotations;
1993
+ }
1994
+ if (data.reporter_name) {
1995
+ payload.reporter_name = data.reporter_name;
1996
+ }
1997
+ if (data.reporter_email) {
1998
+ payload.reporter_email = data.reporter_email;
1999
+ }
2000
+ try {
2001
+ const res = await fetch(`${config.endpoint}/sdk/feedback`, {
2002
+ method: "POST",
2003
+ headers: {
2004
+ "Content-Type": "application/json",
2005
+ "X-API-Key": config.apiKey
2006
+ },
2007
+ body: JSON.stringify(payload)
2008
+ });
2009
+ const json = await res.json();
2010
+ if (!res.ok) {
2011
+ return { success: false, error: json.error || { message: "Request failed", code: "REQUEST_FAILED" } };
2012
+ }
2013
+ return { success: true, data: json.data };
2014
+ } catch (err) {
2015
+ return { success: false, error: { message: err.message, code: "NETWORK_ERROR" } };
2016
+ }
2017
+ }
2018
+ function showWidget() {
2019
+ if (!config) return;
2020
+ mountWidget(
2021
+ config.widget,
2022
+ (data) => {
2023
+ sendFeedback({
2024
+ title: data.title,
2025
+ description: data.description,
2026
+ type: data.type,
2027
+ priority: data.priority,
2028
+ screenshot_data: data.screenshot,
2029
+ annotations: data.annotations,
2030
+ reporter_name: data.reporter_name,
2031
+ reporter_email: data.reporter_email
2032
+ });
2033
+ },
2034
+ {
2035
+ onScreenshot: () => captureScreenshot()
2036
+ }
2037
+ );
2038
+ }
2039
+ function hideWidget() {
2040
+ unmountWidget();
2041
+ }
2042
+ function destroy() {
2043
+ unmountWidget();
2044
+ clearLogs();
2045
+ clearNetworkLogs();
2046
+ clearScreenshot();
2047
+ config = null;
2048
+ }
2049
+ var SDK_VERSION, DEFAULT_ENDPOINT, config;
2050
+ var init_index = __esm({
2051
+ "src/index.ts"() {
2052
+ "use strict";
2053
+ init_collector();
2054
+ init_console_interceptor();
2055
+ init_network_interceptor();
2056
+ init_widget();
2057
+ init_screenshot();
2058
+ init_screenshot();
2059
+ SDK_VERSION = "1.1.0";
2060
+ DEFAULT_ENDPOINT = "https://api.checkflow.space/api/v1";
2061
+ config = null;
2062
+ }
2063
+ });
2064
+
780
2065
  // src/highlighter.ts
781
2066
  var highlighter_exports = {};
782
2067
  __export(highlighter_exports, {
@@ -915,148 +2200,6 @@ var init_highlighter = __esm({
915
2200
  }
916
2201
  });
917
2202
 
918
- // src/index.ts
919
- var index_exports = {};
920
- __export(index_exports, {
921
- captureScreenshot: () => captureScreenshot,
922
- destroy: () => destroy,
923
- hideWidget: () => hideWidget,
924
- init: () => init,
925
- sendFeedback: () => sendFeedback,
926
- showWidget: () => showWidget,
927
- startHighlighting: () => startHighlighting
928
- });
929
- function init(cfg) {
930
- config = {
931
- enabled: true,
932
- endpoint: DEFAULT_ENDPOINT,
933
- environment: "production",
934
- ...cfg
935
- };
936
- if (!config.enabled) return;
937
- installInterceptors();
938
- installNetworkInterceptor();
939
- if (typeof window !== "undefined" && config.widget?.showOnInit !== false) {
940
- mountWidget(
941
- config.widget,
942
- (data) => {
943
- sendFeedback({
944
- title: data.title,
945
- description: data.description,
946
- type: data.type,
947
- priority: data.priority,
948
- screenshot_data: data.screenshot,
949
- annotations: data.annotations,
950
- reporter_name: data.reporter_name,
951
- reporter_email: data.reporter_email
952
- });
953
- },
954
- {
955
- onScreenshot: () => captureScreenshot(),
956
- onHighlight: () => startHighlighting()
957
- }
958
- );
959
- }
960
- }
961
- async function sendFeedback(data) {
962
- if (!config) {
963
- return { success: false, error: { message: "SDK not initialized. Call init() first.", code: "NOT_INITIALIZED" } };
964
- }
965
- const context = typeof window !== "undefined" ? collectContext() : {};
966
- const perf = typeof window !== "undefined" ? collectPerformance() : void 0;
967
- const consoleLogs = getConsoleLogs();
968
- const jsErrors = getJavascriptErrors();
969
- const networkLogs = getNetworkLogs();
970
- const payload = {
971
- ...data,
972
- ...context,
973
- environment: config.environment,
974
- performance_metrics: perf,
975
- console_logs: consoleLogs.length > 0 ? consoleLogs : void 0,
976
- javascript_errors: jsErrors.length > 0 ? jsErrors : void 0,
977
- network_logs: networkLogs.length > 0 ? networkLogs : void 0,
978
- sdk_version: SDK_VERSION
979
- };
980
- if (data.screenshot_data) {
981
- payload.screenshot_data = data.screenshot_data;
982
- }
983
- if (data.annotations && data.annotations.length > 0) {
984
- payload.annotations = data.annotations;
985
- }
986
- if (data.reporter_name) {
987
- payload.reporter_name = data.reporter_name;
988
- }
989
- if (data.reporter_email) {
990
- payload.reporter_email = data.reporter_email;
991
- }
992
- try {
993
- const res = await fetch(`${config.endpoint}/sdk/feedback`, {
994
- method: "POST",
995
- headers: {
996
- "Content-Type": "application/json",
997
- "X-API-Key": config.apiKey
998
- },
999
- body: JSON.stringify(payload)
1000
- });
1001
- const json = await res.json();
1002
- if (!res.ok) {
1003
- return { success: false, error: json.error || { message: "Request failed", code: "REQUEST_FAILED" } };
1004
- }
1005
- return { success: true, data: json.data };
1006
- } catch (err) {
1007
- return { success: false, error: { message: err.message, code: "NETWORK_ERROR" } };
1008
- }
1009
- }
1010
- function showWidget() {
1011
- if (!config) return;
1012
- mountWidget(
1013
- config.widget,
1014
- (data) => {
1015
- sendFeedback({
1016
- title: data.title,
1017
- description: data.description,
1018
- type: data.type,
1019
- priority: data.priority,
1020
- screenshot_data: data.screenshot,
1021
- annotations: data.annotations,
1022
- reporter_name: data.reporter_name,
1023
- reporter_email: data.reporter_email
1024
- });
1025
- },
1026
- {
1027
- onScreenshot: () => captureScreenshot(),
1028
- onHighlight: () => startHighlighting()
1029
- }
1030
- );
1031
- }
1032
- function hideWidget() {
1033
- unmountWidget();
1034
- }
1035
- function destroy() {
1036
- unmountWidget();
1037
- clearLogs();
1038
- clearNetworkLogs();
1039
- clearScreenshot();
1040
- config = null;
1041
- }
1042
- var SDK_VERSION, DEFAULT_ENDPOINT, config;
1043
- var init_index = __esm({
1044
- "src/index.ts"() {
1045
- "use strict";
1046
- init_collector();
1047
- init_console_interceptor();
1048
- init_network_interceptor();
1049
- init_widget();
1050
- init_screenshot();
1051
- init_highlighter();
1052
- init_screenshot();
1053
- init_highlighter();
1054
- SDK_VERSION = "1.1.0";
1055
- DEFAULT_ENDPOINT = "https://api.checkflow.space/api/v1";
1056
- config = null;
1057
- }
1058
- });
1059
-
1060
2203
  // src/vue.ts
1061
2204
  var vue_exports = {};
1062
2205
  __export(vue_exports, {