@checkflow/sdk 1.1.3 → 1.1.5

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/react.js CHANGED
@@ -210,11 +210,1129 @@ 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
1324
+ var widget_exports = {};
1325
+ __export(widget_exports, {
1326
+ mountWidget: () => mountWidget,
1327
+ openFeedbackModal: () => openFeedbackModal,
1328
+ unmountWidget: () => unmountWidget
1329
+ });
214
1330
  function mountWidget(config2 = {}, onSubmit, opts) {
215
1331
  if (container) return;
216
1332
  onSubmitCallback = onSubmit;
1333
+ currentUser = opts?.user;
217
1334
  const cfg = { ...DEFAULT_CONFIG, ...config2 };
1335
+ currentCfg = cfg;
218
1336
  container = document.createElement("div");
219
1337
  container.id = "checkflow-widget";
220
1338
  injectStyles(cfg);
@@ -223,15 +1341,29 @@ function mountWidget(config2 = {}, onSubmit, opts) {
223
1341
  const btn = container.querySelector("#cf-trigger");
224
1342
  btn?.addEventListener("click", () => openModal(cfg, opts));
225
1343
  }
1344
+ function openFeedbackModal(config2 = {}, onSubmit, opts) {
1345
+ onSubmitCallback = onSubmit;
1346
+ currentUser = opts?.user;
1347
+ const cfg = { ...DEFAULT_CONFIG, ...config2 };
1348
+ currentCfg = cfg;
1349
+ injectStyles(cfg);
1350
+ openModal(cfg, opts);
1351
+ }
1352
+ function getBackdropClass(cfg, expanded) {
1353
+ if (expanded) return "cf-backdrop--center";
1354
+ if (cfg.position === "center") return "cf-backdrop--center";
1355
+ return "cf-backdrop--corner cf-pos-" + (cfg.position || "bottom-right");
1356
+ }
226
1357
  function openModal(cfg, opts) {
227
- if (!container) return;
228
1358
  const existing = document.getElementById("cf-modal-backdrop");
229
1359
  if (existing) existing.remove();
230
1360
  isExpanded = false;
231
1361
  screenshotDataUrl = null;
1362
+ annotatedImageUrl = null;
232
1363
  annotationsData = [];
233
1364
  const backdrop = document.createElement("div");
234
1365
  backdrop.id = "cf-modal-backdrop";
1366
+ backdrop.className = getBackdropClass(cfg, false);
235
1367
  backdrop.innerHTML = getModalHTML(cfg, false);
236
1368
  document.body.appendChild(backdrop);
237
1369
  backdrop.addEventListener("click", (e) => {
@@ -244,6 +1376,7 @@ function closeModal() {
244
1376
  if (backdrop) backdrop.remove();
245
1377
  isExpanded = false;
246
1378
  screenshotDataUrl = null;
1379
+ annotatedImageUrl = null;
247
1380
  annotationsData = [];
248
1381
  }
249
1382
  function expandModal(cfg, opts) {
@@ -253,6 +1386,7 @@ function expandModal(cfg, opts) {
253
1386
  const email = backdrop.querySelector("#cf-email")?.value || "";
254
1387
  const desc = backdrop.querySelector("#cf-desc")?.value || "";
255
1388
  isExpanded = true;
1389
+ backdrop.className = getBackdropClass(cfg, true);
256
1390
  backdrop.innerHTML = getModalHTML(cfg, true);
257
1391
  bindModalEvents(cfg, opts);
258
1392
  const nameEl = backdrop.querySelector("#cf-name");
@@ -270,7 +1404,9 @@ function collapseModal(cfg, opts) {
270
1404
  const desc = backdrop.querySelector("#cf-desc")?.value || "";
271
1405
  isExpanded = false;
272
1406
  screenshotDataUrl = null;
1407
+ annotatedImageUrl = null;
273
1408
  annotationsData = [];
1409
+ backdrop.className = getBackdropClass(cfg, false);
274
1410
  backdrop.innerHTML = getModalHTML(cfg, false);
275
1411
  bindModalEvents(cfg, opts);
276
1412
  const nameEl = backdrop.querySelector("#cf-name");
@@ -280,6 +1416,30 @@ function collapseModal(cfg, opts) {
280
1416
  if (emailEl) emailEl.value = email;
281
1417
  if (descEl) descEl.value = desc;
282
1418
  }
1419
+ function openAnnotationEditor(cfg, opts) {
1420
+ if (!screenshotDataUrl) return;
1421
+ const backdrop = document.getElementById("cf-modal-backdrop");
1422
+ if (backdrop) backdrop.style.display = "none";
1423
+ const editor = new AnnotationEditor({
1424
+ tools: ["rectangle", "ellipse", "arrow", "highlight", "blur", "text", "freehand"],
1425
+ defaultStyle: { strokeColor: "#FF3B30", strokeWidth: 3, fillColor: "transparent", opacity: 1, fontSize: 16, fontFamily: "system-ui, -apple-system, sans-serif" },
1426
+ onSave: (annotations, imageData) => {
1427
+ annotationsData = annotations;
1428
+ annotatedImageUrl = imageData;
1429
+ if (backdrop) {
1430
+ backdrop.style.display = "flex";
1431
+ const img = backdrop.querySelector("#cf-screenshot-img");
1432
+ if (img) img.src = annotatedImageUrl;
1433
+ const annotBtn = backdrop.querySelector("#cf-annotate-btn");
1434
+ if (annotBtn) annotBtn.textContent = `Annoter (${annotationsData.length})`;
1435
+ }
1436
+ },
1437
+ onCancel: () => {
1438
+ if (backdrop) backdrop.style.display = "flex";
1439
+ }
1440
+ });
1441
+ editor.open(screenshotDataUrl);
1442
+ }
283
1443
  function bindModalEvents(cfg, opts) {
284
1444
  const backdrop = document.getElementById("cf-modal-backdrop");
285
1445
  if (!backdrop) return;
@@ -302,38 +1462,15 @@ function bindModalEvents(cfg, opts) {
302
1462
  backdrop.querySelector("#cf-remove-screenshot")?.addEventListener("click", () => {
303
1463
  collapseModal(cfg, opts);
304
1464
  });
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";
1465
+ backdrop.querySelector("#cf-annotate-btn")?.addEventListener("click", () => {
1466
+ openAnnotationEditor(cfg, opts);
332
1467
  });
333
1468
  backdrop.querySelector("#cf-submit")?.addEventListener("click", () => {
334
1469
  const desc = backdrop.querySelector("#cf-desc")?.value;
335
- const name = backdrop.querySelector("#cf-name")?.value;
336
- const email = backdrop.querySelector("#cf-email")?.value;
1470
+ const nameEl = backdrop.querySelector("#cf-name");
1471
+ const emailEl = backdrop.querySelector("#cf-email");
1472
+ const name = nameEl?.value || currentUser?.name;
1473
+ const email = emailEl?.value || currentUser?.email;
337
1474
  if (!desc?.trim()) {
338
1475
  const descEl = backdrop.querySelector("#cf-desc");
339
1476
  if (descEl) {
@@ -347,7 +1484,7 @@ function bindModalEvents(cfg, opts) {
347
1484
  description: desc.trim(),
348
1485
  type: "BUG",
349
1486
  priority: "MEDIUM",
350
- screenshot: screenshotDataUrl || void 0,
1487
+ screenshot: annotatedImageUrl || screenshotDataUrl || void 0,
351
1488
  annotations: annotationsData.length > 0 ? annotationsData : void 0,
352
1489
  reporter_name: name?.trim() || void 0,
353
1490
  reporter_email: email?.trim() || void 0
@@ -375,33 +1512,93 @@ function showToast(msg) {
375
1512
  setTimeout(() => toast.remove(), 3e3);
376
1513
  }
377
1514
  function injectStyles(cfg) {
1515
+ const existingStyle = document.getElementById("cf-widget-styles");
1516
+ if (existingStyle) existingStyle.remove();
1517
+ const btn = cfg.button || {};
1518
+ const sizePreset = SIZE_PRESETS[btn.size || "default"] || SIZE_PRESETS.default;
1519
+ const variantPreset = VARIANT_PRESETS[btn.variant || "default"] || VARIANT_PRESETS.default;
1520
+ const bgColor = btn.backgroundColor || cfg.color || variantPreset.bg;
1521
+ const textColor = btn.textColor || variantPreset.color;
1522
+ const borderRadius = btn.borderRadius || "8px";
1523
+ const padding = btn.padding || sizePreset.padding;
1524
+ const fontSize = btn.fontSize || sizePreset.fontSize;
1525
+ const fontWeight = btn.fontWeight || "500";
1526
+ const border = btn.border || variantPreset.border;
1527
+ const boxShadow = btn.boxShadow || "0 2px 8px rgba(0,0,0,0.15)";
1528
+ const primaryColor = cfg.color || "#1e3a5f";
378
1529
  const style = document.createElement("style");
379
1530
  style.id = "cf-widget-styles";
380
1531
  style.textContent = `
381
1532
  @keyframes cf-toast-in { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
382
1533
  @keyframes cf-modal-in { from { opacity:0; transform:scale(.96) translateY(8px); } to { opacity:1; transform:scale(1) translateY(0); } }
383
1534
 
1535
+ /* User card styles */
1536
+ .cf-user-card {
1537
+ display:flex;
1538
+ align-items:center;
1539
+ gap:12px;
1540
+ padding:12px;
1541
+ background:#f8f9fa;
1542
+ border-radius:10px;
1543
+ margin-bottom:4px;
1544
+ }
1545
+ .cf-user-avatar {
1546
+ width:40px;
1547
+ height:40px;
1548
+ border-radius:50%;
1549
+ object-fit:cover;
1550
+ }
1551
+ .cf-user-avatar-placeholder {
1552
+ width:40px;
1553
+ height:40px;
1554
+ border-radius:50%;
1555
+ background:${primaryColor};
1556
+ color:#fff;
1557
+ display:flex;
1558
+ align-items:center;
1559
+ justify-content:center;
1560
+ font-size:16px;
1561
+ font-weight:600;
1562
+ }
1563
+ .cf-user-info {
1564
+ flex:1;
1565
+ min-width:0;
1566
+ }
1567
+ .cf-user-name {
1568
+ font-size:14px;
1569
+ font-weight:600;
1570
+ color:#1a1a2e;
1571
+ }
1572
+ .cf-user-email {
1573
+ font-size:12px;
1574
+ color:#666;
1575
+ overflow:hidden;
1576
+ text-overflow:ellipsis;
1577
+ white-space:nowrap;
1578
+ }
1579
+
1580
+ /* Trigger button - now inline, not fixed position */
384
1581
  #cf-trigger {
385
1582
  position:fixed;
386
- ${getPositionCSS(cfg.position)}
1583
+ ${getPositionCSS(cfg.position || "bottom-right")}
387
1584
  z-index:100000;
388
- background:${cfg.color};
389
- color:#fff;
390
- border:none;
391
- border-radius:50px;
392
- padding:11px 20px;
393
- font-size:14px;
394
- font-weight:500;
1585
+ background:${bgColor};
1586
+ color:${textColor};
1587
+ border:${border};
1588
+ border-radius:${borderRadius};
1589
+ padding:${padding};
1590
+ font-size:${fontSize};
1591
+ font-weight:${fontWeight};
395
1592
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
396
1593
  cursor:pointer;
397
- box-shadow:0 4px 16px rgba(0,0,0,.18);
398
- display:flex;
1594
+ box-shadow:${boxShadow};
1595
+ display:inline-flex;
399
1596
  align-items:center;
400
1597
  gap:8px;
401
- transition:transform .15s,box-shadow .15s;
1598
+ transition:transform .15s,box-shadow .15s,opacity .15s;
402
1599
  }
403
- #cf-trigger:hover { transform:scale(1.04); box-shadow:0 6px 24px rgba(0,0,0,.22); }
404
- #cf-trigger svg { width:18px; height:18px; }
1600
+ #cf-trigger:hover { opacity:0.9; }
1601
+ #cf-trigger svg { width:16px; height:16px; flex-shrink:0; }
405
1602
 
406
1603
  #cf-modal-backdrop {
407
1604
  position:fixed;
@@ -409,9 +1606,30 @@ function injectStyles(cfg) {
409
1606
  background:rgba(0,0,0,.45);
410
1607
  z-index:100001;
411
1608
  display:flex;
1609
+ font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
1610
+ }
1611
+ #cf-modal-backdrop.cf-backdrop--center {
412
1612
  align-items:center;
413
1613
  justify-content:center;
414
- font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
1614
+ }
1615
+ #cf-modal-backdrop.cf-backdrop--corner {
1616
+ padding:20px;
1617
+ }
1618
+ #cf-modal-backdrop.cf-pos-bottom-right {
1619
+ align-items:flex-end;
1620
+ justify-content:flex-end;
1621
+ }
1622
+ #cf-modal-backdrop.cf-pos-bottom-left {
1623
+ align-items:flex-end;
1624
+ justify-content:flex-start;
1625
+ }
1626
+ #cf-modal-backdrop.cf-pos-top-right {
1627
+ align-items:flex-start;
1628
+ justify-content:flex-end;
1629
+ }
1630
+ #cf-modal-backdrop.cf-pos-top-left {
1631
+ align-items:flex-start;
1632
+ justify-content:flex-start;
415
1633
  }
416
1634
 
417
1635
  .cf-modal {
@@ -481,7 +1699,7 @@ function injectStyles(cfg) {
481
1699
  transition:all .15s;
482
1700
  }
483
1701
  .cf-tool-highlight {
484
- background:${cfg.color};
1702
+ background:${primaryColor};
485
1703
  color:#fff;
486
1704
  }
487
1705
  .cf-tool-highlight:hover { opacity:.9; }
@@ -529,8 +1747,8 @@ function injectStyles(cfg) {
529
1747
  }
530
1748
  .cf-field input::placeholder, .cf-field textarea::placeholder { color:#bbb; }
531
1749
  .cf-field input:focus, .cf-field textarea:focus {
532
- border-color:${cfg.color};
533
- box-shadow:0 0 0 3px ${cfg.color}18;
1750
+ border-color:${primaryColor};
1751
+ box-shadow:0 0 0 3px ${primaryColor}18;
534
1752
  }
535
1753
  .cf-field textarea { resize:vertical; min-height:100px; }
536
1754
 
@@ -569,7 +1787,7 @@ function injectStyles(cfg) {
569
1787
  padding:13px;
570
1788
  border:none;
571
1789
  border-radius:10px;
572
- background:${cfg.color};
1790
+ background:${primaryColor};
573
1791
  color:#fff;
574
1792
  font-size:15px;
575
1793
  font-weight:600;
@@ -577,7 +1795,7 @@ function injectStyles(cfg) {
577
1795
  font-family:inherit;
578
1796
  transition:all .15s;
579
1797
  }
580
- .cf-submit-btn:hover { opacity:.92; transform:translateY(-1px); box-shadow:0 4px 12px ${cfg.color}40; }
1798
+ .cf-submit-btn:hover { opacity:.92; transform:translateY(-1px); box-shadow:0 4px 12px ${primaryColor}40; }
581
1799
  .cf-submit-btn:disabled { opacity:.5; cursor:not-allowed; transform:none; }
582
1800
 
583
1801
  .cf-cancel-btn {
@@ -608,7 +1826,7 @@ function injectStyles(cfg) {
608
1826
  text-decoration:none;
609
1827
  font-weight:500;
610
1828
  }
611
- .cf-footer a:hover { color:${cfg.color}; }
1829
+ .cf-footer a:hover { color:${primaryColor}; }
612
1830
  `;
613
1831
  document.head.appendChild(style);
614
1832
  }
@@ -620,15 +1838,22 @@ function getPositionCSS(pos) {
620
1838
  return "top:20px;right:20px;";
621
1839
  case "top-left":
622
1840
  return "top:20px;left:20px;";
1841
+ case "center":
1842
+ return "bottom:20px;left:50%;transform:translateX(-50%);";
623
1843
  default:
624
1844
  return "bottom:20px;right:20px;";
625
1845
  }
626
1846
  }
627
1847
  function getTriggerHTML(cfg) {
1848
+ const btn = cfg.button || {};
1849
+ const text = btn.text || cfg.text || "Report Bug";
1850
+ const showIcon = btn.showIcon !== false;
1851
+ const customClass = btn.className || "";
1852
+ const icon = showIcon ? `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>` : "";
628
1853
  return `
629
- <button id="cf-trigger">
630
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
631
- ${cfg.text}
1854
+ <button id="cf-trigger" class="${customClass}">
1855
+ ${icon}
1856
+ ${text}
632
1857
  </button>
633
1858
  `;
634
1859
  }
@@ -637,21 +1862,37 @@ function getCheckflowLogo() {
637
1862
  }
638
1863
  function getModalHTML(cfg, expanded) {
639
1864
  const modalClass = expanded ? "cf-modal cf-modal--expanded" : "cf-modal cf-modal--compact";
640
- const formFields = `
1865
+ const hasUser = currentUser && (currentUser.name || currentUser.email);
1866
+ const userInfoCard = hasUser ? `
1867
+ <div class="cf-user-card">
1868
+ ${currentUser?.avatar ? `<img class="cf-user-avatar" src="${currentUser.avatar}" alt="${currentUser.name || "User"}" />` : `<div class="cf-user-avatar-placeholder">${(currentUser?.name || currentUser?.email || "U").charAt(0).toUpperCase()}</div>`}
1869
+ <div class="cf-user-info">
1870
+ ${currentUser?.name ? `<div class="cf-user-name">${currentUser.name}</div>` : ""}
1871
+ ${currentUser?.email ? `<div class="cf-user-email">${currentUser.email}</div>` : ""}
1872
+ </div>
1873
+ </div>
1874
+ ` : "";
1875
+ const nameEmailFields = hasUser ? "" : `
641
1876
  <div class="cf-field">
642
- <label>Nom <span>(obligatoire)</span></label>
1877
+ <label>Nom <span>(optionnel)</span></label>
643
1878
  <input id="cf-name" type="text" placeholder="Votre nom" />
644
1879
  </div>
645
1880
  <div class="cf-field">
646
- <label>Email <span>(obligatoire)</span></label>
1881
+ <label>Email <span>(optionnel)</span></label>
647
1882
  <input id="cf-email" type="email" placeholder="votre.email@exemple.com" />
648
1883
  </div>
1884
+ `;
1885
+ const formFields = `
1886
+ ${userInfoCard}
1887
+ ${nameEmailFields}
649
1888
  <div class="cf-field">
650
1889
  <label>Description <span>(obligatoire)</span></label>
651
1890
  <textarea id="cf-desc" placeholder="Quel est le probl\xE8me ? Que vous attendiez-vous \xE0 voir ?"></textarea>
652
1891
  </div>
653
1892
  `;
654
1893
  if (expanded && screenshotDataUrl) {
1894
+ const displayImg = annotatedImageUrl || screenshotDataUrl;
1895
+ const annotLabel = annotationsData.length > 0 ? `Annoter (${annotationsData.length})` : "Annoter";
655
1896
  return `
656
1897
  <div class="${modalClass}">
657
1898
  <div class="cf-modal-header">
@@ -660,10 +1901,9 @@ function getModalHTML(cfg, expanded) {
660
1901
  </div>
661
1902
  <div class="cf-modal-body">
662
1903
  <div class="cf-screenshot-panel">
663
- <img src="${screenshotDataUrl}" alt="Capture d'\xE9cran" />
1904
+ <img id="cf-screenshot-img" src="${displayImg}" alt="Capture d'\xE9cran" />
664
1905
  <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>
1906
+ <button class="cf-tool-highlight" id="cf-annotate-btn">${annotLabel}</button>
667
1907
  </div>
668
1908
  </div>
669
1909
  <div class="cf-form-panel">
@@ -693,21 +1933,53 @@ function getModalHTML(cfg, expanded) {
693
1933
  </div>
694
1934
  `;
695
1935
  }
696
- var DEFAULT_CONFIG, container, onSubmitCallback, screenshotDataUrl, annotationsData, isExpanded;
1936
+ var DEFAULT_BUTTON_CONFIG, SIZE_PRESETS, VARIANT_PRESETS, DEFAULT_CONFIG, container, onSubmitCallback, screenshotDataUrl, annotatedImageUrl, annotationsData, isExpanded, currentCfg, currentUser;
697
1937
  var init_widget = __esm({
698
1938
  "src/widget.ts"() {
699
1939
  "use strict";
1940
+ init_editor();
1941
+ DEFAULT_BUTTON_CONFIG = {
1942
+ text: "Report Bug",
1943
+ backgroundColor: "#1e3a5f",
1944
+ textColor: "#ffffff",
1945
+ borderRadius: "8px",
1946
+ padding: "10px 18px",
1947
+ fontSize: "14px",
1948
+ fontWeight: "500",
1949
+ border: "none",
1950
+ boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
1951
+ className: "",
1952
+ showIcon: true,
1953
+ size: "default",
1954
+ variant: "default"
1955
+ };
1956
+ SIZE_PRESETS = {
1957
+ xs: { padding: "6px 10px", fontSize: "12px" },
1958
+ sm: { padding: "8px 14px", fontSize: "13px" },
1959
+ default: { padding: "10px 18px", fontSize: "14px" },
1960
+ lg: { padding: "12px 24px", fontSize: "15px" }
1961
+ };
1962
+ VARIANT_PRESETS = {
1963
+ default: { bg: "#1e3a5f", color: "#ffffff", border: "none" },
1964
+ outline: { bg: "transparent", color: "#1e3a5f", border: "1px solid #1e3a5f" },
1965
+ ghost: { bg: "transparent", color: "#1e3a5f", border: "none" },
1966
+ link: { bg: "transparent", color: "#1e3a5f", border: "none" }
1967
+ };
700
1968
  DEFAULT_CONFIG = {
701
1969
  position: "bottom-right",
702
1970
  color: "#1e3a5f",
703
1971
  text: "Report Bug",
704
- showOnInit: true
1972
+ showOnInit: true,
1973
+ button: DEFAULT_BUTTON_CONFIG
705
1974
  };
706
1975
  container = null;
707
1976
  onSubmitCallback = null;
708
1977
  screenshotDataUrl = null;
1978
+ annotatedImageUrl = null;
709
1979
  annotationsData = [];
710
1980
  isExpanded = false;
1981
+ currentCfg = { ...DEFAULT_CONFIG };
1982
+ currentUser = void 0;
711
1983
  }
712
1984
  });
713
1985
 
@@ -777,154 +2049,18 @@ var init_screenshot = __esm({
777
2049
  }
778
2050
  });
779
2051
 
780
- // src/highlighter.ts
781
- var highlighter_exports = {};
782
- __export(highlighter_exports, {
783
- isHighlighting: () => isHighlighting,
784
- startHighlighting: () => startHighlighting
785
- });
786
- function getSelector(el) {
787
- if (el.id) return `#${el.id}`;
788
- const parts = [];
789
- let current = el;
790
- while (current && current !== document.body) {
791
- let sel = current.tagName.toLowerCase();
792
- if (current.id) {
793
- parts.unshift(`#${current.id}`);
794
- break;
795
- }
796
- if (current.className && typeof current.className === "string") {
797
- const cls = current.className.trim().split(/\s+/).slice(0, 2).join(".");
798
- if (cls) sel += `.${cls}`;
799
- }
800
- const parent = current.parentElement;
801
- if (parent) {
802
- const siblings = Array.from(parent.children).filter((c) => c.tagName === current.tagName);
803
- if (siblings.length > 1) sel += `:nth-child(${Array.from(parent.children).indexOf(current) + 1})`;
804
- }
805
- parts.unshift(sel);
806
- current = current.parentElement;
807
- }
808
- return parts.join(" > ");
809
- }
810
- function createOverlay() {
811
- overlay = document.createElement("div");
812
- overlay.id = "cf-highlight-overlay";
813
- overlay.style.cssText = "position:fixed;top:0;left:0;width:100%;height:100%;z-index:99999;cursor:crosshair;";
814
- const toolbar = document.createElement("div");
815
- toolbar.style.cssText = "position:fixed;top:12px;left:50%;transform:translateX(-50%);z-index:100002;background:#1e3a5f;color:#fff;padding:8px 16px;border-radius:10px;font-family:system-ui;font-size:13px;display:flex;align-items:center;gap:12px;box-shadow:0 4px 20px rgba(0,0,0,.25);";
816
- toolbar.innerHTML = `
817
- <span>Click elements to highlight them</span>
818
- <button id="cf-hl-done" style="background:#10b981;color:#fff;border:none;border-radius:6px;padding:6px 14px;font-size:12px;cursor:pointer;font-weight:500;">Done (${highlights.length})</button>
819
- <button id="cf-hl-cancel" style="background:transparent;color:#fff;border:1px solid rgba(255,255,255,.3);border-radius:6px;padding:6px 14px;font-size:12px;cursor:pointer;">Cancel</button>
820
- `;
821
- overlay.appendChild(toolbar);
822
- document.body.appendChild(overlay);
823
- overlay.addEventListener("click", handleClick);
824
- overlay.addEventListener("mousemove", handleHover);
825
- document.getElementById("cf-hl-done")?.addEventListener("click", finishHighlighting);
826
- document.getElementById("cf-hl-cancel")?.addEventListener("click", cancelHighlighting);
827
- }
828
- function handleHover(e) {
829
- if (!hoverBox) {
830
- hoverBox = document.createElement("div");
831
- hoverBox.style.cssText = "position:fixed;border:2px solid #0c66e4;background:rgba(12,102,228,0.08);pointer-events:none;z-index:100001;border-radius:3px;transition:all 0.05s;";
832
- document.body.appendChild(hoverBox);
833
- }
834
- const target = document.elementFromPoint(e.clientX, e.clientY);
835
- if (target && target !== overlay && !overlay?.contains(target)) {
836
- const rect = target.getBoundingClientRect();
837
- hoverBox.style.left = rect.left + "px";
838
- hoverBox.style.top = rect.top + "px";
839
- hoverBox.style.width = rect.width + "px";
840
- hoverBox.style.height = rect.height + "px";
841
- hoverBox.style.display = "block";
842
- } else {
843
- hoverBox.style.display = "none";
844
- }
845
- }
846
- function handleClick(e) {
847
- e.preventDefault();
848
- e.stopPropagation();
849
- const target = document.elementFromPoint(e.clientX, e.clientY);
850
- if (!target || target === overlay || overlay?.contains(target)) return;
851
- const rect = target.getBoundingClientRect();
852
- highlights.push({ el: target, rect });
853
- const marker = document.createElement("div");
854
- marker.className = "cf-hl-marker";
855
- marker.style.cssText = `position:fixed;left:${rect.left}px;top:${rect.top}px;width:${rect.width}px;height:${rect.height}px;border:2px solid #ae2a19;background:rgba(174,42,25,0.12);z-index:100001;pointer-events:none;border-radius:3px;`;
856
- const badge = document.createElement("div");
857
- badge.style.cssText = "position:absolute;top:-10px;right:-10px;background:#ae2a19;color:#fff;width:20px;height:20px;border-radius:50%;font-size:11px;display:flex;align-items:center;justify-content:center;font-family:system-ui;font-weight:600;";
858
- badge.textContent = String(highlights.length);
859
- marker.appendChild(badge);
860
- overlay?.appendChild(marker);
861
- const doneBtn = document.getElementById("cf-hl-done");
862
- if (doneBtn) doneBtn.textContent = `Done (${highlights.length})`;
863
- }
864
- function finishHighlighting() {
865
- const annotations = highlights.map((h) => ({
866
- selector: getSelector(h.el),
867
- tagName: h.el.tagName.toLowerCase(),
868
- text: h.el.textContent?.slice(0, 100) || void 0,
869
- rect: { x: Math.round(h.rect.x), y: Math.round(h.rect.y), width: Math.round(h.rect.width), height: Math.round(h.rect.height) },
870
- note: h.note
871
- }));
872
- cleanup();
873
- onDoneCallback?.(annotations);
874
- }
875
- function cancelHighlighting() {
876
- cleanup();
877
- onDoneCallback?.([]);
878
- }
879
- function cleanup() {
880
- if (hoverBox) {
881
- hoverBox.remove();
882
- hoverBox = null;
883
- }
884
- if (overlay) {
885
- overlay.remove();
886
- overlay = null;
887
- }
888
- highlights = [];
889
- isActive = false;
890
- }
891
- function startHighlighting() {
892
- return new Promise((resolve) => {
893
- if (isActive) {
894
- resolve([]);
895
- return;
896
- }
897
- isActive = true;
898
- highlights = [];
899
- onDoneCallback = resolve;
900
- createOverlay();
901
- });
902
- }
903
- function isHighlighting() {
904
- return isActive;
905
- }
906
- var overlay, highlights, isActive, onDoneCallback, hoverBox;
907
- var init_highlighter = __esm({
908
- "src/highlighter.ts"() {
909
- "use strict";
910
- overlay = null;
911
- highlights = [];
912
- isActive = false;
913
- onDoneCallback = null;
914
- hoverBox = null;
915
- }
916
- });
917
-
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
2052
+ // src/index.ts
2053
+ var index_exports = {};
2054
+ __export(index_exports, {
2055
+ captureScreenshot: () => captureScreenshot,
2056
+ clearUser: () => clearUser,
2057
+ destroy: () => destroy,
2058
+ hideWidget: () => hideWidget,
2059
+ init: () => init,
2060
+ openFeedbackModal: () => openFeedbackModal2,
2061
+ sendFeedback: () => sendFeedback,
2062
+ setUser: () => setUser,
2063
+ showWidget: () => showWidget
928
2064
  });
929
2065
  function init(cfg) {
930
2066
  config = {
@@ -953,7 +2089,7 @@ function init(cfg) {
953
2089
  },
954
2090
  {
955
2091
  onScreenshot: () => captureScreenshot(),
956
- onHighlight: () => startHighlighting()
2092
+ user: config.user
957
2093
  }
958
2094
  );
959
2095
  }
@@ -983,12 +2119,22 @@ async function sendFeedback(data) {
983
2119
  if (data.annotations && data.annotations.length > 0) {
984
2120
  payload.annotations = data.annotations;
985
2121
  }
986
- if (data.reporter_name) {
2122
+ if (config.user?.name) {
2123
+ payload.reporter_name = config.user.name;
2124
+ } else if (data.reporter_name) {
987
2125
  payload.reporter_name = data.reporter_name;
988
2126
  }
989
- if (data.reporter_email) {
2127
+ if (config.user?.email) {
2128
+ payload.reporter_email = config.user.email;
2129
+ } else if (data.reporter_email) {
990
2130
  payload.reporter_email = data.reporter_email;
991
2131
  }
2132
+ if (config.user?.id) {
2133
+ payload.user_id = config.user.id;
2134
+ }
2135
+ if (config.user?.avatar) {
2136
+ payload.user_avatar = config.user.avatar;
2137
+ }
992
2138
  try {
993
2139
  const res = await fetch(`${config.endpoint}/sdk/feedback`, {
994
2140
  method: "POST",
@@ -1025,10 +2171,44 @@ function showWidget() {
1025
2171
  },
1026
2172
  {
1027
2173
  onScreenshot: () => captureScreenshot(),
1028
- onHighlight: () => startHighlighting()
2174
+ user: config.user
1029
2175
  }
1030
2176
  );
1031
2177
  }
2178
+ function setUser(user) {
2179
+ if (config) {
2180
+ config.user = user;
2181
+ }
2182
+ }
2183
+ function clearUser() {
2184
+ if (config) {
2185
+ config.user = void 0;
2186
+ }
2187
+ }
2188
+ function openFeedbackModal2() {
2189
+ if (!config) return;
2190
+ Promise.resolve().then(() => (init_widget(), widget_exports)).then(({ openFeedbackModal: openModal2 }) => {
2191
+ openModal2(
2192
+ config.widget,
2193
+ (data) => {
2194
+ sendFeedback({
2195
+ title: data.title,
2196
+ description: data.description,
2197
+ type: data.type,
2198
+ priority: data.priority,
2199
+ screenshot_data: data.screenshot,
2200
+ annotations: data.annotations,
2201
+ reporter_name: data.reporter_name,
2202
+ reporter_email: data.reporter_email
2203
+ });
2204
+ },
2205
+ {
2206
+ onScreenshot: () => captureScreenshot(),
2207
+ user: config.user
2208
+ }
2209
+ );
2210
+ });
2211
+ }
1032
2212
  function hideWidget() {
1033
2213
  unmountWidget();
1034
2214
  }
@@ -1048,32 +2228,189 @@ var init_index = __esm({
1048
2228
  init_network_interceptor();
1049
2229
  init_widget();
1050
2230
  init_screenshot();
1051
- init_highlighter();
1052
2231
  init_screenshot();
1053
- init_highlighter();
1054
2232
  SDK_VERSION = "1.1.0";
1055
2233
  DEFAULT_ENDPOINT = "https://api.checkflow.space/api/v1";
1056
2234
  config = null;
1057
2235
  }
1058
2236
  });
1059
2237
 
2238
+ // src/highlighter.ts
2239
+ var highlighter_exports = {};
2240
+ __export(highlighter_exports, {
2241
+ isHighlighting: () => isHighlighting,
2242
+ startHighlighting: () => startHighlighting
2243
+ });
2244
+ function getSelector(el) {
2245
+ if (el.id) return `#${el.id}`;
2246
+ const parts = [];
2247
+ let current = el;
2248
+ while (current && current !== document.body) {
2249
+ let sel = current.tagName.toLowerCase();
2250
+ if (current.id) {
2251
+ parts.unshift(`#${current.id}`);
2252
+ break;
2253
+ }
2254
+ if (current.className && typeof current.className === "string") {
2255
+ const cls = current.className.trim().split(/\s+/).slice(0, 2).join(".");
2256
+ if (cls) sel += `.${cls}`;
2257
+ }
2258
+ const parent = current.parentElement;
2259
+ if (parent) {
2260
+ const siblings = Array.from(parent.children).filter((c) => c.tagName === current.tagName);
2261
+ if (siblings.length > 1) sel += `:nth-child(${Array.from(parent.children).indexOf(current) + 1})`;
2262
+ }
2263
+ parts.unshift(sel);
2264
+ current = current.parentElement;
2265
+ }
2266
+ return parts.join(" > ");
2267
+ }
2268
+ function createOverlay() {
2269
+ overlay = document.createElement("div");
2270
+ overlay.id = "cf-highlight-overlay";
2271
+ overlay.style.cssText = "position:fixed;top:0;left:0;width:100%;height:100%;z-index:99999;cursor:crosshair;";
2272
+ const toolbar = document.createElement("div");
2273
+ toolbar.style.cssText = "position:fixed;top:12px;left:50%;transform:translateX(-50%);z-index:100002;background:#1e3a5f;color:#fff;padding:8px 16px;border-radius:10px;font-family:system-ui;font-size:13px;display:flex;align-items:center;gap:12px;box-shadow:0 4px 20px rgba(0,0,0,.25);";
2274
+ toolbar.innerHTML = `
2275
+ <span>Click elements to highlight them</span>
2276
+ <button id="cf-hl-done" style="background:#10b981;color:#fff;border:none;border-radius:6px;padding:6px 14px;font-size:12px;cursor:pointer;font-weight:500;">Done (${highlights.length})</button>
2277
+ <button id="cf-hl-cancel" style="background:transparent;color:#fff;border:1px solid rgba(255,255,255,.3);border-radius:6px;padding:6px 14px;font-size:12px;cursor:pointer;">Cancel</button>
2278
+ `;
2279
+ overlay.appendChild(toolbar);
2280
+ document.body.appendChild(overlay);
2281
+ overlay.addEventListener("click", handleClick);
2282
+ overlay.addEventListener("mousemove", handleHover);
2283
+ document.getElementById("cf-hl-done")?.addEventListener("click", finishHighlighting);
2284
+ document.getElementById("cf-hl-cancel")?.addEventListener("click", cancelHighlighting);
2285
+ }
2286
+ function handleHover(e) {
2287
+ if (!hoverBox) {
2288
+ hoverBox = document.createElement("div");
2289
+ hoverBox.style.cssText = "position:fixed;border:2px solid #0c66e4;background:rgba(12,102,228,0.08);pointer-events:none;z-index:100001;border-radius:3px;transition:all 0.05s;";
2290
+ document.body.appendChild(hoverBox);
2291
+ }
2292
+ const target = document.elementFromPoint(e.clientX, e.clientY);
2293
+ if (target && target !== overlay && !overlay?.contains(target)) {
2294
+ const rect = target.getBoundingClientRect();
2295
+ hoverBox.style.left = rect.left + "px";
2296
+ hoverBox.style.top = rect.top + "px";
2297
+ hoverBox.style.width = rect.width + "px";
2298
+ hoverBox.style.height = rect.height + "px";
2299
+ hoverBox.style.display = "block";
2300
+ } else {
2301
+ hoverBox.style.display = "none";
2302
+ }
2303
+ }
2304
+ function handleClick(e) {
2305
+ e.preventDefault();
2306
+ e.stopPropagation();
2307
+ const target = document.elementFromPoint(e.clientX, e.clientY);
2308
+ if (!target || target === overlay || overlay?.contains(target)) return;
2309
+ const rect = target.getBoundingClientRect();
2310
+ highlights.push({ el: target, rect });
2311
+ const marker = document.createElement("div");
2312
+ marker.className = "cf-hl-marker";
2313
+ marker.style.cssText = `position:fixed;left:${rect.left}px;top:${rect.top}px;width:${rect.width}px;height:${rect.height}px;border:2px solid #ae2a19;background:rgba(174,42,25,0.12);z-index:100001;pointer-events:none;border-radius:3px;`;
2314
+ const badge = document.createElement("div");
2315
+ badge.style.cssText = "position:absolute;top:-10px;right:-10px;background:#ae2a19;color:#fff;width:20px;height:20px;border-radius:50%;font-size:11px;display:flex;align-items:center;justify-content:center;font-family:system-ui;font-weight:600;";
2316
+ badge.textContent = String(highlights.length);
2317
+ marker.appendChild(badge);
2318
+ overlay?.appendChild(marker);
2319
+ const doneBtn = document.getElementById("cf-hl-done");
2320
+ if (doneBtn) doneBtn.textContent = `Done (${highlights.length})`;
2321
+ }
2322
+ function finishHighlighting() {
2323
+ const annotations = highlights.map((h) => ({
2324
+ selector: getSelector(h.el),
2325
+ tagName: h.el.tagName.toLowerCase(),
2326
+ text: h.el.textContent?.slice(0, 100) || void 0,
2327
+ rect: { x: Math.round(h.rect.x), y: Math.round(h.rect.y), width: Math.round(h.rect.width), height: Math.round(h.rect.height) },
2328
+ note: h.note
2329
+ }));
2330
+ cleanup();
2331
+ onDoneCallback?.(annotations);
2332
+ }
2333
+ function cancelHighlighting() {
2334
+ cleanup();
2335
+ onDoneCallback?.([]);
2336
+ }
2337
+ function cleanup() {
2338
+ if (hoverBox) {
2339
+ hoverBox.remove();
2340
+ hoverBox = null;
2341
+ }
2342
+ if (overlay) {
2343
+ overlay.remove();
2344
+ overlay = null;
2345
+ }
2346
+ highlights = [];
2347
+ isActive = false;
2348
+ }
2349
+ function startHighlighting() {
2350
+ return new Promise((resolve) => {
2351
+ if (isActive) {
2352
+ resolve([]);
2353
+ return;
2354
+ }
2355
+ isActive = true;
2356
+ highlights = [];
2357
+ onDoneCallback = resolve;
2358
+ createOverlay();
2359
+ });
2360
+ }
2361
+ function isHighlighting() {
2362
+ return isActive;
2363
+ }
2364
+ var overlay, highlights, isActive, onDoneCallback, hoverBox;
2365
+ var init_highlighter = __esm({
2366
+ "src/highlighter.ts"() {
2367
+ "use strict";
2368
+ overlay = null;
2369
+ highlights = [];
2370
+ isActive = false;
2371
+ onDoneCallback = null;
2372
+ hoverBox = null;
2373
+ }
2374
+ });
2375
+
1060
2376
  // src/react.ts
1061
2377
  var react_exports = {};
1062
2378
  __export(react_exports, {
2379
+ clearUser: () => clearUser2,
1063
2380
  destroyCheckflow: () => destroyCheckflow,
2381
+ getFeedbackButtonHandler: () => getFeedbackButtonHandler,
2382
+ openFeedbackModal: () => openFeedbackModal3,
2383
+ setUser: () => setUser2,
1064
2384
  useCheckflowFeedback: () => useCheckflowFeedback,
1065
2385
  useCheckflowInit: () => useCheckflowInit
1066
2386
  });
1067
2387
  module.exports = __toCommonJS(react_exports);
1068
2388
  var _initialized = false;
2389
+ var _config = null;
1069
2390
  function useCheckflowInit(config2) {
1070
2391
  if (typeof window === "undefined") return;
1071
2392
  if (_initialized) return;
1072
2393
  _initialized = true;
2394
+ _config = config2;
1073
2395
  Promise.resolve().then(() => (init_index(), index_exports)).then(({ init: init2 }) => {
1074
2396
  init2(config2);
1075
2397
  });
1076
2398
  }
2399
+ async function setUser2(user) {
2400
+ if (typeof window === "undefined") return;
2401
+ const { setUser: sdkSetUser } = await Promise.resolve().then(() => (init_index(), index_exports));
2402
+ sdkSetUser(user);
2403
+ }
2404
+ async function clearUser2() {
2405
+ if (typeof window === "undefined") return;
2406
+ const { clearUser: sdkClearUser } = await Promise.resolve().then(() => (init_index(), index_exports));
2407
+ sdkClearUser();
2408
+ }
2409
+ async function openFeedbackModal3() {
2410
+ if (typeof window === "undefined") return;
2411
+ const { openFeedbackModal: sdkOpenModal } = await Promise.resolve().then(() => (init_index(), index_exports));
2412
+ sdkOpenModal();
2413
+ }
1077
2414
  function useCheckflowFeedback() {
1078
2415
  return {
1079
2416
  send: async (data) => {
@@ -1100,7 +2437,10 @@ function useCheckflowFeedback() {
1100
2437
  if (typeof window === "undefined") return;
1101
2438
  const { hideWidget: hideWidget2 } = await Promise.resolve().then(() => (init_index(), index_exports));
1102
2439
  hideWidget2();
1103
- }
2440
+ },
2441
+ openModal: openFeedbackModal3,
2442
+ setUser: setUser2,
2443
+ clearUser: clearUser2
1104
2444
  };
1105
2445
  }
1106
2446
  function destroyCheckflow() {
@@ -1108,11 +2448,22 @@ function destroyCheckflow() {
1108
2448
  Promise.resolve().then(() => (init_index(), index_exports)).then(({ destroy: destroy2 }) => {
1109
2449
  destroy2();
1110
2450
  _initialized = false;
2451
+ _config = null;
1111
2452
  });
1112
2453
  }
2454
+ function getFeedbackButtonHandler(onClick) {
2455
+ return () => {
2456
+ onClick?.();
2457
+ openFeedbackModal3();
2458
+ };
2459
+ }
1113
2460
  // Annotate the CommonJS export names for ESM import in node:
1114
2461
  0 && (module.exports = {
2462
+ clearUser,
1115
2463
  destroyCheckflow,
2464
+ getFeedbackButtonHandler,
2465
+ openFeedbackModal,
2466
+ setUser,
1116
2467
  useCheckflowFeedback,
1117
2468
  useCheckflowInit
1118
2469
  });